diff options
15 files changed, 624 insertions, 1006 deletions
@@ -56,21 +56,14 @@ java_library_static { visibility: [ "//cts/tests/tests/keystore", ], - srcs: ["keystore-cts/java/**/*.java"], + srcs: [ + "keystore-cts/java/**/*.java", + "keystore-cts/android/**/*.java", + ], exclude_srcs: [ - "keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/RsaPssTest.java", - "keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java", + "keystore-cts/java/**/JsonEcdhTest.java", + "keystore-cts/java/**/RsaOaepTest.java", + "keystore-cts/java/**/RsaPssTest.java", ], java_resource_dirs: ["keystore-cts/testvectors"], sdk_version: "current", diff --git a/keystore-cts/java/com/google/security/wycheproof/CertificateUtil.java b/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java index d5d343a..798192d 100644 --- a/keystore-cts/java/com/google/security/wycheproof/CertificateUtil.java +++ b/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java @@ -11,8 +11,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.security.wycheproof; +package android.keystore.cts.util; +import android.security.keystore.KeyProtection; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; @@ -22,16 +23,59 @@ import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import java.io.ByteArrayInputStream; import java.io.IOException; import java.math.BigInteger; +import java.security.GeneralSecurityException; import java.security.KeyPair; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.PublicKey; import java.security.SecureRandom; +import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Date; +import java.util.Enumeration; +import javax.crypto.spec.SecretKeySpec; import javax.security.auth.x500.X500Principal; -/** Certificate utilities */ -public class CertificateUtil { +/** Keystore utilities */ +public class KeyStoreUtil { + + public static KeyStore saveKeysToKeystore(String alias, PublicKey pubKey, PrivateKey privKey, + KeyProtection keyProtection) + throws Exception { + KeyPair keyPair = new KeyPair(pubKey, privKey); + X509Certificate certificate = createCertificate(keyPair, + new X500Principal("CN=Test1"), + new X500Principal("CN=Test1")); + Certificate[] certChain = new Certificate[]{certificate}; + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + keyStore.setEntry(alias, + new KeyStore.PrivateKeyEntry(privKey, certChain), + keyProtection); + return keyStore; + } + + public static KeyStore saveSecretKeyToKeystore(String alias, SecretKeySpec keySpec, + KeyProtection keyProtection) + throws Exception { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + keyStore.setEntry(alias, + new KeyStore.SecretKeyEntry(keySpec), + keyProtection); + return keyStore; + } + + public static void cleanUpKeyStore() throws Exception { + KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore"); + keyStore.load(null); + for (Enumeration<String> aliases = keyStore.aliases(); aliases.hasMoreElements();) { + String alias = aliases.nextElement(); + keyStore.deleteEntry(alias); + } + } public static X509Certificate createCertificate( KeyPair keyPair, X500Principal subject, X500Principal issuer) diff --git a/keystore-cts/java/com/google/security/wycheproof/EcUtil.java b/keystore-cts/java/com/google/security/wycheproof/EcUtil.java index 56c6548..492dd43 100644 --- a/keystore-cts/java/com/google/security/wycheproof/EcUtil.java +++ b/keystore-cts/java/com/google/security/wycheproof/EcUtil.java @@ -46,22 +46,6 @@ public class EcUtil { return parameters.getParameterSpec(ECParameterSpec.class); } - public static void printParameters(ECParameterSpec spec) { - System.out.println("cofactor:" + spec.getCofactor()); - EllipticCurve curve = spec.getCurve(); - System.out.println("A:" + curve.getA()); - System.out.println("B:" + curve.getB()); - ECField field = curve.getField(); - System.out.println("field size:" + field.getFieldSize()); - if (field instanceof ECFieldFp) { - ECFieldFp fp = (ECFieldFp) field; - System.out.println("P:" + fp.getP()); - } - ECPoint generator = spec.getGenerator(); - System.out.println("Gx:" + generator.getAffineX()); - System.out.println("Gy:" + generator.getAffineY()); - System.out.println("order:" + spec.getOrder()); - } /** Returns the bit size of a given curve. TODO(bleichen): add all curves that are tested. */ public static int getCurveSize(String name) throws NoSuchAlgorithmException { diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java index d02b4f1..8a574f6 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java @@ -37,6 +37,8 @@ import javax.crypto.spec.SecretKeySpec; import org.junit.Test; import org.junit.Ignore; import org.junit.Before; +import org.junit.After; +import android.keystore.cts.util.KeyStoreUtil; import android.security.keystore.KeyProtection; import android.security.keystore.KeyProperties; import java.security.KeyStore; @@ -54,6 +56,15 @@ import java.security.UnrecoverableKeyException; public class AesGcmTest { private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; private KeyStore keyStore; + private static final String KEY_ALIAS_1 = "Key1"; + private static final String KEY_ALIAS_2 = "Key2"; + private static final String KEY_ALIAS_3 = "Key3"; + private static final String KEY_ALIAS_4 = "Key4"; + private static final String KEY_ALIAS_5 = "Key5"; + private static final String KEY_ALIAS_6 = "Key6"; + private static final String KEY_ALIAS_7 = "Key7"; + private static final String KEY_ALIAS_8 = "Key8"; + private static final String KEY_ALIAS_9 = "Key9"; @Before public void setup() throws Exception { @@ -64,6 +75,11 @@ public class AesGcmTest { } } + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + private SecretKey setKeystoreEntry(String alias, SecretKeySpec key) throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException { keyStore.setEntry( @@ -77,6 +93,10 @@ public class AesGcmTest { // Key imported, obtain a reference to it. return (SecretKey) keyStore.getKey(alias, null); } + + private SecretKey getKey(String alias) throws Exception { + return (SecretKey) keyStore.getKey(alias, null); + } /** Test vectors */ public static class GcmTestVector { @@ -119,35 +139,35 @@ public class AesGcmTest { "028318abc1824029138141a2", "", "26073cc1d851beff176384dc9896d5ff", - "0a3ea7a5487cb5f7d70fb6c58d038554", "Key1"), + "0a3ea7a5487cb5f7d70fb6c58d038554", KEY_ALIAS_1), new GcmTestVector( "001d0c231287c1182784554ca3a21908", "5b9604fe14eadba931b0ccf34843dab9", "921d2507fa8007b7bd067d34", "00112233445566778899aabbccddeeff", "49d8b9783e911913d87094d1f63cc765", - "1e348ba07cca2cf04c618cb4", "Key2"), + "1e348ba07cca2cf04c618cb4", KEY_ALIAS_2), new GcmTestVector( "2035af313d1346ab00154fea78322105", "aa023d0478dcb2b2312498293d9a9129", "0432bc49ac34412081288127", "aac39231129872a2", "eea945f3d0f98cc0fbab472a0cf24e87", - "4bb9b4812519dadf9e1232016d068133", "Key3"), + "4bb9b4812519dadf9e1232016d068133", KEY_ALIAS_3), new GcmTestVector( "2035af313d1346ab00154fea78322105", "aa023d0478dcb2b2312498293d9a9129", "0432bc49ac344120", "aac39231129872a2", "64c36bb3b732034e3a7d04efc5197785", - "b7d0dd70b00d65b97cfd080ff4b819d1", "Key4"), + "b7d0dd70b00d65b97cfd080ff4b819d1", KEY_ALIAS_4), new GcmTestVector( "02efd2e5782312827ed5d230189a2a342b277ce048462193", "2034a82547276c83dd3212a813572bce", "3254202d854734812398127a3d134421", "1a0293d8f90219058902139013908190bc490890d3ff12a3", "64069c2d58690561f27ee199e6b479b6369eec688672bde9", - "9b7abadd6e69c1d9ec925786534f5075", "Key5"), + "9b7abadd6e69c1d9ec925786534f5075", KEY_ALIAS_5), // 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. @@ -158,7 +178,7 @@ public class AesGcmTest { "7b95b8c356810a84711d68150a1b7750", "", "84d4c9c08b4f482861e3a9c6c35bc4d91df927374513bfd49f436bd73f325285daef4ff7e13d46a6", - "213a3cb93855d18e69337eee66aeec07", "Key6"), + "213a3cb93855d18e69337eee66aeec07", KEY_ALIAS_6), // J0:ffffffffffffffffffffffffffffffff new GcmTestVector( "00000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -166,7 +186,7 @@ public class AesGcmTest { "1a552e67cdc4dc1a33b824874ebf0bed", "", "948ca37a8e6649e88aeffb1c598f3607007702417ea0e0bc3c60ad5a949886de968cf53ea6462aed", - "99b381bfa2af9751c39d1b6e86d1be6a", "Key6"), + "99b381bfa2af9751c39d1b6e86d1be6a", KEY_ALIAS_7), // J0:000102030405060708090a0bffffffff new GcmTestVector( "00000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -174,7 +194,7 @@ public class AesGcmTest { "99821c2dd5daecded07300f577f7aff1", "", "127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4af37d0fe9af033052bd537c4ae978f60", - "07eb2fe4a958f8434d40684899507c7c", "Key7"), + "07eb2fe4a958f8434d40684899507c7c", KEY_ALIAS_8), // J0:000102030405060708090a0bfffffffe new GcmTestVector( "00000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -182,7 +202,7 @@ public class AesGcmTest { "5e4a3900142358d1c774d8d124d8d27d", "", "0cf6ae47156b14dce03c8a07a2e172b1127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4", - "f145c2dcaf339eede427be934357eac0", "Key8"), + "f145c2dcaf339eede427be934357eac0", KEY_ALIAS_9), }; /** @@ -203,7 +223,7 @@ public class AesGcmTest { // Checks whether the parameter size is supported. // It would be nice if there was a way to check this without trying to encrypt. Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); } catch (InvalidKeyException | InvalidAlgorithmParameterException ex) { // Not supported continue; @@ -218,7 +238,7 @@ public class AesGcmTest { public void testVectors() throws Exception { for (GcmTestVector test : getTestVectors()) { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(test.aad); byte[] ct = cipher.doFinal(test.pt); assertEquals(test.ctHex, TestUtil.bytesToHex(ct)); @@ -232,7 +252,7 @@ public class AesGcmTest { // Encryption byte[] empty = new byte[0]; Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.pt.length); ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); cipher.updateAAD(empty); @@ -258,7 +278,7 @@ public class AesGcmTest { for (GcmTestVector test : getTestVectors()) { byte[] empty = new byte[0]; Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.ct.length); ByteBuffer ptBuffer = ByteBuffer.allocate(outputSize); cipher.updateAAD(empty); @@ -279,7 +299,7 @@ public class AesGcmTest { // Simple test that a modified ciphertext fails. ptBuffer.clear(); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(empty); cipher.updateAAD(test.aad); cipher.updateAAD(new byte[1]); @@ -315,7 +335,7 @@ public class AesGcmTest { public void testLateUpdateAAD() throws Exception { for (GcmTestVector test : getTestVectors()) { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); byte[] c0 = cipher.update(test.pt); try { cipher.updateAAD(test.aad); @@ -348,7 +368,7 @@ public class AesGcmTest { public void testIvReuse() throws Exception { for (GcmTestVector test : getTestVectors()) { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(test.aad); byte[] ct1 = cipher.doFinal(test.pt); try { @@ -375,11 +395,11 @@ public class AesGcmTest { for (GcmTestVector test : getTestVectors()) { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); // Encryption - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.pt.length); assertEquals("plaintext size:" + test.pt.length, test.ct.length, outputSize); // Decryption - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); outputSize = cipher.getOutputSize(test.ct.length); assertEquals("ciphertext size:" + test.ct.length, test.pt.length, outputSize); } @@ -392,7 +412,7 @@ public class AesGcmTest { // Encryption Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.pt.length); ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); cipher.updateAAD(test.aad); @@ -401,7 +421,7 @@ public class AesGcmTest { // Decryption ctBuffer.flip(); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); outputSize = cipher.getOutputSize(test.ct.length); ByteBuffer decrypted = ByteBuffer.allocate(outputSize); cipher.updateAAD(test.aad); @@ -416,7 +436,7 @@ public class AesGcmTest { for (GcmTestVector test : getTestVectors()) { // Encryption Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.pt.length); byte[] backingArray = new byte[outputSize]; ByteBuffer ptBuffer = ByteBuffer.wrap(backingArray); @@ -430,7 +450,7 @@ public class AesGcmTest { // Decryption ByteBuffer decrypted = ByteBuffer.wrap(backingArray); ctBuffer.flip(); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(test.aad); cipher.doFinal(ctBuffer, decrypted); assertEquals(test.ptHex, TestUtil.byteBufferToHex(decrypted)); @@ -640,7 +660,7 @@ public class AesGcmTest { // Encryption Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt).asReadOnlyBuffer(); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.pt.length); ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); cipher.updateAAD(test.aad); @@ -650,7 +670,7 @@ public class AesGcmTest { // Decryption ctBuffer.flip(); ctBuffer = ctBuffer.asReadOnlyBuffer(); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); outputSize = cipher.getOutputSize(test.ct.length); ByteBuffer decrypted = ByteBuffer.allocate(outputSize); cipher.updateAAD(test.aad); @@ -678,7 +698,7 @@ public class AesGcmTest { ByteBuffer ctBuffer = ByteBuffer.wrap(new byte[test.ct.length + 50]); ctBuffer.position(8); ctBuffer = ctBuffer.slice(); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(test.aad); cipher.doFinal(ptBuffer, ctBuffer); assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer)); @@ -688,7 +708,7 @@ public class AesGcmTest { ByteBuffer decBuffer = ByteBuffer.wrap(new byte[test.pt.length + 50]); decBuffer.position(6); decBuffer = decBuffer.slice(); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(test.aad); cipher.doFinal(ctBuffer, decBuffer); assertEquals(test.ptHex, TestUtil.byteBufferToHex(decBuffer)); @@ -702,7 +722,7 @@ public class AesGcmTest { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt); ByteBuffer ctBuffer = ByteBuffer.allocate(test.ct.length - 1); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(test.aad); try { cipher.doFinal(ptBuffer, ctBuffer); @@ -714,7 +734,7 @@ public class AesGcmTest { // Decryption ctBuffer = ByteBuffer.wrap(test.ct); ByteBuffer decrypted = ByteBuffer.allocate(test.pt.length - 1); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(test.aad); try { cipher.doFinal(ctBuffer, decrypted); @@ -736,7 +756,7 @@ public class AesGcmTest { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); ByteBuffer empty = ByteBuffer.allocate(0); ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt); - cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.ENCRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.pt.length); ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize); cipher.updateAAD(empty); @@ -754,7 +774,7 @@ public class AesGcmTest { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); ByteBuffer empty = ByteBuffer.allocate(0); ByteBuffer ctBuffer = ByteBuffer.wrap(test.ct); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); int outputSize = cipher.getOutputSize(test.ct.length); ByteBuffer ptBuffer = ByteBuffer.allocate(outputSize); cipher.updateAAD(empty); @@ -767,7 +787,7 @@ public class AesGcmTest { // Simple test that a modified ciphertext fails. ctBuffer.flip(); ptBuffer.clear(); - cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters); + cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters); cipher.updateAAD(empty); cipher.updateAAD(test.aad); cipher.updateAAD(new byte[1]); @@ -807,12 +827,7 @@ public class AesGcmTest { fail("Failed to set secret key entry in KeyStore."); } Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - try { - cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(counter)); - } catch (InvalidAlgorithmParameterException ex) { - // OpenJDK8 does not support IvParameterSpec for GCM. - throw ex; - } + cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(counter)); byte[] output = cipher.doFinal(input); assertEquals(input.length + 16, output.length); } @@ -841,12 +856,7 @@ public class AesGcmTest { fail("Failed to set secret key entry in KeyStore."); } Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME); - try { - AlgorithmParameterGenerator.getInstance("GCM"); - } catch (NoSuchAlgorithmException ex) { - // Conscrypt does not support AlgorithmParameterGenerator for GCM. - throw ex; - } + AlgorithmParameterGenerator.getInstance("GCM"); AlgorithmParameters param = AlgorithmParameterGenerator.getInstance("GCM").generateParameters(); cipher.init(Cipher.ENCRYPT_MODE, secretKey, param); byte[] output = cipher.doFinal(input); diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java index 3698e4e..d0f85d4 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java @@ -25,12 +25,17 @@ import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.Arrays; import javax.crypto.Cipher; +import javax.crypto.SecretKey; import javax.crypto.CipherInputStream; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.Ignore; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import java.security.KeyStore; +import android.keystore.cts.util.KeyStoreUtil; /** * CipherInputStream tests @@ -51,18 +56,31 @@ import org.junit.runners.JUnit4; * All other tests run under the assumption that returning an empty plaintext is acceptable * behaviour, so that the tests are able to catch additional problems. */ -@RunWith(JUnit4.class) public class CipherInputStreamTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; static final SecureRandom rand = new SecureRandom(); + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + static byte[] randomBytes(int size) { byte[] bytes = new byte[size]; rand.nextBytes(bytes); return bytes; } - static SecretKeySpec randomKey(String algorithm, int keySizeInBytes) { - return new SecretKeySpec(randomBytes(keySizeInBytes), "AES"); + static SecretKey randomKey(String algorithm, String alias, int keySizeInBytes) throws Exception { + SecretKeySpec keySpec = new SecretKeySpec(randomBytes(keySizeInBytes), "AES"); + KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(alias, keySpec, + new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setRandomizedEncryptionRequired(false) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + // Key imported, obtain a reference to it. + return (SecretKey) keyStore.getKey(alias, null); } static AlgorithmParameterSpec randomParameters( @@ -76,7 +94,7 @@ public class CipherInputStreamTest { /** Test vectors */ public static class TestVector { public String algorithm; - public SecretKeySpec key; + public SecretKey key; public AlgorithmParameterSpec params; public byte[] pt; public byte[] aad; @@ -84,14 +102,14 @@ public class CipherInputStreamTest { @SuppressWarnings("InsecureCryptoUsage") public TestVector( - String algorithm, int keySize, int ivSize, int tagSize, int ptSize, int aadSize) - throws Exception { + String algorithm, String alias, int keySize, + int ivSize, int tagSize, int ptSize, int aadSize) throws Exception { this.algorithm = algorithm; - this.key = randomKey(algorithm, keySize); + this.key = randomKey(algorithm, alias, keySize); this.params = randomParameters(algorithm, ivSize, tagSize); this.pt = randomBytes(ptSize); this.aad = randomBytes(aadSize); - Cipher cipher = Cipher.getInstance(algorithm); + Cipher cipher = Cipher.getInstance(algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE, this.key, this.params); cipher.updateAAD(aad); this.ct = cipher.doFinal(pt); @@ -112,7 +130,10 @@ public class CipherInputStreamTest { for (int tagSize : tagSizes) { for (int ptSize : ptSizes) { for (int aadSize : aadSizes) { - result.add(new TestVector(algorithm, keySize, ivSize, tagSize, ptSize, aadSize)); + String keyAlias = "Key-" + keySize + "-" + ivSize + "-" + tagSize + + "-" + ptSize + "-" + aadSize; + result.add(new TestVector(algorithm, keyAlias, keySize, + ivSize, tagSize, ptSize, aadSize)); } } } @@ -124,7 +145,7 @@ public class CipherInputStreamTest { @SuppressWarnings("InsecureCryptoUsage") public void testEncrypt(Iterable<TestVector> tests) throws Exception { for (TestVector t : tests) { - Cipher cipher = Cipher.getInstance(t.algorithm); + Cipher cipher = Cipher.getInstance(t.algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE, t.key, t.params); cipher.updateAAD(t.aad); InputStream is = new ByteArrayInputStream(t.pt); @@ -148,7 +169,7 @@ public class CipherInputStreamTest { @SuppressWarnings("InsecureCryptoUsage") public void testDecrypt(Iterable<TestVector> tests) throws Exception { for (TestVector t : tests) { - Cipher cipher = Cipher.getInstance(t.algorithm); + Cipher cipher = Cipher.getInstance(t.algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, t.key, t.params); cipher.updateAAD(t.aad); InputStream is = new ByteArrayInputStream(t.ct); @@ -184,7 +205,7 @@ public class CipherInputStreamTest { public void testCorruptDecrypt(Iterable<TestVector> tests, boolean acceptEmptyPlaintext) throws Exception { for (TestVector t : tests) { - Cipher cipher = Cipher.getInstance(t.algorithm); + Cipher cipher = Cipher.getInstance(t.algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, t.key, t.params); cipher.updateAAD(t.aad); byte[] ct = Arrays.copyOf(t.ct, t.ct.length); @@ -263,6 +284,7 @@ public class CipherInputStreamTest { /** Tests CipherOutputStream with AES-EAX if this algorithm is supported by the provider. */ @Test + @Ignore // Ignored due to AES/EAX algorithm is not supported in AndroidKeyStore public void testAesEax() throws Exception { final String algorithm = "AES/EAX/NoPadding"; final int[] keySizes = {16, 32}; @@ -270,12 +292,6 @@ public class CipherInputStreamTest { final int[] tagSizes = {12, 16}; final int[] ptSizes = {0, 8, 16, 65, 8100}; final int[] aadSizes = {0, 8, 24}; - try { - Cipher.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("Skipping testAesEax"); - return; - } Iterable<TestVector> v = getTestVectors(algorithm, keySizes, ivSizes, tagSizes, ptSizes, aadSizes); testEncrypt(v); diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java index d016941..bf863fe 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java @@ -24,12 +24,17 @@ import java.security.spec.AlgorithmParameterSpec; import java.util.ArrayList; import java.util.Arrays; import javax.crypto.Cipher; +import javax.crypto.SecretKey; import javax.crypto.CipherOutputStream; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.Ignore; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import java.security.KeyStore; +import android.keystore.cts.util.KeyStoreUtil; /** * CipherOutputStream tests @@ -51,18 +56,31 @@ import org.junit.runners.JUnit4; * behaviour, so that the tests are able to catch additional problems. */ -@RunWith(JUnit4.class) public class CipherOutputStreamTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; static final SecureRandom rand = new SecureRandom(); + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + static byte[] randomBytes(int size) { byte[] bytes = new byte[size]; rand.nextBytes(bytes); return bytes; } - static SecretKeySpec randomKey(String algorithm, int keySizeInBytes) { - return new SecretKeySpec(randomBytes(keySizeInBytes), "AES"); + static SecretKey randomKey(String algorithm, String alias, int keySizeInBytes) throws Exception{ + SecretKeySpec keySpec = new SecretKeySpec(randomBytes(keySizeInBytes), "AES"); + KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(alias, keySpec, + new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setRandomizedEncryptionRequired(false) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .build()); + // Key imported, obtain a reference to it. + return (SecretKey) keyStore.getKey(alias, null); } static AlgorithmParameterSpec randomParameters( @@ -77,21 +95,21 @@ public class CipherOutputStreamTest { @SuppressWarnings("InsecureCryptoUsage") public static class TestVector { public String algorithm; - public SecretKeySpec key; + public SecretKey key; public AlgorithmParameterSpec params; public byte[] pt; public byte[] aad; public byte[] ct; public TestVector( - String algorithm, int keySize, int ivSize, int tagSize, int ptSize, int aadSize) - throws Exception { + String algorithm, String alias, int keySize, + int ivSize, int tagSize, int ptSize, int aadSize) throws Exception { this.algorithm = algorithm; - this.key = randomKey(algorithm, keySize); + this.key = randomKey(algorithm, alias, keySize); this.params = randomParameters(algorithm, ivSize, tagSize); this.pt = randomBytes(ptSize); this.aad = randomBytes(aadSize); - Cipher cipher = Cipher.getInstance(algorithm); + Cipher cipher = Cipher.getInstance(algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE, this.key, this.params); cipher.updateAAD(aad); this.ct = cipher.doFinal(pt); @@ -106,13 +124,16 @@ public class CipherOutputStreamTest { int[] ptSizes, int[] aadSizes) throws Exception { + int counter = 0; ArrayList<TestVector> result = new ArrayList<TestVector>(); for (int keySize : keySizes) { for (int ivSize : ivSizes) { for (int tagSize : tagSizes) { for (int ptSize : ptSizes) { for (int aadSize : aadSizes) { - result.add(new TestVector(algorithm, keySize, ivSize, tagSize, ptSize, aadSize)); + String keyAlias = "Key" + counter++; + result.add(new TestVector(algorithm, keyAlias, keySize, + ivSize, tagSize, ptSize, aadSize)); } } } @@ -124,7 +145,7 @@ public class CipherOutputStreamTest { @SuppressWarnings("InsecureCryptoUsage") public void testEncrypt(Iterable<TestVector> tests) throws Exception { for (TestVector t : tests) { - Cipher cipher = Cipher.getInstance(t.algorithm); + Cipher cipher = Cipher.getInstance(t.algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.ENCRYPT_MODE, t.key, t.params); cipher.updateAAD(t.aad); ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -138,7 +159,7 @@ public class CipherOutputStreamTest { @SuppressWarnings("InsecureCryptoUsage") public void testDecrypt(Iterable<TestVector> tests) throws Exception { for (TestVector t : tests) { - Cipher cipher = Cipher.getInstance(t.algorithm); + Cipher cipher = Cipher.getInstance(t.algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, t.key, t.params); cipher.updateAAD(t.aad); ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -160,7 +181,7 @@ public class CipherOutputStreamTest { public void testCorruptDecrypt(Iterable<TestVector> tests, boolean acceptEmptyPlaintext) throws Exception { for (TestVector t : tests) { - Cipher cipher = Cipher.getInstance(t.algorithm); + Cipher cipher = Cipher.getInstance(t.algorithm, EXPECTED_PROVIDER_NAME); cipher.init(Cipher.DECRYPT_MODE, t.key, t.params); cipher.updateAAD(t.aad); byte[] ct = Arrays.copyOf(t.ct, t.ct.length); @@ -228,6 +249,7 @@ public class CipherOutputStreamTest { /** Tests CipherOutputStream with AES-EAX if AES-EAS is supported by the provider. */ @SuppressWarnings("InsecureCryptoUsage") @Test + @Ignore // Ignored due to AES/EAX algorithm is not supported in AndroidKeyStore public void testAesEax() throws Exception { final String algorithm = "AES/EAX/NoPadding"; final int[] keySizes = {16, 32}; @@ -235,12 +257,6 @@ public class CipherOutputStreamTest { final int[] tagSizes = {12, 16}; final int[] ptSizes = {8, 16, 65, 8100}; final int[] aadSizes = {0, 8, 24}; - try { - Cipher.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("Skipping testAesEax"); - return; - } Iterable<TestVector> v = getTestVectors(algorithm, keySizes, ivSizes, tagSizes, ptSizes, aadSizes); testEncrypt(v); diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java index 517d23b..4f53650 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java @@ -16,18 +16,13 @@ package com.google.security.wycheproof; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; - -import com.google.security.wycheproof.WycheproofRunner.NoPresubmitTest; -import com.google.security.wycheproof.WycheproofRunner.ProviderType; -import com.google.security.wycheproof.WycheproofRunner.SlowTest; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; import java.math.BigInteger; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; @@ -44,10 +39,13 @@ import java.security.spec.EllipticCurve; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import javax.crypto.KeyAgreement; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - +import org.junit.Ignore; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import android.security.keystore.KeyGenParameterSpec; +import android.keystore.cts.util.KeyStoreUtil; /** * Testing ECDH. * @@ -89,8 +87,36 @@ import org.junit.runners.JUnit4; // certificate. // - CVE-2014-3572: OpenSSL downgrades ECDHE to ECDH // - CVE-2011-3210: OpenSSL was not thread safe -@RunWith(JUnit4.class) public class EcdhTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "TestKey"; + private static final String KEY_ALIAS_2 = "wycheproofkey1"; + private static final String KEY_ALIAS_3 = "wycheproofkey2"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + + private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey) + throws Exception { + return (PrivateKey) KeyStoreUtil.saveKeysToKeystore( + KEY_ALIAS_1, pubKey, privKey, + new KeyProtection.Builder(KeyProperties.PURPOSE_AGREE_KEY) + .build()) + .getKey(KEY_ALIAS_1, null); + } + + private KeyPair generateECKeyPair(String alias, ECGenParameterSpec ecSpec) throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", EXPECTED_PROVIDER_NAME); + KeyGenParameterSpec ecKeySpec = + new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_AGREE_KEY) + .setAlgorithmParameterSpec(ecSpec) + .build(); + + keyGen.initialize(ecKeySpec); + return keyGen.generateKeyPair(); + } static final String[] ECDH_VARIANTS = { // Raw ECDH. The shared secret is the x-coordinate of the ECDH computation. @@ -161,7 +187,6 @@ public class EcdhTest { ECPublicKeySpec pub = new ECPublicKeySpec(pubPoint, params); return pub; } catch (Exception ex) { - System.out.println(comment + " throws " + ex.toString()); return null; } } @@ -509,14 +534,11 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = /** Checks that key agreement using ECDH works. */ @Test public void testBasic() throws Exception { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1"); - keyGen.initialize(ecSpec); - KeyPair keyPairA = keyGen.generateKeyPair(); - KeyPair keyPairB = keyGen.generateKeyPair(); + KeyPair keyPairA = generateECKeyPair(KEY_ALIAS_2, new ECGenParameterSpec("secp256r1")); + KeyPair keyPairB = generateECKeyPair(KEY_ALIAS_3, new ECGenParameterSpec("secp256r1")); - KeyAgreement kaA = KeyAgreement.getInstance("ECDH"); - KeyAgreement kaB = KeyAgreement.getInstance("ECDH"); + KeyAgreement kaA = KeyAgreement.getInstance("ECDH", EXPECTED_PROVIDER_NAME); + KeyAgreement kaB = KeyAgreement.getInstance("ECDH", EXPECTED_PROVIDER_NAME); kaA.init(keyPairA.getPrivate()); kaB.init(keyPairB.getPrivate()); kaA.doPhase(keyPairB.getPublic(), true); @@ -526,10 +548,6 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = assertEquals(TestUtil.bytesToHex(kAB), TestUtil.bytesToHex(kBA)); } - @NoPresubmitTest( - providers = {ProviderType.BOUNCY_CASTLE}, - bugs = {"BouncyCastle uses long encoding. Is this a bug?"} - ) @Test public void testEncode() throws Exception { KeyFactory kf = KeyFactory.getInstance("EC"); @@ -559,26 +577,18 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = */ @SuppressWarnings("InsecureCryptoUsage") public void testModifiedPublic(String algorithm) throws Exception { - KeyAgreement ka; - try { - ka = KeyAgreement.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("testWrongOrder: " + algorithm + " not supported"); - return; - } - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(EcUtil.getNistP256Params()); - ECPrivateKey priv = (ECPrivateKey) keyGen.generateKeyPair().getPrivate(); + KeyAgreement ka = KeyAgreement.getInstance(algorithm, EXPECTED_PROVIDER_NAME); + KeyPair pair = generateECKeyPair(KEY_ALIAS_1, new ECGenParameterSpec("secp256r1")); KeyFactory kf = KeyFactory.getInstance("EC"); ECPublicKey validKey = (ECPublicKey) kf.generatePublic(EC_VALID_PUBLIC_KEY.getSpec()); - ka.init(priv); + ka.init(pair.getPrivate()); ka.doPhase(validKey, true); String expected = TestUtil.bytesToHex(ka.generateSecret()); for (EcPublicKeyTestVector test : EC_MODIFIED_PUBLIC_KEYS) { try { X509EncodedKeySpec spec = test.getX509EncodedKeySpec(); ECPublicKey modifiedKey = (ECPublicKey) kf.generatePublic(spec); - ka.init(priv); + ka.init(pair.getPrivate()); ka.doPhase(modifiedKey, true); String shared = TestUtil.bytesToHex(ka.generateSecret()); // The implementation did not notice that the public key was modified. @@ -593,7 +603,6 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = assertEquals("algorithm:" + algorithm + " test:" + test.comment, expected, shared); } catch (GeneralSecurityException ex) { // OK, since the public keys have been modified. - System.out.println("testModifiedPublic:" + test.comment + " throws " + ex.toString()); } } } @@ -604,19 +613,11 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = */ @SuppressWarnings("InsecureCryptoUsage") public void testModifiedPublicSpec(String algorithm) throws Exception { - KeyAgreement ka; - try { - ka = KeyAgreement.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("testWrongOrder: " + algorithm + " not supported"); - return; - } - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - keyGen.initialize(EcUtil.getNistP256Params()); - ECPrivateKey priv = (ECPrivateKey) keyGen.generateKeyPair().getPrivate(); + KeyAgreement ka = KeyAgreement.getInstance(algorithm, EXPECTED_PROVIDER_NAME); + KeyPair pair = generateECKeyPair(KEY_ALIAS_1, new ECGenParameterSpec("secp256r1")); KeyFactory kf = KeyFactory.getInstance("EC"); ECPublicKey validKey = (ECPublicKey) kf.generatePublic(EC_VALID_PUBLIC_KEY.getSpec()); - ka.init(priv); + ka.init(pair.getPrivate()); ka.doPhase(validKey, true); String expected = TestUtil.bytesToHex(ka.generateSecret()); for (EcPublicKeyTestVector test : EC_MODIFIED_PUBLIC_KEYS) { @@ -628,7 +629,7 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = } try { ECPublicKey modifiedKey = (ECPublicKey) kf.generatePublic(spec); - ka.init(priv); + ka.init(pair.getPrivate()); ka.doPhase(modifiedKey, true); String shared = TestUtil.bytesToHex(ka.generateSecret()); // The implementation did not notice that the public key was modified. @@ -643,77 +644,30 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = assertEquals("algorithm:" + algorithm + " test:" + test.comment, expected, shared); } catch (GeneralSecurityException ex) { // OK, since the public keys have been modified. - System.out.println("testModifiedPublic:" + test.comment + " throws " + ex.toString()); } } } @Test - public void testModifiedPublic() throws Exception { + public void testEcdhModifiedPublic() throws Exception { testModifiedPublic("ECDH"); + } + + @Test + @Ignore // ECDHC algorithm is not supported in AndroidKeyStore + public void testEcdhcModifiedPublic() throws Exception { testModifiedPublic("ECDHC"); } @Test - public void testModifiedPublicSpec() throws Exception { + public void testEcdhModifiedPublicSpec() throws Exception { testModifiedPublicSpec("ECDH"); - testModifiedPublicSpec("ECDHC"); } - @SuppressWarnings("InsecureCryptoUsage") - public void testDistinctCurves(String algorithm, ECPrivateKey priv, ECPublicKey pub) - throws Exception { - KeyAgreement kaA; - try { - kaA = KeyAgreement.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("Algorithm not supported: " + algorithm); - return; - } - byte[] shared; - try { - kaA.init(priv); - kaA.doPhase(pub, true); - shared = kaA.generateSecret(); - } catch (InvalidKeyException ex) { - // This is expected. - return; - } - // Printing some information to determine what might have gone wrong: - // E.g., if the generated secret is the same as the x-coordinate of the public key - // then it is likely that the ECDH computation was using a fake group with small order. - // Such a situation is probably exploitable. - // This probably is exploitable. If the curve of the private key was used for the ECDH - // then the generated secret and the x-coordinate of the public key are likely - // distinct. - EllipticCurve pubCurve = pub.getParams().getCurve(); - EllipticCurve privCurve = priv.getParams().getCurve(); - ECPoint pubW = pub.getW(); - System.out.println("testDistinctCurves: algorithm=" + algorithm); - System.out.println( - "Private key: a=" - + privCurve.getA() - + " b=" - + privCurve.getB() - + " p" - + EcUtil.getModulus(privCurve)); - System.out.println(" s =" + priv.getS()); - System.out.println( - "Public key: a=" - + pubCurve.getA() - + " b=" - + pubCurve.getB() - + " p" - + EcUtil.getModulus(pubCurve)); - System.out.println(" w = (" + pubW.getAffineX() + ", " + pubW.getAffineY() + ")"); - System.out.println( - " = (" - + pubW.getAffineX().toString(16) - + ", " - + pubW.getAffineY().toString(16) - + ")"); - System.out.println("generated shared secret:" + TestUtil.bytesToHex(shared)); - fail("Generated secret with distinct Curves using " + algorithm); + @Test + @Ignore // ECDHC algorithm is not supported in AndroidKeyStore + public void testEcdhcModifiedPublicSpec() throws Exception { + testModifiedPublicSpec("ECDHC"); } /** @@ -725,25 +679,11 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = // TODO(bleichen): This can be merged with testModifiedPublic once this is fixed. @SuppressWarnings("InsecureCryptoUsage") public void testWrongOrder(String algorithm, ECParameterSpec spec) throws Exception { - KeyAgreement ka; - try { - ka = KeyAgreement.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("testWrongOrder: " + algorithm + " not supported"); - return; - } - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - ECPrivateKey priv; - ECPublicKey pub; - try { - keyGen.initialize(spec); - priv = (ECPrivateKey) keyGen.generateKeyPair().getPrivate(); - pub = (ECPublicKey) keyGen.generateKeyPair().getPublic(); - } catch (GeneralSecurityException ex) { - // This is OK, since not all provider support Brainpool curves - System.out.println("testWrongOrder: could not generate keys for curve"); - return; - } + KeyAgreement ka = KeyAgreement.getInstance(algorithm, EXPECTED_PROVIDER_NAME); + PrivateKey priv = generateECKeyPair(KEY_ALIAS_2, + new ECGenParameterSpec("secp256r1")).getPrivate(); + ECPublicKey pub = (ECPublicKey) generateECKeyPair(KEY_ALIAS_3, + new ECGenParameterSpec("secp256r1")).getPublic(); // Get the shared secret for the unmodified keys. ka.init(priv); ka.doPhase(pub, true); @@ -754,15 +694,7 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = spec.getCurve(), spec.getGenerator(), spec.getOrder().shiftRight(16), 1); ECPublicKeySpec modifiedPubSpec = new ECPublicKeySpec(pub.getW(), modifiedParams); KeyFactory kf = KeyFactory.getInstance("EC"); - ECPublicKey modifiedPub; - try { - modifiedPub = (ECPublicKey) kf.generatePublic(modifiedPubSpec); - } catch (GeneralSecurityException ex) { - // The provider does not support non-standard curves or did a validity check. - // Both would be correct. - System.out.println("testWrongOrder: can't modify order."); - return; - } + ECPublicKey modifiedPub = (ECPublicKey) kf.generatePublic(modifiedPubSpec); byte[] shared2; try { ka.init(priv); @@ -770,7 +702,6 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = shared2 = ka.generateSecret(); } catch (GeneralSecurityException ex) { // This is the expected behavior - System.out.println("testWrongOrder:" + ex.toString()); return; } // TODO(bleichen): Getting here is already a bug and we might flag this later. @@ -782,25 +713,24 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = // of modular reduction, can determine the private key, either by a binary search or by trying // to guess the private key modulo some small "order". // BouncyCastle v.1.53 fails this test, and leaks the private key. - System.out.println( - "Generated shared secret with a modified order:" - + algorithm - + "\n" - + "expected:" - + TestUtil.bytesToHex(shared) - + " computed:" - + TestUtil.bytesToHex(shared2)); + assertEquals( "Algorithm:" + algorithm, TestUtil.bytesToHex(shared), TestUtil.bytesToHex(shared2)); } @Test - public void testWrongOrderEcdh() throws Exception { + public void testWrongOrderEcdhNist() throws Exception { testWrongOrder("ECDH", EcUtil.getNistP256Params()); + } + + @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore. + public void testWrongOrderEcdhBrainpool() throws Exception { testWrongOrder("ECDH", EcUtil.getBrainpoolP256r1Params()); } @Test + @Ignore // ECDHC algorithm not supported in AndroidKeyStore. public void testWrongOrderEcdhc() throws Exception { testWrongOrder("ECDHC", EcUtil.getNistP256Params()); testWrongOrder("ECDHC", EcUtil.getBrainpoolP256r1Params()); @@ -816,24 +746,23 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = private void testLargePrivateKey(ECParameterSpec spec) throws Exception { BigInteger order = spec.getOrder(); KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - ECPublicKey pub; - try { - keyGen.initialize(spec); - pub = (ECPublicKey) keyGen.generateKeyPair().getPublic(); - } catch (GeneralSecurityException ex) { - // curve is not supported - return; - } + keyGen.initialize(spec); + ECPublicKey pub = (ECPublicKey) keyGen.generateKeyPair().getPublic(); KeyFactory kf = KeyFactory.getInstance("EC"); - KeyAgreement ka = KeyAgreement.getInstance("ECDH"); + KeyAgreement ka = KeyAgreement.getInstance("ECDH", EXPECTED_PROVIDER_NAME); for (int i = 1; i <= 64; i++) { BigInteger p1 = BigInteger.valueOf(i); ECPrivateKeySpec spec1 = new ECPrivateKeySpec(p1, spec); ECPrivateKeySpec spec2 = new ECPrivateKeySpec(order.subtract(p1), spec); - ka.init(kf.generatePrivate(spec1)); + PrivateKey priv1 = kf.generatePrivate(spec1); + // This Public key is not pair of priv1, but it is required to create KeyPair to import into + // AndroidKeyStore, So using dummy public key. + PublicKey pub1 = kf.generatePublic(EC_VALID_PUBLIC_KEY.getX509EncodedKeySpec()); + ka.init(getKeystorePrivateKey(pub1, priv1)); ka.doPhase(pub, true); byte[] shared1 = ka.generateSecret(); - ka.init(kf.generatePrivate(spec2)); + PrivateKey priv2 = kf.generatePrivate(spec2); + ka.init(getKeystorePrivateKey(pub1, priv2)); ka.doPhase(pub, true); byte[] shared2 = ka.generateSecret(); // The private keys p1 and p2 are equivalent, since only the x-coordinate of the @@ -843,230 +772,18 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY = } @Test - public void testLargePrivateKey() throws Exception { + public void testNistCurveLargePrivateKey() throws Exception { testLargePrivateKey(EcUtil.getNistP224Params()); testLargePrivateKey(EcUtil.getNistP256Params()); testLargePrivateKey(EcUtil.getNistP384Params()); // This test failed before CVE-2017-10176 was fixed. testLargePrivateKey(EcUtil.getNistP521Params()); - testLargePrivateKey(EcUtil.getBrainpoolP256r1Params()); } - /** - * This test tries to determine whether point multipliplication using two distinct - * points leads to distinguishable timings. - * - * The main goal here is to determine if the attack by Toru Akishita and Tsuyoshi Takagi - * in https://www-old.cdc.informatik.tu-darmstadt.de/reports/TR/TI-03-01.zvp.pdf - * might be applicable. I.e. one of the points contains a zero value when multiplied - * by mul, the other one does not. - * - * In its current form the test here is quite weak for a number of reasons: - * (1) The timing is often noisy, because the test is run as a unit test. - * (2) The test is executed with only a small number of input points. - * (3) The number of samples is rather low. Running this test with a larger sample - * size would detect more timing differences. Unfortunately - * (4) The test does not determine if a variable run time is exploitable. For example - * if the tested provider uses windowed exponentiation and the special point is - * in the precomputation table then timing differences are easy to spot, but more - * difficult to exploit and hence additional experiments would be necessary. - * - * @param spec the specification of the curve - * @param p0 This is a special point. I.e. multiplying this point by mul - * may lead to a zero value that may be observable. - * @param p1 a random point on the curve - * @param mul an integer, such that multiplying p0 with this value may lead to a timing - * difference - * @param privKeySize the size of the private key in bits - * @param comment describes the test case - */ - private void testTiming(ECParameterSpec spec, ECPoint p0, ECPoint p1, - BigInteger mul, int privKeySize, String comment) throws Exception { - ThreadMXBean bean = ManagementFactory.getThreadMXBean(); - if (!bean.isCurrentThreadCpuTimeSupported()) { - System.out.println("getCurrentThreadCpuTime is not supported. Skipping"); - return; - } - SecureRandom random = new SecureRandom(); - int fixedSize = mul.bitLength(); - int missingBits = privKeySize - 2 * fixedSize; - assertTrue(missingBits > 0); - // possible values for tests, minCount: - // 1024, 410 - // 2048, 880 - // 4096, 1845 - // 10000, 4682 - // I.e. these are values, such that doing 'tests' coin flips results in <= minCount heads or - // tails with a probability smaller than 2^-32. - // - // def min_count(n, b=33): - // res, sum, k = 1,1,0 - // bnd = 2**(n-b) - // while sum < bnd: - // res *= n - k - // res //= 1 + k - // k += 1 - // sum += res - // return k - 1 - final int tests = 2048; - final int minCount = 880; - // the number of measurements done with each point - final int repetitions = 8; - // the number of warmup experiments that are ignored - final int warmup = 8; - final int sampleSize = warmup + tests; - KeyFactory kf = KeyFactory.getInstance("EC"); - PublicKey[] publicKeys = new PublicKey[2]; - try { - publicKeys[0] = kf.generatePublic(new ECPublicKeySpec(p0, spec)); - publicKeys[1] = kf.generatePublic(new ECPublicKeySpec(p1, spec)); - } catch (InvalidKeySpecException ex) { - // unsupported curve - return; - } - PrivateKey[] privKeys = new PrivateKey[sampleSize]; - for (int i = 0; i < sampleSize; i++) { - BigInteger m = new BigInteger(missingBits, random); - m = mul.shiftLeft(missingBits).add(m); - m = m.shiftLeft(fixedSize).add(mul); - ECPrivateKeySpec privSpec = new ECPrivateKeySpec(m, spec); - privKeys[i] = kf.generatePrivate(privSpec); - } - KeyAgreement ka = KeyAgreement.getInstance("ECDH"); - long[][] timings = new long[2][sampleSize]; - for (int i = 0; i < sampleSize; i++) { - for (int j = 0; j < 2 * repetitions; j++) { - // idx determines which key to use. - int idx = (j ^ i) & 1; - ka.init(privKeys[i]); - long start = bean.getCurrentThreadCpuTime(); - ka.doPhase(publicKeys[idx], true); - byte[] unused = ka.generateSecret(); - long time = bean.getCurrentThreadCpuTime() - start; - timings[idx][i] += time; - } - } - for (int i = 0; i < sampleSize; i++) { - for (int j = 0; j < 2; j++) { - timings[j][i] /= repetitions; - } - } - - // Performs some statistics. - boolean noisy = false; // Set to true, if the timings have a large variance. - System.out.println("ECDH timing test:" + comment); - double[] avg = new double[2]; - double[] var = new double[2]; - for (int i = 0; i < 2; i++) { - double sum = 0.0; - double sumSqr = 0.0; - for (int j = warmup; j < sampleSize; j++) { - double val = (double) timings[i][j]; - sum += val; - sumSqr += val * val; - } - avg[i] = sum / tests; - var[i] = (sumSqr - avg[i] * sum) / (tests - 1); - double stdDev = Math.sqrt(var[i]); - double cv = stdDev / avg[i]; - System.out.println("Timing for point " + i + " avg: " + avg[i] + " std dev: " + stdDev - + " cv:" + cv); - // The ratio 0.05 below is a somewhat arbitrary value that tries to determine if the noise - // is too big to detect even larger timing differences. - if (cv > 0.05) { - noisy = true; - } - } - // Paired Z-test: - // The outcome of this value can be significantly influenced by extreme outliers, such - // as slow timings because of things like a garbage collection. - double sigmas = Math.abs(avg[0] - avg[1]) / Math.sqrt((var[0] + var[1]) / tests); - System.out.println("Sigmas: " + sigmas); - - // Pairwise comparison: - // this comparison has the property that it compares timings done with the same - // private key, hence timing differences from using different addition chain sizes - // are ignored. Extreme outliers should not influence the result a lot, as long as the - // number of outliers is small. - int point0Faster = 0; - int equal = 0; - for (int i = 0; i < sampleSize; i++) { - if (timings[0][i] < timings[1][i]) { - point0Faster += 1; - } else if (timings[0][i] < timings[1][i]) { - equal += 1; - } - } - point0Faster += equal / 2; - System.out.println("Point 0 multiplication is faster: " + point0Faster); - if (point0Faster < minCount || point0Faster > sampleSize - minCount) { - fail("Timing differences in ECDH computation detected"); - } else if (noisy) { - System.out.println("Timing was too noisy to expect results."); - } - } - - @SlowTest(providers = - {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE, ProviderType.OPENJDK}) - @Test - public void testTimingSecp256r1() throws Exception { - // edge case for projective coordinates - BigInteger x1 = - new BigInteger("81bfb55b010b1bdf08b8d9d8590087aa278e28febff3b05632eeff09011c5579", 16); - BigInteger y1 = - new BigInteger("732d0e65267ea28b7af8cfcb148936c2af8664cbb4f04e188148a1457400c2a7", 16); - ECPoint p1 = new ECPoint(x1, y1); - // random point - BigInteger x2 = - new BigInteger("8608e36a91f1fba12e4074972af446176b5608c9c58dc318bd0742754c3dcee7", 16); - BigInteger y2 = - new BigInteger("bc2c9ecd44af916ca58d9e3ef1257f698d350ef486eb86137fe69a7375bcc191", 16); - ECPoint p2 = new ECPoint(x2, y2); - testTiming(EcUtil.getNistP256Params(), p1, p2, new BigInteger("2"), 256, "secp256r1"); - } - - @SlowTest(providers = - {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE, ProviderType.OPENJDK}) @Test - public void testTimingSecp384r1() throws Exception { - // edge case for projective coordinates - BigInteger x1 = - new BigInteger("7a6fadfee03eb09554f2a04fe08300aca88bb3a46e8f6347bace672cfe427698" - + "8541cef8dc10536a84580215f5f90a3b", 16); - BigInteger y1 = - new BigInteger("6d243d5d9de1cdddd04cbeabdc7a0f6c244391f7cb2d5738fe13c334add4b458" - + "5fef61ffd446db33b39402278713ae78", 16); - ECPoint p1 = new ECPoint(x1, y1); - // random point - BigInteger x2 = - new BigInteger("71f3c57d6a879889e582af2c7c5444b0eb6ba95d88365b21ca9549475273ecdd" - + "3930aa0bebbd1cf084e4049667278602", 16); - BigInteger y2 = - new BigInteger("9dcbc4d843af8944eb4ba018d369b351a9ea0f7b9e3561df2ee218d54e198f7c" - + "837a3abaa41dffd2d2cb771a7599ed9e", 16); - ECPoint p2 = new ECPoint(x2, y2); - testTiming(EcUtil.getNistP384Params(), p1, p2, new BigInteger("2"), 384, "secp384r1"); - } - - @SlowTest(providers = - {ProviderType.BOUNCY_CASTLE, ProviderType.SPONGY_CASTLE, ProviderType.OPENJDK}) - @Test - public void testTimingBrainpoolP256r1() throws Exception { - // edge case for Jacobian and projective coordinates - BigInteger x1 = - new BigInteger("79838c22d2b8dc9af2e6cf56f8826dc3dfe10fcb17b6aaaf551ee52bef12f826", 16); - BigInteger y1 = - new BigInteger("1e2ed3d453088c8552c6feecf898667bc1e15905002edec6b269feb7bea09d5b", 16); - ECPoint p1 = new ECPoint(x1, y1); - - // random point - BigInteger x2 = - new BigInteger("2720b2e821b2ac8209b573bca755a68821e1e09deb580666702570dd527dd4c1", 16); - BigInteger y2 = - new BigInteger("25cdd610243c7e693fad7bd69b43ae3e63e94317c4c6b717d9c8bc3be8c996fb", 16); - ECPoint p2 = new ECPoint(x2, y2); - testTiming(EcUtil.getBrainpoolP256r1Params(), p1, p2, new BigInteger("2"), 255, - "brainpoolP256r1"); + @Ignore // Brainpool curves are not supported in AndroidKeyStore. + public void testBrainpoolCurveLargePrivateKey() throws Exception { + testLargePrivateKey(EcUtil.getBrainpoolP256r1Params()); } } diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java index a6ce23a..3da6d83 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java @@ -16,25 +16,27 @@ package com.google.security.wycheproof; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import com.google.security.wycheproof.WycheproofRunner.ProviderType; -import com.google.security.wycheproof.WycheproofRunner.SlowTest; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadMXBean; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.KeyStore; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Signature; +import java.security.PrivateKey; +import java.security.PublicKey; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; import java.security.spec.ECGenParameterSpec; import java.security.spec.ECParameterSpec; import java.util.Arrays; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.Ignore; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import android.keystore.cts.util.KeyStoreUtil; /** * Tests ECDSA signatures. @@ -44,8 +46,26 @@ import org.junit.runners.JUnit4; * * @author bleichen@google.com (Daniel Bleichenbacher) */ -@RunWith(JUnit4.class) public class EcdsaTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "TestKey"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + + private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey) + throws Exception { + KeyProtection keyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) + .setDigests(KeyProperties.DIGEST_SHA224, + KeyProperties.DIGEST_SHA256, + KeyProperties.DIGEST_SHA384, + KeyProperties.DIGEST_SHA512) + .build(); + KeyStore keyStore = KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey, keyProtection); + return (PrivateKey) keyStore.getKey(KEY_ALIAS_1, null); + } /** * Determines the Hash name from the ECDSA algorithm. There is a small inconsistency in the naming @@ -149,15 +169,11 @@ public class EcdsaTest { ECPublicKey pub = (ECPublicKey) keyPair.getPublic(); ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate(); - // Print the parameters. - System.out.println("Parameters for curve:" + curve); - EcUtil.printParameters(pub.getParams()); - Signature signer; Signature verifier; try { - signer = Signature.getInstance(algorithm); - verifier = Signature.getInstance(algorithm); + signer = Signature.getInstance(algorithm, EXPECTED_PROVIDER_NAME); + verifier = Signature.getInstance(algorithm, EXPECTED_PROVIDER_NAME); } catch (NoSuchAlgorithmException ex) { // The algorithm is not supported. return false; @@ -165,7 +181,7 @@ public class EcdsaTest { // Both algorithm and curve are supported. // Hence, we expect that signing and verifying properly works. byte[] messageBytes = message.getBytes("UTF-8"); - signer.initSign(priv); + signer.initSign(getKeystorePrivateKey(pub, priv)); signer.update(messageBytes); byte[] signature = signer.sign(); verifier.initVerify(pub); @@ -188,12 +204,7 @@ public class EcdsaTest { /** Checks whether the one time key k in ECDSA is biased. */ public void testBias(String algorithm, String curve, ECParameterSpec ecParams) throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - try { - keyGen.initialize(ecParams); - } catch (InvalidAlgorithmParameterException ex) { - System.out.println("This provider does not support curve:" + curve); - return; - } + keyGen.initialize(ecParams); KeyPair keyPair = keyGen.generateKeyPair(); ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate(); // If we throw a fair coin tests times then the probability that @@ -214,8 +225,8 @@ public class EcdsaTest { BigInteger q = priv.getParams().getOrder(); BigInteger qHalf = q.shiftRight(1); - Signature signer = Signature.getInstance(algorithm); - signer.initSign(priv); + Signature signer = Signature.getInstance(algorithm, EXPECTED_PROVIDER_NAME); + signer.initSign(getKeystorePrivateKey(keyPair.getPublic(), keyPair.getPrivate())); BigInteger[] kList = new BigInteger[tests]; for (int i = 0; i < tests; i++) { signer.update(messageBytes); @@ -277,141 +288,17 @@ public class EcdsaTest { } } - @SlowTest( - providers = { - ProviderType.BOUNCY_CASTLE, - ProviderType.CONSCRYPT, - ProviderType.OPENJDK, - ProviderType.SPONGY_CASTLE - } - ) @Test public void testBiasAll() throws Exception { testBias("SHA256WithECDSA", "secp256r1", EcUtil.getNistP256Params()); testBias("SHA224WithECDSA", "secp224r1", EcUtil.getNistP224Params()); testBias("SHA384WithECDSA", "secp384r1", EcUtil.getNistP384Params()); testBias("SHA512WithECDSA", "secp521r1", EcUtil.getNistP521Params()); - testBias("SHA256WithECDSA", "brainpoolP256r1", EcUtil.getBrainpoolP256r1Params()); - } - - /** - * Tests for a potential timing attack. This test checks if there is a correlation between the - * timing of signature generation and the size of the one-time key k. This is for example the case - * if a double and add method is used for the point multiplication. The test fails if such a - * correlation can be shown with high confidence. Further analysis will be necessary to determine - * how easy it is to exploit the bias in a timing attack. - */ - // TODO(bleichen): Determine if there are exploitable providers. - // - // SunEC currently fails this test. Since ECDSA typically is used with EC groups whose order - // is 224 bits or larger, it is unclear whether the same attacks that apply to DSA are practical. - // - // The ECDSA implementation in BouncyCastle leaks information about k through timing too. - // The test has not been optimized to detect this bias. It would require about 5'000'000 samples, - // which is too much for a simple unit test. - // - // BouncyCastle uses FixedPointCombMultiplier for ECDSA. This is a method using - // precomputation. The implementation is not constant time, since the precomputation table - // contains the point at infinity and adding this point is faster than ordinary point additions. - // The timing leak only has a small correlation to the size of k and at the moment it is is very - // unclear if the can be exploited. (Randomizing the precomputation table by adding the same - // random point to each element in the table and precomputing the necessary offset to undo the - // precomputation seems much easier than analyzing this.) - public void testTiming(String algorithm, String curve, ECParameterSpec ecParams) - throws Exception { - ThreadMXBean bean = ManagementFactory.getThreadMXBean(); - if (!bean.isCurrentThreadCpuTimeSupported()) { - System.out.println("getCurrentThreadCpuTime is not supported. Skipping"); - return; - } - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC"); - try { - keyGen.initialize(ecParams); - } catch (InvalidAlgorithmParameterException ex) { - System.out.println("This provider does not support curve:" + curve); - return; - } - KeyPair keyPair = keyGen.generateKeyPair(); - ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate(); - - String message = "Hello"; - String hashAlgorithm = getHashAlgorithm(algorithm); - byte[] messageBytes = message.getBytes("UTF-8"); - byte[] digest = MessageDigest.getInstance(hashAlgorithm).digest(messageBytes); - BigInteger h = new BigInteger(1, digest); - Signature signer = Signature.getInstance(algorithm); - signer.initSign(priv); - // The number of samples used for the test. This number is a bit low. - // I.e. it just barely detects that SunEC leaks information about the size of k. - int samples = 50000; - long[] timing = new long[samples]; - BigInteger[] k = new BigInteger[samples]; - for (int i = 0; i < samples; i++) { - long start = bean.getCurrentThreadCpuTime(); - signer.update(messageBytes); - byte[] signature = signer.sign(); - timing[i] = bean.getCurrentThreadCpuTime() - start; - k[i] = extractK(signature, h, priv); - } - long[] sorted = Arrays.copyOf(timing, timing.length); - Arrays.sort(sorted); - double n = priv.getParams().getOrder().doubleValue(); - double expectedAverage = n / 2; - double maxSigma = 0; - System.out.println("testTiming algorithm:" + algorithm); - for (int idx = samples - 1; idx > 10; idx /= 2) { - long cutoff = sorted[idx]; - int count = 0; - BigInteger total = BigInteger.ZERO; - for (int i = 0; i < samples; i++) { - if (timing[i] <= cutoff) { - total = total.add(k[i]); - count += 1; - } - } - double expectedStdDev = n / Math.sqrt(12 * count); - double average = total.doubleValue() / count; - // Number of standard deviations that the average is away from - // the expected value: - double sigmas = Math.abs(expectedAverage - average) / expectedStdDev; - if (sigmas > maxSigma) { - maxSigma = sigmas; - } - System.out.println( - "count:" - + count - + " cutoff:" - + cutoff - + " relative average:" - + (average / expectedAverage) - + " sigmas:" - + sigmas); - } - // Checks if the signatures with a small timing have a biased k. - // We use 7 standard deviations, so that the probability of a false positive is smaller - // than 10^{-10}. - if (maxSigma >= 7) { - fail("Signatures with short timing have a biased k"); - } } - @SlowTest( - providers = { - ProviderType.BOUNCY_CASTLE, - ProviderType.CONSCRYPT, - ProviderType.OPENJDK, - ProviderType.SPONGY_CASTLE - } - ) @Test - public void testTimingAll() throws Exception { - testTiming("SHA256WithECDSA", "secp256r1", EcUtil.getNistP256Params()); - // TODO(bleichen): crypto libraries sometimes use optimized code for curves that are frequently - // used. Hence it would make sense to test distinct curves. But at the moment testing many - // curves is not practical since one test alone is already quite time consuming. - // testTiming("SHA224WithECDSA", "secp224r1", EcUtil.getNistP224Params()); - // testTiming("SHA384WithECDSA", "secp384r1", EcUtil.getNistP384Params()); - // testTiming("SHA512WithECDSA", "secp521r1", EcUtil.getNistP521Params()); - // testTiming("SHA256WithECDSA", "brainpoolP256r1", EcUtil.getBrainpoolP256r1Params()); + @Ignore // Brainpool curve are not supported in AndroidKeyStore + public void testBiasBrainpoolCurve() throws Exception { + testBias("SHA256WithECDSA", "brainpoolP256r1", EcUtil.getBrainpoolP256r1Params()); } } diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java index c34f3f7..91a0d2c 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java @@ -14,12 +14,14 @@ package com.google.security.wycheproof; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import java.security.AlgorithmParameters; import java.security.GeneralSecurityException; +import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -28,15 +30,23 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.junit.Test; import org.junit.Ignore; +import org.junit.After; import android.security.keystore.KeyProtection; import android.security.keystore.KeyProperties; -import java.security.KeyStore; +import android.keystore.cts.util.KeyStoreUtil; /** This test uses test vectors in JSON format to test AEAD schemes. */ public class JsonAeadTest { private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_PROVIDER_NAME; - private static final String EXPECTED_CRYPTO_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String EXPECTED_CRYPTO_PROVIDER_NAME = + TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "Key1"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } /** Joins two bytearrays. */ protected static byte[] join(byte[] head, byte[] tail) { @@ -84,19 +94,14 @@ public class JsonAeadTest { Cipher cipher = Cipher.getInstance(algorithm, EXPECTED_CRYPTO_PROVIDER_NAME); if (algorithm.equalsIgnoreCase("AES/GCM/NoPadding")) { SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); - - KeyStore keyStore = KeyStore.getInstance(EXPECTED_PROVIDER_NAME); - keyStore.load(null); - keyStore.setEntry( - "key1", - new KeyStore.SecretKeyEntry(keySpec), + KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(KEY_ALIAS_1, keySpec, new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setRandomizedEncryptionRequired(false) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); // Key imported, obtain a reference to it. - SecretKey keyStoreKey = (SecretKey) keyStore.getKey("key1", null); + SecretKey keyStoreKey = (SecretKey) keyStore.getKey(KEY_ALIAS_1, null); AlgorithmParameters params = AlgorithmParameters.getInstance("GCM"); params.init(new GCMParameterSpec(tagSize, iv)); @@ -162,14 +167,17 @@ public class JsonAeadTest { final String expectedVersion = "0.6"; // Checking preconditions. - try { - Cipher.getInstance(algorithm, EXPECTED_CRYPTO_PROVIDER_NAME); - } catch (NoSuchAlgorithmException ex) { - throw ex; - } + Cipher.getInstance(algorithm, EXPECTED_CRYPTO_PROVIDER_NAME); JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename); String generatorVersion = test.get("generatorVersion").getAsString(); + assertFalse( + algorithm + + ": expecting test vectors with version " + + expectedVersion + + " found vectors with version " + + generatorVersion, + generatorVersion.equals(expectedVersion)); int numTests = test.get("numberOfTests").getAsInt(); int cntTests = 0; int errors = 0; diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java index a072989..e0c5b27 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java @@ -14,6 +14,7 @@ package com.google.security.wycheproof; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import com.google.gson.JsonElement; @@ -23,11 +24,15 @@ import java.security.NoSuchAlgorithmException; import java.util.Set; import java.util.TreeSet; import javax.crypto.Cipher; +import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import java.security.KeyStore; +import android.keystore.cts.util.KeyStoreUtil; /** * This test uses test vectors in JSON format to test symmetric ciphers. @@ -36,8 +41,14 @@ import org.junit.runners.JUnit4; * are randomized using an initialization vector as long as the JSON test vectors are represented * with the type "IndCpaTest". */ -@RunWith(JUnit4.class) public class JsonCipherTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "Key1"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } /** Convenience method to get a byte array from a JsonObject. */ protected static byte[] getBytes(JsonObject object, String name) throws Exception { @@ -73,7 +84,16 @@ public class JsonCipherTest { fail("Unsupported algorithm:" + algorithm); } IvParameterSpec ivSpec = new IvParameterSpec(iv); - cipher.init(opmode, keySpec, ivSpec); + KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(KEY_ALIAS_1, keySpec, + new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_CBC) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) + .setRandomizedEncryptionRequired(false) + .build()); + // Key imported, obtain a reference to it. + SecretKey keyStoreKey = (SecretKey) keyStore.getKey(KEY_ALIAS_1, null); + + cipher.init(opmode, keyStoreKey, ivSpec); } @@ -111,27 +131,20 @@ public class JsonCipherTest { // the minor number if only the test vectors (but not the format) changes. // Versions meant for distribution have no status. final String expectedVersion = "0.4"; - JsonObject test = JsonUtil.getTestVectors(filename); + JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename); Set<String> exceptions = new TreeSet<String>(); String generatorVersion = test.get("generatorVersion").getAsString(); - if (!generatorVersion.equals(expectedVersion)) { - System.out.println( + assertFalse( algorithm + ": expecting test vectors with version " + expectedVersion + " found vectors with version " - + generatorVersion); - } + + generatorVersion, + generatorVersion.equals(expectedVersion)); int numTests = test.get("numberOfTests").getAsInt(); int cntTests = 0; int errors = 0; - Cipher cipher; - try { - cipher = Cipher.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("Algorithm is not supported. Skipping test for " + algorithm); - return; - } + Cipher cipher = Cipher.getInstance(algorithm, EXPECTED_PROVIDER_NAME); for (JsonElement g : test.getAsJsonArray("testGroups")) { JsonObject group = g.getAsJsonObject(); for (JsonElement t : group.getAsJsonArray("tests")) { @@ -155,7 +168,6 @@ public class JsonCipherTest { } catch (GeneralSecurityException ex) { // Some libraries restrict key size, iv size and tag size. // Because of the initialization of the cipher might fail. - System.out.println(ex.toString()); continue; } try { @@ -164,22 +176,15 @@ public class JsonCipherTest { if (result.equals("invalid")) { if (eq) { // Some test vectors use invalid parameters that should be rejected. - System.out.println("Encrypted " + tc); errors++; } } else { if (!eq) { - System.out.println( - "Incorrect ciphertext for " - + tc - + " ciphertext:" - + TestUtil.bytesToHex(encrypted)); errors++; } } } catch (GeneralSecurityException ex) { if (result.equals("valid")) { - System.out.println("Failed to encrypt " + tc); errors++; } } @@ -192,7 +197,6 @@ public class JsonCipherTest { try { initCipher(cipher, algorithm, Cipher.DECRYPT_MODE, key, iv); } catch (GeneralSecurityException ex) { - System.out.println("Parameters accepted for encryption but not decryption " + tc); errors++; continue; } @@ -200,18 +204,15 @@ public class JsonCipherTest { byte[] decrypted = cipher.doFinal(ciphertext); boolean eq = arrayEquals(decrypted, msg); if (result.equals("invalid")) { - System.out.println("Decrypted invalid ciphertext " + tc + " eq:" + eq); errors++; } else { if (!eq) { - System.out.println( - "Incorrect decryption " + tc + " decrypted:" + TestUtil.bytesToHex(decrypted)); + errors++; } } } catch (GeneralSecurityException ex) { - exceptions.add(ex.getMessage()); + exceptions.add(ex.getMessage() == null ? "" : ex.getMessage()); if (result.equals("valid")) { - System.out.println("Failed to decrypt " + tc); errors++; } } @@ -228,15 +229,18 @@ public class JsonCipherTest { // AES/CBC/PKCS5Padding with the tested provider are vulnerable to attacks. Rather it means // that the provider might simplify attacks if the protocol is using AES/CBC/PKCS5Padding // incorrectly. - System.out.println("Number of distinct exceptions:" + exceptions.size()); + StringBuilder sb = new StringBuilder(); + sb.append("Exceptions: "); for (String ex : exceptions) { - System.out.println(ex); + sb.append(ex.toString() + " "); } - assertEquals(1, exceptions.size()); + assertEquals(sb.toString(), 1, exceptions.size()); } @Test public void testAesCbcPkcs5() throws Exception { - testCipher("aes_cbc_pkcs5_test.json", "AES/CBC/PKCS5Padding"); + // AndroidKeyStore only suuport AES/CBC/PKCS7Padding algorithm, + // so it is used instead of PKCS5Padding + testCipher("aes_cbc_pkcs5_test.json", "AES/CBC/PKCS7Padding"); } } diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java index eeb48ec..c1dddc5 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java @@ -12,9 +12,12 @@ package com.google.security.wycheproof; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.security.Key; +import java.security.KeyStore; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -23,13 +26,23 @@ import java.util.Locale; import javax.crypto.Mac; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.Ignore; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import java.io.IOException; +import android.keystore.cts.util.KeyStoreUtil; /** This test uses test vectors in JSON format to test MAC primitives. */ -@RunWith(JUnit4.class) public class JsonMacTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "Key1"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } /** Convenience method to get a byte array from an JsonObject */ protected static byte[] getBytes(JsonObject obj, String name) throws Exception { @@ -60,8 +73,8 @@ public class JsonMacTest { * bits, since the JCE interface does not seem to support such a specification. */ protected static byte[] computeMac(String algorithm, byte[] key, byte[] msg, int tagSize) - throws GeneralSecurityException { - Mac mac = Mac.getInstance(algorithm); + throws Exception { + Mac mac = Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); algorithm = algorithm.toUpperCase(Locale.ENGLISH); if (algorithm.startsWith("HMAC")) { SecretKeySpec keySpec = new SecretKeySpec(key, algorithm); @@ -77,7 +90,11 @@ public class JsonMacTest { // But this class is often not supported. Hence the computation here, just computes a // full length tag and truncates it. The drawback of having to truncate tags is that // the caller has to compare truncated tags during verification. - mac.init(keySpec); + KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(KEY_ALIAS_1, keySpec, + new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build()); + // Key imported, obtain a reference to it. + Key keyStoreKey = keyStore.getKey(KEY_ALIAS_1, null); + mac.init(keyStoreKey); mac.update(msg); byte[] tag = mac.doFinal(); return Arrays.copyOf(tag, tagSize / 8); @@ -94,14 +111,9 @@ public class JsonMacTest { */ public void testMac(String filename) throws Exception { // Checking preconditions. - JsonObject test = JsonUtil.getTestVectors(filename); + JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename); String algorithm = test.get("algorithm").getAsString(); - try { - Mac.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("Algorithm is not supported. Skipping test for " + algorithm); - return; - } + Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); int numTests = test.get("numberOfTests").getAsInt(); int cntTests = 0; @@ -125,16 +137,7 @@ public class JsonMacTest { String result = testcase.get("result").getAsString(); byte[] computedTag = null; - try { - computedTag = computeMac(algorithm, key, msg, tagSize); - } catch (GeneralSecurityException ex) { - // Some libraries restrict key size or tag size. Hence valid MACs might be - // rejected. - continue; - } catch (IllegalArgumentException ex) { - // Thrown by javax.crypto.spec.SecretKeySpec (e.g. when the key is empty). - continue; - } + computedTag = computeMac(algorithm, key, msg, tagSize); boolean eq = arrayEquals(expectedTag, computedTag); if (result.equals("invalid")) { @@ -142,26 +145,17 @@ public class JsonMacTest { // Some test vectors use invalid parameters that should be rejected. // E.g. an implementation must not allow AES-GMAC with an IV of length 0, // since this leaks the authentication key. - System.out.println("Computed mac for test case " + tc); errors++; } } else { if (eq) { passedTests++; } else { - System.out.println( - "Incorrect tag for " - + tc - + " expected:" - + TestUtil.bytesToHex(expectedTag) - + " computed:" - + TestUtil.bytesToHex(computedTag)); errors++; } } } } - System.out.println("passed Tests for " + algorithm + ":" + passedTests); assertEquals(0, errors); assertEquals(numTests, cntTests); } @@ -180,7 +174,7 @@ public class JsonMacTest { */ protected static Mac getInitializedMacWithIv(String algorithm, byte[] key, byte[] iv, int tagSize) throws GeneralSecurityException { - Mac mac = Mac.getInstance(algorithm); + Mac mac = Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); algorithm = algorithm.toUpperCase(Locale.ENGLISH); if (algorithm.equals("AES-GMAC")) { SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); @@ -207,14 +201,9 @@ public class JsonMacTest { */ public void testMacWithIv(String filename, String algorithm) throws Exception { // Checking preconditions. - try { - Mac.getInstance(algorithm); - } catch (NoSuchAlgorithmException ex) { - System.out.println("Algorithm is not supported. Skipping test for " + algorithm); - return; - } + Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); - JsonObject test = JsonUtil.getTestVectors(filename); + JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename); int numTests = test.get("numberOfTests").getAsInt(); int cntTests = 0; int passedTests = 0; @@ -237,17 +226,7 @@ public class JsonMacTest { // "acceptable" are test vectors with weak parameters or legacy formats. String result = testcase.get("result").getAsString(); - Mac mac; - try { - mac = getInitializedMacWithIv(algorithm, key, iv, tagSize); - } catch (GeneralSecurityException ex) { - // Some libraries restrict key size, iv size and tag size. - // Because of the initialization of the Mac might fail. - continue; - } catch (IllegalArgumentException ex) { - // Thrown by javax.crypto.spec.SecretKeySpec (e.g. when the key is empty). - continue; - } + Mac mac = getInitializedMacWithIv(algorithm, key, iv, tagSize); byte[] computedTag = mac.doFinal(msg); boolean eq = arrayEquals(expectedTag, computedTag); @@ -256,26 +235,17 @@ public class JsonMacTest { // Some test vectors use invalid parameters that should be rejected. // E.g. an implementation must not allow AES-GMAC with an IV of length 0, // since this leaks the authentication key. - System.out.println("Computed mac for test case " + tc); errors++; } } else { if (eq) { passedTests++; } else { - System.out.println( - "Incorrect tag for " - + tc - + " expected:" - + TestUtil.bytesToHex(expectedTag) - + " computed:" - + TestUtil.bytesToHex(computedTag)); errors++; } } } } - System.out.println("passed Tests for " + algorithm + ":" + passedTests); assertEquals(0, errors); assertEquals(numTests, cntTests); } @@ -306,26 +276,31 @@ public class JsonMacTest { } @Test + @Ignore // HMAC Sha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_224() throws Exception { testMac("hmac_sha3_224_test.json"); } @Test + @Ignore // HMAC Sha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_256() throws Exception { testMac("hmac_sha3_256_test.json"); } @Test + @Ignore // HMAC Sha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_384() throws Exception { testMac("hmac_sha3_384_test.json"); } @Test + @Ignore // HMAC Sha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_512() throws Exception { testMac("hmac_sha3_512_test.json"); } @Test + @Ignore // Ignored due to AES-GMAC algorithm not supported in AndroidKeyStore public void testAesGmac() throws Exception { testMacWithIv("gmac_test.json", "AES-GMAC"); } diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java index 21bf0d5..58da530 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java @@ -15,16 +15,16 @@ package com.google.security.wycheproof; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.security.wycheproof.WycheproofRunner.ExcludedTest; -import com.google.security.wycheproof.WycheproofRunner.NoPresubmitTest; -import com.google.security.wycheproof.WycheproofRunner.ProviderType; import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.KeyFactory; +import java.security.KeyStore; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; @@ -33,17 +33,39 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.HashSet; import java.util.Set; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.Ignore; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import android.keystore.cts.util.KeyStoreUtil; /** * 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 */ -@RunWith(JUnit4.class) public class JsonSignatureTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "TestKey"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + + private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey) + throws Exception { + KeyProtection keyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) + .setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA224, + KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384, + KeyProperties.DIGEST_SHA512) + .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) + .build(); + KeyStore keyStore = + KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey, keyProtection); + return (PrivateKey) keyStore.getKey(KEY_ALIAS_1, null); + } /** * Defines the format of the signatures. RAW is used when the signature scheme already @@ -105,14 +127,14 @@ public class JsonSignatureTest { */ protected static Signature getSignatureInstance( JsonObject group, String signatureAlgorithm, Format signatureFormat) - throws NoSuchAlgorithmException { + throws NoSuchAlgorithmException, NoSuchProviderException { String md = ""; if (group.has("sha")) { md = convertMdName(getString(group, "sha")); } if (signatureAlgorithm.equals("ECDSA") || signatureAlgorithm.equals("DSA")) { if (signatureFormat == Format.ASN) { - return Signature.getInstance(md + "WITH" + signatureAlgorithm); + return Signature.getInstance(md + "WITH" + signatureAlgorithm, EXPECTED_PROVIDER_NAME); } else if (signatureFormat == Format.P1363) { // The algorithm names for signature schemes with P1363 format have distinct names // in distinct providers. This is mainly the case since the P1363 format has only @@ -120,32 +142,28 @@ public class JsonSignatureTest { // than that. Hence the code below just tries known algorithm names. try { String jdkName = md + "WITH" + signatureAlgorithm + "inP1363Format"; - return Signature.getInstance(jdkName); + return Signature.getInstance(jdkName, EXPECTED_PROVIDER_NAME); } catch (NoSuchAlgorithmException ex) { // jdkName is not known. } - try { - String bcName = md + "WITHPLAIN-" + signatureAlgorithm; - return Signature.getInstance(bcName); - } catch (NoSuchAlgorithmException ex) { - // bcName is not known. - } + String bcName = md + "WITHPLAIN-" + signatureAlgorithm; + return Signature.getInstance(bcName, EXPECTED_PROVIDER_NAME); } } else if (signatureAlgorithm.equals("RSA")) { if (signatureFormat == Format.RAW) { - return Signature.getInstance(md + "WITH" + signatureAlgorithm); + return Signature.getInstance(md + "WITH" + signatureAlgorithm, EXPECTED_PROVIDER_NAME); } } else if (signatureAlgorithm.equals("ED25519") || signatureAlgorithm.equals("ED448")) { if (signatureFormat == Format.RAW) { // http://openjdk.java.net/jeps/339 try { - return Signature.getInstance(signatureAlgorithm); + return Signature.getInstance(signatureAlgorithm, EXPECTED_PROVIDER_NAME); } catch (NoSuchAlgorithmException ex) { // signatureAlgorithm is not known. } // An alternative name (e.g. used by BouncyCastle) is "EDDSA". try { - return Signature.getInstance("EDDSA"); + return Signature.getInstance("EDDSA", EXPECTED_PROVIDER_NAME); } catch (NoSuchAlgorithmException ex) { // "EDDSA" is not known either. } @@ -249,8 +267,12 @@ public class JsonSignatureTest { if (algorithm.equals("RSA")) { KeyFactory kf = KeyFactory.getInstance(algorithm); byte[] encoded = TestUtil.hexToBytes(getString(object, "privateKeyPkcs8")); + byte[] pubEncoded = TestUtil.hexToBytes(getString(object, "keyDer")); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); - return kf.generatePrivate(keySpec); + PrivateKey intermediateKey = kf.generatePrivate(keySpec); + X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(pubEncoded); + PublicKey pubKey = kf.generatePublic(x509keySpec); + return getKeystorePrivateKey(pubKey, intermediateKey); } else { throw new NoSuchAlgorithmException("Algorithm " + algorithm + " is not supported"); } @@ -298,19 +320,18 @@ public class JsonSignatureTest { public void testVerification( String filename, String signatureAlgorithm, Format signatureFormat, boolean allowSkippingKeys) throws Exception { - JsonObject test = JsonUtil.getTestVectors(filename); + JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename); // Checks whether the test vectors in the file use the expected algorithm and the expected // format for the signatures. String schema = expectedSchema(signatureAlgorithm, signatureFormat, true); String actualSchema = getString(test, "schema"); - if (!schema.isEmpty() && !schema.equals(actualSchema)) { - System.out.println( + assertFalse( signatureAlgorithm + ": expecting test vectors with schema " + schema + " found vectors with schema " - + actualSchema); - } + + actualSchema, + !schema.isEmpty() && !schema.equals(actualSchema)); int numTests = test.get("numberOfTests").getAsInt(); int cntTests = 0; int verifiedSignatures = 0; @@ -376,49 +397,14 @@ public class JsonSignatureTest { } 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() - + " " - + filename - + " tcId:" - + tcid - + " sig:" - + sig); verified = false; failure = ex; errors++; } if (!verified && result.equals("valid")) { - String reason = ""; - if (failure != null) { - reason = " reason:" + failure; - } - System.out.println( - "Valid " - + signatureAlgorithm - + " signature not verified." - + " " - + filename - + " tcId:" - + tcid - + " sig:" - + sig - + reason); errors++; } else if (verified) { if (result.equals("invalid")) { - System.out.println( - "Invalid" - + signatureAlgorithm - + " signature verified." - + " " - + filename - + " tcId:" - + tcid - + " sig:" - + sig); errors++; } else { verifiedSignatures++; @@ -426,24 +412,6 @@ public class JsonSignatureTest { } } } - // Prints some information if tests were skipped. This avoids giving - // the impression that algorithms are supported. - if (skippedKeys > 0 || skippedAlgorithms > 0 || verifiedSignatures == 0) { - System.out.println( - "File:" - + filename - + " number of skipped keys:" - + skippedKeys - + " number of skipped algorithms:" - + skippedAlgorithms - + " number of supported keys:" - + supportedKeys - + " verified signatures:" - + verifiedSignatures); - for (String s : skippedGroups) { - System.out.println("Skipped groups where " + s); - } - } assertEquals(0, errors); if (skippedKeys == 0 && skippedAlgorithms == 0) { assertEquals(numTests, cntTests); @@ -466,19 +434,7 @@ public class JsonSignatureTest { public void testSigning( String filename, String signatureAlgorithm, Format signatureFormat, boolean allowSkippingKeys) throws Exception { - JsonObject test = JsonUtil.getTestVectors(filename); - // Checks whether the test vectors in the file use the expected algorithm and the expected - // format for the signatures. - String schema = expectedSchema(signatureAlgorithm, signatureFormat, false); - String actualSchema = getString(test, "schema"); - if (!schema.isEmpty() && !schema.equals(actualSchema)) { - System.out.println( - signatureAlgorithm - + ": expecting test vectors with schema " - + schema - + " found vectors with schema " - + actualSchema); - } + JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename); int cntTests = 0; int errors = 0; int skippedKeys = 0; @@ -510,29 +466,12 @@ public class JsonSignatureTest { signer.update(message); String sig = TestUtil.bytesToHex(signer.sign()); if (!sig.equals(expectedSig)) { - System.out.println( - "Incorrect signature generated " - + filename - + " tcId:" - + tcid - + " expected:" - + expectedSig - + " sig:" - + sig); errors++; } else { cntTests++; } } catch (InvalidKeyException | SignatureException ex) { if (result.equals("valid")) { - System.out.println( - "Failed to sign " - + filename - + " tcId:" - + tcid - + " with exception:" - + ex); - errors++; } } @@ -540,9 +479,6 @@ public class JsonSignatureTest { } assertEquals(0, errors); if (skippedKeys > 0) { - System.out.println("File:" + filename); - System.out.println("Number of signatures verified:" + cntTests); - System.out.println("Number of skipped keys:" + skippedKeys); assertTrue(allowSkippingKeys); } } @@ -594,171 +530,195 @@ public class JsonSignatureTest { // Testing curves that may not be supported by a provider. @Test + @Ignore // Secp256k1 curve not supported in AndroidKeystore public void testSecp256k1Sha256() throws Exception { testVerification("ecdsa_secp256k1_sha256_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // Secp256k1 curve not supported in AndroidKeystore public void testSecp256k1Sha512() throws Exception { testVerification("ecdsa_secp256k1_sha512_test.json", "ECDSA", Format.ASN, true); } - @NoPresubmitTest( - providers = {ProviderType.OPENJDK}, - bugs = {"b/117643131"} - ) @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP224r1Sha224() throws Exception { testVerification("ecdsa_brainpoolP224r1_sha224_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP256r1Sha256() throws Exception { testVerification("ecdsa_brainpoolP256r1_sha256_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP320r1Sha384() throws Exception { testVerification("ecdsa_brainpoolP320r1_sha384_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP384r1Sha384() throws Exception { testVerification("ecdsa_brainpoolP384r1_sha384_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP512r1Sha512() throws Exception { testVerification("ecdsa_brainpoolP512r1_sha512_test.json", "ECDSA", Format.ASN, true); } // SHA-3 signatures @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp224r1Sha3_224 () throws Exception { testVerification("ecdsa_secp224r1_sha3_224_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp224r1Sha3_256 () throws Exception { testVerification("ecdsa_secp224r1_sha3_256_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp224r1Sha3_512 () throws Exception { testVerification("ecdsa_secp224r1_sha3_512_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp256r1Sha3_256 () throws Exception { testVerification("ecdsa_secp256r1_sha3_256_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp256r1Sha3_512 () throws Exception { testVerification("ecdsa_secp256r1_sha3_512_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // Secp256k1 curve and SHA3 algorithms not supported in AndroidKeystore public void testSecp256k1Sha3_256 () throws Exception { testVerification("ecdsa_secp256k1_sha3_256_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // Secp256k1 curve and SHA3 algorithms not supported in AndroidKeystore public void testSecp256k1Sha3_512 () throws Exception { testVerification("ecdsa_secp256k1_sha3_512_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp384r1Sha3_384 () throws Exception { testVerification("ecdsa_secp384r1_sha3_384_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp384r1Sha3_512 () throws Exception { testVerification("ecdsa_secp384r1_sha3_512_test.json", "ECDSA", Format.ASN, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testSecp521r1Sha3_512 () throws Exception { testVerification("ecdsa_secp521r1_sha3_512_test.json", "ECDSA", Format.ASN, true); } // jdk11 adds P1363 encoded signatures. @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp224r1Sha224inP1363Format() throws Exception { testVerification("ecdsa_secp224r1_sha224_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp224r1Sha256inP1363Format() throws Exception { testVerification("ecdsa_secp224r1_sha256_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp224r1Sha512inP1363Format() throws Exception { testVerification("ecdsa_secp224r1_sha512_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp256r1Sha256inP1363Format() throws Exception { testVerification("ecdsa_secp256r1_sha256_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp256r1Sha512inP1363Format() throws Exception { testVerification("ecdsa_secp256r1_sha512_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp384r1Sha384inP1363Format() throws Exception { testVerification("ecdsa_secp384r1_sha384_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp384r1Sha512inP1363Format() throws Exception { testVerification("ecdsa_secp384r1_sha512_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // P1363 encoding not supported in AndroidKeyStore public void testSecp521r1Sha512inP1363Format() throws Exception { testVerification("ecdsa_secp521r1_sha512_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // Secp256k1 curve not supported in AndroidKeystore public void testSecp256k1Sha256inP1363Format() throws Exception { testVerification("ecdsa_secp256k1_sha256_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // Secp256k1 curve not supported in AndroidKeystore public void testSecp256k1Sha512inP1363Format() throws Exception { testVerification("ecdsa_secp256k1_sha512_p1363_test.json", "ECDSA", Format.P1363, true); } - @NoPresubmitTest( - providers = {ProviderType.OPENJDK}, - bugs = {"b/117643131"} - ) @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP224r1Sha224inP1363Format() throws Exception { testVerification("ecdsa_brainpoolP224r1_sha224_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP256r1Sha256inP1363Format() throws Exception { testVerification("ecdsa_brainpoolP256r1_sha256_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP320r1Sha384inP1363Format() throws Exception { testVerification("ecdsa_brainpoolP320r1_sha384_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP384r1Sha384inP1363Format() throws Exception { testVerification("ecdsa_brainpoolP384r1_sha384_p1363_test.json", "ECDSA", Format.P1363, true); } @Test + @Ignore // Brainpool curves are not supported in AndroidKeyStore public void testBrainpoolP512r1Sha512inP1363Format() throws Exception { testVerification("ecdsa_brainpoolP512r1_sha512_p1363_test.json", "ECDSA", Format.P1363, true); } @@ -822,21 +782,25 @@ public class JsonSignatureTest { // RSA signatures with truncated hashes. Tests may be skipped if the provider // does not support the hash. @Test + @Ignore // SHA-512/224 algorithm not supported in AndrdoiKeyStore public void testRsaSignatures2048sha512_224() throws Exception { testVerification("rsa_signature_2048_sha512_224_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA-512/256 algorithm not supported in AndrdoiKeyStore public void testRsaSignatures2048sha512_256() throws Exception { testVerification("rsa_signature_2048_sha512_256_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA-512/256 algorithm not supported in AndrdoiKeyStore public void testRsaSignatures3072sha512_256() throws Exception { testVerification("rsa_signature_3072_sha512_256_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA-512/256 algorithm not supported in AndrdoiKeyStore public void testRsaSignatures4096sha512_256() throws Exception { testVerification("rsa_signature_4096_sha512_256_test.json", "RSA", Format.RAW, true); } @@ -844,48 +808,50 @@ public class JsonSignatureTest { // RSA signatures with SHA-3. Not every provider supports SHA-3. Hence the tests // may be skipped. @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testRsaSignature2048sha3_224() throws Exception { testVerification("rsa_signature_2048_sha3_224_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testRsaSignatures2048sha3_256() throws Exception { testVerification("rsa_signature_2048_sha3_256_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testRsaSignatures2048sha3_512() throws Exception { testVerification("rsa_signature_2048_sha3_512_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testRsaSignatures3072sha3_256() throws Exception { testVerification("rsa_signature_3072_sha3_256_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testRsaSignatures3072sha3_384() throws Exception { testVerification("rsa_signature_3072_sha3_384_test.json", "RSA", Format.RAW, true); } @Test + @Ignore // SHA3 algorithms are not supported in AndroidKeyStore public void testRsaSignatures3072sha3_512() throws Exception { testVerification("rsa_signature_3072_sha3_512_test.json", "RSA", Format.RAW, true); } // EdDSA - @NoPresubmitTest( - providers = {ProviderType.BOUNCY_CASTLE}, - bugs = {"https://github.com/bcgit/bc-java/issues/508"}) @Test + @Ignore // Ed25519 algorithm not supported in AndroidKeyStore public void testEd25519Verify() throws Exception { testVerification("eddsa_test.json", "ED25519", Format.RAW, true); } - @NoPresubmitTest( - providers = {ProviderType.BOUNCY_CASTLE}, - bugs = {"https://github.com/bcgit/bc-java/issues/508"}) @Test + @Ignore // Ed448 algorithm not supported in AndroidKeyStore public void testEd448Verify() throws Exception { testVerification("ed448_test.json", "ED448", Format.RAW, true); } @@ -893,10 +859,8 @@ public class JsonSignatureTest { // DSA // Two signature encodings for DSA are tested below: ASN encoded signatures // and P1363 encoded signatures. - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa2048Sha224() throws Exception { testVerification("dsa_2048_224_sha224_test.json", "DSA", Format.ASN, true); } @@ -904,63 +868,48 @@ public class JsonSignatureTest { // NIST allows 2048-bit DSA keys with either a 224-bit q or a 256-bit q. // In both cases the security level is 112-bit. // Jdk generates DSA keys with a 224-bit q (unless specified). - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa2048JdkSha256() throws Exception { testVerification("dsa_2048_224_sha256_test.json", "DSA", Format.ASN, true); } // OpenSSL generates DSA keys with a 256-bit q (unless specified). - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa2048Sha256() throws Exception { testVerification("dsa_2048_256_sha256_test.json", "DSA", Format.ASN, true); } - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa3072Sha256() throws Exception { testVerification("dsa_3072_256_sha256_test.json", "DSA", Format.ASN, true); } // DSA tests using P1363 formated signatures. - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa2048Sha224inP1363Format() throws Exception { testVerification("dsa_2048_224_sha224_p1363_test.json", "DSA", Format.P1363, true); } - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa2048JdkSha256inP1363Format() throws Exception { testVerification("dsa_2048_224_sha256_p1363_test.json", "DSA", Format.P1363, true); } - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa2048Sha256inP1363Format() throws Exception { testVerification("dsa_2048_256_sha256_p1363_test.json", "DSA", Format.P1363, true); } - @ExcludedTest( - providers = {ProviderType.CONSCRYPT}, - comment = "Conscrypt does not support DSA.") @Test + @Ignore // DSA algorithm not supported in AndroidKeyStore public void testDsa3072Sha256inP1363Format() throws Exception { testVerification("dsa_3072_256_sha256_p1363_test.json", "DSA", Format.P1363, true); } - } diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java index 34b6115..171af58 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java @@ -15,18 +15,20 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import com.google.security.wycheproof.WycheproofRunner.ProviderType; -import com.google.security.wycheproof.WycheproofRunner.SlowTest; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.Key; +import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import org.junit.Ignore; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import android.keystore.cts.util.KeyStoreUtil; /** * Tests for MACs. @@ -35,8 +37,21 @@ import org.junit.runners.JUnit4; * with known results are necessary. So far only simple test vectors for long messages are * available. */ -@RunWith(JUnit4.class) public class MacTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "TestKey"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + + private static Key getKeyStoreSecretKey(byte[] keyMaterial, String algorithm) throws Exception { + KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(KEY_ALIAS_1, + new SecretKeySpec(keyMaterial, algorithm), + new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build()); + return keyStore.getKey(KEY_ALIAS_1, null); + } /** * Computes the maximum of an array with at least one element. @@ -79,7 +94,7 @@ public class MacTest { */ private void testUpdateWithChunks(String algorithm, Key key, byte[] data, int... chunkSizes) throws Exception { - Mac mac = Mac.getInstance(algorithm); + Mac mac = Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); // First evaluation: compute MAC in one piece. int totalLength = 0; @@ -178,15 +193,14 @@ public class MacTest { public void testMac(String algorithm, int keySize) throws Exception { try { - Mac.getInstance(algorithm); + Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); } catch (NoSuchAlgorithmException ex) { - System.out.println("Algorithm " + algorithm + " is not supported. Skipping test."); - return; + fail("Algorithm " + algorithm + " is not supported."); } byte[] key = new byte[keySize]; SecureRandom rand = new SecureRandom(); rand.nextBytes(key); - testUpdate(algorithm, new SecretKeySpec(key, algorithm)); + testUpdate(algorithm, getKeyStoreSecretKey(key, algorithm)); } @Test @@ -215,21 +229,25 @@ public class MacTest { } @Test + @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_224() throws Exception { testMac("HMACSHA3-224", 28); } @Test + @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_256() throws Exception { testMac("HMACSHA3-256", 32); } @Test + @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_384() throws Exception { testMac("HMACSHA3-384", 48); } @Test + @Ignore // HmacSha3 algorithms are not supported in AndroidKeyStore public void testHmacSha3_512() throws Exception { testMac("HMACSHA3-512", 64); } @@ -246,7 +264,7 @@ public class MacTest { */ public byte[] macRepeatedMessage(String algorithm, Key key, byte[] message, long repetitions) throws Exception { - Mac mac = Mac.getInstance(algorithm); + Mac mac = Mac.getInstance(algorithm, EXPECTED_PROVIDER_NAME); mac.init(key); // If the message is short then it is more efficient to collect multiple copies // of the message in one chunk and call update with the larger chunk. @@ -283,26 +301,18 @@ public class MacTest { String algorithm, String keyhex, String message, long repetitions, String expected) throws Exception { - Key key = new SecretKeySpec(TestUtil.hexToBytes(keyhex), algorithm); + Key key = getKeyStoreSecretKey(TestUtil.hexToBytes(keyhex), algorithm); byte[] bytes = message.getBytes(UTF_8); byte[] mac = null; try { mac = macRepeatedMessage(algorithm, key, bytes, repetitions); } catch (NoSuchAlgorithmException ex) { - System.out.println("Algorithm " + algorithm + " is not supported. Skipping test."); - return; + fail("Algorithm " + algorithm + " is not supported."); } String hexmac = TestUtil.bytesToHex(mac); assertEquals(expected, hexmac); } - @SlowTest( - providers = { - ProviderType.OPENJDK, - ProviderType.BOUNCY_CASTLE, - ProviderType.SPONGY_CASTLE, - ProviderType.CONSCRYPT - }) @Test public void testLongMacSha1() throws Exception { testLongMac( @@ -319,13 +329,6 @@ public class MacTest { "d7f4c387f2237ea119fcc27cd7520fc5132b6230"); } - @SlowTest( - providers = { - ProviderType.OPENJDK, - ProviderType.BOUNCY_CASTLE, - ProviderType.SPONGY_CASTLE, - ProviderType.CONSCRYPT - }) @Test public void testLongMacSha256() throws Exception { testLongMac( @@ -342,13 +345,6 @@ public class MacTest { "59a75754df7093fa4339aa618b64b104f153a5b42cc85394fdb8735b13ea684a"); } - @SlowTest( - providers = { - ProviderType.OPENJDK, - ProviderType.BOUNCY_CASTLE, - ProviderType.SPONGY_CASTLE, - ProviderType.CONSCRYPT - }) @Test public void testLongMacSha384() throws Exception { testLongMac( @@ -369,13 +365,6 @@ public class MacTest { + "a477e6a84d159d8b7a3daaa89c4f2372"); } - @SlowTest( - providers = { - ProviderType.OPENJDK, - ProviderType.BOUNCY_CASTLE, - ProviderType.SPONGY_CASTLE, - ProviderType.CONSCRYPT - }) @Test public void testLongMacSha512() throws Exception { testLongMac( diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java index 5f82420..32a78b3 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java @@ -18,25 +18,39 @@ import static org.junit.Assert.fail; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.math.BigInteger; +import java.security.KeyStore; +import java.security.KeyPair; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.PublicKey; import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.security.spec.RSAPublicKeySpec; import java.util.Set; import java.util.TreeSet; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import android.keystore.cts.util.KeyStoreUtil; /** * RSA encryption tests * * @author bleichen@google.com (Daniel Bleichenbacher) */ -@RunWith(JUnit4.class) public class RsaEncryptionTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME; + private static final String KEY_ALIAS_SIG = "SigningKey"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } /** * Providers that implement RSA with PKCS1Padding but not OAEP are outdated and should be avoided @@ -46,15 +60,11 @@ public class RsaEncryptionTest { */ @Test public void testOutdatedProvider() throws Exception { + Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding", EXPECTED_PROVIDER_NAME); try { - Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding"); - try { - Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING"); - } catch (NoSuchPaddingException | NoSuchAlgorithmException ex) { - fail("Provider " + c.getProvider().getName() + " is outdated and should not be used."); - } + Cipher.getInstance("RSA/ECB/OAEPWITHSHA-1ANDMGF1PADDING", EXPECTED_PROVIDER_NAME); } catch (NoSuchPaddingException | NoSuchAlgorithmException ex) { - System.out.println("RSA/ECB/PKCS1Padding is not implemented"); + fail("Provider " + c.getProvider().getName() + " is outdated and should not be used."); } } @@ -71,8 +81,17 @@ public class RsaEncryptionTest { KeyFactory kf; kf = KeyFactory.getInstance("RSA"); byte[] encoded = TestUtil.hexToBytes(object.get("privateKeyPkcs8").getAsString()); + BigInteger modulus = new BigInteger(TestUtil.hexToBytes(object.get("n").getAsString())); + BigInteger exponent = new BigInteger(TestUtil.hexToBytes(object.get("e").getAsString())); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded); - return kf.generatePrivate(keySpec); + PrivateKey intermediateKey = kf.generatePrivate(keySpec); + PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, exponent)); + KeyStore keyStore = KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_SIG, pubKey, intermediateKey, + new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1) + .build()); + return (PrivateKey) keyStore.getKey(KEY_ALIAS_SIG, null); } /** Convenience method to get a byte array from a JsonObject */ @@ -124,16 +143,7 @@ public class RsaEncryptionTest { */ @SuppressWarnings("InsecureCryptoUsage") public void testDecryption(String filename) throws Exception { - final String expectedSchema = "rsaes_pkcs1_decrypt_schema.json"; - JsonObject test = JsonUtil.getTestVectors(filename); - String schema = test.get("schema").getAsString(); - if (!schema.equals(expectedSchema)) { - System.out.println( - "Expecting test vectors with schema " - + expectedSchema - + " found vectors with schema " - + schema); - } + JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename); // Padding oracle attacks become simpler when the decryption leaks detailed information about // invalid paddings. Hence implementations are expected to not include such information in the // exception thrown in the case of an invalid padding. @@ -144,7 +154,7 @@ public class RsaEncryptionTest { Set<String> exceptions = new TreeSet<String>(); int errors = 0; - Cipher decrypter = Cipher.getInstance("RSA/ECB/PKCS1Padding"); + Cipher decrypter = Cipher.getInstance("RSA/ECB/PKCS1Padding", EXPECTED_PROVIDER_NAME); for (JsonElement g : test.getAsJsonArray("testGroups")) { JsonObject group = g.getAsJsonObject(); PrivateKey key = getPrivateKey(group); @@ -178,31 +188,18 @@ public class RsaEncryptionTest { } } if (decrypted == null && result.equals("valid")) { - System.out.printf( - "Valid ciphertext not decrypted. filename:%s tcId:%d ct:%s cause:%s\n", - filename, tcid, ciphertextHex, exception); errors++; } else if (decrypted != null) { String decryptedHex = TestUtil.bytesToHex(decrypted); if (result.equals("invalid")) { - System.out.printf( - "Invalid ciphertext decrypted. filename:%s tcId:%d expected:%s decrypted:%s\n", - filename, tcid, messageHex, decryptedHex); errors++; } else if (!decryptedHex.equals(messageHex)) { - System.out.printf( - "Incorrect decryption. filename:%s tcId:%d expected:%s decrypted:%s\n", - filename, tcid, messageHex, decryptedHex); errors++; } } } } if (exceptions.size() != 1) { - System.out.println("Exceptions for RSA/ECB/PKCS1Padding"); - for (String s : exceptions) { - System.out.println(s); - } fail("Exceptions leak information about the padding"); } assertEquals(0, errors); diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java index 32ea493..57382f6 100644 --- a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java +++ b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.math.BigInteger; +import java.security.KeyStore; import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -31,16 +32,42 @@ import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateCrtKeySpec; import java.security.spec.RSAPublicKeySpec; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import org.junit.After; import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; +import java.security.cert.X509Certificate; +import javax.security.auth.x500.X500Principal; +import java.security.cert.Certificate; +import android.security.keystore.KeyProtection; +import android.security.keystore.KeyProperties; +import android.keystore.cts.util.KeyStoreUtil; /** Tests PKCS #1 v 1.5 signatures */ // TODO(bleichen): // - document stuff // - Join other RSA tests -@RunWith(JUnit4.class) public class RsaSignatureTest { + private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_PROVIDER_NAME; + private static final String KEY_ALIAS_1 = "TestKey"; + private static final String KEY_ALIAS_INVALID = "InvalidSigningKey"; + + @After + public void tearDown() throws Exception { + KeyStoreUtil.cleanUpKeyStore(); + } + + private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey) + throws Exception { + KeyStore keyStore = KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey, + new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN | + KeyProperties.PURPOSE_VERIFY) + .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512) + .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) + .build()); + return (PrivateKey) keyStore.getKey(KEY_ALIAS_1, null); + } + static final RSAPublicKeySpec RSA_KEY1 = new RSAPublicKeySpec( new BigInteger( @@ -1095,29 +1122,15 @@ public class RsaSignatureTest { RSAPrivateKey priv = (RSAPrivateKey) keyPair.getPrivate(); byte[] messageBytes = message.getBytes("UTF-8"); - Signature signer = Signature.getInstance(algorithm); - Signature verifier = Signature.getInstance(algorithm); - signer.initSign(priv); + Signature signer = Signature.getInstance(algorithm, TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME); + Signature verifier = Signature.getInstance(algorithm, + TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME); + signer.initSign(getKeystorePrivateKey(pub, priv)); signer.update(messageBytes); byte[] signature = signer.sign(); verifier.initVerify(pub); verifier.update(messageBytes); assertTrue(verifier.verify(signature)); - - // Extract some parameters. - byte[] rawHash = MessageDigest.getInstance(hashAlgorithm).digest(messageBytes); - - // Print keys and signature, so that it can be used to generate new test vectors. - System.out.println("Message:" + message); - System.out.println("Hash:" + TestUtil.bytesToHex(rawHash)); - System.out.println("Public key:"); - System.out.println("Modulus:" + pub.getModulus().toString()); - System.out.println("E:" + pub.getPublicExponent().toString()); - System.out.println("encoded:" + TestUtil.bytesToHex(pub.getEncoded())); - System.out.println("Private key:"); - System.out.println("D:" + priv.getPrivateExponent().toString()); - System.out.println("encoded:" + TestUtil.bytesToHex(priv.getEncoded())); - System.out.println("Signature:" + TestUtil.bytesToHex(signature)); } /** @@ -1135,7 +1148,8 @@ public class RsaSignatureTest { private void testVectors(RSAPublicKeySpec key, String algorithm, String[] testvectors) throws Exception { byte[] message = "Test".getBytes("UTF-8"); - Signature verifier = Signature.getInstance(algorithm); + Signature verifier = Signature.getInstance(algorithm, + TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME); KeyFactory kf = KeyFactory.getInstance("RSA"); PublicKey pub = kf.generatePublic(key); int errors = 0; @@ -1151,10 +1165,8 @@ public class RsaSignatureTest { // verify can throw SignatureExceptions if the signature is malformed. } if (first && !verified) { - System.out.println("Valid signature not verified:" + signature); errors++; } else if (!first && verified) { - System.out.println("Incorrect signature verified:" + signature); errors++; } first = false; @@ -1201,9 +1213,11 @@ public class RsaSignatureTest { RSAPublicKeySpec key = RSA_KEY1; String algorithm = ALGORITHM_KEY1; byte[] message = "Test".getBytes("UTF-8"); - Signature verifier = Signature.getInstance(algorithm); + Signature verifier = Signature.getInstance(algorithm, + TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME); KeyFactory kf = KeyFactory.getInstance("RSA"); PublicKey pub = kf.generatePublic(key); + int nonverified = 0; for (String signature : LEGACY_SIGNATURES_KEY1) { byte[] signatureBytes = TestUtil.hexToBytes(signature); verifier.initVerify(pub); @@ -1213,13 +1227,10 @@ public class RsaSignatureTest { verified = verifier.verify(signatureBytes); } catch (SignatureException ex) { verified = false; - } - if (verified) { - System.out.println("Verfied legacy signature:" + signature); - } else { - System.out.println("Rejected legacy signature:" + signature); + nonverified++; } } + assertEquals(0, nonverified); } /** @@ -1280,30 +1291,49 @@ public class RsaSignatureTest { byte[] message = "Test".getBytes("UTF-8"); KeyFactory kf = KeyFactory.getInstance("RSA"); PrivateKey validPrivKey = kf.generatePrivate(validKey); - Signature signer = Signature.getInstance("SHA256WithRSA"); - signer.initSign(validPrivKey); + Signature signer = Signature.getInstance("SHA256WithRSA", + TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME); + RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(n, e); + PublicKey pubKey = kf.generatePublic(pubKeySpec); + + signer.initSign(getKeystorePrivateKey(pubKey, validPrivKey)); signer.update(message); byte[] signature = signer.sign(); PrivateKey invalidPrivKey = null; + // Here if we use common code to get key from keystore (getKeystorePrivateKey). + // It throws run time exception while creating Certificate. + // Hence creating certificate using vaid keys. + KeyPair keyPair = new KeyPair(pubKey, validPrivKey); + X509Certificate certificate = KeyStoreUtil.createCertificate(keyPair, + new X500Principal("CN=Test1"), + new X500Principal("CN=Test1")); + Certificate[] certChain = new Certificate[]{certificate}; + KeyStore keyStore2 = KeyStore.getInstance("AndroidKeyStore"); + keyStore2.load(null); try { invalidPrivKey = kf.generatePrivate(invalidKey); - } catch (InvalidKeySpecException ex) { + keyStore2.setEntry( + KEY_ALIAS_INVALID, + new KeyStore.PrivateKeyEntry(invalidPrivKey, certChain), + new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) + .setDigests(KeyProperties.DIGEST_SHA256) + .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) + .build()); + } catch (InvalidKeySpecException | java.security.KeyStoreException ex) { // The provider checks the private key and notices a mismatch. // This is a good sign, though of course in this case it means that we can't // check for faults. - System.out.println("Provider catches invalid RSA key:" + ex); return; } byte[] invalidSignature = null; try { - signer.initSign(invalidPrivKey); + signer.initSign((PrivateKey)keyStore2.getKey(KEY_ALIAS_INVALID, null)); signer.update(message); invalidSignature = signer.sign(); } catch (Exception ex) { // We do not necessarily expect a checked exception here, since generating // an invalid signature typically indicates a programming error. // Though RuntimeExceptions are fine here. - System.out.println("Generating PKCS#1 signature with faulty key throws:" + ex); return; } String signatureHex = TestUtil.bytesToHex(signature); @@ -1311,7 +1341,6 @@ public class RsaSignatureTest { if (signatureHex.equals(invalidSignatureHex)) { // The provider generated a correct signature. This can for example happen if the provider // does not use the CRT parameters. - System.out.println("Signature generation did not use faulty parameter"); return; } fail("Generated faulty PKCS #1 signature with faulty parameters" |