1 /**
2  * @license
3  * Copyright 2016 Google Inc. All rights reserved.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.security.wycheproof;
18 
19 import java.math.BigInteger;
20 import java.security.AlgorithmParameters;
21 import java.security.GeneralSecurityException;
22 import java.security.KeyPair;
23 import java.security.KeyPairGenerator;
24 import java.security.NoSuchAlgorithmException;
25 import java.security.interfaces.ECPublicKey;
26 import java.security.spec.ECField;
27 import java.security.spec.ECFieldFp;
28 import java.security.spec.ECGenParameterSpec;
29 import java.security.spec.ECParameterSpec;
30 import java.security.spec.ECPoint;
31 import java.security.spec.ECPublicKeySpec;
32 import java.security.spec.EllipticCurve;
33 import java.security.spec.InvalidParameterSpecException;
34 import java.util.Arrays;
35 
36 /**
37  * Some utilities for testing Elliptic curve crypto. This code is for testing only and hasn't been
38  * reviewed for production.
39  */
40 public class EcUtil {
41   /**
42    * Returns the ECParameterSpec for a named curve. Not every provider implements the
43    * AlgorithmParameters. Therefore, most test use alternative functions.
44    */
getCurveSpec(String name)45   public static ECParameterSpec getCurveSpec(String name)
46       throws NoSuchAlgorithmException, InvalidParameterSpecException {
47     AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
48     parameters.init(new ECGenParameterSpec(name));
49     return parameters.getParameterSpec(ECParameterSpec.class);
50   }
51 
52   /**
53    * Returns the ECParameterSpec for a named curve. Only a handful curves that are used in the tests
54    * are implemented.
55    */
getCurveSpecRef(String name)56   public static ECParameterSpec getCurveSpecRef(String name) throws NoSuchAlgorithmException {
57     if (name.equals("secp224r1")) {
58       return getNistP224Params();
59     } else if (name.equals("secp256r1")) {
60       return getNistP256Params();
61     } else if (name.equals("secp384r1")) {
62       return getNistP384Params();
63     } else if (name.equals("secp521r1")) {
64       return getNistP521Params();
65     } else if (name.equals("brainpoolp256r1")) {
66       return getBrainpoolP256r1Params();
67     } else {
68       throw new NoSuchAlgorithmException("Curve not implemented:" + name);
69     }
70   }
71 
getNistCurveSpec( String decimalP, String decimalN, String hexB, String hexGX, String hexGY)72   public static ECParameterSpec getNistCurveSpec(
73       String decimalP, String decimalN, String hexB, String hexGX, String hexGY) {
74     final BigInteger p = new BigInteger(decimalP);
75     final BigInteger n = new BigInteger(decimalN);
76     final BigInteger three = new BigInteger("3");
77     final BigInteger a = p.subtract(three);
78     final BigInteger b = new BigInteger(hexB, 16);
79     final BigInteger gx = new BigInteger(hexGX, 16);
80     final BigInteger gy = new BigInteger(hexGY, 16);
81     final int h = 1;
82     ECFieldFp fp = new ECFieldFp(p);
83     java.security.spec.EllipticCurve curveSpec = new java.security.spec.EllipticCurve(fp, a, b);
84     ECPoint g = new ECPoint(gx, gy);
85     ECParameterSpec ecSpec = new ECParameterSpec(curveSpec, g, n, h);
86     return ecSpec;
87   }
88 
getNistP224Params()89   public static ECParameterSpec getNistP224Params() {
90     return getNistCurveSpec(
91         "26959946667150639794667015087019630673557916260026308143510066298881",
92         "26959946667150639794667015087019625940457807714424391721682722368061",
93         "b4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4",
94         "b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
95         "bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34");
96   }
97 
getNistP256Params()98   public static ECParameterSpec getNistP256Params() {
99     return getNistCurveSpec(
100         "115792089210356248762697446949407573530086143415290314195533631308867097853951",
101         "115792089210356248762697446949407573529996955224135760342422259061068512044369",
102         "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b",
103         "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
104         "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5");
105   }
106 
getNistP384Params()107   public static ECParameterSpec getNistP384Params() {
108     return getNistCurveSpec(
109         "3940200619639447921227904010014361380507973927046544666794829340"
110             + "4245721771496870329047266088258938001861606973112319",
111         "3940200619639447921227904010014361380507973927046544666794690527"
112             + "9627659399113263569398956308152294913554433653942643",
113         "b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875a"
114             + "c656398d8a2ed19d2a85c8edd3ec2aef",
115         "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38"
116             + "5502f25dbf55296c3a545e3872760ab7",
117         "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0"
118             + "0a60b1ce1d7e819d7a431d7c90ea0e5f");
119   }
120 
getNistP521Params()121   public static ECParameterSpec getNistP521Params() {
122     return getNistCurveSpec(
123         "6864797660130609714981900799081393217269435300143305409394463459"
124             + "18554318339765605212255964066145455497729631139148085803712198"
125             + "7999716643812574028291115057151",
126         "6864797660130609714981900799081393217269435300143305409394463459"
127             + "18554318339765539424505774633321719753296399637136332111386476"
128             + "8612440380340372808892707005449",
129         "051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef10"
130             + "9e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00",
131         "c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3d"
132             + "baa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66",
133         "11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e6"
134             + "62c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650");
135   }
136 
getBrainpoolP256r1Params()137   public static ECParameterSpec getBrainpoolP256r1Params() {
138     BigInteger p =
139         new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16);
140     BigInteger a =
141         new BigInteger("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", 16);
142     BigInteger b =
143         new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16);
144     BigInteger x =
145         new BigInteger("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16);
146     BigInteger y =
147         new BigInteger("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16);
148     BigInteger n =
149         new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16);
150     final int h = 1;
151     ECFieldFp fp = new ECFieldFp(p);
152     EllipticCurve curve = new EllipticCurve(fp, a, b);
153     ECPoint g = new ECPoint(x, y);
154     return new ECParameterSpec(curve, g, n, h);
155   }
156 
157   /**
158    * Compute the Legendre symbol of x mod p. This implementation is slow. Faster would be the
159    * computation for the Jacobi symbol.
160    *
161    * @param x an integer
162    * @param p a prime modulus
163    * @returns 1 if x is a quadratic residue, -1 if x is a non-quadratic residue and 0 if x and p are
164    *     not coprime.
165    * @throws GeneralSecurityException when the computation shows that p is not prime.
166    */
legendre(BigInteger x, BigInteger p)167   public static int legendre(BigInteger x, BigInteger p) throws GeneralSecurityException {
168     BigInteger q = p.subtract(BigInteger.ONE).shiftRight(1);
169     BigInteger t = x.modPow(q, p);
170     if (t.equals(BigInteger.ONE)) {
171       return 1;
172     } else if (t.equals(BigInteger.ZERO)) {
173       return 0;
174     } else if (t.add(BigInteger.ONE).equals(p)) {
175       return -1;
176     } else {
177       throw new GeneralSecurityException("p is not prime");
178     }
179   }
180 
181   /**
182    * Computes a modular square root. Timing and exceptions can leak information about the inputs.
183    * Therefore this method must only be used in tests.
184    *
185    * @param x the square
186    * @param p the prime modulus
187    * @returns a value s such that s^2 mod p == x mod p
188    * @throws GeneralSecurityException if the square root could not be found.
189    */
modSqrt(BigInteger x, BigInteger p)190   public static BigInteger modSqrt(BigInteger x, BigInteger p) throws GeneralSecurityException {
191     if (p.signum() != 1) {
192       throw new GeneralSecurityException("p must be positive");
193     }
194     x = x.mod(p);
195     BigInteger squareRoot = null;
196     // Special case for x == 0.
197     // This check is necessary for Cipolla's algorithm.
198     if (x.equals(BigInteger.ZERO)) {
199       return x;
200     }
201     if (p.testBit(0) && p.testBit(1)) {
202       // Case p % 4 == 3
203       // q = (p + 1) / 4
204       BigInteger q = p.add(BigInteger.ONE).shiftRight(2);
205       squareRoot = x.modPow(q, p);
206     } else if (p.testBit(0) && !p.testBit(1)) {
207       // Case p % 4 == 1
208       // For this case we use Cipolla's algorithm.
209       // This alogorithm is preferrable to Tonelli-Shanks for primes p where p-1 is divisible by
210       // a large power of 2, which is a frequent choice since it simplifies modular reduction.
211       BigInteger a = BigInteger.ONE;
212       BigInteger d = null;
213       while (true) {
214         d = a.multiply(a).subtract(x).mod(p);
215         // Computes the Legendre symbol. Using the Jacobi symbol would be a faster. Using Legendre
216         // has the advantage, that it detects a non prime p with high probability.
217         // On the other hand if p = q^2 then the Jacobi (d/p)==1 for almost all d's and thus
218         // using the Jacobi symbol here can result in an endless loop with invalid inputs.
219         int t = legendre(d, p);
220         if (t == -1) {
221           break;
222         } else {
223           a = a.add(BigInteger.ONE);
224         }
225       }
226       // Since d = a^2 - n is a non-residue modulo p, we have
227       //   a - sqrt(d) == (a+sqrt(d))^p (mod p),
228       // and hence
229       //   n == (a + sqrt(d))(a - sqrt(d) == (a+sqrt(d))^(p+1) (mod p).
230       // Thus if n is square then (a+sqrt(d))^((p+1)/2) (mod p) is a square root of n.
231       BigInteger q = p.add(BigInteger.ONE).shiftRight(1);
232       BigInteger u = a;
233       BigInteger v = BigInteger.ONE;
234       for (int bit = q.bitLength() - 2; bit >= 0; bit--) {
235         // Compute (u + v sqrt(d))^2
236         BigInteger tmp = u.multiply(v);
237         u = u.multiply(u).add(v.multiply(v).mod(p).multiply(d)).mod(p);
238         v = tmp.add(tmp).mod(p);
239         if (q.testBit(bit)) {
240           tmp = u.multiply(a).add(v.multiply(d)).mod(p);
241           v = a.multiply(v).add(u).mod(p);
242           u = tmp;
243         }
244       }
245       squareRoot = u;
246     }
247     // The methods used to compute the square root only guarantee a correct result if the
248     // preconditions (i.e. p prime and x is a square) are satisfied. Otherwise the value is
249     // undefined. Hence, it is important to verify that squareRoot is indeed a square root.
250     if (squareRoot != null && squareRoot.multiply(squareRoot).mod(p).compareTo(x) != 0) {
251       throw new GeneralSecurityException("Could not find square root");
252     }
253     return squareRoot;
254   }
255 
256   /**
257    * Returns the modulus of the field used by the curve specified in ecParams.
258    *
259    * @param curve must be a prime order elliptic curve
260    * @return the order of the finite field over which curve is defined.
261    */
getModulus(EllipticCurve curve)262   public static BigInteger getModulus(EllipticCurve curve) throws GeneralSecurityException {
263     java.security.spec.ECField field = curve.getField();
264     if (field instanceof java.security.spec.ECFieldFp) {
265       return ((java.security.spec.ECFieldFp) field).getP();
266     } else {
267       throw new GeneralSecurityException("Only curves over prime order fields are supported");
268     }
269   }
270 
271   /**
272    * Returns the size of an element of the field over which the curve is defined.
273    *
274    * @param curve must be a prime order elliptic curve
275    * @return the size of an element in bits
276    */
fieldSizeInBits(EllipticCurve curve)277   public static int fieldSizeInBits(EllipticCurve curve) throws GeneralSecurityException {
278     return getModulus(curve).subtract(BigInteger.ONE).bitLength();
279   }
280 
281   /**
282    * Returns the size of an element of the field over which the curve is defined.
283    *
284    * @param curve must be a prime order elliptic curve
285    * @return the size of an element in bytes.
286    */
fieldSizeInBytes(EllipticCurve curve)287   public static int fieldSizeInBytes(EllipticCurve curve) throws GeneralSecurityException {
288     return (fieldSizeInBits(curve) + 7) / 8;
289   }
290 
291   /**
292    * Checks that a point is on a given elliptic curve. This method implements the partial public key
293    * validation routine from Section 5.6.2.6 of NIST SP 800-56A
294    * http://csrc.nist.gov/publications/nistpubs/800-56A/SP800-56A_Revision1_Mar08-2007.pdf A partial
295    * public key validation is sufficient for curves with cofactor 1. See Section B.3 of
296    * http://www.nsa.gov/ia/_files/SuiteB_Implementer_G-113808.pdf The point validations above are
297    * taken from recommendations for ECDH, because parameter checks in ECDH are much more important
298    * than for the case of ECDSA. Performing this test for ECDSA keys is mainly a sanity check.
299    *
300    * @param point the point that needs verification
301    * @param ec the elliptic curve. This must be a curve over a prime order field.
302    * @throws GeneralSecurityException if the field is binary or if the point is not on the curve.
303    */
checkPointOnCurve(ECPoint point, EllipticCurve ec)304   public static void checkPointOnCurve(ECPoint point, EllipticCurve ec)
305       throws GeneralSecurityException {
306     BigInteger p = getModulus(ec);
307     BigInteger x = point.getAffineX();
308     BigInteger y = point.getAffineY();
309     if (x == null || y == null) {
310       throw new GeneralSecurityException("point is at infinity");
311     }
312     // Check 0 <= x < p and 0 <= y < p.
313     if (x.signum() == -1 || x.compareTo(p) != -1) {
314       throw new GeneralSecurityException("x is out of range");
315     }
316     if (y.signum() == -1 || y.compareTo(p) != -1) {
317       throw new GeneralSecurityException("y is out of range");
318     }
319     // Check y^2 == x^3 + a x + b (mod p)
320     BigInteger lhs = y.multiply(y).mod(p);
321     BigInteger rhs = x.multiply(x).add(ec.getA()).multiply(x).add(ec.getB()).mod(p);
322     if (!lhs.equals(rhs)) {
323       throw new GeneralSecurityException("Point is not on curve");
324     }
325   }
326 
327   /**
328    * Checks a public key. I.e. this checks that the point defining the public key is on the curve.
329    *
330    * @param key must be a key defined over a curve using a prime order field.
331    * @throws GeneralSecurityException if the key is not valid.
332    */
checkPublicKey(ECPublicKey key)333   public static void checkPublicKey(ECPublicKey key) throws GeneralSecurityException {
334     checkPointOnCurve(key.getW(), key.getParams().getCurve());
335   }
336 
337   /**
338    * Decompress a point
339    *
340    * @param x The x-coordinate of the point
341    * @param bit0 true if the least significant bit of y is set.
342    * @param ecParams contains the curve of the point. This must be over a prime order field.
343    */
getPoint(BigInteger x, boolean bit0, ECParameterSpec ecParams)344   public static ECPoint getPoint(BigInteger x, boolean bit0, ECParameterSpec ecParams)
345       throws GeneralSecurityException {
346     EllipticCurve ec = ecParams.getCurve();
347     ECField field = ec.getField();
348     if (!(field instanceof ECFieldFp)) {
349       throw new GeneralSecurityException("Only curves over prime order fields are supported");
350     }
351     BigInteger p = ((java.security.spec.ECFieldFp) field).getP();
352     if (x.compareTo(BigInteger.ZERO) == -1 || x.compareTo(p) != -1) {
353       throw new GeneralSecurityException("x is out of range");
354     }
355     // Compute rhs == x^3 + a x + b (mod p)
356     BigInteger rhs = x.multiply(x).add(ec.getA()).multiply(x).add(ec.getB()).mod(p);
357     BigInteger y = modSqrt(rhs, p);
358     if (bit0 != y.testBit(0)) {
359       y = p.subtract(y).mod(p);
360     }
361     return new ECPoint(x, y);
362   }
363 
364   /**
365    * Decompress a point on an elliptic curve.
366    *
367    * @param bytes The compressed point. Its representation is z || x where z is 2+lsb(y) and x is
368    *     using a unsigned fixed length big-endian representation.
369    * @param ecParams the specification of the curve. Only Weierstrass curves over prime order fields
370    *     are implemented.
371    */
decompressPoint(byte[] bytes, ECParameterSpec ecParams)372   public static ECPoint decompressPoint(byte[] bytes, ECParameterSpec ecParams)
373       throws GeneralSecurityException {
374     EllipticCurve ec = ecParams.getCurve();
375     ECField field = ec.getField();
376     if (!(field instanceof ECFieldFp)) {
377       throw new GeneralSecurityException("Only curves over prime order fields are supported");
378     }
379     BigInteger p = ((java.security.spec.ECFieldFp) field).getP();
380     int expectedLength = 1 + (p.bitLength() + 7) / 8;
381     if (bytes.length != expectedLength) {
382       throw new GeneralSecurityException("compressed point has wrong length");
383     }
384     boolean lsb;
385     switch (bytes[0]) {
386       case 2:
387         lsb = false;
388         break;
389       case 3:
390         lsb = true;
391         break;
392       default:
393         throw new GeneralSecurityException("Invalid format");
394     }
395     BigInteger x = new BigInteger(1, Arrays.copyOfRange(bytes, 1, bytes.length));
396     if (x.compareTo(BigInteger.ZERO) == -1 || x.compareTo(p) != -1) {
397       throw new GeneralSecurityException("x is out of range");
398     }
399     // Compute rhs == x^3 + a x + b (mod p)
400     BigInteger rhs = x.multiply(x).add(ec.getA()).multiply(x).add(ec.getB()).mod(p);
401     BigInteger y = modSqrt(rhs, p);
402     if (lsb != y.testBit(0)) {
403       y = p.subtract(y).mod(p);
404     }
405     return new ECPoint(x, y);
406   }
407 
408   /**
409    * Returns a weak public key of order 3 such that the public key point is on the curve specified
410    * in ecParams. This method is used to check ECC implementations for missing step in the
411    * verification of the public key. E.g. implementations of ECDH must verify that the public key
412    * contains a point on the curve as well as public and secret key are using the same curve.
413    *
414    * @param ecParams the parameters of the key to attack. This must be a curve in Weierstrass form
415    *     over a prime order field.
416    * @return a weak EC group with a genrator of order 3.
417    */
getWeakPublicKey(ECParameterSpec ecParams)418   public static ECPublicKeySpec getWeakPublicKey(ECParameterSpec ecParams)
419       throws GeneralSecurityException {
420     EllipticCurve curve = ecParams.getCurve();
421     KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
422     keyGen.initialize(ecParams);
423     BigInteger p = getModulus(curve);
424     BigInteger three = new BigInteger("3");
425     while (true) {
426       // Generate a point on the original curve
427       KeyPair keyPair = keyGen.generateKeyPair();
428       ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
429       ECPoint w = pub.getW();
430       BigInteger x = w.getAffineX();
431       BigInteger y = w.getAffineY();
432       // Find the curve parameters a,b such that 3*w = infinity.
433       // This is the case if the following equations are satisfied:
434       //    3x == l^2 (mod p)
435       //    l == (3x^2 + a) / 2*y (mod p)
436       //    y^2 == x^3 + ax + b (mod p)
437       BigInteger l;
438       try {
439         l = modSqrt(x.multiply(three), p);
440       } catch (GeneralSecurityException ex) {
441         continue;
442       }
443       BigInteger xSqr = x.multiply(x).mod(p);
444       BigInteger a = l.multiply(y.add(y)).subtract(xSqr.multiply(three)).mod(p);
445       BigInteger b = y.multiply(y).subtract(x.multiply(xSqr.add(a))).mod(p);
446       EllipticCurve newCurve = new EllipticCurve(curve.getField(), a, b);
447       // Just a sanity check.
448       checkPointOnCurve(w, newCurve);
449       // Cofactor and order are of course wrong.
450       ECParameterSpec spec = new ECParameterSpec(newCurve, w, p, 1);
451       return new ECPublicKeySpec(w, spec);
452     }
453   }
454 }
455