Add Web Crypto API tests and Java tests using test vectors (#35)
* Add test vectors * Add Java tests using test vectors * Add Web Crypto API tests.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* 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 org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
import org.junit.runners.Suite.SuiteClasses;
|
||||
|
||||
/** Provider independent tests using the JSON test vectors */
|
||||
@RunWith(Suite.class)
|
||||
@SuiteClasses({JsonSignatureTest.class})
|
||||
public final class JsonTest {}
|
||||
@@ -114,6 +114,41 @@ public class AesGcmTest extends TestCase {
|
||||
"1a0293d8f90219058902139013908190bc490890d3ff12a3",
|
||||
"64069c2d58690561f27ee199e6b479b6369eec688672bde9",
|
||||
"9b7abadd6e69c1d9ec925786534f5075"),
|
||||
// GCM uses GHASH to compute the initial counter J0 if the nonce is not 12 bytes long.
|
||||
// The counter is incremented modulo 2^32 in counter mode. The following test vectors verify
|
||||
// the behavior of an implementation for initial counter values J0 close to a 2^32 limit.
|
||||
// J0:00000000000000000000000000000000
|
||||
new GcmTestVector(
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"00112233445566778899aabbccddeeff",
|
||||
"7b95b8c356810a84711d68150a1b7750",
|
||||
"",
|
||||
"84d4c9c08b4f482861e3a9c6c35bc4d91df927374513bfd49f436bd73f325285daef4ff7e13d46a6",
|
||||
"213a3cb93855d18e69337eee66aeec07"),
|
||||
// J0:ffffffffffffffffffffffffffffffff
|
||||
new GcmTestVector(
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"00112233445566778899aabbccddeeff",
|
||||
"1a552e67cdc4dc1a33b824874ebf0bed",
|
||||
"",
|
||||
"948ca37a8e6649e88aeffb1c598f3607007702417ea0e0bc3c60ad5a949886de968cf53ea6462aed",
|
||||
"99b381bfa2af9751c39d1b6e86d1be6a"),
|
||||
// J0:000102030405060708090a0bffffffff
|
||||
new GcmTestVector(
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"00112233445566778899aabbccddeeff",
|
||||
"99821c2dd5daecded07300f577f7aff1",
|
||||
"",
|
||||
"127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4af37d0fe9af033052bd537c4ae978f60",
|
||||
"07eb2fe4a958f8434d40684899507c7c"),
|
||||
// J0:000102030405060708090a0bfffffffe
|
||||
new GcmTestVector(
|
||||
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"00112233445566778899aabbccddeeff",
|
||||
"5e4a3900142358d1c774d8d124d8d27d",
|
||||
"",
|
||||
"0cf6ae47156b14dce03c8a07a2e172b1127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4",
|
||||
"f145c2dcaf339eede427be934357eac0"),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -612,7 +647,6 @@ public class AesGcmTest extends TestCase {
|
||||
* <p>The test is slow as we have to encrypt 2^32 blocks.
|
||||
*/
|
||||
// TODO(quannguyen): Is there a faster way to test it?
|
||||
/*
|
||||
@ExcludedTest(
|
||||
providers = {ProviderType.CONSCRYPT},
|
||||
comment = "Conscrypt doesn't support streaming, would crash")
|
||||
@@ -637,5 +671,4 @@ public class AesGcmTest extends TestCase {
|
||||
System.out.println("testWrappedAroundcounter:" + expected.toString());
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ public class DhTest extends TestCase {
|
||||
BigInteger p = priv.getParams().getP();
|
||||
BigInteger g = priv.getParams().getG();
|
||||
int keySize = p.bitLength();
|
||||
assertEquals("wrong key size", keySize, expectedKeySize);
|
||||
assertEquals("wrong key size", expectedKeySize, keySize);
|
||||
|
||||
// Checks the key size of the private key.
|
||||
// NIST SP 800-56A requires that x is in the range (1, q-1).
|
||||
@@ -322,6 +322,7 @@ public class DhTest extends TestCase {
|
||||
* for each new key.
|
||||
*/
|
||||
@SuppressWarnings("InsecureCryptoUsage")
|
||||
@SlowTest(providers = {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE})
|
||||
public void testKeyPairGenerator() throws Exception {
|
||||
int keySize = 1024;
|
||||
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DH");
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google Inc. All rights reserved.
|
||||
* 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 java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* This test uses test vectors in JSON format to check digital signature schemes.
|
||||
* There are still a lot of open questions, e.g. the format for the test vectors is not
|
||||
* yet finalized. Therefore, we are not integrating the tests here into other tests
|
||||
*/
|
||||
public class JsonSignatureTest extends TestCase {
|
||||
|
||||
static protected String getString(JsonObject object, String name) throws Exception {
|
||||
return object.get(name).getAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wycheproof represents BigIntegers as hexadecimal strings, using bigendian order and
|
||||
* twos complement representation.
|
||||
*/
|
||||
static protected BigInteger getBigInteger(JsonObject object, String name) throws Exception {
|
||||
String hex = getString(object, name);
|
||||
byte[] bytes = TestUtil.hexToBytes(hex);
|
||||
return new BigInteger(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wycheproof represents byte arrays as hexadeciamal strings.
|
||||
*/
|
||||
static protected byte[] getBytes(JsonObject object, String name) throws Exception {
|
||||
String hex = getString(object, name);
|
||||
return TestUtil.hexToBytes(hex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the algorithm name for a digital signature algorithm with a given message digest. The
|
||||
* algorithm names used in JCA are a bit inconsequential. E.g. a dash is necessary for message
|
||||
* digests (e.g. "SHA-256") but are not used in the corresponding names for digital signatures
|
||||
* (e.g. "SHA256WITHECDSA").
|
||||
*
|
||||
* <p>See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
|
||||
*
|
||||
* @param md the name of the message digest (e.g. "SHA-256")
|
||||
* @param signatureAlgorithm the name of the signature algorithm (e.g. "ECDSA")
|
||||
* @return the algorithm name for the signature scheme with the given hash.
|
||||
*/
|
||||
protected static String getAlgorithmName(String md, String signatureAlgorithm) {
|
||||
if (md.equals("SHA-1")) {
|
||||
md = "SHA1";
|
||||
} else if (md.equals("SHA-224")) {
|
||||
md = "SHA224";
|
||||
} else if (md.equals("SHA-256")) {
|
||||
md = "SHA256";
|
||||
} else if (md.equals("SHA-384")) {
|
||||
md = "SHA384";
|
||||
} else if (md.equals("SHA-512")) {
|
||||
md = "SHA512";
|
||||
}
|
||||
return md + "WITH" + signatureAlgorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* object should contain the key in multiple formats:
|
||||
* "key" : elements of the public key
|
||||
* "keyDer": the key in ASN encoding encoded hexadecimal
|
||||
* "keyPem": the key in Pem format encoded hexadecimal
|
||||
* The test can use the format that is most convenient.
|
||||
*/
|
||||
// This is a false positive, since errorprone cannot track values passed into a method.
|
||||
@SuppressWarnings("InsecureCryptoUsage")
|
||||
static protected PublicKey getPublicKey(JsonObject object, String algorithm) throws Exception {
|
||||
KeyFactory kf;
|
||||
if (algorithm.equals("ECDSA")) {
|
||||
kf = KeyFactory.getInstance("EC");
|
||||
} else {
|
||||
kf = KeyFactory.getInstance(algorithm);
|
||||
}
|
||||
byte[] encoded = TestUtil.hexToBytes(getString(object, "keyDer"));
|
||||
X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(encoded);
|
||||
return kf.generatePublic(x509keySpec);
|
||||
}
|
||||
|
||||
/** Example format for test vectors
|
||||
* {
|
||||
* "algorithm": "ECDSA",
|
||||
* "generatorVersion": "0.0a10",
|
||||
* "numberOfTests": 217,
|
||||
* "testGroups": [
|
||||
* {
|
||||
* "key": {
|
||||
* "curve": "secp256r1",
|
||||
* "type": "ECPublicKey",
|
||||
* "wx": "0c9c4bc2617c81eb2dcbfda2db2a370a955be86a0d2e95fcb86a99f90cf046573",
|
||||
* "wy": "0c400363b1b6bcc3595a7d6d3575ccebcbb03f90ba8e58da2bc4824272f4fecff"
|
||||
* },
|
||||
* "keyDer": <X509encoded key>
|
||||
* "keyPem": "-----BEGIN PUBLIC KEY-----\ ... \n-----END PUBLIC KEY-----",
|
||||
* "sha": "SHA-256",
|
||||
* "tests": [
|
||||
* {
|
||||
* "comment": "random signature",
|
||||
* "message": "48656c6c6f",
|
||||
* "result": "valid",
|
||||
* "sig": "...",
|
||||
* "tcId": 1
|
||||
* },
|
||||
* ...
|
||||
**/
|
||||
public void testVectors(JsonObject test, String signatureAlgorithm) throws Exception {
|
||||
// This test expects that the version in the test vectors and the version here match
|
||||
// exactly, since the formats have not yet been fixed.
|
||||
final String expectedVersion = "0.0a10";
|
||||
String generatorVersion = getString(test, "generatorVersion");
|
||||
assertEquals(expectedVersion, generatorVersion);
|
||||
int numTests = test.get("numberOfTests").getAsInt();
|
||||
int cntTests = 0;
|
||||
int errors = 0;
|
||||
for (JsonElement g : test.getAsJsonArray("testGroups")) {
|
||||
JsonObject group = g.getAsJsonObject();
|
||||
PublicKey key = getPublicKey(group, signatureAlgorithm);
|
||||
String md = getString(group, "sha");
|
||||
String algorithm = getAlgorithmName(md, signatureAlgorithm);
|
||||
Signature verifier = Signature.getInstance(algorithm);
|
||||
for (JsonElement t : group.getAsJsonArray("tests")) {
|
||||
cntTests++;
|
||||
JsonObject testcase = t.getAsJsonObject();
|
||||
byte[] message = getBytes(testcase, "message");
|
||||
byte[] signature = getBytes(testcase, "sig");
|
||||
int tcid = testcase.get("tcId").getAsInt();
|
||||
String sig = TestUtil.bytesToHex(signature);
|
||||
String result = getString(testcase, "result");
|
||||
verifier.initVerify(key);
|
||||
verifier.update(message);
|
||||
boolean verified = false;
|
||||
try {
|
||||
verified = verifier.verify(signature);
|
||||
} catch (SignatureException ex) {
|
||||
// verify can throw SignatureExceptions if the signature is malformed.
|
||||
// We don't flag these cases and simply consider the signature as invalid.
|
||||
verified = false;
|
||||
} catch (java.lang.ArithmeticException ex) {
|
||||
// b/33446454 The Sun provider may throw an ArithmeticException instead of
|
||||
// the expected SignatureException for DSA signatures.
|
||||
// We should eventually remove this.
|
||||
verified = false;
|
||||
} catch (Exception ex) {
|
||||
// Other exceptions (i.e. unchecked exceptions) are considered as error
|
||||
// since a third party should never be able to cause such exceptions.
|
||||
System.out.println(signatureAlgorithm + " signature throws " + ex.toString()
|
||||
+ " tcId:" + tcid + " sig:" + sig);
|
||||
verified = false;
|
||||
errors++;
|
||||
}
|
||||
if (!verified && result.equals("valid")) {
|
||||
System.out.println("Valid " + signatureAlgorithm + " signature not verified."
|
||||
+ " tcId:" + tcid + " sig:" + sig);
|
||||
errors++;
|
||||
} else if (verified && result.equals("invalid")) {
|
||||
System.out.println("Invalid" + signatureAlgorithm + " signature verified."
|
||||
+ " tcId:" + tcid + " sig:" + sig);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
assertEquals(0, errors);
|
||||
assertEquals(numTests, cntTests);
|
||||
}
|
||||
|
||||
public JsonObject getJsonObjectFromFile(String filename) throws Exception {
|
||||
FileInputStream is = new FileInputStream(filename);
|
||||
JsonReader reader = new JsonReader(new InputStreamReader(is, UTF_8));
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonElement elem = parser.parse(reader);
|
||||
return elem.getAsJsonObject();
|
||||
}
|
||||
|
||||
public void testSignatureScheme(String filename, String algorithm) throws Exception {
|
||||
// TODO(bleichen): This (likely) will not work when published.
|
||||
String TEST_VECTORS = "third_party/wycheproof/testvectors/";
|
||||
JsonObject tests = getJsonObjectFromFile(TEST_VECTORS + filename);
|
||||
testVectors(tests, algorithm);
|
||||
}
|
||||
|
||||
public void testEcdsa() throws Exception {
|
||||
testSignatureScheme("ecdsa_test.json", "ECDSA");
|
||||
}
|
||||
|
||||
public void testRsaSignatures() throws Exception {
|
||||
testSignatureScheme("rsa_signature_test.json", "RSA");
|
||||
}
|
||||
}
|
||||
@@ -1430,7 +1430,7 @@ public class RsaKeyTest extends TestCase {
|
||||
assertEquals(pub.getModulus(), priv.getModulus());
|
||||
BigInteger e = pub.getPublicExponent();
|
||||
// Checks that e > 1. [CVE-1999-1444]
|
||||
assertEquals(e.compareTo(BigInteger.ONE), 1);
|
||||
assertEquals(1, e.compareTo(BigInteger.ONE));
|
||||
}
|
||||
|
||||
private void checkKeyPair(KeyPair keypair, int keySizeInBits) throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user