1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.subtle; 18 19 import com.google.crypto.tink.annotations.Alpha; 20 import com.google.crypto.tink.internal.Curve25519; 21 import com.google.crypto.tink.internal.Field25519; 22 import java.security.InvalidKeyException; 23 import java.util.Arrays; 24 25 /** 26 * Defines <a href="https://cr.yp.to/ecdh/curve25519-20060209.pdf">the ECDH Curve25519 function</a>, 27 * also known as the X25519 function. 28 * 29 * <p>This implementation is based on <a 30 * href="https://github.com/agl/curve25519-donna/blob/master/curve25519-donna.c">curve255-donna</a>. 31 * 32 * <h3>Warning</h3> 33 * 34 * <p>Do not use this API or any other APIs including fields and methods marked with the @Alpha 35 * annotation. They can be modified in any way, or even removed, at any time. They are in the 36 * package, but not for official, production release, but only for testing. 37 * 38 * <h3>Usage</h3> 39 * 40 * <pre> 41 * Alice: 42 * byte[] privateKeyA = X25519.generatePrivateKey(); 43 * byte[] publicKeyA = X25519.publicFromPrivate(privateKeyA); 44 * Bob: 45 * byte[] privateKeyB = X25519.generatePrivateKey(); 46 * byte[] publicKeyB = X25519.publicFromPrivate(privateKeyB); 47 * 48 * Alice sends publicKeyA to Bob and Bob sends publicKeyB to Alice. 49 * Alice: 50 * byte[] sharedSecretA = X25519.computeSharedSecret(privateKeyA, publicKeyB); 51 * Bob: 52 * byte[] sharedSecretB = X25519.computeSharedSecret(privateKeyB, publicKeyA); 53 * such that sharedSecretA == sharedSecretB. 54 * </pre> 55 */ 56 @Alpha 57 public final class X25519 { 58 /** 59 * Returns a 32-byte private key for Curve25519. 60 * 61 * <p>Note from BoringSSL: All X25519 implementations should decode scalars correctly (see 62 * https://tools.ietf.org/html/rfc7748#section-5). However, if an implementation doesn't then it 63 * might interoperate with random keys a fraction of the time because they'll, randomly, happen to 64 * be correctly formed. 65 * 66 * <p>Thus we do the opposite of the masking here to make sure that our private keys are never 67 * correctly masked and so, hopefully, any incorrect implementations are deterministically broken. 68 * 69 * <p>This does not affect security because, although we're throwing away entropy, a valid 70 * implementation of computeSharedSecret should throw away the exact same bits anyway. 71 */ 72 @SuppressWarnings("NarrowingCompoundAssignment") generatePrivateKey()73 public static byte[] generatePrivateKey() { 74 byte[] privateKey = Random.randBytes(Field25519.FIELD_LEN); 75 76 privateKey[0] |= 7; 77 privateKey[31] &= 63; 78 privateKey[31] |= 128; 79 80 return privateKey; 81 } 82 83 /** 84 * Returns the 32-byte shared key (i.e., privateKey·peersPublicValue on the curve). 85 * 86 * @param privateKey 32-byte private key 87 * @param peersPublicValue 32-byte public value 88 * @return the 32-byte shared key 89 * @throws InvalidKeyException when {@code privateKey} is not 32-byte or {@code peersPublicValue} 90 * is invalid. 91 */ 92 @SuppressWarnings("NarrowingCompoundAssignment") computeSharedSecret(byte[] privateKey, byte[] peersPublicValue)93 public static byte[] computeSharedSecret(byte[] privateKey, byte[] peersPublicValue) 94 throws InvalidKeyException { 95 if (privateKey.length != Field25519.FIELD_LEN) { 96 throw new InvalidKeyException("Private key must have 32 bytes."); 97 } 98 long[] x = new long[Field25519.LIMB_CNT + 1]; 99 100 byte[] e = Arrays.copyOf(privateKey, Field25519.FIELD_LEN); 101 e[0] &= 248; 102 e[31] &= 127; 103 e[31] |= 64; 104 105 Curve25519.curveMult(x, e, peersPublicValue); 106 return Field25519.contract(x); 107 } 108 109 /** 110 * Returns the 32-byte Diffie-Hellman public value based on the given {@code privateKey} (i.e., 111 * {@code privateKey}·[9] on the curve). 112 * 113 * @param privateKey 32-byte private key 114 * @return 32-byte Diffie-Hellman public value 115 * @throws InvalidKeyException when the {@code privateKey} is not 32 bytes. 116 */ publicFromPrivate(byte[] privateKey)117 public static byte[] publicFromPrivate(byte[] privateKey) throws InvalidKeyException { 118 if (privateKey.length != Field25519.FIELD_LEN) { 119 throw new InvalidKeyException("Private key must have 32 bytes."); 120 } 121 byte[] base = new byte[Field25519.FIELD_LEN]; 122 base[0] = 9; 123 return computeSharedSecret(privateKey, base); 124 } 125 X25519()126 private X25519() {} 127 } 128