aboutsummaryrefslogtreecommitdiff
path: root/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java')
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java209
1 files changed, 209 insertions, 0 deletions
diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
new file mode 100644
index 0000000..5a8b877
--- /dev/null
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
@@ -0,0 +1,209 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.security.wycheproof;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.X509EncodedKeySpec;
+import javax.crypto.KeyAgreement;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** This test uses test vectors in JSON format to check implementations of ECDH. */
+@RunWith(JUnit4.class)
+public class JsonEcdhTest {
+
+ /** Convenience mehtod to get a String from a JsonObject */
+ protected static String getString(JsonObject object, String name) throws Exception {
+ return object.get(name).getAsString();
+ }
+
+ /** Convenience method to get a BigInteger from a JsonObject */
+ protected static BigInteger getBigInteger(JsonObject object, String name) throws Exception {
+ return JsonUtil.asBigInteger(object.get(name));
+ }
+
+ /** Convenience method to get a byte array from a JsonObject */
+ protected static byte[] getBytes(JsonObject object, String name) throws Exception {
+ return JsonUtil.asByteArray(object.get(name));
+ }
+
+ /**
+ * Example for test vector
+ * {
+ * "algorithm" : "ECDH",
+ * "header" : [],
+ * "notes" : {
+ * "AddSubChain" : "The private key has a special value....",
+ * }
+ * "generatorVersion" : "0.7",
+ * "numberOfTests" : 308,
+ * "testGroups" : [
+ * {
+ * "type" : "EcdhTest",
+ * "tests" : [
+ * {
+ * "comment" : "normal case",
+ * "curve" : "secp224r1",
+ * "private" : "565577a49415ca761a0322ad54e4ad0ae7625174baf372c2816f5328",
+ * "public" : "30...",
+ * "result" : "valid",
+ * "shared" : "b8ecdb552d39228ee332bafe4886dbff272f7109edf933bc7542bd4f",
+ * "tcId" : 1
+ * },
+ * ...
+ **/
+ public void testEcdhComp(String filename) throws Exception {
+ JsonObject test = JsonUtil.getTestVectors(filename);
+
+ // This test expects test vectors as defined in wycheproof/schemas/ecdh_test_schema.json.
+ // In particular, this means that the public keys use X509 encoding.
+ // Test vectors with different encodings of the keys have a different schema.
+ final String expectedSchema = "ecdh_test_schema.json";
+ String schema = test.get("schema").getAsString();
+ assertEquals("Unexpected schema in file:" + filename, expectedSchema, schema);
+
+ int numTests = test.get("numberOfTests").getAsInt();
+ int passedTests = 0;
+ int rejectedTests = 0; // invalid test vectors leading to exceptions
+ int skippedTests = 0; // valid test vectors leading to exceptions
+ int errors = 0;
+ for (JsonElement g : test.getAsJsonArray("testGroups")) {
+ JsonObject group = g.getAsJsonObject();
+ String curve = getString(group, "curve");
+ for (JsonElement t : group.getAsJsonArray("tests")) {
+ JsonObject testcase = t.getAsJsonObject();
+ int tcid = testcase.get("tcId").getAsInt();
+ String comment = getString(testcase, "comment");
+ BigInteger priv = getBigInteger(testcase, "private");
+ byte[] publicEncoded = getBytes(testcase, "public");
+ String result = getString(testcase, "result");
+ String expectedHex = getString(testcase, "shared");
+ KeyFactory kf = KeyFactory.getInstance("EC");
+ try {
+ ECPrivateKeySpec spec = new ECPrivateKeySpec(priv, EcUtil.getCurveSpecRef(curve));
+ PrivateKey privKey = kf.generatePrivate(spec);
+ X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(publicEncoded);
+ PublicKey pubKey = kf.generatePublic(x509keySpec);
+ KeyAgreement ka = KeyAgreement.getInstance("ECDH");
+ ka.init(privKey);
+ ka.doPhase(pubKey, true);
+ String sharedHex = TestUtil.bytesToHex(ka.generateSecret());
+ if (result.equals("invalid")) {
+ System.out.println(
+ "Computed ECDH with invalid parameters"
+ + " tcId:"
+ + tcid
+ + " comment:"
+ + comment
+ + " shared:"
+ + sharedHex);
+ errors++;
+ } else if (!expectedHex.equals(sharedHex)) {
+ System.out.println(
+ "Incorrect ECDH computation"
+ + " tcId:"
+ + tcid
+ + " comment:"
+ + comment
+ + "\nshared:"
+ + sharedHex
+ + "\nexpected:"
+ + expectedHex);
+ errors++;
+ } else {
+ passedTests++;
+ }
+ } catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException ex) {
+ // These are the exception that we expect to see when a curve is not implemented
+ // or when a key is not valid.
+ if (result.equals("valid")) {
+ skippedTests++;
+ } else {
+ rejectedTests++;
+ }
+ } catch (Exception ex) {
+ // Other exceptions typically indicate that something is wrong with the implementation.
+ System.out.println(
+ "Test vector with tcId:" + tcid + " comment:" + comment + " throws:" + ex.toString());
+ errors++;
+ }
+ }
+ }
+ assertEquals(0, errors);
+ assertEquals(numTests, passedTests + rejectedTests + skippedTests);
+ }
+
+ @Test
+ public void testSecp224r1() throws Exception {
+ testEcdhComp("ecdh_secp224r1_test.json");
+ }
+
+ @Test
+ public void testSecp256r1() throws Exception {
+ testEcdhComp("ecdh_secp256r1_test.json");
+ }
+
+ @Test
+ public void testSecp384r1() throws Exception {
+ testEcdhComp("ecdh_secp384r1_test.json");
+ }
+
+ @Test
+ public void testSecp521r1() throws Exception {
+ testEcdhComp("ecdh_secp521r1_test.json");
+ }
+
+ @Test
+ public void testSecp256k1() throws Exception {
+ testEcdhComp("ecdh_secp256k1_test.json");
+ }
+
+ @Test
+ public void testBrainpoolP224r1() throws Exception {
+ testEcdhComp("ecdh_brainpoolP224r1_test.json");
+ }
+
+ @Test
+ public void testBrainpoolP256r1() throws Exception {
+ testEcdhComp("ecdh_brainpoolP256r1_test.json");
+ }
+
+ @Test
+ public void testBrainpoolP320r1() throws Exception {
+ testEcdhComp("ecdh_brainpoolP320r1_test.json");
+ }
+
+ @Test
+ public void testBrainpoolP384r1() throws Exception {
+ testEcdhComp("ecdh_brainpoolP384r1_test.json");
+ }
+
+ @Test
+ public void testBrainpoolP512r1() throws Exception {
+ testEcdhComp("ecdh_brainpoolP512r1_test.json");
+ }
+
+}