• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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