| /** |
| * 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"); |
| } |
| |
| } |