• 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.internal.BigIntegerEncoding;
20 import com.google.crypto.tink.internal.EllipticCurvesUtil;
21 import java.math.BigInteger;
22 import java.security.GeneralSecurityException;
23 import java.security.InvalidAlgorithmParameterException;
24 import java.security.KeyFactory;
25 import java.security.KeyPair;
26 import java.security.KeyPairGenerator;
27 import java.security.NoSuchAlgorithmException;
28 import java.security.PublicKey;
29 import java.security.interfaces.ECPrivateKey;
30 import java.security.interfaces.ECPublicKey;
31 import java.security.spec.ECParameterSpec;
32 import java.security.spec.ECPoint;
33 import java.security.spec.ECPrivateKeySpec;
34 import java.security.spec.ECPublicKeySpec;
35 import java.security.spec.EllipticCurve;
36 import java.security.spec.PKCS8EncodedKeySpec;
37 import java.security.spec.X509EncodedKeySpec;
38 import java.util.Arrays;
39 import javax.crypto.KeyAgreement;
40 
41 /**
42  * Utility functions and enums for elliptic curve crypto, used in ECDSA and ECDH.
43  *
44  * @since 1.0.0
45  */
46 public final class EllipticCurves {
47 
48   /**
49    * Point format types UNCOMPRESSED and COMPRESSED are defined in https://www.secg.org/sec1-v2.pdf,
50    * Sections 2.3.3 and 2.3.4.
51    */
52   public enum PointFormatType {
53     UNCOMPRESSED,
54     COMPRESSED,
55     // Like UNCOMPRESSED but without the \x04 prefix. Crunchy uses this format.
56     // DO NOT USE unless you are a Crunchy user moving to Tink.
57     DO_NOT_USE_CRUNCHY_UNCOMPRESSED,
58   }
59 
60   /** Elliptic curve types. */
61   public enum CurveType {
62     NIST_P256,
63     NIST_P384,
64     NIST_P521,
65   }
66 
67   /** Ecdsa signature encoding. */
68   public enum EcdsaEncoding {
69     IEEE_P1363,
70     DER,
71   }
72 
getNistP256Params()73   public static ECParameterSpec getNistP256Params() {
74     return EllipticCurvesUtil.NIST_P256_PARAMS;
75   }
76 
getNistP384Params()77   public static ECParameterSpec getNistP384Params() {
78     return EllipticCurvesUtil.NIST_P384_PARAMS;
79   }
80 
getNistP521Params()81   public static ECParameterSpec getNistP521Params() {
82     return EllipticCurvesUtil.NIST_P521_PARAMS;
83   }
84 
85 
86   /**
87    * Checks that the point of the public key is on the curve of the public key.
88    *
89    * <h3>Warning</h3>
90    *
91    * <p>Please use {@link #validatePublicKey} if you want to validate a public key to avoid invalid
92    * curve attacks or small subgroup attacks in ECDH.
93    *
94    * <p>This is a sanity check, because the curve of the public key might be under control of the
95    * adversary.
96    *
97    * @param key must be a key defined over a curve using a prime order field.
98    * @throws GeneralSecurityException if the key is not valid.
99    */
checkPublicKey(ECPublicKey key)100   static void checkPublicKey(ECPublicKey key) throws GeneralSecurityException {
101     EllipticCurvesUtil.checkPointOnCurve(key.getW(), key.getParams().getCurve());
102   }
103 
104   /** Returns whether {@code spec} is a {@link ECParameterSpec} of one of the NIST curves. */
isNistEcParameterSpec(ECParameterSpec spec)105   public static boolean isNistEcParameterSpec(ECParameterSpec spec) {
106     return EllipticCurvesUtil.isNistEcParameterSpec(spec);
107   }
108 
109   /** Returns whether {@code one} is the same {@link ECParameterSpec} as {@code two}. */
isSameEcParameterSpec(ECParameterSpec one, ECParameterSpec two)110   public static boolean isSameEcParameterSpec(ECParameterSpec one, ECParameterSpec two) {
111     return EllipticCurvesUtil.isSameEcParameterSpec(one, two);
112   }
113 
114   /**
115    * Checks that the public key's params is the same as the private key's params, and the public key
116    * is a valid point on the private key's curve.
117    *
118    * @since 1.1.0
119    */
validatePublicKey(ECPublicKey publicKey, ECPrivateKey privateKey)120   public static void validatePublicKey(ECPublicKey publicKey, ECPrivateKey privateKey)
121       throws GeneralSecurityException {
122     validatePublicKeySpec(publicKey, privateKey);
123     EllipticCurvesUtil.checkPointOnCurve(publicKey.getW(), privateKey.getParams().getCurve());
124   }
125 
126   /** Checks that the public key's params spec is the same as the private key's params spec. */
validatePublicKeySpec(ECPublicKey publicKey, ECPrivateKey privateKey)127   static void validatePublicKeySpec(ECPublicKey publicKey, ECPrivateKey privateKey)
128       throws GeneralSecurityException {
129     try {
130       ECParameterSpec publicKeySpec = publicKey.getParams();
131       ECParameterSpec privateKeySpec = privateKey.getParams();
132       if (!isSameEcParameterSpec(publicKeySpec, privateKeySpec)) {
133         throw new GeneralSecurityException("invalid public key spec");
134       }
135     } catch (IllegalArgumentException | NullPointerException ex) {
136       // The Java security providers on Android K and Android L might throw these unchecked
137       // exceptions, converting them to a checked one to not crash the JVM.
138       throw new GeneralSecurityException(ex);
139     }
140   }
141 
142   /**
143    * Returns the modulus of the field used by the curve specified in ecParams.
144    *
145    * @param curve must be a prime order elliptic curve
146    * @return the order of the finite field over which curve is defined.
147    */
getModulus(EllipticCurve curve)148   public static BigInteger getModulus(EllipticCurve curve) throws GeneralSecurityException {
149     return EllipticCurvesUtil.getModulus(curve);
150   }
151 
152   /**
153    * Returns the size of an element of the field over which the curve is defined.
154    *
155    * @param curve must be a prime order elliptic curve
156    * @return the size of an element in bits
157    */
fieldSizeInBits(EllipticCurve curve)158   public static int fieldSizeInBits(EllipticCurve curve) throws GeneralSecurityException {
159     return getModulus(curve).subtract(BigInteger.ONE).bitLength();
160   }
161 
162   /**
163    * Returns the size of an element of the field over which the curve is defined.
164    *
165    * @param curve must be a prime order elliptic curve
166    * @return the size of an element in bytes.
167    */
fieldSizeInBytes(EllipticCurve curve)168   public static int fieldSizeInBytes(EllipticCurve curve) throws GeneralSecurityException {
169     return (fieldSizeInBits(curve) + 7) / 8;
170   }
171 
172   /**
173    * Computes a square root modulo an odd prime. Timing and exceptions can leak information about
174    * the inputs. Therefore this method must only be used to decompress public keys.
175    *
176    * @param x the square
177    * @param p the prime modulus (the behaviour of the method is undefined if p is not prime).
178    * @return a value s such that s^2 mod p == x mod p
179    * @throws GeneralSecurityException if the square root could not be found.
180    */
modSqrt(BigInteger x, BigInteger p)181   private static BigInteger modSqrt(BigInteger x, BigInteger p) throws GeneralSecurityException {
182     if (p.signum() != 1) {
183       throw new InvalidAlgorithmParameterException("p must be positive");
184     }
185     x = x.mod(p);
186     BigInteger squareRoot = null;
187     // Special case for x == 0.
188     // This check is necessary for Cipolla's algorithm.
189     if (x.equals(BigInteger.ZERO)) {
190       return BigInteger.ZERO;
191     }
192     if (p.testBit(0) && p.testBit(1)) {
193       // Case p % 4 == 3
194       // q = (p + 1) / 4
195       BigInteger q = p.add(BigInteger.ONE).shiftRight(2);
196       squareRoot = x.modPow(q, p);
197     } else if (p.testBit(0) && !p.testBit(1)) {
198       // Case p % 4 == 1
199       // For this case we use Cipolla's algorithm.
200       // This alogorithm is preferrable to Tonelli-Shanks for primes p where p-1 is divisible by
201       // a large power of 2, which is a frequent choice since it simplifies modular reduction.
202       BigInteger a = BigInteger.ONE;
203       BigInteger d = null;
204       BigInteger q1 = p.subtract(BigInteger.ONE).shiftRight(1);
205       int tries = 0;
206       while (true) {
207         d = a.multiply(a).subtract(x).mod(p);
208         // Special case d==0. We need d!=0 below.
209         if (d.equals(BigInteger.ZERO)) {
210           return a;
211         }
212         // Computes the Legendre symbol. Using the Jacobi symbol would be a faster.
213         BigInteger t = d.modPow(q1, p);
214         if (t.add(BigInteger.ONE).equals(p)) {
215           // d is a quadratic non-residue.
216           break;
217         } else if (!t.equals(BigInteger.ONE)) {
218           // p does not divide d. Hence, t != 1 implies that p is not a prime.
219           throw new InvalidAlgorithmParameterException("p is not prime");
220         } else {
221           a = a.add(BigInteger.ONE);
222         }
223         tries++;
224         // If 128 tries were not enough to find a quadratic non-residue, then it is likely that
225         // p is not prime. To avoid an infinite loop in this case we perform a primality test.
226         // If p is prime then this test will be done with a negligible probability of 2^{-128}.
227         if (tries == 128) {
228           if (!p.isProbablePrime(80)) {
229             throw new InvalidAlgorithmParameterException("p is not prime");
230           }
231         }
232       }
233       // Since d = a^2 - x is a quadratic non-residue modulo p, we have
234       //   a - sqrt(d) == (a + sqrt(d))^p (mod p),
235       // and hence
236       //   x == (a + sqrt(d))(a - sqrt(d)) == (a + sqrt(d))^(p+1) (mod p).
237       // Thus if x is square then (a + sqrt(d))^((p+1)/2) (mod p) is a square root of x.
238       BigInteger q = p.add(BigInteger.ONE).shiftRight(1);
239       BigInteger u = a;
240       BigInteger v = BigInteger.ONE;
241       for (int bit = q.bitLength() - 2; bit >= 0; bit--) {
242         // Square u + v sqrt(d) and reduce mod p.
243         BigInteger tmp = u.multiply(v);
244         u = u.multiply(u).add(v.multiply(v).mod(p).multiply(d)).mod(p);
245         v = tmp.add(tmp).mod(p);
246         if (q.testBit(bit)) {
247           // Multiply u + v sqrt(d) by a + sqrt(d) and reduce mod p.
248           tmp = u.multiply(a).add(v.multiply(d)).mod(p);
249           v = a.multiply(v).add(u).mod(p);
250           u = tmp;
251         }
252       }
253       squareRoot = u;
254     }
255     // The methods used to compute the square root only guarantees a correct result if the
256     // preconditions (i.e. p prime and x is a square) are satisfied. Otherwise the value is
257     // undefined. Hence it is important to verify that squareRoot is indeed a square root.
258     if (squareRoot != null && squareRoot.multiply(squareRoot).mod(p).compareTo(x) != 0) {
259       throw new GeneralSecurityException("Could not find a modular square root");
260     }
261     return squareRoot;
262   }
263 
264   /**
265    * Computes the y coordinate of a point on an elliptic curve. This method can be used to
266    * decompress elliptic curve points.
267    *
268    * @param x the x-coordinate of the point
269    * @param lsb the least significant bit of the y-coordinate of the point.
270    * @param curve this must be an elliptic curve over a prime field using Weierstrass
271    *     representation.
272    * @return the y coordinate.
273    * @throws GeneralSecurityException if there is no point with coordinate x on the curve, or if
274    *     curve is not supported.
275    */
computeY(BigInteger x, boolean lsb, EllipticCurve curve)276   private static BigInteger computeY(BigInteger x, boolean lsb, EllipticCurve curve)
277       throws GeneralSecurityException {
278     BigInteger p = getModulus(curve);
279     BigInteger a = curve.getA();
280     BigInteger b = curve.getB();
281     BigInteger rhs = x.multiply(x).add(a).multiply(x).add(b).mod(p);
282     BigInteger y = modSqrt(rhs, p);
283     if (lsb != y.testBit(0)) {
284       y = p.subtract(y).mod(p);
285     }
286     return y;
287   }
288 
289   /**
290    * Computes the y coordinate of a point on an elliptic curve.
291    *
292    * @deprecated This shouldn't be used directly, use {@link pointDecode} to decompress points.
293    */
294   @Deprecated
getY(BigInteger x, boolean lsb, EllipticCurve curve)295   public static BigInteger getY(BigInteger x, boolean lsb, EllipticCurve curve)
296       throws GeneralSecurityException {
297     return computeY(x, lsb, curve);
298   }
299 
300   /**
301    * Transforms a big integer to its minimal signed form, i.e., no extra zero byte at the beginning
302    * except single one when the highest bit is set.
303    */
toMinimalSignedNumber(byte[] bs)304   private static byte[] toMinimalSignedNumber(byte[] bs) {
305     // Remove zero prefixes.
306     int start = 0;
307     while (start < bs.length && bs[start] == 0) {
308       start++;
309     }
310     if (start == bs.length) {
311       start = bs.length - 1;
312     }
313 
314     int extraZero = 0;
315     // If the 1st bit is not zero, add 1 zero byte.
316     if ((bs[start] & 0x80) == 0x80) {
317       // Add extra zero.
318       extraZero = 1;
319     }
320     byte[] res = new byte[bs.length - start + extraZero];
321     System.arraycopy(bs, start, res, extraZero, bs.length - start);
322     return res;
323   }
324 
325   /**
326    * Transforms ECDSA IEEE_P1363 signature encoding to DER encoding.
327    *
328    * <p>The IEEE_P1363 signature's format is r || s, where r and s are zero-padded and have the same
329    * size in bytes as the order of the curve. For example, for NIST P-256 curve, r and s are
330    * zero-padded to 32 bytes.
331    *
332    * <p>The DER signature is encoded using ASN.1 (https://tools.ietf.org/html/rfc5480#appendix-A):
333    * ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }. In particular, the encoding is: 0x30 ||
334    * totalLength || 0x02 || r's length || r || 0x02 || s's length || s.
335    *
336    * @param ieee ECDSA's signature in IEEE_P1363 format.
337    * @return ECDSA's signature in DER format.
338    * @throws GeneralSecurityException if ieee's length is zero, greater than 132-byte (corresponding
339    *     to NIST P521) or not divisible by 2.
340    */
ecdsaIeee2Der(byte[] ieee)341   public static byte[] ecdsaIeee2Der(byte[] ieee) throws GeneralSecurityException {
342     if (ieee.length % 2 != 0 || ieee.length == 0 || ieee.length > 132) {
343       throw new GeneralSecurityException("Invalid IEEE_P1363 encoding");
344     }
345     byte[] r = toMinimalSignedNumber(Arrays.copyOf(ieee, ieee.length / 2));
346     byte[] s = toMinimalSignedNumber(Arrays.copyOfRange(ieee, ieee.length / 2, ieee.length));
347 
348     int offset = 0;
349     int length = 1 + 1 + r.length + 1 + 1 + s.length;
350     byte[] der;
351     if (length >= 128) {
352       der = new byte[length + 3];
353       der[offset++] = (byte) 0x30;
354       der[offset++] = (byte) (0x80 + 0x01);
355       der[offset++] = (byte) length;
356     } else {
357       der = new byte[length + 2];
358       der[offset++] = (byte) 0x30;
359       der[offset++] = (byte) length;
360     }
361     der[offset++] = (byte) 0x02;
362     der[offset++] = (byte) r.length;
363     System.arraycopy(r, 0, der, offset, r.length);
364     offset += r.length;
365     der[offset++] = (byte) 0x02;
366     der[offset++] = (byte) s.length;
367     System.arraycopy(s, 0, der, offset, s.length);
368     return der;
369   }
370 
371   /**
372    * Transforms ECDSA DER signature encoding to IEEE_P1363 encoding.
373    *
374    * <p>The IEEE_P1363 signature's format is r || s, where r and s are zero-padded and have the same
375    * size in bytes as the order of the curve. For example, for NIST P-256 curve, r and s are
376    * zero-padded to 32 bytes.
377    *
378    * <p>The DER signature is encoded using ASN.1 (https://tools.ietf.org/html/rfc5480#appendix-A):
379    * ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }. In particular, the encoding is: 0x30 ||
380    * totalLength || 0x02 || r's length || r || 0x02 || s's length || s.
381    *
382    * @param der ECDSA's signature in DER encoding.
383    * @param ieeeLength length of ECDSA signature's in IEEE_P1363's format which equals to 2 * (size
384    *     of elliptic curve's field in bytes).
385    * @return ECDSA's signature in IEEE_P1363 format.
386    * @throws GeneralSecurityException if the signature is not valid DER encoding.
387    */
ecdsaDer2Ieee(byte[] der, int ieeeLength)388   public static byte[] ecdsaDer2Ieee(byte[] der, int ieeeLength) throws GeneralSecurityException {
389     if (!isValidDerEncoding(der)) {
390       throw new GeneralSecurityException("Invalid DER encoding");
391     }
392     byte[] ieee = new byte[ieeeLength];
393     int length = der[1] & 0xff;
394     int offset = 1 /* 0x30 */ + 1 /* totalLength */;
395     if (length >= 128) {
396       offset++; // Long form length
397     }
398     offset++; // 0x02
399     int rLength = der[offset++];
400     int extraZero = 0;
401     if (der[offset] == 0) {
402       extraZero = 1;
403     }
404     System.arraycopy(
405         der, offset + extraZero, ieee, ieeeLength / 2 - rLength + extraZero, rLength - extraZero);
406     offset += rLength /* r byte array */ + 1 /* 0x02 */;
407     int sLength = der[offset++];
408     extraZero = 0;
409     if (der[offset] == 0) {
410       extraZero = 1;
411     }
412     System.arraycopy(
413         der, offset + extraZero, ieee, ieeeLength - sLength + extraZero, sLength - extraZero);
414     return ieee;
415   }
416 
417   // Validates that the signature is in DER encoding, based on
418   // https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki.
isValidDerEncoding(final byte[] sig)419   public static boolean isValidDerEncoding(final byte[] sig) {
420     // Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S]
421     // * total-length: 1-byte or 2-byte length descriptor of everything that follows.
422     // * R-length: 1-byte length descriptor of the R value that follows.
423     // * R: arbitrary-length big-endian encoded R value. It must use the shortest
424     //   possible encoding for a positive integers (which means no null bytes at
425     //   the start, except a single one when the next byte has its highest bit set).
426     // * S-length: 1-byte length descriptor of the S value that follows.
427     // * S: arbitrary-length big-endian encoded S value. The same rules apply.
428 
429     if (sig.length
430         < 1 /* 0x30 */
431             + 1 /* total-length */
432             + 1 /* 0x02 */
433             + 1 /* R-length */
434             + 1 /* R */
435             + 1 /* 0x02 */
436             + 1 /* S-length */
437             + 1 /* S */) {
438       // Signature is too short.
439       return false;
440     }
441 
442     // Checking bytes from left to right.
443 
444     // byte #1: a signature is of type 0x30 (compound).
445     if (sig[0] != 0x30) {
446       return false;
447     }
448 
449     // byte #2 and maybe #3: the total length of the signature.
450     int totalLen = sig[1] & 0xff;
451     int totalLenLen = 1; // the length of the total length field, could be 2-byte.
452     if (totalLen == 129) {
453       // The signature is >= 128 bytes thus total length field is in long-form encoding and occupies
454       // 2 bytes.
455       totalLenLen = 2;
456       // byte #3 is the total length.
457       totalLen = sig[2] & 0xff;
458       if (totalLen < 128) {
459         // Length in long-form encoding must be >= 128.
460         return false;
461       }
462     } else if (totalLen == 128 || totalLen > 129) {
463       // Impossible values for the second byte.
464       return false;
465     }
466 
467     // Make sure the length covers the entire sig.
468     if (totalLen != sig.length - 1 - totalLenLen) {
469       return false;
470     }
471 
472     // Start checking R.
473     // Check whether the R element is an integer.
474     if (sig[1 + totalLenLen] != 0x02) {
475       return false;
476     }
477     // Extract the length of the R element.
478     int rLen = sig[1 /* 0x30 */ + totalLenLen + 1 /* 0x02 */] & 0xff;
479     // Make sure the length of the S element is still inside the signature.
480     if (1 /* 0x30 */ + totalLenLen + 1 /* 0x02 */ + 1 /* rLen */ + rLen + 1 /* 0x02 */
481         >= sig.length) {
482       return false;
483     }
484     // Zero-length integers are not allowed for R.
485     if (rLen == 0) {
486       return false;
487     }
488     // Negative numbers are not allowed for R.
489     if ((sig[3 + totalLenLen] & 0xff) >= 128) {
490       return false;
491     }
492     // Null bytes at the start of R are not allowed, unless R would
493     // otherwise be interpreted as a negative number.
494     if (rLen > 1 && (sig[3 + totalLenLen] == 0x00) && ((sig[4 + totalLenLen] & 0xff) < 128)) {
495       return false;
496     }
497 
498     // Start checking S.
499     // Check whether the S element is an integer.
500     if (sig[3 + totalLenLen + rLen] != 0x02) {
501       return false;
502     }
503     // Extract the length of the S element.
504     int sLen =
505         sig[1 /* 0x30 */ + totalLenLen + 1 /* 0x02 */ + 1 /* rLen */ + rLen + 1 /* 0x02 */] & 0xff;
506     // Verify that the length of the signature matches the sum of the length of the elements.
507     if (1 /* 0x30 */
508             + totalLenLen
509             + 1 /* 0x02 */
510             + 1 /* rLen */
511             + rLen
512             + 1 /* 0x02 */
513             + 1 /* sLen */
514             + sLen
515         != sig.length) {
516       return false;
517     }
518     // Zero-length integers are not allowed for S.
519     if (sLen == 0) {
520       return false;
521     }
522     // Negative numbers are not allowed for S.
523     if ((sig[5 + totalLenLen + rLen] & 0xff) >= 128) {
524       return false;
525     }
526     // Null bytes at the start of S are not allowed, unless S would
527     // otherwise be interpreted as a negative number.
528     if (sLen > 1
529         && (sig[5 + totalLenLen + rLen] == 0x00)
530         && ((sig[6 + totalLenLen + rLen] & 0xff) < 128)) {
531       return false;
532     }
533 
534     return true;
535   }
536 
537   /**
538    * Returns the encoding size of a point on an elliptic curve.
539    *
540    * @param curve the elliptic curve
541    * @param format the format used to encode the point
542    * @return the size of an encoded point in bytes
543    * @throws GeneralSecurityException if the point format is unknown or if the elliptic curve is not
544    *     supported
545    */
encodingSizeInBytes(EllipticCurve curve, PointFormatType format)546   public static int encodingSizeInBytes(EllipticCurve curve, PointFormatType format)
547       throws GeneralSecurityException {
548     int coordinateSize = fieldSizeInBytes(curve);
549     switch (format) {
550       case UNCOMPRESSED:
551         return 2 * coordinateSize + 1;
552       case DO_NOT_USE_CRUNCHY_UNCOMPRESSED:
553         return 2 * coordinateSize;
554       case COMPRESSED:
555         return coordinateSize + 1;
556     }
557     throw new GeneralSecurityException("unknown EC point format");
558   }
559 
560   /**
561    * Decodes an encoded point on an elliptic curve. This method checks that the encoded point is on
562    * the curve.
563    *
564    * @param curve the elliptic curve
565    * @param format the format used to enocde the point
566    * @param encoded the encoded point
567    * @return the point
568    * @throws GeneralSecurityException if the encoded point is invalid or if the curve or format are
569    *     not supported.
570    */
ecPointDecode(EllipticCurve curve, PointFormatType format, byte[] encoded)571   public static ECPoint ecPointDecode(EllipticCurve curve, PointFormatType format, byte[] encoded)
572       throws GeneralSecurityException {
573     return pointDecode(curve, format, encoded);
574   }
575 
576   /**
577    * Decodes an encoded point on an elliptic curve. This method checks that the encoded point is on
578    * the curve.
579    *
580    * @param curveType the elliptic curve
581    * @param format the format used to enocde the point
582    * @param encoded the encoded point
583    * @return the point
584    * @throws GeneralSecurityException if the encoded point is invalid or if the curve or format are
585    *     not supported.
586    * @since 1.1.0
587    */
pointDecode(CurveType curveType, PointFormatType format, byte[] encoded)588   public static ECPoint pointDecode(CurveType curveType, PointFormatType format, byte[] encoded)
589       throws GeneralSecurityException {
590     return pointDecode(getCurveSpec(curveType).getCurve(), format, encoded);
591   }
592 
593   /**
594    * Decodes an encoded point on an elliptic curve. This method checks that the encoded point is on
595    * the curve.
596    *
597    * @param curve the elliptic curve
598    * @param format the format used to enocde the point
599    * @param encoded the encoded point
600    * @return the point
601    * @throws GeneralSecurityException if the encoded point is invalid or if the curve or format are
602    *     not supported.
603    * @since 1.1.0
604    */
pointDecode(EllipticCurve curve, PointFormatType format, byte[] encoded)605   public static ECPoint pointDecode(EllipticCurve curve, PointFormatType format, byte[] encoded)
606       throws GeneralSecurityException {
607     int coordinateSize = fieldSizeInBytes(curve);
608     switch (format) {
609       case UNCOMPRESSED:
610         {
611           if (encoded.length != 2 * coordinateSize + 1) {
612             throw new GeneralSecurityException("invalid point size");
613           }
614           if (encoded[0] != 4) {
615             throw new GeneralSecurityException("invalid point format");
616           }
617           BigInteger x = new BigInteger(1, Arrays.copyOfRange(encoded, 1, coordinateSize + 1));
618           BigInteger y =
619               new BigInteger(1, Arrays.copyOfRange(encoded, coordinateSize + 1, encoded.length));
620           ECPoint point = new ECPoint(x, y);
621           EllipticCurvesUtil.checkPointOnCurve(point, curve);
622           return point;
623         }
624       case DO_NOT_USE_CRUNCHY_UNCOMPRESSED:
625         {
626           if (encoded.length != 2 * coordinateSize) {
627             throw new GeneralSecurityException("invalid point size");
628           }
629           BigInteger x = new BigInteger(1, Arrays.copyOf(encoded, coordinateSize));
630           BigInteger y =
631               new BigInteger(1, Arrays.copyOfRange(encoded, coordinateSize, encoded.length));
632           ECPoint point = new ECPoint(x, y);
633           EllipticCurvesUtil.checkPointOnCurve(point, curve);
634           return point;
635         }
636       case COMPRESSED:
637         {
638           BigInteger p = getModulus(curve);
639           if (encoded.length != coordinateSize + 1) {
640             throw new GeneralSecurityException("compressed point has wrong length");
641           }
642           boolean lsb;
643           if (encoded[0] == 2) {
644             lsb = false;
645           } else if (encoded[0] == 3) {
646             lsb = true;
647           } else {
648             throw new GeneralSecurityException("invalid format");
649           }
650           BigInteger x = new BigInteger(1, Arrays.copyOfRange(encoded, 1, encoded.length));
651           if (x.signum() == -1 || x.compareTo(p) >= 0) {
652             throw new GeneralSecurityException("x is out of range");
653           }
654           BigInteger y = computeY(x, lsb, curve);
655           return new ECPoint(x, y);
656         }
657     }
658     throw new GeneralSecurityException("invalid format:" + format);
659   }
660 
661   /**
662    * Encodes a point on an elliptic curve.
663    *
664    * @param curveType the elliptic curve
665    * @param format the format for the encoding
666    * @param point the point to encode
667    * @return the encoded key exchange
668    * @throws GeneralSecurityException if the point is not on the curve or if the format is not
669    *     supported.
670    * @since 1.1.0
671    */
pointEncode(CurveType curveType, PointFormatType format, ECPoint point)672   public static byte[] pointEncode(CurveType curveType, PointFormatType format, ECPoint point)
673       throws GeneralSecurityException {
674     return pointEncode(getCurveSpec(curveType).getCurve(), format, point);
675   }
676 
677   /**
678    * Encodes a point on an elliptic curve.
679    *
680    * @param curve the elliptic curve
681    * @param format the format for the encoding
682    * @param point the point to encode
683    * @return the encoded key exchange
684    * @throws GeneralSecurityException if the point is not on the curve or if the format is not
685    *     supported.
686    * @since 1.1.0
687    */
pointEncode(EllipticCurve curve, PointFormatType format, ECPoint point)688   public static byte[] pointEncode(EllipticCurve curve, PointFormatType format, ECPoint point)
689       throws GeneralSecurityException {
690     EllipticCurvesUtil.checkPointOnCurve(point, curve);
691     int coordinateSize = fieldSizeInBytes(curve);
692     switch (format) {
693       case UNCOMPRESSED:
694         {
695           byte[] encoded = new byte[2 * coordinateSize + 1];
696           byte[] x = BigIntegerEncoding.toBigEndianBytes(point.getAffineX());
697           byte[] y = BigIntegerEncoding.toBigEndianBytes(point.getAffineY());
698           // Order of System.arraycopy is important because x,y can have leading 0's.
699           System.arraycopy(y, 0, encoded, 1 + 2 * coordinateSize - y.length, y.length);
700           System.arraycopy(x, 0, encoded, 1 + coordinateSize - x.length, x.length);
701           encoded[0] = 4;
702           return encoded;
703         }
704       case DO_NOT_USE_CRUNCHY_UNCOMPRESSED:
705         {
706           byte[] encoded = new byte[2 * coordinateSize];
707           byte[] x = BigIntegerEncoding.toBigEndianBytes(point.getAffineX());
708           if (x.length > coordinateSize) {
709             // x has leading 0's, strip them.
710             x = Arrays.copyOfRange(x, x.length - coordinateSize, x.length);
711           }
712           byte[] y = BigIntegerEncoding.toBigEndianBytes(point.getAffineY());
713           if (y.length > coordinateSize) {
714             // y has leading 0's, strip them.
715             y = Arrays.copyOfRange(y, y.length - coordinateSize, y.length);
716           }
717           System.arraycopy(y, 0, encoded, 2 * coordinateSize - y.length, y.length);
718           System.arraycopy(x, 0, encoded, coordinateSize - x.length, x.length);
719           return encoded;
720         }
721       case COMPRESSED:
722         {
723           byte[] encoded = new byte[coordinateSize + 1];
724           byte[] x = BigIntegerEncoding.toBigEndianBytes(point.getAffineX());
725           System.arraycopy(x, 0, encoded, 1 + coordinateSize - x.length, x.length);
726           encoded[0] = (byte) (point.getAffineY().testBit(0) ? 3 : 2);
727           return encoded;
728         }
729     }
730     throw new GeneralSecurityException("invalid format:" + format);
731   }
732 
733   /**
734    * Returns the ECParameterSpec for a named curve.
735    *
736    * @param curve the curve type
737    * @return the ECParameterSpec for the curve.
738    */
getCurveSpec(CurveType curve)739   public static ECParameterSpec getCurveSpec(CurveType curve) throws NoSuchAlgorithmException {
740     switch (curve) {
741       case NIST_P256:
742         return getNistP256Params();
743       case NIST_P384:
744         return getNistP384Params();
745       case NIST_P521:
746         return getNistP521Params();
747     }
748     throw new NoSuchAlgorithmException("curve not implemented:" + curve);
749   }
750 
751   /**
752    * Returns an {@link ECPublicKey} from {@code x509PublicKey} which is an encoding of a public key,
753    * encoded according to the ASN.1 type SubjectPublicKeyInfo.
754    *
755    * <p>TODO(b/68672497): test that in Java one can always get this representation by using {@link
756    * ECPublicKey#getEncoded}, regardless of the provider.
757    */
getEcPublicKey(final byte[] x509PublicKey)758   public static ECPublicKey getEcPublicKey(final byte[] x509PublicKey)
759       throws GeneralSecurityException {
760     KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("EC");
761     return (ECPublicKey) kf.generatePublic(new X509EncodedKeySpec(x509PublicKey));
762   }
763 
764   /**
765    * Returns an {@link ECPublicKey} from {@code publicKey} that is a public key in point format
766    * {@code pointFormat} on {@code curve}.
767    */
getEcPublicKey( CurveType curve, PointFormatType pointFormat, final byte[] publicKey)768   public static ECPublicKey getEcPublicKey(
769       CurveType curve, PointFormatType pointFormat, final byte[] publicKey)
770       throws GeneralSecurityException {
771     return getEcPublicKey(getCurveSpec(curve), pointFormat, publicKey);
772   }
773 
774   /**
775    * Returns an {@link ECPublicKey} from {@code publicKey} that is a public key in point format
776    * {@code pointFormat} on {@code curve}.
777    */
getEcPublicKey( ECParameterSpec spec, PointFormatType pointFormat, final byte[] publicKey)778   public static ECPublicKey getEcPublicKey(
779       ECParameterSpec spec, PointFormatType pointFormat, final byte[] publicKey)
780       throws GeneralSecurityException {
781     ECPoint point = pointDecode(spec.getCurve(), pointFormat, publicKey);
782     ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, spec);
783     KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("EC");
784     return (ECPublicKey) kf.generatePublic(pubSpec);
785   }
786 
787   /**
788    * Returns an {@code ECPublicKey} from {@code curve} type and {@code x} and {@code y} coordinates.
789    */
getEcPublicKey(CurveType curve, final byte[] x, final byte[] y)790   public static ECPublicKey getEcPublicKey(CurveType curve, final byte[] x, final byte[] y)
791       throws GeneralSecurityException {
792     ECParameterSpec ecParams = getCurveSpec(curve);
793     BigInteger pubX = new BigInteger(1, x);
794     BigInteger pubY = new BigInteger(1, y);
795     ECPoint w = new ECPoint(pubX, pubY);
796     EllipticCurvesUtil.checkPointOnCurve(w, ecParams.getCurve());
797     ECPublicKeySpec spec = new ECPublicKeySpec(w, ecParams);
798     KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("EC");
799     return (ECPublicKey) kf.generatePublic(spec);
800   }
801 
802   /**
803    * Returns an {@code ECPrivateKey} from {@code pkcs8PrivateKey} which is an encoding of a private
804    * key, encoded according to the ASN.1 type SubjectPublicKeyInfo.
805    *
806    * <p>TODO(b/68672497): test that in Java one can always get this representation by using {@link
807    * ECPrivateKey#getEncoded}, regardless of the provider.
808    */
getEcPrivateKey(final byte[] pkcs8PrivateKey)809   public static ECPrivateKey getEcPrivateKey(final byte[] pkcs8PrivateKey)
810       throws GeneralSecurityException {
811     KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("EC");
812     return (ECPrivateKey) kf.generatePrivate(new PKCS8EncodedKeySpec(pkcs8PrivateKey));
813   }
814 
815   /** Returns an {@code ECPrivateKey} from {@code curve} type and {@code keyValue}. */
getEcPrivateKey(CurveType curve, final byte[] keyValue)816   public static ECPrivateKey getEcPrivateKey(CurveType curve, final byte[] keyValue)
817       throws GeneralSecurityException {
818     ECParameterSpec ecParams = getCurveSpec(curve);
819     BigInteger privValue = BigIntegerEncoding.fromUnsignedBigEndianBytes(keyValue);
820     ECPrivateKeySpec spec = new ECPrivateKeySpec(privValue, ecParams);
821     KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("EC");
822     return (ECPrivateKey) kf.generatePrivate(spec);
823   }
824 
825   /** Generates a new key pair for {@code curve}. */
generateKeyPair(CurveType curve)826   public static KeyPair generateKeyPair(CurveType curve) throws GeneralSecurityException {
827     return generateKeyPair(getCurveSpec(curve));
828   }
829 
830   /** Generates a new key pair for {@code spec}. */
generateKeyPair(ECParameterSpec spec)831   public static KeyPair generateKeyPair(ECParameterSpec spec) throws GeneralSecurityException {
832     KeyPairGenerator keyGen = EngineFactory.KEY_PAIR_GENERATOR.getInstance("EC");
833     keyGen.initialize(spec);
834     return keyGen.generateKeyPair();
835   }
836 
837   /**
838    * Basic validation of the shared secret.
839    *
840    * <p>Checks that there exists a point on the curve of {@code privateKey} that has {@code secret}
841    * as x coordinate. This validation gives very limited protection against arithmetic errors
842    * or fault attacks.
843    *
844    * <p>Only visible for testing.
845    */
validateSharedSecret(byte[] secret, ECPrivateKey privateKey)846   static void validateSharedSecret(byte[] secret, ECPrivateKey privateKey)
847       throws GeneralSecurityException {
848     EllipticCurve privateKeyCurve = privateKey.getParams().getCurve();
849     BigInteger x = new BigInteger(1, secret);
850     if (x.signum() == -1 || x.compareTo(getModulus(privateKeyCurve)) >= 0) {
851       throw new GeneralSecurityException("shared secret is out of range");
852     }
853     // This will throw if x is not a valid coordinate.
854     Object unused = computeY(x, true /* lsb, doesn't matter here */, privateKeyCurve);
855   }
856 
857   /* Generates the DH shared secret using {@code myPrivateKey} and {@code peerPublicKey} */
computeSharedSecret(ECPrivateKey myPrivateKey, ECPublicKey peerPublicKey)858   public static byte[] computeSharedSecret(ECPrivateKey myPrivateKey, ECPublicKey peerPublicKey)
859       throws GeneralSecurityException {
860     validatePublicKeySpec(peerPublicKey, myPrivateKey);
861     return computeSharedSecret(myPrivateKey, peerPublicKey.getW());
862   }
863 
864   /**
865    * Generates the DH shared secret using {@code myPrivateKey} and {@code publicPoint}
866    *
867    * @since 1.1.0
868    */
computeSharedSecret(ECPrivateKey myPrivateKey, ECPoint publicPoint)869   public static byte[] computeSharedSecret(ECPrivateKey myPrivateKey, ECPoint publicPoint)
870       throws GeneralSecurityException {
871     EllipticCurvesUtil.checkPointOnCurve(publicPoint, myPrivateKey.getParams().getCurve());
872     // Explicitly reconstruct the peer public key using private key's spec.
873     ECParameterSpec privSpec = myPrivateKey.getParams();
874     ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(publicPoint, privSpec);
875     KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("EC");
876     PublicKey publicKey = kf.generatePublic(publicKeySpec);
877     KeyAgreement ka = EngineFactory.KEY_AGREEMENT.getInstance("ECDH");
878     ka.init(myPrivateKey);
879     try {
880       ka.doPhase(publicKey, /* lastPhase= */ true);
881       byte[] secret = ka.generateSecret();
882       validateSharedSecret(secret, myPrivateKey);
883       return secret;
884     } catch (IllegalStateException ex) {
885       // Due to CVE-2017-10176 some versions of OpenJDK might throw this unchecked exception,
886       // converting it to a checked one to not crash the JVM. See also b/73760761.
887       throw new GeneralSecurityException(ex);
888     }
889   }
890 
EllipticCurves()891   private EllipticCurves() {}
892 }
893