• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Licensed under the Apache License, Version 2.0 (the "License");
3  * you may not use this file except in compliance with the License.
4  * You may obtain a copy of the License at
5  *
6  *   http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 package com.google.security.wycheproof;
15 
16 import static org.junit.Assert.assertEquals;
17 
18 import com.google.gson.JsonElement;
19 import com.google.gson.JsonObject;
20 import java.math.BigInteger;
21 import java.security.InvalidKeyException;
22 import java.security.KeyFactory;
23 import java.security.NoSuchAlgorithmException;
24 import java.security.PrivateKey;
25 import java.security.PublicKey;
26 import java.security.spec.ECPrivateKeySpec;
27 import java.security.spec.InvalidKeySpecException;
28 import java.security.spec.X509EncodedKeySpec;
29 import javax.crypto.KeyAgreement;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.junit.runners.JUnit4;
33 
34 /** This test uses test vectors in JSON format to check implementations of ECDH. */
35 @RunWith(JUnit4.class)
36 public class JsonEcdhTest {
37 
38   /** Convenience mehtod to get a String from a JsonObject */
getString(JsonObject object, String name)39   protected static String getString(JsonObject object, String name) throws Exception {
40     return object.get(name).getAsString();
41   }
42 
43   /** Convenience method to get a BigInteger from a JsonObject */
getBigInteger(JsonObject object, String name)44   protected static BigInteger getBigInteger(JsonObject object, String name) throws Exception {
45     return JsonUtil.asBigInteger(object.get(name));
46   }
47 
48   /** Convenience method to get a byte array from a JsonObject */
getBytes(JsonObject object, String name)49   protected static byte[] getBytes(JsonObject object, String name) throws Exception {
50     return JsonUtil.asByteArray(object.get(name));
51   }
52 
53   /**
54    * Example for test vector
55    * {
56    * "algorithm" : "ECDH",
57    * "header" : [],
58    * "notes" : {
59    *   "AddSubChain" : "The private key has a special value....",
60    * }
61    * "generatorVersion" : "0.7",
62    * "numberOfTests" : 308,
63    * "testGroups" : [
64    *   {
65    *     "type" : "EcdhTest",
66    *     "tests" : [
67    *        {
68    *         "comment" : "normal case",
69    *         "curve" : "secp224r1",
70    *         "private" : "565577a49415ca761a0322ad54e4ad0ae7625174baf372c2816f5328",
71    *         "public" : "30...",
72    *         "result" : "valid",
73    *         "shared" : "b8ecdb552d39228ee332bafe4886dbff272f7109edf933bc7542bd4f",
74    *         "tcId" : 1
75    *        },
76    *     ...
77    **/
testEcdhComp(String filename)78   public void testEcdhComp(String filename) throws Exception {
79     JsonObject test = JsonUtil.getTestVectors(filename);
80 
81     // This test expects test vectors as defined in wycheproof/schemas/ecdh_test_schema.json.
82     // In particular, this means that the public keys use X509 encoding.
83     // Test vectors with different encodings of the keys have a different schema.
84     final String expectedSchema = "ecdh_test_schema.json";
85     String schema = test.get("schema").getAsString();
86     assertEquals("Unexpected schema in file:" + filename, expectedSchema, schema);
87 
88     int numTests = test.get("numberOfTests").getAsInt();
89     int passedTests = 0;
90     int rejectedTests = 0;  // invalid test vectors leading to exceptions
91     int skippedTests = 0;  // valid test vectors leading to exceptions
92     int errors = 0;
93     for (JsonElement g : test.getAsJsonArray("testGroups")) {
94       JsonObject group = g.getAsJsonObject();
95       String curve = getString(group, "curve");
96       for (JsonElement t : group.getAsJsonArray("tests")) {
97         JsonObject testcase = t.getAsJsonObject();
98         int tcid = testcase.get("tcId").getAsInt();
99         String comment = getString(testcase, "comment");
100         BigInteger priv = getBigInteger(testcase, "private");
101         byte[] publicEncoded = getBytes(testcase, "public");
102         String result = getString(testcase, "result");
103         String expectedHex = getString(testcase, "shared");
104         KeyFactory kf = KeyFactory.getInstance("EC");
105         try {
106           ECPrivateKeySpec spec = new ECPrivateKeySpec(priv, EcUtil.getCurveSpecRef(curve));
107           PrivateKey privKey = kf.generatePrivate(spec);
108           X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(publicEncoded);
109           PublicKey pubKey = kf.generatePublic(x509keySpec);
110           KeyAgreement ka = KeyAgreement.getInstance("ECDH");
111           ka.init(privKey);
112           ka.doPhase(pubKey, true);
113           String sharedHex = TestUtil.bytesToHex(ka.generateSecret());
114           if (result.equals("invalid")) {
115             System.out.println(
116                 "Computed ECDH with invalid parameters"
117                     + " tcId:"
118                     + tcid
119                     + " comment:"
120                     + comment
121                     + " shared:"
122                     + sharedHex);
123             errors++;
124           } else if (!expectedHex.equals(sharedHex)) {
125             System.out.println(
126                 "Incorrect ECDH computation"
127                     + " tcId:"
128                     + tcid
129                     + " comment:"
130                     + comment
131                     + "\nshared:"
132                     + sharedHex
133                     + "\nexpected:"
134                     + expectedHex);
135             errors++;
136           } else {
137             passedTests++;
138           }
139         } catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException ex) {
140           // These are the exception that we expect to see when a curve is not implemented
141           // or when a key is not valid.
142           if (result.equals("valid")) {
143             skippedTests++;
144           } else {
145             rejectedTests++;
146           }
147         } catch (Exception ex) {
148           // Other exceptions typically indicate that something is wrong with the implementation.
149           System.out.println(
150               "Test vector with tcId:" + tcid + " comment:" + comment + " throws:" + ex.toString());
151           errors++;
152         }
153       }
154     }
155     assertEquals(0, errors);
156     assertEquals(numTests, passedTests + rejectedTests + skippedTests);
157   }
158 
159   @Test
testSecp224r1()160   public void testSecp224r1() throws Exception {
161     testEcdhComp("ecdh_secp224r1_test.json");
162   }
163 
164   @Test
testSecp256r1()165   public void testSecp256r1() throws Exception {
166     testEcdhComp("ecdh_secp256r1_test.json");
167   }
168 
169   @Test
testSecp384r1()170   public void testSecp384r1() throws Exception {
171     testEcdhComp("ecdh_secp384r1_test.json");
172   }
173 
174   @Test
testSecp521r1()175   public void testSecp521r1() throws Exception {
176     testEcdhComp("ecdh_secp521r1_test.json");
177   }
178 
179   @Test
testSecp256k1()180   public void testSecp256k1() throws Exception {
181     testEcdhComp("ecdh_secp256k1_test.json");
182   }
183 
184   @Test
testBrainpoolP224r1()185   public void testBrainpoolP224r1() throws Exception {
186     testEcdhComp("ecdh_brainpoolP224r1_test.json");
187   }
188 
189   @Test
testBrainpoolP256r1()190   public void testBrainpoolP256r1() throws Exception {
191     testEcdhComp("ecdh_brainpoolP256r1_test.json");
192   }
193 
194   @Test
testBrainpoolP320r1()195   public void testBrainpoolP320r1() throws Exception {
196     testEcdhComp("ecdh_brainpoolP320r1_test.json");
197   }
198 
199   @Test
testBrainpoolP384r1()200   public void testBrainpoolP384r1() throws Exception {
201     testEcdhComp("ecdh_brainpoolP384r1_test.json");
202   }
203 
204   @Test
testBrainpoolP512r1()205   public void testBrainpoolP512r1() throws Exception {
206     testEcdhComp("ecdh_brainpoolP512r1_test.json");
207   }
208 
209 }
210