• 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 static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertArrayEquals;
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertThrows;
24 
25 import com.google.crypto.tink.testing.TestUtil;
26 import com.google.crypto.tink.testing.WycheproofTestUtil;
27 import com.google.gson.JsonArray;
28 import com.google.gson.JsonObject;
29 import java.math.BigInteger;
30 import java.security.GeneralSecurityException;
31 import java.security.KeyFactory;
32 import java.security.interfaces.ECPrivateKey;
33 import java.security.interfaces.ECPublicKey;
34 import java.security.spec.ECPoint;
35 import java.security.spec.EllipticCurve;
36 import java.security.spec.X509EncodedKeySpec;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.JUnit4;
40 
41 /** Unit tests for {@link com.google.crypto.tink.subtle.EllipticCurves}. */
42 @RunWith(JUnit4.class)
43 public class EllipticCurvesTest {
44   // The tests are from
45   // http://google.github.io/end-to-end/api/source/src/javascript/crypto/e2e/ecc/ecdh_testdata.js.src.html.
46 
47   /**
48    * A class for storing test vectors. This class contains the directory for the public and private
49    * key, the message and the corresponding signature.
50    */
51   protected static class TestVector2 {
52     protected EllipticCurves.CurveType curve;
53     protected EllipticCurves.PointFormatType format;
54     protected byte[] encoded;
55     BigInteger x;
56     BigInteger y;
57 
TestVector2( EllipticCurves.CurveType curve, EllipticCurves.PointFormatType format, String encodedHex, String x, String y)58     protected TestVector2(
59         EllipticCurves.CurveType curve,
60         EllipticCurves.PointFormatType format,
61         String encodedHex,
62         String x,
63         String y) {
64       this.curve = curve;
65       this.format = format;
66       this.encoded = Hex.decode(encodedHex);
67       this.x = new BigInteger(x);
68       this.y = new BigInteger(y);
69     }
70   }
71 
72   protected static final TestVector2[] testVectors2 = {
73     // NIST_P256
74     new TestVector2(
75         EllipticCurves.CurveType.NIST_P256,
76         EllipticCurves.PointFormatType.UNCOMPRESSED,
77         "04"
78             + "b0cfc7bc02fc980d858077552947ffb449b10df8949dee4e56fe21e016dcb25a"
79             + "1886ccdca5487a6772f9401888203f90587cc00a730e2b83d5c6f89b3b568df7",
80         "79974177209371530366349631093481213364328002500948308276357601809416549347930",
81         "11093679777528052772423074391650378811758820120351664471899251711300542565879"),
82     new TestVector2(
83         EllipticCurves.CurveType.NIST_P256,
84         EllipticCurves.PointFormatType.DO_NOT_USE_CRUNCHY_UNCOMPRESSED,
85         "b0cfc7bc02fc980d858077552947ffb449b10df8949dee4e56fe21e016dcb25a"
86             + "1886ccdca5487a6772f9401888203f90587cc00a730e2b83d5c6f89b3b568df7",
87         "79974177209371530366349631093481213364328002500948308276357601809416549347930",
88         "11093679777528052772423074391650378811758820120351664471899251711300542565879"),
89     new TestVector2(
90         EllipticCurves.CurveType.NIST_P256,
91         EllipticCurves.PointFormatType.COMPRESSED,
92         "03b0cfc7bc02fc980d858077552947ffb449b10df8949dee4e56fe21e016dcb25a",
93         "79974177209371530366349631093481213364328002500948308276357601809416549347930",
94         "11093679777528052772423074391650378811758820120351664471899251711300542565879"),
95     // Exceptional point: x==0
96     new TestVector2(
97         EllipticCurves.CurveType.NIST_P256,
98         EllipticCurves.PointFormatType.UNCOMPRESSED,
99         "04"
100             + "0000000000000000000000000000000000000000000000000000000000000000"
101             + "66485c780e2f83d72433bd5d84a06bb6541c2af31dae871728bf856a174f93f4",
102         "0",
103         "46263761741508638697010950048709651021688891777877937875096931459006746039284"),
104     new TestVector2(
105         EllipticCurves.CurveType.NIST_P256,
106         EllipticCurves.PointFormatType.DO_NOT_USE_CRUNCHY_UNCOMPRESSED,
107         "0000000000000000000000000000000000000000000000000000000000000000"
108             + "66485c780e2f83d72433bd5d84a06bb6541c2af31dae871728bf856a174f93f4",
109         "0",
110         "46263761741508638697010950048709651021688891777877937875096931459006746039284"),
111     new TestVector2(
112         EllipticCurves.CurveType.NIST_P256,
113         EllipticCurves.PointFormatType.COMPRESSED,
114         "020000000000000000000000000000000000000000000000000000000000000000",
115         "0",
116         "46263761741508638697010950048709651021688891777877937875096931459006746039284"),
117     // Exceptional point: x==-3
118     new TestVector2(
119         EllipticCurves.CurveType.NIST_P256,
120         EllipticCurves.PointFormatType.UNCOMPRESSED,
121         "04"
122             + "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc"
123             + "19719bebf6aea13f25c96dfd7c71f5225d4c8fc09eb5a0ab9f39e9178e55c121",
124         "115792089210356248762697446949407573530086143415290314195533631308867097853948",
125         "11508551065151498768481026661199445482476508121209842448718573150489103679777"),
126     new TestVector2(
127         EllipticCurves.CurveType.NIST_P256,
128         EllipticCurves.PointFormatType.DO_NOT_USE_CRUNCHY_UNCOMPRESSED,
129         "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc"
130             + "19719bebf6aea13f25c96dfd7c71f5225d4c8fc09eb5a0ab9f39e9178e55c121",
131         "115792089210356248762697446949407573530086143415290314195533631308867097853948",
132         "11508551065151498768481026661199445482476508121209842448718573150489103679777"),
133     new TestVector2(
134         EllipticCurves.CurveType.NIST_P256,
135         EllipticCurves.PointFormatType.COMPRESSED,
136         "03ffffffff00000001000000000000000000000000fffffffffffffffffffffffc",
137         "115792089210356248762697446949407573530086143415290314195533631308867097853948",
138         "11508551065151498768481026661199445482476508121209842448718573150489103679777"),
139     // NIST_P384
140     new TestVector2(
141         EllipticCurves.CurveType.NIST_P384,
142         EllipticCurves.PointFormatType.UNCOMPRESSED,
143         "04aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a"
144             + "385502f25dbf55296c3a545e3872760ab73617de4a96262c6f5d9e98bf9292dc"
145             + "29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e"
146             + "5f",
147         "2624703509579968926862315674456698189185292349110921338781561590"
148             + "0925518854738050089022388053975719786650872476732087",
149         "8325710961489029985546751289520108179287853048861315594709205902"
150             + "480503199884419224438643760392947333078086511627871"),
151     new TestVector2(
152         EllipticCurves.CurveType.NIST_P384,
153         EllipticCurves.PointFormatType.COMPRESSED,
154         "03aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a"
155             + "385502f25dbf55296c3a545e3872760ab7",
156         "2624703509579968926862315674456698189185292349110921338781561590"
157             + "0925518854738050089022388053975719786650872476732087",
158         "8325710961489029985546751289520108179287853048861315594709205902"
159             + "480503199884419224438643760392947333078086511627871"),
160     // x = 0
161     new TestVector2(
162         EllipticCurves.CurveType.NIST_P384,
163         EllipticCurves.PointFormatType.UNCOMPRESSED,
164         "0400000000000000000000000000000000000000000000000000000000000000"
165             + "00000000000000000000000000000000003cf99ef04f51a5ea630ba3f9f960dd"
166             + "593a14c9be39fd2bd215d3b4b08aaaf86bbf927f2c46e52ab06fb742b8850e52"
167             + "1e",
168         "0",
169         "9384923975005507693384933751151973636103286582194273515051780595"
170             + "652610803541482195894618304099771370981414591681054"),
171     new TestVector2(
172         EllipticCurves.CurveType.NIST_P384,
173         EllipticCurves.PointFormatType.COMPRESSED,
174         "0200000000000000000000000000000000000000000000000000000000000000"
175             + "0000000000000000000000000000000000",
176         "0",
177         "9384923975005507693384933751151973636103286582194273515051780595"
178             + "652610803541482195894618304099771370981414591681054"),
179     // x = 2
180     new TestVector2(
181         EllipticCurves.CurveType.NIST_P384,
182         EllipticCurves.PointFormatType.UNCOMPRESSED,
183         "0400000000000000000000000000000000000000000000000000000000000000"
184             + "0000000000000000000000000000000002732152442fb6ee5c3e6ce1d920c059"
185             + "bc623563814d79042b903ce60f1d4487fccd450a86da03f3e6ed525d02017bfd"
186             + "b3",
187         "2",
188         "1772015366480916228638409476801818679957736647795608728422858375"
189             + "4887974043472116432532980617621641492831213601947059"),
190     new TestVector2(
191         EllipticCurves.CurveType.NIST_P384,
192         EllipticCurves.PointFormatType.COMPRESSED,
193         "0300000000000000000000000000000000000000000000000000000000000000"
194             + "0000000000000000000000000000000002",
195         "2",
196         "1772015366480916228638409476801818679957736647795608728422858375"
197             + "4887974043472116432532980617621641492831213601947059"),
198     // x = -3
199     new TestVector2(
200         EllipticCurves.CurveType.NIST_P384,
201         EllipticCurves.PointFormatType.UNCOMPRESSED,
202         "04ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
203             + "feffffffff0000000000000000fffffffc2de9de09a95b74e6b2c430363e1afb"
204             + "8dff7164987a8cfe0a0d5139250ac02f797f81092a9bdc0e09b574a8f43bf80c"
205             + "17",
206         "3940200619639447921227904010014361380507973927046544666794829340"
207             + "4245721771496870329047266088258938001861606973112316",
208         "7066741234775658874139271223692271325950306561732202191471600407"
209             + "582071247913794644254895122656050391930754095909911"),
210     new TestVector2(
211         EllipticCurves.CurveType.NIST_P384,
212         EllipticCurves.PointFormatType.COMPRESSED,
213         "03ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
214             + "feffffffff0000000000000000fffffffc",
215         "3940200619639447921227904010014361380507973927046544666794829340"
216             + "4245721771496870329047266088258938001861606973112316",
217         "7066741234775658874139271223692271325950306561732202191471600407"
218             + "582071247913794644254895122656050391930754095909911"),
219     // NIST_P521
220     new TestVector2(
221         EllipticCurves.CurveType.NIST_P521,
222         EllipticCurves.PointFormatType.UNCOMPRESSED,
223         "0400c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b"
224             + "4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2"
225             + "e5bd66011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd"
226             + "17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94"
227             + "769fd16650",
228         "2661740802050217063228768716723360960729859168756973147706671368"
229             + "4188029449964278084915450806277719023520942412250655586621571135"
230             + "45570916814161637315895999846",
231         "3757180025770020463545507224491183603594455134769762486694567779"
232             + "6155444774405563166912344050129455395621444445372894285225856667"
233             + "29196580810124344277578376784"),
234     new TestVector2(
235         EllipticCurves.CurveType.NIST_P521,
236         EllipticCurves.PointFormatType.COMPRESSED,
237         "0200c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b"
238             + "4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2"
239             + "e5bd66",
240         "2661740802050217063228768716723360960729859168756973147706671368"
241             + "4188029449964278084915450806277719023520942412250655586621571135"
242             + "45570916814161637315895999846",
243         "3757180025770020463545507224491183603594455134769762486694567779"
244             + "6155444774405563166912344050129455395621444445372894285225856667"
245             + "29196580810124344277578376784"),
246     // x = 0
247     new TestVector2(
248         EllipticCurves.CurveType.NIST_P521,
249         EllipticCurves.PointFormatType.UNCOMPRESSED,
250         "0400000000000000000000000000000000000000000000000000000000000000"
251             + "0000000000000000000000000000000000000000000000000000000000000000"
252             + "00000000d20ec9fea6b577c10d26ca1bb446f40b299e648b1ad508aad068896f"
253             + "ee3f8e614bc63054d5772bf01a65d412e0bcaa8e965d2f5d332d7f39f846d440"
254             + "ae001f4f87",
255         "0",
256         "2816414230262626695230339754503506208598534788872316917808418392"
257             + "0894686826982898181454171638541149642517061885689521392260532032"
258             + "30035588176689756661142736775"),
259     new TestVector2(
260         EllipticCurves.CurveType.NIST_P521,
261         EllipticCurves.PointFormatType.COMPRESSED,
262         "0300000000000000000000000000000000000000000000000000000000000000"
263             + "0000000000000000000000000000000000000000000000000000000000000000"
264             + "000000",
265         "0",
266         "2816414230262626695230339754503506208598534788872316917808418392"
267             + "0894686826982898181454171638541149642517061885689521392260532032"
268             + "30035588176689756661142736775"),
269     // x = 1
270     new TestVector2(
271         EllipticCurves.CurveType.NIST_P521,
272         EllipticCurves.PointFormatType.UNCOMPRESSED,
273         "0400000000000000000000000000000000000000000000000000000000000000"
274             + "0000000000000000000000000000000000000000000000000000000000000000"
275             + "0000010010e59be93c4f269c0269c79e2afd65d6aeaa9b701eacc194fb3ee03d"
276             + "f47849bf550ec636ebee0ddd4a16f1cd9406605af38f584567770e3f272d688c"
277             + "832e843564",
278         "1",
279         "2265505274322546447629271557184988697103589068170534253193208655"
280             + "0778100463909972583865730916407864371153050622267306901033104806"
281             + "9570407113457901669103973732"),
282     new TestVector2(
283         EllipticCurves.CurveType.NIST_P521,
284         EllipticCurves.PointFormatType.COMPRESSED,
285         "0200000000000000000000000000000000000000000000000000000000000000"
286             + "0000000000000000000000000000000000000000000000000000000000000000"
287             + "000001",
288         "1",
289         "2265505274322546447629271557184988697103589068170534253193208655"
290             + "0778100463909972583865730916407864371153050622267306901033104806"
291             + "9570407113457901669103973732"),
292     // x = 2
293     new TestVector2(
294         EllipticCurves.CurveType.NIST_P521,
295         EllipticCurves.PointFormatType.UNCOMPRESSED,
296         "0400000000000000000000000000000000000000000000000000000000000000"
297             + "0000000000000000000000000000000000000000000000000000000000000000"
298             + "00000200d9254fdf800496acb33790b103c5ee9fac12832fe546c632225b0f7f"
299             + "ce3da4574b1a879b623d722fa8fc34d5fc2a8731aad691a9a8bb8b554c95a051"
300             + "d6aa505acf",
301         "2",
302         "2911448509017565583245824537994174021964465504209366849707937264"
303             + "0417919148200722009442607963590225526059407040161685364728526719"
304             + "10134103604091376779754756815"),
305     new TestVector2(
306         EllipticCurves.CurveType.NIST_P521,
307         EllipticCurves.PointFormatType.COMPRESSED,
308         "0300000000000000000000000000000000000000000000000000000000000000"
309             + "0000000000000000000000000000000000000000000000000000000000000000"
310             + "000002",
311         "2",
312         "2911448509017565583245824537994174021964465504209366849707937264"
313             + "0417919148200722009442607963590225526059407040161685364728526719"
314             + "10134103604091376779754756815"),
315     // x = -2
316     new TestVector2(
317         EllipticCurves.CurveType.NIST_P521,
318         EllipticCurves.PointFormatType.UNCOMPRESSED,
319         "0401ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
320             + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
321             + "fffffd0010e59be93c4f269c0269c79e2afd65d6aeaa9b701eacc194fb3ee03d"
322             + "f47849bf550ec636ebee0ddd4a16f1cd9406605af38f584567770e3f272d688c"
323             + "832e843564",
324         "6864797660130609714981900799081393217269435300143305409394463459"
325             + "1855431833976560521225596406614545549772963113914808580371219879"
326             + "99716643812574028291115057149",
327         "2265505274322546447629271557184988697103589068170534253193208655"
328             + "0778100463909972583865730916407864371153050622267306901033104806"
329             + "9570407113457901669103973732"),
330     new TestVector2(
331         EllipticCurves.CurveType.NIST_P521,
332         EllipticCurves.PointFormatType.COMPRESSED,
333         "0201ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
334             + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
335             + "fffffd",
336         "6864797660130609714981900799081393217269435300143305409394463459"
337             + "1855431833976560521225596406614545549772963113914808580371219879"
338             + "99716643812574028291115057149",
339         "2265505274322546447629271557184988697103589068170534253193208655"
340             + "0778100463909972583865730916407864371153050622267306901033104806"
341             + "9570407113457901669103973732"),
342   };
343 
344   @Test
testFieldSizeInBytes()345   public void testFieldSizeInBytes() throws Exception {
346     assertThat(
347             EllipticCurves.fieldSizeInBytes(
348                 EllipticCurves.getCurveSpec(EllipticCurves.CurveType.NIST_P256).getCurve()))
349         .isEqualTo(32);
350     assertThat(
351             EllipticCurves.fieldSizeInBytes(
352                 EllipticCurves.getCurveSpec(EllipticCurves.CurveType.NIST_P384).getCurve()))
353         .isEqualTo(48);
354     assertThat(
355             EllipticCurves.fieldSizeInBytes(
356                 EllipticCurves.getCurveSpec(EllipticCurves.CurveType.NIST_P521).getCurve()))
357         .isEqualTo(66);
358   }
359 
360   @Test
testPointDecode()361   public void testPointDecode() throws Exception {
362     for (TestVector2 test : testVectors2) {
363       EllipticCurve curve = EllipticCurves.getCurveSpec(test.curve).getCurve();
364       ECPoint p = EllipticCurves.pointDecode(curve, test.format, test.encoded);
365       assertEquals(p.getAffineX(), test.x);
366       assertEquals(p.getAffineY(), test.y);
367     }
368   }
369 
370   @Test
testPointEncode()371   public void testPointEncode() throws Exception {
372     for (TestVector2 test : testVectors2) {
373       EllipticCurve curve = EllipticCurves.getCurveSpec(test.curve).getCurve();
374       ECPoint p = new ECPoint(test.x, test.y);
375       byte[] encoded = EllipticCurves.pointEncode(curve, test.format, p);
376       assertEquals(Hex.encode(encoded), Hex.encode(test.encoded));
377     }
378   }
379 
380   @Test
pointEncode_failsIfPointIsNotOnCurve()381   public void pointEncode_failsIfPointIsNotOnCurve() throws Exception {
382     // Same an entry of testVectors2, but the value of y has been incremented by 1.
383     BigInteger x = new BigInteger(
384         "79974177209371530366349631093481213364328002500948308276357601809416549347930");
385     BigInteger y = new BigInteger(
386            "11093679777528052772423074391650378811758820120351664471899251711300542565880");
387     // Adding one to y make the point not be on the curve.
388     assertThrows(
389         GeneralSecurityException.class,
390         () ->
391             EllipticCurves.pointEncode(
392                 EllipticCurves.CurveType.NIST_P256,
393                 EllipticCurves.PointFormatType.UNCOMPRESSED,
394                 new ECPoint(x, y)));
395   }
396 
397   @Test
pointDecode_uncompressed_failsIfPointIsNotOnCurve()398   public void pointDecode_uncompressed_failsIfPointIsNotOnCurve() throws Exception {
399     // Same an entry of testVectors2, but the last byte is changed from f7 to f6
400     byte[] encoded =
401         Hex.decode(
402             "04"
403                 + "b0cfc7bc02fc980d858077552947ffb449b10df8949dee4e56fe21e016dcb25a"
404                 + "1886ccdca5487a6772f9401888203f90587cc00a730e2b83d5c6f89b3b568df6");
405     // Adding one to y make the point not be on the curve.
406     assertThrows(GeneralSecurityException.class,
407         () -> EllipticCurves.pointDecode(EllipticCurves.CurveType.NIST_P256,
408             EllipticCurves.PointFormatType.UNCOMPRESSED, encoded));
409   }
410 
411   @Test
pointDecode_crunchy_failsIfPointIsNotOnCurve()412   public void pointDecode_crunchy_failsIfPointIsNotOnCurve() throws Exception {
413     // Same as an entry of testVectors2, but the last byte is changed from f4 to f5
414     byte[] encoded =
415         Hex.decode(
416             "0000000000000000000000000000000000000000000000000000000000000000"
417                 + "66485c780e2f83d72433bd5d84a06bb6541c2af31dae871728bf856a174f93f5");
418     // Adding one to y make the point not be on the curve.
419     assertThrows(GeneralSecurityException.class,
420         () -> EllipticCurves.pointDecode(EllipticCurves.CurveType.NIST_P256,
421             EllipticCurves.PointFormatType.DO_NOT_USE_CRUNCHY_UNCOMPRESSED, encoded));
422   }
423 
424   @Test
pointDecode_compressed_failsIfEncodingIsInvalid()425   public void pointDecode_compressed_failsIfEncodingIsInvalid() throws Exception {
426     // Same as an entry of testVectors2, but the last byte is changed from 00 to 01
427     byte[] encoded =
428         Hex.decode("020000000000000000000000000000000000000000000000000000000000000001");
429     assertThrows(GeneralSecurityException.class,
430         () -> EllipticCurves.pointDecode(EllipticCurves.CurveType.NIST_P256,
431             EllipticCurves.PointFormatType.COMPRESSED, encoded));
432   }
433 
434   /** A class to store a pair of valid Ecdsa signature in IEEE_P1363 and DER format. */
435   protected static class EcdsaIeeeDer {
436     public String hexIeee;
437     public String hexDer;
438 
EcdsaIeeeDer(String hexIeee, String hexDer)439     protected EcdsaIeeeDer(String hexIeee, String hexDer) {
440       this.hexIeee = hexIeee;
441       this.hexDer = hexDer;
442     }
443   };
444 
445   protected static final EcdsaIeeeDer[] ieeeDerTestVector =
446       new EcdsaIeeeDer[] {
447         new EcdsaIeeeDer( // normal case, short-form length
448             "0102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f10",
449             "302402100102030405060708090a0b0c0d0e0f1002100102030405060708090a0b0c0d0e0f10"),
450         new EcdsaIeeeDer( // normal case, long-form length
451             "010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000203010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000203",
452             "30818802420100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000002030242010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000100000001000000010000000203"),
453         new EcdsaIeeeDer( // zero prefix.
454             "0002030405060708090a0b0c0d0e0f100002030405060708090a0b0c0d0e0f10",
455             "3022020f02030405060708090a0b0c0d0e0f10020f02030405060708090a0b0c0d0e0f10"),
456         new EcdsaIeeeDer( // highest bit is set.
457             "00ff030405060708090a0b0c0d0e0f1000ff030405060708090a0b0c0d0e0f10",
458             "3024021000ff030405060708090a0b0c0d0e0f10021000ff030405060708090a0b0c0d0e0f10"),
459         new EcdsaIeeeDer( // highest bit is set, full length.
460             "ff02030405060708090a0b0c0d0e0f10ff02030405060708090a0b0c0d0e0f10",
461             "3026021100ff02030405060708090a0b0c0d0e0f10021100ff02030405060708090a0b0c0d0e0f10"),
462         new EcdsaIeeeDer( // all zeros.
463             "0000000000000000000000000000000000000000000000000000000000000000", "3006020100020100"),
464       };
465 
466   @Test
testEcdsaIeee2Der()467   public void testEcdsaIeee2Der() throws Exception {
468     for (EcdsaIeeeDer test : ieeeDerTestVector) {
469       assertArrayEquals(
470           Hex.decode(test.hexDer), EllipticCurves.ecdsaIeee2Der(Hex.decode(test.hexIeee)));
471     }
472   }
473 
474   @Test
testEcdsaDer2Ieee()475   public void testEcdsaDer2Ieee() throws Exception {
476     for (EcdsaIeeeDer test : ieeeDerTestVector) {
477       assertArrayEquals(
478           Hex.decode(test.hexIeee),
479           EllipticCurves.ecdsaDer2Ieee(Hex.decode(test.hexDer), test.hexIeee.length() / 2));
480     }
481   }
482 
483   protected static final String[] invalidEcdsaDers =
484       new String[] {
485         "2006020101020101", // 1st byte is not 0x30 (SEQUENCE tag)
486         "3006050101020101", // 3rd byte is not 0x02 (INTEGER tag)
487         "3006020101050101", // 6th byte is not 0x02 (INTEGER tag)
488         "308206020101020101", // long form length is not 0x81
489         "30ff020101020101", // invalid total length
490         "3006020201020101", // invalid rLength
491         "3006020101020201", // invalid sLength
492         "30060201ff020101", // no extra zero when highest bit of r is set
493         "30060201010201ff", // no extra zero when highest bit of s is set
494       };
495 
496   @Test
testIsValidDerEncoding()497   public void testIsValidDerEncoding() throws Exception {
498     for (String der : invalidEcdsaDers) {
499       assertFalse(EllipticCurves.isValidDerEncoding(Hex.decode(der)));
500     }
501   }
502 
503   @Test
testComputeSharedSecretWithWycheproofTestVectors()504   public void testComputeSharedSecretWithWycheproofTestVectors() throws Exception {
505     if (TestUtil.isTsan()) {
506       return;
507     }
508 
509     // NOTE(bleichen): Instead of ecdh_test.json it might be easier to use the
510     //   files ecdh_<curve>_ecpoint.json, which encode the public key point just as DER
511     //   encoded bitsequence.
512     JsonObject json =
513         WycheproofTestUtil.readJson("../wycheproof/testvectors/ecdh_test.json");
514     int errors = 0;
515     JsonArray testGroups = json.get("testGroups").getAsJsonArray();
516     for (int i = 0; i < testGroups.size(); i++) {
517       JsonObject group = testGroups.get(i).getAsJsonObject();
518       JsonArray tests = group.get("tests").getAsJsonArray();
519       String curve = group.get("curve").getAsString();
520       if (!curve.equals("secp256r1") && !curve.equals("secp384r1") && !curve.equals("secp521r1")) {
521         // Only NIST curves P-256, P-384 and P-521 are supported.
522         continue;
523       }
524       EllipticCurves.CurveType curveType = WycheproofTestUtil.getCurveType(curve);
525       for (int j = 0; j < tests.size(); j++) {
526         JsonObject testcase = tests.get(j).getAsJsonObject();
527         String tcId =
528             String.format(
529                 "testcase %d (%s)",
530                 testcase.get("tcId").getAsInt(), testcase.get("comment").getAsString());
531         String result = testcase.get("result").getAsString();
532         String hexPubKey = testcase.get("public").getAsString();
533         String expectedSharedSecret = testcase.get("shared").getAsString();
534         String hexPrivKey = testcase.get("private").getAsString();
535         if (hexPrivKey.length() % 2 == 1) {
536           hexPrivKey = "0" + hexPrivKey;
537         }
538         KeyFactory kf = EngineFactory.KEY_FACTORY.getInstance("EC");
539         try {
540           ECPrivateKey privKey = EllipticCurves.getEcPrivateKey(curveType, Hex.decode(hexPrivKey));
541 
542           X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(Hex.decode(hexPubKey));
543           ECPublicKey pubKey = (ECPublicKey) kf.generatePublic(x509keySpec);
544           String sharedSecret = Hex.encode(EllipticCurves.computeSharedSecret(privKey, pubKey));
545           if (result.equals("invalid")) {
546             if (expectedSharedSecret.equals(sharedSecret)
547                 && WycheproofTestUtil.checkFlags(
548                     testcase, "WrongOrder", "WeakPublicKey", "UnnamedCurve")) {
549               System.out.println(
550                   tcId + " accepted invalid parameters but shared secret is correct.");
551             } else {
552               System.out.println(
553                   "FAIL " + tcId + " accepted invalid parameters, shared secret: " + sharedSecret);
554               errors++;
555             }
556           } else if (!expectedSharedSecret.equals(sharedSecret)) {
557             System.out.println(
558                 "FAIL "
559                     + tcId
560                     + " incorrect shared secret, computed: "
561                     + sharedSecret
562                     + " expected: "
563                     + expectedSharedSecret);
564             errors++;
565           }
566         } catch (GeneralSecurityException ex) {
567           System.out.println(tcId + " threw exception: " + ex.toString());
568           if (result.equals("valid")) {
569             System.out.println("FAIL " + tcId + " exception: " + ex.toString());
570             ex.printStackTrace();
571             errors++;
572           }
573         } catch (Exception ex) {
574           // Other exceptions typically indicate that something is wrong with the implementation.
575           System.out.println("FAIL " + tcId + " exception: " + ex.toString());
576           ex.printStackTrace();
577           errors++;
578         }
579       }
580     }
581     assertEquals(0, errors);
582   }
583 
584   @Test
computeSharedSecretWithPublicPoint()585   public void computeSharedSecretWithPublicPoint() throws Exception {
586     // test vector from wycheproof's ecdh_secp256r1_ecpoint_test.json, normal case.
587     ECPrivateKey ecPrivateKey =
588         EllipticCurves.getEcPrivateKey(
589             EllipticCurves.CurveType.NIST_P256,
590             Hex.decode("0612465c89a023ab17855b0a6bcebfd3febb53aef84138647b5352e02c10c346"));
591     ECPoint ecPublicPoint =
592         EllipticCurves.pointDecode(
593             EllipticCurves.CurveType.NIST_P256,
594             EllipticCurves.PointFormatType.UNCOMPRESSED,
595             Hex.decode(
596                 "0462d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f26ac333a93"
597                     + "a9e70a81cd5a95b5bf8d13990eb741c8c38872b4a07d275a014e30cf"));
598     byte[] expected =
599         Hex.decode("53020d908b0219328b658b525f26780e3ae12bcd952bb25a93bc0895e1714285");
600 
601     byte[] sharedSecret = EllipticCurves.computeSharedSecret(ecPrivateKey, ecPublicPoint);
602     assertThat(sharedSecret).isEqualTo(expected);
603 
604     ECPoint publicPointNotOnCurve =
605         new ECPoint(ecPublicPoint.getAffineX(), ecPublicPoint.getAffineY().add(BigInteger.ONE));
606     assertThrows(
607         GeneralSecurityException.class,
608         () -> EllipticCurves.computeSharedSecret(ecPrivateKey, publicPointNotOnCurve));
609   }
610 
611   @Test
computeSharedSecretWithPublicPointP256()612   public void computeSharedSecretWithPublicPointP256() throws Exception {
613     // test vector from golang's crypto/ecdh/ecdh_test.go.
614     ECPrivateKey ecPrivateKey =
615         EllipticCurves.getEcPrivateKey(
616             EllipticCurves.CurveType.NIST_P256,
617             Hex.decode("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534"));
618     ECPoint ecPublicPoint =
619         EllipticCurves.pointDecode(
620             EllipticCurves.CurveType.NIST_P256,
621             EllipticCurves.PointFormatType.UNCOMPRESSED,
622             Hex.decode(
623                 "04700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287"
624                     + "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac"));
625     byte[] expected =
626         Hex.decode("46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b");
627 
628     byte[] sharedSecret = EllipticCurves.computeSharedSecret(ecPrivateKey, ecPublicPoint);
629     assertThat(sharedSecret).isEqualTo(expected);
630   }
631 
632   @Test
computeSharedSecretWithPublicPointP384()633   public void computeSharedSecretWithPublicPointP384() throws Exception {
634     // test vector from golang's crypto/ecdh/ecdh_test.go.
635     ECPrivateKey ecPrivateKey =
636         EllipticCurves.getEcPrivateKey(
637             EllipticCurves.CurveType.NIST_P384,
638             Hex.decode(
639                 "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b15618b6818a661774ad463b205da88cf699ab4d43c9cf98a1"));
640     ECPoint ecPublicPoint =
641         EllipticCurves.pointDecode(
642             EllipticCurves.CurveType.NIST_P384,
643             EllipticCurves.PointFormatType.UNCOMPRESSED,
644             Hex.decode(
645                 "04a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e764592efda27fe7513272734466b400091adbf2d68c58e0c50066"
646                     + "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b661efedf243451915ed0905a32b060992b468c64766fc8437a"));
647     byte[] expected =
648         Hex.decode(
649             "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f40ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1");
650 
651     byte[] sharedSecret = EllipticCurves.computeSharedSecret(ecPrivateKey, ecPublicPoint);
652     assertThat(sharedSecret).isEqualTo(expected);
653   }
654 
655   @Test
computeSharedSecretWithPublicPointP521()656   public void computeSharedSecretWithPublicPointP521() throws Exception {
657     // test vector from golang's crypto/ecdh/ecdh_test.go.
658     ECPrivateKey ecPrivateKey =
659         EllipticCurves.getEcPrivateKey(
660             EllipticCurves.CurveType.NIST_P521,
661             Hex.decode(
662                 "017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4eac6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47"));
663     ECPoint ecPublicPoint =
664         EllipticCurves.pointDecode(
665             EllipticCurves.CurveType.NIST_P521,
666             EllipticCurves.PointFormatType.UNCOMPRESSED,
667             Hex.decode(
668                 "0400685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a9490340854334b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d"
669                     + "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676"));
670     byte[] expected =
671         Hex.decode(
672             "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e136672d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831");
673 
674     byte[] sharedSecret = EllipticCurves.computeSharedSecret(ecPrivateKey, ecPublicPoint);
675     assertThat(sharedSecret).isEqualTo(expected);
676   }
677 
678   @Test
validateSharedSecret()679   public void validateSharedSecret() throws Exception {
680     // test vector from wycheproof's ecdh_secp256r1_ecpoint_test.json, normal case.
681     ECPrivateKey ecPrivateKey =
682         EllipticCurves.getEcPrivateKey(
683             EllipticCurves.CurveType.NIST_P256,
684             Hex.decode("0612465c89a023ab17855b0a6bcebfd3febb53aef84138647b5352e02c10c346"));
685     byte[] sharedSecret =
686         Hex.decode("53020d908b0219328b658b525f26780e3ae12bcd952bb25a93bc0895e1714285");
687     EllipticCurves.validateSharedSecret(sharedSecret, ecPrivateKey);
688 
689     // Most byte strings shorter than sharedSecret are valid.
690     byte[] emptySharedSecret = new byte[0];
691     EllipticCurves.validateSharedSecret(emptySharedSecret, ecPrivateKey);
692     byte[] anotherSharedSecret = Hex.decode("00112233445566778899aabbccddeeff");
693     EllipticCurves.validateSharedSecret(anotherSharedSecret, ecPrivateKey);
694     byte[] ffSharedSecret = Hex.decode("ffffffffffffffffffffffffffffffffffffffff");
695     EllipticCurves.validateSharedSecret(ffSharedSecret, ecPrivateKey);
696 
697     // The modulusMinus1 is not a valid secret, because computeY fails.
698     byte[] modulusMinus1 =
699         Hex.decode("00ffffffff00000001000000000000000000000000fffffffffffffffffffffffe");
700     assertThrows(
701         GeneralSecurityException.class,
702         () -> EllipticCurves.validateSharedSecret(modulusMinus1, ecPrivateKey));
703 
704     // The modulus is not a valid secret, because it is out of range.
705     byte[] modulus =
706         Hex.decode("00ffffffff00000001000000000000000000000000ffffffffffffffffffffffff");
707     assertThrows(
708         GeneralSecurityException.class,
709         () -> EllipticCurves.validateSharedSecret(modulus, ecPrivateKey));
710   }
711 }
712