aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 04:58:56 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 04:58:56 +0000
commit1659bf81857ac7d54e085d3c15ad08dbf54ebfa1 (patch)
tree9ac96473c50aa2acf7282589fb25f892024a1c0a
parent0f533fc2c2e144ced8eca14121e75640c36a40fb (diff)
parent2e04f8c5649563fe5846226db10a00285b680565 (diff)
downloadwycheproof-1659bf81857ac7d54e085d3c15ad08dbf54ebfa1.tar.gz
Snap for 10453563 from 2e04f8c5649563fe5846226db10a00285b680565 to mainline-media-swcodec-release
Change-Id: I580817d509cf174209a810a617c072155444b9bb
-rw-r--r--Android.bp27
-rw-r--r--java/com/google/security/wycheproof/testcases/EcdhTest.java2
-rw-r--r--keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java176
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/CertificateUtil.java83
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/EcUtil.java16
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java342
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java97
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java86
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java513
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java384
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java53
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java95
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java59
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java112
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java272
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java109
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java82
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java190
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/RsaPssTest.java129
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java121
20 files changed, 1629 insertions, 1319 deletions
diff --git a/Android.bp b/Android.bp
index 3439961..a68f02f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -56,34 +56,27 @@ java_library_static {
visibility: [
"//cts/tests/tests/keystore",
],
- srcs: ["keystore-cts/java/**/*.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",
+ srcs: [
+ "keystore-cts/java/**/*.java",
+ "keystore-cts/android/**/*.java",
],
java_resource_dirs: ["keystore-cts/testvectors"],
- sdk_version: "current",
libs: [
"bouncycastle-bcpkix-unbundled",
"bouncycastle-unbundled",
"junit",
"wycheproof-gson",
+ "cts-core-test-runner-axt",
+ "cts-keystore-test-util",
],
+ platform_apis: true,
}
java_import {
name: "wycheproof-gson",
- visibility: ["//cts/tests/tests/keystore"],
+ visibility: [
+ "//cts/tests/tests/keystore",
+ "//external/android-key-attestation",
+ ],
jars: ["keystore-cts/libs/gson-2.9.0.jar"],
}
diff --git a/java/com/google/security/wycheproof/testcases/EcdhTest.java b/java/com/google/security/wycheproof/testcases/EcdhTest.java
index c1a8b82..07c0b1e 100644
--- a/java/com/google/security/wycheproof/testcases/EcdhTest.java
+++ b/java/com/google/security/wycheproof/testcases/EcdhTest.java
@@ -990,7 +990,7 @@ public class EcdhTest extends TestCase {
testModifiedPublicSpec("ECDHC");
}
- @SuppressWarnings("InsecureCryptoUsage")
+ @SuppressWarnings({"InsecureCryptoUsage", "JUnit3TestNotRun"})
public void testDistinctCurves(String algorithm, ECPrivateKey priv, ECPublicKey pub)
throws Exception {
KeyAgreement kaA;
diff --git a/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java b/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java
new file mode 100644
index 0000000..ea5d91b
--- /dev/null
+++ b/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java
@@ -0,0 +1,176 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.keystore.cts.util;
+
+import android.content.Context;
+import android.security.keystore.KeyProtection;
+import android.keystore.cts.util.TestUtils;
+import androidx.test.core.app.ApplicationProvider;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.operator.OperatorCreationException;
+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 java.util.List;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.x500.X500Principal;
+
+/** Keystore utilities */
+public class KeyStoreUtil {
+ // Known KeyMaster/KeyMint versions. This is the version number
+ // which appear in the keymasterVersion field.
+ public static final int KM_VERSION_KEYMASTER_1 = 10;
+ public static final int KM_VERSION_KEYMASTER_1_1 = 11;
+ public static final int KM_VERSION_KEYMASTER_2 = 20;
+ public static final int KM_VERSION_KEYMASTER_3 = 30;
+ public static final int KM_VERSION_KEYMASTER_4 = 40;
+ public static final int KM_VERSION_KEYMASTER_4_1 = 41;
+ public static final int KM_VERSION_KEYMINT_1 = 100;
+
+ private static final List kmSupportedDigests = List.of("md5","sha-1","sha-224","sha-384",
+ "sha-256","sha-512");
+
+ 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 int getFeatureVersionKeystore() {
+ return TestUtils.getFeatureVersionKeystore(ApplicationProvider.getApplicationContext());
+ }
+
+ public static boolean hasStrongBox() {
+ Context context = ApplicationProvider.getApplicationContext();
+ return TestUtils.hasStrongBox(context);
+ }
+
+ public static void assumeStrongBox() {
+ TestUtils.assumeStrongBox();
+ }
+
+ public static boolean isSupportedDigest(String digest, boolean isStrongBox) {
+ if (isStrongBox) {
+ return digest.equalsIgnoreCase("sha-256");
+ }
+ return kmSupportedDigests.contains(digest.toLowerCase());
+ }
+
+ public static boolean isSupportedMgfDigest(String digest, boolean isStrongBox) {
+ if (isStrongBox) {
+ return digest.equalsIgnoreCase("sha-1")
+ || digest.equalsIgnoreCase("sha-256");
+ }
+ return kmSupportedDigests.contains(digest.toLowerCase());
+ }
+
+ public static boolean isSupportedRsaKeySize(int keySize, boolean isStrongBox) {
+ if (isStrongBox) {
+ return keySize == 2048;
+ }
+ return keySize == 2048 || keySize == 3072 || keySize == 4096;
+ }
+
+ public static X509Certificate createCertificate(
+ KeyPair keyPair, X500Principal subject, X500Principal issuer)
+ throws OperatorCreationException, CertificateException, IOException {
+ // Make the certificate valid for two days.
+ long millisPerDay = 24 * 60 * 60 * 1000;
+ long now = System.currentTimeMillis();
+ Date start = new Date(now - millisPerDay);
+ Date end = new Date(now + millisPerDay);
+
+ // Assign a random serial number.
+ byte[] serialBytes = new byte[16];
+ new SecureRandom().nextBytes(serialBytes);
+ BigInteger serialNumber = new BigInteger(1, serialBytes);
+
+ // Create the certificate builder
+ X509v3CertificateBuilder x509cg =
+ new X509v3CertificateBuilder(
+ X500Name.getInstance(issuer.getEncoded()),
+ serialNumber,
+ start,
+ end,
+ X500Name.getInstance(subject.getEncoded()),
+ SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
+
+ // Choose a signature algorithm matching the key format.
+ String keyAlgorithm = keyPair.getPrivate().getAlgorithm();
+ String signatureAlgorithm;
+ if (keyAlgorithm.equals("RSA")) {
+ signatureAlgorithm = "SHA256withRSA";
+ } else if (keyAlgorithm.equals("EC")) {
+ signatureAlgorithm = "SHA256withECDSA";
+ } else {
+ throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
+ }
+
+ // Sign the certificate and generate it.
+ X509CertificateHolder x509holder =
+ x509cg.build(
+ new JcaContentSignerBuilder(signatureAlgorithm)
+ .build(keyPair.getPrivate()));
+ CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ X509Certificate x509c =
+ (X509Certificate)
+ certFactory.generateCertificate(
+ new ByteArrayInputStream(x509holder.getEncoded()));
+ return x509c;
+ }
+}
diff --git a/keystore-cts/java/com/google/security/wycheproof/CertificateUtil.java b/keystore-cts/java/com/google/security/wycheproof/CertificateUtil.java
deleted file mode 100644
index d5d343a..0000000
--- a/keystore-cts/java/com/google/security/wycheproof/CertificateUtil.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.google.security.wycheproof;
-
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.SecureRandom;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import javax.security.auth.x500.X500Principal;
-
-/** Certificate utilities */
-public class CertificateUtil {
-
- public static X509Certificate createCertificate(
- KeyPair keyPair, X500Principal subject, X500Principal issuer)
- throws OperatorCreationException, CertificateException, IOException {
- // Make the certificate valid for two days.
- long millisPerDay = 24 * 60 * 60 * 1000;
- long now = System.currentTimeMillis();
- Date start = new Date(now - millisPerDay);
- Date end = new Date(now + millisPerDay);
-
- // Assign a random serial number.
- byte[] serialBytes = new byte[16];
- new SecureRandom().nextBytes(serialBytes);
- BigInteger serialNumber = new BigInteger(1, serialBytes);
-
- // Create the certificate builder
- X509v3CertificateBuilder x509cg =
- new X509v3CertificateBuilder(
- X500Name.getInstance(issuer.getEncoded()),
- serialNumber,
- start,
- end,
- X500Name.getInstance(subject.getEncoded()),
- SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()));
-
- // Choose a signature algorithm matching the key format.
- String keyAlgorithm = keyPair.getPrivate().getAlgorithm();
- String signatureAlgorithm;
- if (keyAlgorithm.equals("RSA")) {
- signatureAlgorithm = "SHA256withRSA";
- } else if (keyAlgorithm.equals("EC")) {
- signatureAlgorithm = "SHA256withECDSA";
- } else {
- throw new IllegalArgumentException("Unknown key algorithm " + keyAlgorithm);
- }
-
- // Sign the certificate and generate it.
- X509CertificateHolder x509holder =
- x509cg.build(
- new JcaContentSignerBuilder(signatureAlgorithm)
- .build(keyPair.getPrivate()));
- CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
- X509Certificate x509c =
- (X509Certificate)
- certFactory.generateCertificate(
- new ByteArrayInputStream(x509holder.getEncoded()));
- return x509c;
- }
-}
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..725dff1 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java
@@ -24,11 +24,16 @@ import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.Arrays;
import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.GCMParameterSpec;
@@ -37,11 +42,10 @@ 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;
-import java.security.KeyStoreException;
-import java.security.UnrecoverableKeyException;
// TODO(bleichen):
// - For EAX I was able to derive some special cases by inverting OMAC.
@@ -54,17 +58,35 @@ 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 {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
+ boolean hasStrongBox = KeyStoreUtil.hasStrongBox();
for (GcmTestVector test : GCM_TEST_VECTORS) {
- setKeystoreEntry(test.alias, test.key);
+ setKeystoreEntry(test.alias, test.key, false);
+ if (hasStrongBox) {
+ setKeystoreEntry(test.alias_sb, test.key, true);
+ }
}
}
- private SecretKey setKeystoreEntry(String alias, SecretKeySpec key)
+ @After
+ public void tearDown() throws Exception {
+ KeyStoreUtil.cleanUpKeyStore();
+ }
+
+ private SecretKey setKeystoreEntry(String alias, SecretKeySpec key, boolean isStrongBox)
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
keyStore.setEntry(
alias,
@@ -73,11 +95,26 @@ public class AesGcmTest {
.setBlockModes(KeyProperties.BLOCK_MODE_ECB, KeyProperties.BLOCK_MODE_GCM)
.setRandomizedEncryptionRequired(false)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setIsStrongBoxBacked(isStrongBox)
.build());
// 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);
+ }
+
+ private Cipher getInitializedCipherInstance(String algorithm, int operationMode,
+ GcmTestVector testVector, boolean isStrongBox)
+ throws Exception {
+ Cipher cipher = Cipher.getInstance(algorithm,
+ EXPECTED_PROVIDER_NAME);
+ cipher.init(operationMode, getKey(isStrongBox ? testVector.alias_sb : testVector.alias),
+ testVector.parameters);
+ return cipher;
+ }
+
/** Test vectors */
public static class GcmTestVector {
final byte[] pt;
@@ -90,6 +127,7 @@ public class AesGcmTest {
final int nonceLengthInBits;
final int tagLengthInBits;
final String alias;
+ final String alias_sb;
public GcmTestVector(
String message,
@@ -109,6 +147,7 @@ public class AesGcmTest {
this.parameters = new GCMParameterSpec(tagLengthInBits, TestUtil.hexToBytes(nonce));
this.key = new SecretKeySpec(TestUtil.hexToBytes(keyMaterial), "AES");
this.alias = alias;
+ this.alias_sb = alias + "_sb";
}
};
@@ -119,35 +158,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 +197,7 @@ public class AesGcmTest {
"7b95b8c356810a84711d68150a1b7750",
"",
"84d4c9c08b4f482861e3a9c6c35bc4d91df927374513bfd49f436bd73f325285daef4ff7e13d46a6",
- "213a3cb93855d18e69337eee66aeec07", "Key6"),
+ "213a3cb93855d18e69337eee66aeec07", KEY_ALIAS_6),
// J0:ffffffffffffffffffffffffffffffff
new GcmTestVector(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
@@ -166,7 +205,7 @@ public class AesGcmTest {
"1a552e67cdc4dc1a33b824874ebf0bed",
"",
"948ca37a8e6649e88aeffb1c598f3607007702417ea0e0bc3c60ad5a949886de968cf53ea6462aed",
- "99b381bfa2af9751c39d1b6e86d1be6a", "Key6"),
+ "99b381bfa2af9751c39d1b6e86d1be6a", KEY_ALIAS_7),
// J0:000102030405060708090a0bffffffff
new GcmTestVector(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
@@ -174,7 +213,7 @@ public class AesGcmTest {
"99821c2dd5daecded07300f577f7aff1",
"",
"127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4af37d0fe9af033052bd537c4ae978f60",
- "07eb2fe4a958f8434d40684899507c7c", "Key7"),
+ "07eb2fe4a958f8434d40684899507c7c", KEY_ALIAS_8),
// J0:000102030405060708090a0bfffffffe
new GcmTestVector(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000",
@@ -182,7 +221,7 @@ public class AesGcmTest {
"5e4a3900142358d1c774d8d124d8d27d",
"",
"0cf6ae47156b14dce03c8a07a2e172b1127af9b39ecdfc57bb11a2847c7c2d3d8f938f40f877e0c4",
- "f145c2dcaf339eede427be934357eac0", "Key8"),
+ "f145c2dcaf339eede427be934357eac0", KEY_ALIAS_9),
};
/**
@@ -195,15 +234,15 @@ public class AesGcmTest {
* <p>The only assumption we make here is that all test vectors with 128 bit tags and nonces with
* at least 96 bits are supported.
*/
- private Iterable<GcmTestVector> getTestVectors() throws Exception {
+ private Iterable<GcmTestVector> getTestVectors(boolean isStrongBox) throws Exception {
ArrayList<GcmTestVector> supported = new ArrayList<GcmTestVector>();
for (GcmTestVector test : GCM_TEST_VECTORS) {
if (test.nonceLengthInBits != 96 || test.tagLengthInBits != 128) {
try {
// 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);
+ getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
} catch (InvalidKeyException | InvalidAlgorithmParameterException ex) {
// Not supported
continue;
@@ -216,9 +255,17 @@ public class AesGcmTest {
@Test
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);
+ testVectors(false);
+ }
+ @Test
+ public void testVectors_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testVectors(true);
+ }
+ private void testVectors(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
cipher.updateAAD(test.aad);
byte[] ct = cipher.doFinal(test.pt);
assertEquals(test.ctHex, TestUtil.bytesToHex(ct));
@@ -228,11 +275,19 @@ public class AesGcmTest {
/** Test encryption when update and doFinal are done with empty byte arrays. */
@Test
public void testEncryptWithEmptyArrays() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testEncryptWithEmptyArrays(false);
+ }
+ @Test
+ public void testEncryptWithEmptyArrays_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testEncryptWithEmptyArrays(true);
+ }
+ private void testEncryptWithEmptyArrays(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
// 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 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
int outputSize = cipher.getOutputSize(test.pt.length);
ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(empty);
@@ -255,10 +310,18 @@ public class AesGcmTest {
@Test
public void testDecryptWithEmptyArrays() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testDecryptWithEmptyArrays(false);
+ }
+ @Test
+ public void testDecryptWithEmptyArrays_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testDecryptWithEmptyArrays(true);
+ }
+ private void testDecryptWithEmptyArrays(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
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 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
int outputSize = cipher.getOutputSize(test.ct.length);
ByteBuffer ptBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(empty);
@@ -279,7 +342,8 @@ 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 = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, test,
+ isStrongBox);
cipher.updateAAD(empty);
cipher.updateAAD(test.aad);
cipher.updateAAD(new byte[1]);
@@ -313,9 +377,17 @@ public class AesGcmTest {
*/
@Test
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);
+ testLateUpdateAAD(false);
+ }
+ @Test
+ public void testLateUpdateAAD_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testLateUpdateAAD(true);
+ }
+ private void testLateUpdateAAD(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, test,
+ isStrongBox);
byte[] c0 = cipher.update(test.pt);
try {
cipher.updateAAD(test.aad);
@@ -346,9 +418,17 @@ public class AesGcmTest {
*/
@Test
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);
+ testIvReuse(false);
+ }
+ @Test
+ public void testIvReuse_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testIvReuse(true);
+ }
+ private void testIvReuse(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE, test,
+ isStrongBox);
cipher.updateAAD(test.aad);
byte[] ct1 = cipher.doFinal(test.pt);
try {
@@ -372,14 +452,22 @@ public class AesGcmTest {
*/
@Test
public void testByteBufferSize() throws Exception {
- 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);
+ testByteBufferSize(false);
+ }
+ @Test
+ public void testByteBufferSize_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testByteBufferSize(true);
+ }
+ private void testByteBufferSize(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
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 = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
outputSize = cipher.getOutputSize(test.ct.length);
assertEquals("ciphertext size:" + test.ct.length, test.pt.length, outputSize);
}
@@ -388,11 +476,19 @@ public class AesGcmTest {
/** Encryption with ByteBuffers. */
@Test
public void testByteBuffer() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testByteBuffer(false);
+ }
+ @Test
+ public void testByteBuffer_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testByteBuffer(true);
+ }
+ private void testByteBuffer(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
// 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 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
int outputSize = cipher.getOutputSize(test.pt.length);
ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(test.aad);
@@ -401,7 +497,8 @@ public class AesGcmTest {
// Decryption
ctBuffer.flip();
- cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters);
+ cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
outputSize = cipher.getOutputSize(test.ct.length);
ByteBuffer decrypted = ByteBuffer.allocate(outputSize);
cipher.updateAAD(test.aad);
@@ -413,10 +510,18 @@ public class AesGcmTest {
/** Encryption with ByteBuffers should be copy-safe. */
@Test
public void testByteBufferAlias() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testByteBufferAlias(false);
+ }
+ @Test
+ public void testByteBufferAlias_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testByteBufferAlias(true);
+ }
+ private void testByteBufferAlias(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
// 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 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
int outputSize = cipher.getOutputSize(test.pt.length);
byte[] backingArray = new byte[outputSize];
ByteBuffer ptBuffer = ByteBuffer.wrap(backingArray);
@@ -430,7 +535,8 @@ 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 = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
cipher.updateAAD(test.aad);
cipher.doFinal(ctBuffer, decrypted);
assertEquals(test.ptHex, TestUtil.byteBufferToHex(decrypted));
@@ -440,6 +546,14 @@ public class AesGcmTest {
/** Encryption and decryption with large arrays should be copy-safe. */
@Test
public void testLargeArrayAlias() throws Exception {
+ testLargeArrayAlias(false);
+ }
+ @Test
+ public void testLargeArrayAlias_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testLargeArrayAlias(true);
+ }
+ private void testLargeArrayAlias(boolean isStrongBox) throws Exception {
byte[] ptVector = new byte[8192];
// this offset is relative to the start of the input, not the start of the buffer.
@@ -450,7 +564,7 @@ public class AesGcmTest {
try {
String alias = "TestKey" + 1;
SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES");
- secretKey = setKeystoreEntry(alias, keySpec);
+ secretKey = setKeystoreEntry(alias, keySpec, isStrongBox);
} catch (Exception e) {
fail("Failed to set secret key entry in KeyStore.");
}
@@ -522,6 +636,14 @@ public class AesGcmTest {
*/
@Test
public void testByteBufferShiftedAlias() throws Exception {
+ testByteBufferShiftedAlias(false);
+ }
+ @Test
+ public void testByteBufferShiftedAlias_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testByteBufferShiftedAlias(true);
+ }
+ private void testByteBufferShiftedAlias(boolean isStrongBox) throws Exception {
byte[] ptVector = new byte[8192];
for (int i = 0; i < 3; i++) {
@@ -532,7 +654,7 @@ public class AesGcmTest {
try {
String alias = "TestKey" + 1;
SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES");
- secretKey = setKeystoreEntry(alias, keySpec);
+ secretKey = setKeystoreEntry(alias, keySpec, isStrongBox);
} catch (Exception e) {
fail("Failed to set secret key entry in KeyStore.");
}
@@ -636,11 +758,19 @@ public class AesGcmTest {
@Test
public void testReadOnlyByteBuffer() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testReadOnlyByteBuffer(false);
+ }
+ @Test
+ public void testReadOnlyByteBuffer_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testReadOnlyByteBuffer(true);
+ }
+ private void testReadOnlyByteBuffer(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
// 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 cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
int outputSize = cipher.getOutputSize(test.pt.length);
ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(test.aad);
@@ -650,7 +780,8 @@ public class AesGcmTest {
// Decryption
ctBuffer.flip();
ctBuffer = ctBuffer.asReadOnlyBuffer();
- cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters);
+ cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
outputSize = cipher.getOutputSize(test.ct.length);
ByteBuffer decrypted = ByteBuffer.allocate(outputSize);
cipher.updateAAD(test.aad);
@@ -666,9 +797,18 @@ public class AesGcmTest {
*/
@Test
public void testByteBufferWithOffset() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testByteBufferWithOffset(false);
+ }
+ @Test
+ public void testByteBufferWithOffset_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testByteBufferWithOffset(true);
+ }
+ private void testByteBufferWithOffset(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
// Encryption
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME);
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
ByteBuffer ptBuffer = ByteBuffer.wrap(new byte[test.pt.length + 50]);
ptBuffer.position(5);
ptBuffer = ptBuffer.slice();
@@ -678,7 +818,6 @@ 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.updateAAD(test.aad);
cipher.doFinal(ptBuffer, ctBuffer);
assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer));
@@ -688,7 +827,8 @@ 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 = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
cipher.updateAAD(test.aad);
cipher.doFinal(ctBuffer, decBuffer);
assertEquals(test.ptHex, TestUtil.byteBufferToHex(decBuffer));
@@ -697,12 +837,20 @@ public class AesGcmTest {
@Test
public void testByteBufferTooShort() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testByteBufferTooShort(false);
+ }
+ @Test
+ public void testByteBufferTooShort_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testByteBufferTooShort(true);
+ }
+ private void testByteBufferTooShort(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
// Encryption
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME);
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
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.updateAAD(test.aad);
try {
cipher.doFinal(ptBuffer, ctBuffer);
@@ -714,7 +862,8 @@ 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 = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
cipher.updateAAD(test.aad);
try {
cipher.doFinal(ctBuffer, decrypted);
@@ -731,12 +880,20 @@ public class AesGcmTest {
*/
@Test
public void testEncryptWithEmptyByteBuffer() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
+ testEncryptWithEmptyByteBuffer(false);
+ }
+ @Test
+ public void testEncryptWithEmptyByteBuffer_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testEncryptWithEmptyByteBuffer(true);
+ }
+ private void testEncryptWithEmptyByteBuffer(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
// Encryption
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME);
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
ByteBuffer empty = ByteBuffer.allocate(0);
ByteBuffer ptBuffer = ByteBuffer.wrap(test.pt);
- cipher.init(Cipher.ENCRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters);
int outputSize = cipher.getOutputSize(test.pt.length);
ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(empty);
@@ -750,11 +907,19 @@ public class AesGcmTest {
@Test
public void testDecryptWithEmptyBuffer() throws Exception {
- for (GcmTestVector test : getTestVectors()) {
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", EXPECTED_PROVIDER_NAME);
+ testDecryptWithEmptyBuffer(false);
+ }
+ @Test
+ public void testDecryptWithEmptyBuffer_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testDecryptWithEmptyBuffer(true);
+ }
+ private void testDecryptWithEmptyBuffer(boolean isStrongBox) throws Exception {
+ for (GcmTestVector test : getTestVectors(isStrongBox)) {
+ Cipher cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
ByteBuffer empty = ByteBuffer.allocate(0);
ByteBuffer ctBuffer = ByteBuffer.wrap(test.ct);
- cipher.init(Cipher.DECRYPT_MODE, (SecretKey) keyStore.getKey(test.alias, null), test.parameters);
int outputSize = cipher.getOutputSize(test.ct.length);
ByteBuffer ptBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(empty);
@@ -767,7 +932,8 @@ 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 = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
cipher.updateAAD(empty);
cipher.updateAAD(test.aad);
cipher.updateAAD(new byte[1]);
@@ -802,17 +968,12 @@ public class AesGcmTest {
try {
String alias = "TestKey" + 1;
SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES");
- secretKey = setKeystoreEntry(alias, keySpec);
+ secretKey = setKeystoreEntry(alias, keySpec, /*isStrongBox*/ false);
} catch (Exception e) {
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);
}
@@ -836,17 +997,12 @@ public class AesGcmTest {
try {
String alias = "TestKey" + 1;
SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES");
- secretKey = setKeystoreEntry(alias, keySpec);
+ secretKey = setKeystoreEntry(alias, keySpec, /*isStrongBox*/ false);
} catch (Exception e) {
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);
@@ -897,7 +1053,7 @@ public class AesGcmTest {
try {
String alias = "TestKey" + 1;
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
- secretKey = setKeystoreEntry(alias, keySpec);
+ secretKey = setKeystoreEntry(alias, keySpec, /*isStrongBox*/ false);
} catch (Exception e) {
fail("Failed to set secret key entry in KeyStore.");
}
@@ -929,6 +1085,15 @@ public class AesGcmTest {
*/
@Test
public void testEncryptEmptyPlaintextWithEmptyIv() throws Exception {
+ testEncryptEmptyPlaintextWithEmptyIv(false);
+ }
+ @Test
+ public void testEncryptEmptyPlaintextWithEmptyIv_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testEncryptEmptyPlaintextWithEmptyIv(true);
+ }
+
+ private void testEncryptEmptyPlaintextWithEmptyIv(boolean isStrongBox) throws Exception {
byte[] emptyIv = new byte[0];
byte[] input = new byte[0];
byte[] key = TestUtil.hexToBytes("56aae7bd5cbefc71d31c4338e6ddd6c5");
@@ -936,7 +1101,7 @@ public class AesGcmTest {
try {
String alias = "TestKey" + 1;
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
- secretKey = setKeystoreEntry(alias, keySpec);
+ secretKey = setKeystoreEntry(alias, keySpec, isStrongBox);
} catch (Exception e) {
fail("Failed to set secret key entry in KeyStore.");
}
@@ -957,13 +1122,22 @@ public class AesGcmTest {
@Test
public void testDecryptWithEmptyIv() throws Exception {
+ testDecryptWithEmptyIv(false);
+ }
+ @Test
+ public void testDecryptWithEmptyIv_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testDecryptWithEmptyIv(true);
+ }
+
+ private void testDecryptWithEmptyIv(boolean isStrongBox) throws Exception {
byte[] emptyIv = new byte[0];
byte[] key = TestUtil.hexToBytes("56aae7bd5cbefc71d31c4338e6ddd6c5");
SecretKey secretKey = null;
try {
String alias = "TestKey" + 1;
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
- secretKey = setKeystoreEntry(alias, keySpec);
+ secretKey = setKeystoreEntry(alias, keySpec, isStrongBox);
} catch (Exception e) {
fail("Failed to set secret key entry in KeyStore.");
}
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..85d84d7 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,33 @@ 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,
+ boolean isStrongBox) 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)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build());
+ // Key imported, obtain a reference to it.
+ return (SecretKey) keyStore.getKey(alias, null);
}
static AlgorithmParameterSpec randomParameters(
@@ -76,7 +96,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 +104,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, boolean isStrongBox) throws Exception {
this.algorithm = algorithm;
- this.key = randomKey(algorithm, keySize);
+ this.key = randomKey(algorithm, alias, keySize, isStrongBox);
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);
@@ -104,7 +124,8 @@ public class CipherInputStreamTest {
int[] ivSizes,
int[] tagSizes,
int[] ptSizes,
- int[] aadSizes)
+ int[] aadSizes,
+ boolean isStrongBox)
throws Exception {
ArrayList<TestVector> result = new ArrayList<TestVector>();
for (int keySize : keySizes) {
@@ -112,7 +133,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, isStrongBox));
}
}
}
@@ -124,7 +148,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 +172,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 +208,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);
@@ -219,26 +243,44 @@ public class CipherInputStreamTest {
@Test
public void testAesGcm() throws Exception {
+ testAesGcm(false);
+ }
+ @Test
+ public void testAesGcm_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testAesGcm(true);
+ }
+ private void testAesGcm(boolean isStrongBox) throws Exception {
final int[] keySizes = {16, 32};
final int[] ivSizes = {12};
final int[] tagSizes = {12, 16};
final int[] ptSizes = {0, 8, 16, 65, 8100};
final int[] aadSizes = {0, 8, 24};
Iterable<TestVector> v =
- getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
+ getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes,
+ tagSizes, ptSizes, aadSizes, isStrongBox);
testEncrypt(v);
testDecrypt(v);
}
@Test
public void testCorruptAesGcm() throws Exception {
+ testCorruptAesGcm(false);
+ }
+ @Test
+ public void testCorruptAesGcm_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testCorruptAesGcm(true);
+ }
+ private void testCorruptAesGcm(boolean isStrongBox) throws Exception {
final int[] keySizes = {16, 32};
final int[] ivSizes = {12};
final int[] tagSizes = {12, 16};
final int[] ptSizes = {8, 16, 65, 8100};
final int[] aadSizes = {0, 8, 24};
Iterable<TestVector> v =
- getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
+ getTestVectors("AES/GCM/NoPadding", keySizes,
+ ivSizes, tagSizes, ptSizes, aadSizes, isStrongBox);
boolean acceptEmptyPlaintext = true;
testCorruptDecrypt(v, acceptEmptyPlaintext);
}
@@ -250,19 +292,29 @@ public class CipherInputStreamTest {
*/
@Test
public void testEmptyPlaintext() throws Exception {
+ testEmptyPlaintext(false);
+ }
+ @Test
+ public void testEmptyPlaintext_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testEmptyPlaintext(true);
+ }
+ private void testEmptyPlaintext(boolean isStrongBox) throws Exception {
final int[] keySizes = {16, 32};
final int[] ivSizes = {12};
final int[] tagSizes = {12, 16};
final int[] ptSizes = {0};
final int[] aadSizes = {0, 8, 24};
Iterable<TestVector> v =
- getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
+ getTestVectors("AES/GCM/NoPadding", keySizes,
+ ivSizes, tagSizes, ptSizes, aadSizes, isStrongBox);
boolean acceptEmptyPlaintext = false;
testCorruptDecrypt(v, acceptEmptyPlaintext);
}
/** 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,14 +322,9 @@ 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);
+ getTestVectors(algorithm, keySizes, ivSizes, tagSizes, ptSizes,
+ aadSizes, /*isStrongBox*/ false);
testEncrypt(v);
testDecrypt(v);
boolean acceptEmptyPlaintext = true;
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..39a6044 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,33 @@ 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,
+ boolean isStrongBox) 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)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build());
+ // Key imported, obtain a reference to it.
+ return (SecretKey) keyStore.getKey(alias, null);
}
static AlgorithmParameterSpec randomParameters(
@@ -77,21 +97,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, boolean isStrongBox) throws Exception {
this.algorithm = algorithm;
- this.key = randomKey(algorithm, keySize);
+ this.key = randomKey(algorithm, alias, keySize, isStrongBox);
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);
@@ -104,15 +124,19 @@ public class CipherOutputStreamTest {
int[] ivSizes,
int[] tagSizes,
int[] ptSizes,
- int[] aadSizes)
+ int[] aadSizes,
+ boolean isStrongBox)
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, isStrongBox));
}
}
}
@@ -124,7 +148,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 +162,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 +184,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);
@@ -192,13 +216,22 @@ public class CipherOutputStreamTest {
@Test
public void testAesGcm() throws Exception {
+ testAesGcm(false);
+ }
+ @Test
+ public void testAesGcm_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testAesGcm(true);
+ }
+ private void testAesGcm(boolean isStrongBox) throws Exception {
final int[] keySizes = {16, 32};
final int[] ivSizes = {12};
final int[] tagSizes = {12, 16};
final int[] ptSizes = {8, 16, 65, 8100};
final int[] aadSizes = {0, 8, 24};
Iterable<TestVector> v =
- getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
+ getTestVectors("AES/GCM/NoPadding", keySizes,
+ ivSizes, tagSizes, ptSizes, aadSizes, isStrongBox);
testEncrypt(v);
testDecrypt(v);
boolean acceptEmptyPlaintext = true;
@@ -212,13 +245,22 @@ public class CipherOutputStreamTest {
*/
@Test
public void testEmptyPlaintext() throws Exception {
+ testEmptyPlaintext(false);
+ }
+ @Test
+ public void testEmptyPlaintext_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testEmptyPlaintext(true);
+ }
+ private void testEmptyPlaintext(boolean isStrongBox) throws Exception {
final int[] keySizes = {16, 32};
final int[] ivSizes = {12};
final int[] tagSizes = {12, 16};
final int[] ptSizes = {0};
final int[] aadSizes = {0, 8, 24};
Iterable<TestVector> v =
- getTestVectors("AES/GCM/NoPadding", keySizes, ivSizes, tagSizes, ptSizes, aadSizes);
+ getTestVectors("AES/GCM/NoPadding", keySizes,
+ ivSizes, tagSizes, ptSizes, aadSizes, isStrongBox);
testEncrypt(v);
testDecrypt(v);
boolean acceptEmptyPlaintext = false;
@@ -228,6 +270,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,14 +278,9 @@ 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);
+ getTestVectors(algorithm, keySizes, ivSizes, tagSizes, ptSizes,
+ aadSizes, /*isStrongBox*/ false);
testEncrypt(v);
testDecrypt(v);
boolean acceptEmptyPlaintext = true;
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..f57ad29 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,14 @@ 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 static org.junit.Assume.assumeTrue;
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,9 +40,16 @@ 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.content.Context;
+import android.security.keystore.KeyProtection;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyGenParameterSpec;
+import android.keystore.cts.util.KeyStoreUtil;
+
+import androidx.test.InstrumentationRegistry;
/**
* Testing ECDH.
@@ -89,8 +92,40 @@ 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,
+ boolean isStrongBox)
+ throws Exception {
+ return (PrivateKey) KeyStoreUtil.saveKeysToKeystore(
+ KEY_ALIAS_1, pubKey, privKey,
+ new KeyProtection.Builder(KeyProperties.PURPOSE_AGREE_KEY)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build())
+ .getKey(KEY_ALIAS_1, null);
+ }
+
+ private KeyPair generateECKeyPair(String alias, ECGenParameterSpec ecSpec, boolean isStrongBox)
+ throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC", EXPECTED_PROVIDER_NAME);
+ KeyGenParameterSpec ecKeySpec =
+ new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_AGREE_KEY)
+ .setAlgorithmParameterSpec(ecSpec)
+ .setIsStrongBoxBacked(isStrongBox)
+ .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 +196,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 +543,21 @@ 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();
+ testBasic(false);
+ }
+ @Test
+ public void testBasic_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testBasic(true);
+ }
+ private void testBasic(boolean isStrongBox) throws Exception {
+ KeyPair keyPairA = generateECKeyPair(KEY_ALIAS_2, new ECGenParameterSpec("secp256r1"),
+ isStrongBox);
+ KeyPair keyPairB = generateECKeyPair(KEY_ALIAS_3, new ECGenParameterSpec("secp256r1"),
+ isStrongBox);
- 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 +567,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 +596,23 @@ 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();
+ testModifiedPublic(algorithm, false);
+ }
+ @SuppressWarnings("InsecureCryptoUsage")
+ public void testModifiedPublic(String algorithm, boolean isStrongBox) throws Exception {
+ KeyAgreement ka = KeyAgreement.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ KeyPair pair = generateECKeyPair(KEY_ALIAS_1, new ECGenParameterSpec("secp256r1"),
+ isStrongBox);
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 +627,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 +637,16 @@ 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();
+ testModifiedPublicSpec(algorithm, false);
+ }
+ @SuppressWarnings("InsecureCryptoUsage")
+ public void testModifiedPublicSpec(String algorithm, boolean isStrongBox) throws Exception {
+ KeyAgreement ka = KeyAgreement.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ KeyPair pair = generateECKeyPair(KEY_ALIAS_1, new ECGenParameterSpec("secp256r1"),
+ isStrongBox);
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 +658,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 +673,40 @@ 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
+ public void testEcdhModifiedPublic_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testModifiedPublic("ECDH", true);
+ }
+
+ @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");
+ }
+ @Test
+ public void testEcdhModifiedPublicSpec_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testModifiedPublicSpec("ECDH", true);
}
- @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");
}
/**
@@ -724,26 +717,20 @@ 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;
- }
+ public void testWrongOrder(String algorithm, ECParameterSpec spec)
+ throws Exception {
+ testWrongOrder(algorithm, spec, false);
+ }
+ @SuppressWarnings("InsecureCryptoUsage")
+ public void testWrongOrder(String algorithm, ECParameterSpec spec, boolean isStrongBox)
+ throws Exception {
+ KeyAgreement ka = KeyAgreement.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ PrivateKey priv = generateECKeyPair(KEY_ALIAS_2,
+ new ECGenParameterSpec("secp256r1"), isStrongBox)
+ .getPrivate();
+ ECPublicKey pub = (ECPublicKey) generateECKeyPair(KEY_ALIAS_3,
+ new ECGenParameterSpec("secp256r1"), isStrongBox)
+ .getPublic();
// Get the shared secret for the unmodified keys.
ka.init(priv);
ka.doPhase(pub, true);
@@ -754,15 +741,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 +749,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 +760,29 @@ 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
+ public void testWrongOrderEcdhNist_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testWrongOrder("ECDH", EcUtil.getNistP256Params(), true);
+ }
+
+ @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());
@@ -814,26 +796,28 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
* occur for example when the private key is close to the order of the curve.
*/
private void testLargePrivateKey(ECParameterSpec spec) throws Exception {
+ testLargePrivateKey(spec, false);
+ }
+ private void testLargePrivateKey(ECParameterSpec spec, boolean isStrongBox) 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, isStrongBox));
ka.doPhase(pub, true);
byte[] shared1 = ka.generateSecret();
- ka.init(kf.generatePrivate(spec2));
+ PrivateKey priv2 = kf.generatePrivate(spec2);
+ ka.init(getKeystorePrivateKey(pub1, priv2, isStrongBox));
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 +827,23 @@ 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");
+ public void testNistCurveLargePrivateKey_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testLargePrivateKey(EcUtil.getNistP256Params(), true);
}
- @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..2e7108f 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,29 @@ 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.nio.ByteBuffer;
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 java.util.HashSet;
+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 +48,29 @@ 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";
+ private static final String TAG = "EcdsaTest";
+
+ @After
+ public void tearDown() throws Exception {
+ KeyStoreUtil.cleanUpKeyStore();
+ }
+
+ private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey,
+ boolean isStrongBox) throws Exception {
+ KeyProtection keyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA256,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512)
+ .setIsStrongBoxBacked(isStrongBox)
+ .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
@@ -66,6 +91,43 @@ public class EcdsaTest {
}
/**
+ * Returns true if the signature scheme is deterministic. Even though a non-deterministic
+ * signature scheme can in principle return the same signature twice this should never happen in
+ * practice.
+ */
+ private boolean isDeterministic(Signature signer, PrivateKey priv) throws Exception {
+ byte[][] signature = new byte[2][];
+ byte[] message = new byte[1];
+ for (int i = 0; i < 2; i++) {
+ signer.initSign(priv);
+ signer.update(message);
+ signature[i] = signer.sign();
+ }
+ return Arrays.equals(signature[0], signature[1]);
+ }
+
+ /**
+ * Returns number of count messages to sign. If the signature scheme is deterministic then the
+ * messages are all different. If the signature scheme is randomized then the messages are all
+ * the same. If the messages signed are all the same then it may be easier to detect a bias.
+ */
+ private byte[][] getMessagesToSign(int count, Signature signer, PrivateKey priv)
+ throws Exception {
+ byte[][] messages = new byte[count][];
+ if (isDeterministic(signer, priv)) {
+ for (int i = 0; i < count; i++) {
+ messages[i] = ByteBuffer.allocate(4).putInt(i).array();
+ }
+ } else {
+ byte[] msg = new byte[4];
+ for (int i = 0; i < count; i++) {
+ messages[i] = msg;
+ }
+ }
+ return messages;
+ }
+
+ /**
* Extract the integer r from an ECDSA signature. This method implicitely assumes that the ECDSA
* signature is DER encoded. and that the order of the curve is smaller than 2^1024.
*/
@@ -96,14 +158,13 @@ public class EcdsaTest {
/**
* Computes the bias of samples as
*
- * abs(sum(e^(2 pi i s m / modulus) for s in samples) / sqrt(samples.length).
+ * <p>abs(sum(e^(2 pi i s m / modulus) for s in samples) / sqrt(samples.length).
*
- * If the samples are taken from a uniform distribution in the range 0 .. modulus - 1
- * and the number of samples is significantly larger than L^2
- * then the probability that the result is larger than L is approximately e^(-L^2).
- * The approximation can be derived from the assumption that samples taken from
- * a uniform distribution give a result that approximates a standard complex normal
- * distribution Z. I.e. Z has a density f_Z(z) = exp(-abs(z)^2) / pi.
+ * <p>If the samples are taken from a uniform distribution in the range 0 .. modulus - 1 and the
+ * number of samples is significantly larger than L^2 then the probability that the result is
+ * larger than L is approximately e^(-L^2). The approximation can be derived from the assumption
+ * that samples taken from a uniform distribution give a result that approximates a standard
+ * complex normal distribution Z. I.e. Z has a density f_Z(z) = exp(-abs(z)^2) / pi.
* https://en.wikipedia.org/wiki/Complex_normal_distribution
*/
double bias(BigInteger[] samples, BigInteger modulus, BigInteger m) {
@@ -114,7 +175,7 @@ public class EcdsaTest {
// multiplier = 2 * pi / 2^52
double multiplier = 1.3951473992034527e-15;
// computes the quotent 2 * pi * r / modulus
- double quot = r.shiftLeft(52).divide(modulus).doubleValue() * multiplier;
+ double quot = r.shiftLeft(52).divide(modulus).doubleValue() * multiplier;
sumReal += Math.cos(quot);
sumImag += Math.sin(quot);
}
@@ -131,13 +192,18 @@ public class EcdsaTest {
* @throws Exception if an unexpected error occurred.
*/
boolean testParameters(String algorithm, String curve) throws Exception {
+ return testParameters(algorithm, curve, false);
+ }
+ boolean testParameters(String algorithm, String curve, boolean isStrongBox) throws Exception {
+ if (isStrongBox) {
+ KeyStoreUtil.assumeStrongBox();
+ }
String message = "123400";
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
- ECGenParameterSpec ecSpec = new ECGenParameterSpec(curve);
KeyPair keyPair;
try {
- keyGen.initialize(ecSpec);
+ keyGen.initialize(new ECGenParameterSpec(curve));
keyPair = keyGen.generateKeyPair();
} catch (InvalidAlgorithmParameterException ex) {
// The curve is not supported.
@@ -149,15 +215,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 +227,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, isStrongBox));
signer.update(messageBytes);
byte[] signature = signer.sign();
verifier.initVerify(pub);
@@ -184,18 +246,30 @@ public class EcdsaTest {
String curve = "secp256r1";
assertTrue(testParameters(algorithm, curve));
}
+ @Test
+ public void testBasic_StrongBox() throws Exception {
+ String algorithm = "SHA256WithECDSA";
+ String curve = "secp256r1";
+ assertTrue(testParameters(algorithm, curve, true));
+ }
/** 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;
+ public void testBias(String algorithm, String curve) throws Exception {
+ testBias(algorithm, curve, false);
+ }
+ public void testBias(String algorithm, String curve,
+ boolean isStrongBox) throws Exception {
+ if (isStrongBox) {
+ KeyStoreUtil.assumeStrongBox();
}
+ Signature signer = Signature.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC");
+ keyGen.initialize(new ECGenParameterSpec(curve));
KeyPair keyPair = keyGen.generateKeyPair();
- ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
+
+ ECPrivateKey priv = (ECPrivateKey)keyPair.getPrivate();
+ PrivateKey keystorePrivateKey = getKeystorePrivateKey(keyPair.getPublic(),
+ keyPair.getPrivate(), isStrongBox);
// If we throw a fair coin tests times then the probability that
// either heads or tails appears less than mincount is less than 2^{-32}.
// Therefore the test below is not expected to fail unless the generation
@@ -203,23 +277,17 @@ public class EcdsaTest {
final int tests = 1024;
final int mincount = 410;
- String hashAlgorithm = getHashAlgorithm(algorithm);
- String message = "Hello";
- byte[] messageBytes = message.getBytes("UTF-8");
- byte[] digest = MessageDigest.getInstance(hashAlgorithm).digest(messageBytes);
-
- // TODO(bleichen): Truncate the digest if the digest size is larger than the
- // curve size.
- BigInteger h = new BigInteger(1, digest);
- BigInteger q = priv.getParams().getOrder();
- BigInteger qHalf = q.shiftRight(1);
-
- Signature signer = Signature.getInstance(algorithm);
- signer.initSign(priv);
BigInteger[] kList = new BigInteger[tests];
+ byte[][] message = getMessagesToSign(tests, signer, keystorePrivateKey);
+ signer.initSign(keystorePrivateKey);
+ String hashAlgorithm = getHashAlgorithm(algorithm);
for (int i = 0; i < tests; i++) {
- signer.update(messageBytes);
+ signer.update(message[i]);
byte[] signature = signer.sign();
+ byte[] digest = MessageDigest.getInstance(hashAlgorithm).digest(message[i]);
+ // TODO(bleichen): Truncate the digest if the digest size is larger than the
+ // curve size.
+ BigInteger h = new BigInteger(1, digest);
kList[i] = extractK(signature, h, priv);
}
@@ -227,6 +295,8 @@ public class EcdsaTest {
// of the value k are unbiased.
int countMsb = 0; // count the number of k's with lsb set
int countLsb = 0; // count the number of k's with msb set
+ BigInteger q = priv.getParams().getOrder();
+ BigInteger qHalf = q.shiftRight(1);
for (BigInteger k : kList) {
if (k.testBit(0)) {
countLsb++;
@@ -259,7 +329,7 @@ public class EcdsaTest {
fail("Bias for k detected. bias1 = " + bias1);
}
// Same as above but shifing by one bit.
- double bias2 = bias(kList, q, BigInteger.valueOf(2));
+ double bias2 = bias(kList, q, BigInteger.valueOf(2));
if (bias2 > threshold) {
fail("Bias for k detected. bias2 = " + bias2);
}
@@ -277,141 +347,117 @@ 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());
+ public void testBiasSecp224r1() throws Exception {
+ testBias("SHA224WithECDSA", "secp224r1");
+ }
+
+ @Test
+ public void testBiasSecp256r1() throws Exception {
+ testBias("SHA256WithECDSA", "secp256r1");
+ }
+
+ @Test
+ public void testBiasSecp256r1_StrongBox() throws Exception {
+ testBias("SHA256WithECDSA", "secp256r1", true);
+ }
+
+ @Test
+ public void testBiasSecp384r1() throws Exception {
+ testBias("SHA384WithECDSA", "secp384r1");
+ }
+
+ @Test
+ public void testBiasSecp521r1() throws Exception {
+ testBias("SHA512WithECDSA", "secp521r1");
+ }
+
+ @Test
+ @Ignore // Brainpool curve are not supported in AndroidKeyStore
+ public void testBiasBrainpoolP256r1() throws Exception {
+ testBias("SHA512WithECDSA", "brainpoolP256r1");
+ }
+
+ /**
+ * This test uses the deterministic ECDSA implementation from BouncyCastle (if BouncyCastle is
+ * being tested.)
+ */
+ @Test
+ @Ignore // Algorithm SHA256WithECDDSA is not supported in AndroidKeyStore.
+ public void testBiasSecp256r1ECDDSA() throws Exception {
+ testBias("SHA256WithECDDSA", "secp256r1");
}
/**
- * 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.
+ * Tests initSign with a null value for SecureRandom. The expected behaviour is that a default
+ * instance of SecureRandom is used and that this instance is properly seeded. I.e., the expected
+ * behaviour is that Signature.initSign(ECPrivateKey, null) behaves like
+ * Signature.initSign(ECPrivateKey). If the signature scheme normally is randomized then
+ * Signature.initSign(ECprivateKey, null) should still be a randomized signature scheme. If the
+ * implementation is deterministic then we simply want this to work.
+ *
+ * <p>In principle, the correct behaviour is not really defined. However, if a provider would
+ * throw a null pointer exception then this can lead to unnecessary breakages.
*/
- // 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;
+ public void testNullRandom(String algorithm, String curve) throws Exception {
+ testNullRandom(algorithm, curve, false);
+ }
+ public void testNullRandom(String algorithm, String curve, boolean isStrongBox)
+ throws Exception {
+ if (isStrongBox) {
+ KeyStoreUtil.assumeStrongBox();
}
+ int samples = 8;
+ Signature signer = Signature.getInstance(algorithm);
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(new ECGenParameterSpec(curve));
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];
+ PrivateKey priv = getKeystorePrivateKey(keyPair.getPublic(), keyPair.getPrivate(),
+ isStrongBox);
+ byte[][] message = getMessagesToSign(samples, signer, priv);
+ HashSet<BigInteger> rSet = new HashSet<>();
for (int i = 0; i < samples; i++) {
- long start = bean.getCurrentThreadCpuTime();
- signer.update(messageBytes);
+ // This is the function call that is tested by this test.
+ signer.initSign(priv, null);
+ signer.update(message[i]);
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");
+ BigInteger r = extractR(signature);
+ assertTrue("Same r computed twice", rSet.add(r));
}
}
- @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());
+ public void testNullRandomSecp224r1() throws Exception {
+ testNullRandom("SHA224WithECDSA", "secp224r1");
+ }
+
+ @Test
+ public void testNullRandomSecp256r1() throws Exception {
+ testNullRandom("SHA256WithECDSA", "secp256r1");
+ }
+
+ @Test
+ public void testNullRandomSecp256r1_StrongBox() throws Exception {
+ testNullRandom("SHA256WithECDSA", "secp256r1", true);
+ }
+
+ @Test
+ public void testNullRandomSecp384r1() throws Exception {
+ testNullRandom("SHA384WithECDSA", "secp384r1");
+ }
+
+ @Test
+ public void testNullRandomSecp521r1() throws Exception {
+ testNullRandom("SHA512WithECDSA", "secp521r1");
+ }
+
+ /**
+ * This test uses the deterministic ECDSA implementation from BouncyCastle (if BouncyCastle is
+ * being tested.)
+ */
+ @Test
+ @Ignore // Algorithm SHA256WithECdDSA is not supported in AndroidKeyStore.
+ public void testNullRandomSecp256r1ECDDSA() throws Exception {
+ testNullRandom("SHA256WithECdDSA", "secp256r1");
}
}
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..c4a6b63 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) {
@@ -79,24 +89,20 @@ public class JsonAeadTest {
* @throws Exception if the initialization failed.
*/
protected static Cipher getInitializedCipher(
- String algorithm, int opmode, byte[] key, byte[] iv, int tagSize)
+ String algorithm, int opmode, byte[] key, byte[] iv, int tagSize, boolean isStrongBox)
throws Exception {
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)
+ .setIsStrongBoxBacked(isStrongBox)
.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));
@@ -147,6 +153,9 @@ public class JsonAeadTest {
// This is a false positive, since errorprone cannot track values passed into a method.
@SuppressWarnings("InsecureCryptoUsage")
public void testAead(String filename, String algorithm) throws Exception {
+ testAead(filename, algorithm, false);
+ }
+ public void testAead(String filename, String algorithm, boolean isStrongBox) throws Exception {
// Version number have the format major.minor[.subversion].
// Versions before 1.0 are experimental and use formats that are expected to change.
// Versions after 1.0 change the major number if the format changes and change
@@ -162,14 +171,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;
@@ -195,7 +207,8 @@ public class JsonAeadTest {
// Test encryption
Cipher cipher;
try {
- cipher = getInitializedCipher(algorithm, Cipher.ENCRYPT_MODE, key, iv, tagSize);
+ cipher = getInitializedCipher(algorithm, Cipher.ENCRYPT_MODE, key, iv, tagSize,
+ isStrongBox);
} catch (GeneralSecurityException ex) {
// Some libraries restrict key size, iv size and tag size.
// Because of the initialization of the cipher might fail.
@@ -226,7 +239,8 @@ public class JsonAeadTest {
// Test decryption
Cipher decCipher;
try {
- decCipher = getInitializedCipher(algorithm, Cipher.DECRYPT_MODE, key, iv, tagSize);
+ decCipher = getInitializedCipher(algorithm, Cipher.DECRYPT_MODE, key, iv, tagSize,
+ isStrongBox);
} catch (GeneralSecurityException ex) {
errors++;
continue;
@@ -257,6 +271,11 @@ public class JsonAeadTest {
public void testAesGcm() throws Exception {
testAead("aes_gcm_test.json", "AES/GCM/NoPadding");
}
+ @Test
+ public void testAesGcm_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testAead("aes_gcm_test.json", "AES/GCM/NoPadding", true);
+ }
@Test
@Ignore // Ignored due to AES/EAX algorithm not supported in AndroidKeyStore.
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..d6dae08 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 {
@@ -63,9 +74,10 @@ public class JsonCipherTest {
* @param opmode either Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE
* @param key raw key bytes
* @param iv the initialisation vector
+ * @param isStrongBox whether key should store in StrongBox or not
*/
- protected static void initCipher(
- Cipher cipher, String algorithm, int opmode, byte[] key, byte[] iv) throws Exception {
+ protected static void initCipher(Cipher cipher, String algorithm, int opmode,
+ byte[] key, byte[] iv, boolean isStrongBox) throws Exception {
SecretKeySpec keySpec = null;
if (algorithm.startsWith("AES/")) {
keySpec = new SecretKeySpec(key, "AES");
@@ -73,7 +85,17 @@ 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)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build());
+ // Key imported, obtain a reference to it.
+ SecretKey keyStoreKey = (SecretKey) keyStore.getKey(KEY_ALIAS_1, null);
+
+ cipher.init(opmode, keyStoreKey, ivSpec);
}
@@ -104,6 +126,10 @@ public class JsonCipherTest {
// This is a false positive, since errorprone cannot track values passed into a method.
@SuppressWarnings("InsecureCryptoUsage")
public void testCipher(String filename, String algorithm) throws Exception {
+ testCipher(filename, algorithm, false);
+ }
+ @SuppressWarnings("InsecureCryptoUsage")
+ public void testCipher(String filename, String algorithm, boolean isStrongBox) throws Exception {
// Testing with old test vectors may a reason for a test failure.
// Version number have the format major.minor[status].
// Versions before 1.0 are experimental and use formats that are expected to change.
@@ -111,27 +137,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")) {
@@ -151,11 +170,10 @@ public class JsonCipherTest {
// Test encryption
try {
- initCipher(cipher, algorithm, Cipher.ENCRYPT_MODE, key, iv);
+ initCipher(cipher, algorithm, Cipher.ENCRYPT_MODE, key, iv, isStrongBox);
} 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 +182,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++;
}
}
@@ -190,9 +201,8 @@ public class JsonCipherTest {
// However, all the test vectors in Wycheproof are constructed such that they have
// invalid padding. If this changes then the test below is too strict.
try {
- initCipher(cipher, algorithm, Cipher.DECRYPT_MODE, key, iv);
+ initCipher(cipher, algorithm, Cipher.DECRYPT_MODE, key, iv, isStrongBox);
} catch (GeneralSecurityException ex) {
- System.out.println("Parameters accepted for encryption but not decryption " + tc);
errors++;
continue;
}
@@ -200,18 +210,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 +235,25 @@ 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");
+ public void testAesCbcPkcs7() throws Exception {
+ // AndroidKeyStore only suuport AES/CBC/PKCS7Padding algorithm,
+ // so it is used instead of PKCS5Padding
+ testCipher("aes_cbc_pkcs5_test.json", "AES/CBC/PKCS7Padding");
+ }
+ @Test
+ public void testAesCbcPkcs7_StrongBox() throws Exception {
+ // AndroidKeyStore only suuport AES/CBC/PKCS7Padding algorithm,
+ // so it is used instead of PKCS5Padding
+ KeyStoreUtil.assumeStrongBox();
+ testCipher("aes_cbc_pkcs5_test.json", "AES/CBC/PKCS7Padding", true);
}
}
diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
index 5a8b877..779dc42 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
@@ -20,6 +20,7 @@ import com.google.gson.JsonObject;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
+import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -27,13 +28,24 @@ import java.security.spec.ECPrivateKeySpec;
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.keystore.cts.util.KeyStoreUtil;
+import android.util.Log;
/** This test uses test vectors in JSON format to check implementations of ECDH. */
-@RunWith(JUnit4.class)
public class JsonEcdhTest {
+ private static final String TAG = "JsonEcdhTest";
+ private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_PROVIDER_NAME;
+ private static final String KEY_ALIAS_1 = "Key1";
+
+ @After
+ public void tearDown() throws Exception {
+ KeyStoreUtil.cleanUpKeyStore();
+ }
/** Convenience mehtod to get a String from a JsonObject */
protected static String getString(JsonObject object, String name) throws Exception {
@@ -76,7 +88,10 @@ public class JsonEcdhTest {
* ...
**/
public void testEcdhComp(String filename) throws Exception {
- JsonObject test = JsonUtil.getTestVectors(filename);
+ testEcdhComp(filename, false);
+ }
+ public void testEcdhComp(String filename, boolean isStrongBox) throws Exception {
+ JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename);
// This test expects test vectors as defined in wycheproof/schemas/ecdh_test_schema.json.
// In particular, this means that the public keys use X509 encoding.
@@ -107,12 +122,20 @@ public class JsonEcdhTest {
PrivateKey privKey = kf.generatePrivate(spec);
X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(publicEncoded);
PublicKey pubKey = kf.generatePublic(x509keySpec);
- KeyAgreement ka = KeyAgreement.getInstance("ECDH");
- ka.init(privKey);
- ka.doPhase(pubKey, true);
+
+ KeyStore keyStore = KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey,
+ new KeyProtection.Builder(KeyProperties.PURPOSE_AGREE_KEY)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build());
+ KeyAgreement ka = KeyAgreement.getInstance("ECDH", EXPECTED_PROVIDER_NAME);
+ PrivateKey keyStorePrivateKey = (PrivateKey) keyStore.getKey(KEY_ALIAS_1, null);
+ PublicKey publicKey = keyStore.getCertificate(KEY_ALIAS_1).getPublicKey();
+
+ ka.init(keyStorePrivateKey);
+ ka.doPhase(publicKey, true);
String sharedHex = TestUtil.bytesToHex(ka.generateSecret());
if (result.equals("invalid")) {
- System.out.println(
+ Log.e(TAG,
"Computed ECDH with invalid parameters"
+ " tcId:"
+ tcid
@@ -122,7 +145,7 @@ public class JsonEcdhTest {
+ sharedHex);
errors++;
} else if (!expectedHex.equals(sharedHex)) {
- System.out.println(
+ Log.e(TAG,
"Incorrect ECDH computation"
+ " tcId:"
+ tcid
@@ -137,6 +160,8 @@ public class JsonEcdhTest {
passedTests++;
}
} catch (InvalidKeySpecException | InvalidKeyException | NoSuchAlgorithmException ex) {
+ Log.e(TAG,
+ "Test vector with tcId:" + tcid + " comment:" + comment + " throws:" + ex.toString());
// These are the exception that we expect to see when a curve is not implemented
// or when a key is not valid.
if (result.equals("valid")) {
@@ -146,14 +171,14 @@ public class JsonEcdhTest {
}
} catch (Exception ex) {
// Other exceptions typically indicate that something is wrong with the implementation.
- System.out.println(
+ Log.e(TAG,
"Test vector with tcId:" + tcid + " comment:" + comment + " throws:" + ex.toString());
errors++;
}
}
}
assertEquals(0, errors);
- assertEquals(numTests, passedTests + rejectedTests + skippedTests);
+ assertEquals(numTests, passedTests + rejectedTests);
}
@Test
@@ -165,6 +190,12 @@ public class JsonEcdhTest {
public void testSecp256r1() throws Exception {
testEcdhComp("ecdh_secp256r1_test.json");
}
+ @Test
+ @Ignore //TODO Reverify after bug b/215175472 is fixed.
+ public void testSecp256r1_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testEcdhComp("ecdh_secp256r1_test.json", true);
+ }
@Test
public void testSecp384r1() throws Exception {
@@ -177,31 +208,37 @@ public class JsonEcdhTest {
}
@Test
+ @Ignore // Secp256k1 curve not supported in AndroidKeystore
public void testSecp256k1() throws Exception {
testEcdhComp("ecdh_secp256k1_test.json");
}
@Test
+ @Ignore // Brainpool curves are not supported in AndroidKeystore
public void testBrainpoolP224r1() throws Exception {
testEcdhComp("ecdh_brainpoolP224r1_test.json");
}
@Test
+ @Ignore // Brainpool curves are not supported in AndroidKeystore
public void testBrainpoolP256r1() throws Exception {
testEcdhComp("ecdh_brainpoolP256r1_test.json");
}
@Test
+ @Ignore // Brainpool curves are not supported in AndroidKeystore
public void testBrainpoolP320r1() throws Exception {
testEcdhComp("ecdh_brainpoolP320r1_test.json");
}
@Test
+ @Ignore // Brainpool curves are not supported in AndroidKeystore
public void testBrainpoolP384r1() throws Exception {
testEcdhComp("ecdh_brainpoolP384r1_test.json");
}
@Test
+ @Ignore // Brainpool curves are not supported in AndroidKeystore
public void testBrainpoolP512r1() throws Exception {
testEcdhComp("ecdh_brainpoolP512r1_test.json");
}
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..d813d02 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 {
@@ -59,9 +72,9 @@ public class JsonMacTest {
* if the initialization failed. For example one case are GMACs with a tag size othe than 128
* 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);
+ protected static byte[] computeMac(String algorithm, byte[] key, byte[] msg, int tagSize,
+ boolean isStrongBox) 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,13 @@ 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)
+ .setIsStrongBoxBacked(isStrongBox)
+ .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);
@@ -93,15 +112,13 @@ public class JsonMacTest {
* @param filename the JSON file with the test vectors.
*/
public void testMac(String filename) throws Exception {
+ testMac(filename, false);
+ }
+ public void testMac(String filename, boolean isStrongBox) 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;
@@ -118,23 +135,17 @@ public class JsonMacTest {
byte[] key = getBytes(testcase, "key");
byte[] msg = getBytes(testcase, "msg");
byte[] expectedTag = getBytes(testcase, "tag");
+ // Strongbox only supports key size from 8 to 32 bytes.
+ if (isStrongBox && (key.length < 8 || key.length > 32)) {
+ continue;
+ }
// Result is one of "valid", "invalid", "acceptable".
// "valid" are test vectors with matching plaintext, ciphertext and tag.
// "invalid" are test vectors with invalid parameters or invalid ciphertext and tag.
// "acceptable" are test vectors with weak parameters or legacy formats.
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, isStrongBox);
boolean eq = arrayEquals(expectedTag, computedTag);
if (result.equals("invalid")) {
@@ -142,26 +153,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 +182,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 +209,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 +234,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 +243,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);
}
@@ -294,6 +272,11 @@ public class JsonMacTest {
public void testHmacSha256() throws Exception {
testMac("hmac_sha256_test.json");
}
+ @Test
+ public void testHmacSha256_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testMac("hmac_sha256_test.json", true);
+ }
@Test
public void testHmacSha384() throws Exception {
@@ -306,26 +289,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..bbea28a 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,38 @@ 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, String digest,
+ boolean isStrongBox) throws Exception {
+ KeyProtection keyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
+ .setDigests(digest)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setIsStrongBoxBacked(isStrongBox)
+ .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 +126,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 +141,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.
}
@@ -245,12 +262,18 @@ public class JsonSignatureTest {
*/
// This is a false positive, since errorprone cannot track values passed into a method.
@SuppressWarnings("InsecureCryptoUsage")
- protected static PrivateKey getPrivateKey(JsonObject object, String algorithm) throws Exception {
+ protected static PrivateKey getPrivateKey(JsonObject object, String algorithm,
+ boolean isStrongBox) throws Exception {
if (algorithm.equals("RSA")) {
KeyFactory kf = KeyFactory.getInstance(algorithm);
byte[] encoded = TestUtil.hexToBytes(getString(object, "privateKeyPkcs8"));
+ byte[] pubEncoded = TestUtil.hexToBytes(getString(object, "keyDer"));
+ String digest = getString(object, "sha");
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, digest, isStrongBox);
} else {
throw new NoSuchAlgorithmException("Algorithm " + algorithm + " is not supported");
}
@@ -298,19 +321,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 +398,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 +413,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);
@@ -464,21 +433,14 @@ public class JsonSignatureTest {
* @param allowSkippingKeys if true then keys that cannot be constructed will not fail the test.
*/
public void testSigning(
+ String filename, String signatureAlgorithm, Format signatureFormat,
+ boolean allowSkippingKeys) throws Exception {
+ testSigning(filename, signatureAlgorithm, signatureFormat, allowSkippingKeys, false);
+ }
+ 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);
- }
+ boolean allowSkippingKeys, boolean isStrongBox) throws Exception {
+ JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename);
int cntTests = 0;
int errors = 0;
int skippedKeys = 0;
@@ -486,7 +448,7 @@ public class JsonSignatureTest {
JsonObject group = g.getAsJsonObject();
PrivateKey key;
try {
- key = getPrivateKey(group, signatureAlgorithm);
+ key = getPrivateKey(group, signatureAlgorithm, isStrongBox);
} catch (GeneralSecurityException ex) {
skippedKeys++;
continue;
@@ -510,39 +472,23 @@ 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);
+ android.util.Log.e("JsonSignatureTest", "Signature mismatch error for test id " + tcid);
errors++;
} else {
cntTests++;
}
} catch (InvalidKeyException | SignatureException ex) {
if (result.equals("valid")) {
- System.out.println(
- "Failed to sign "
- + filename
- + " tcId:"
- + tcid
- + " with exception:"
- + ex);
-
- errors++;
+ android.util.Log.e("JsonSignatureTest", "Unexpected exception for test id " + tcid, ex);
+ if (!isStrongBox) {
+ errors++;
+ }
}
}
}
}
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,180 +540,209 @@ 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);
}
// Testing RSA PKCS#1 v1.5 signatures.
@Test
- public void testRsaSigning() throws Exception {
+ public void testRsaSigning() throws Exception {
testSigning("rsa_sig_gen_misc_test.json", "RSA", Format.RAW, true);
}
+ @Test
+ public void testRsaSigning_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testSigning("rsa_sig_gen_misc_test.json", "RSA", Format.RAW, true, true);
+ }
@Test
public void testRsaSignatures() throws Exception {
@@ -822,21 +797,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 +823,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 +874,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 +883,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..3a42761 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,23 @@ 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, boolean isStrongBox)
+ throws Exception {
+ KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(KEY_ALIAS_1,
+ new SecretKeySpec(keyMaterial, algorithm),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
+ .setIsStrongBoxBacked(isStrongBox).build());
+ return keyStore.getKey(KEY_ALIAS_1, null);
+ }
/**
* Computes the maximum of an array with at least one element.
@@ -79,7 +96,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;
@@ -177,16 +194,18 @@ public class MacTest {
}
public void testMac(String algorithm, int keySize) throws Exception {
+ testMac(algorithm, keySize, false);
+ }
+ public void testMac(String algorithm, int keySize, boolean isStrongBox) 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, isStrongBox));
}
@Test
@@ -205,6 +224,13 @@ public class MacTest {
}
@Test
+ @Ignore // StrongBox takes very long time to complete this test and CTS timed out (b/242028608), hence ignoring it.
+ public void testHmacSha256_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testMac("HMACSHA256", 32, true);
+ }
+
+ @Test
public void testHmacSha384() throws Exception {
testMac("HMACSHA384", 48);
}
@@ -215,21 +241,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 +276,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.
@@ -280,29 +310,26 @@ public class MacTest {
* IMPLEMENTATION RETURNS INCORRECT HASH FOR LARGE SETS OF DATA
*/
private void testLongMac(
- String algorithm, String keyhex, String message, long repetitions, String expected)
- throws Exception {
+ String algorithm, String keyhex, String message, long repetitions, String expected)
+ throws Exception {
+ testLongMac(algorithm, keyhex, message, repetitions, expected, false);
+ }
+ private void testLongMac(
+ String algorithm, String keyhex, String message, long repetitions, String expected,
+ boolean isStrongBox) throws Exception {
- Key key = new SecretKeySpec(TestUtil.hexToBytes(keyhex), algorithm);
+ Key key = getKeyStoreSecretKey(TestUtil.hexToBytes(keyhex), algorithm, isStrongBox);
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,36 +346,33 @@ public class MacTest {
"d7f4c387f2237ea119fcc27cd7520fc5132b6230");
}
- @SlowTest(
- providers = {
- ProviderType.OPENJDK,
- ProviderType.BOUNCY_CASTLE,
- ProviderType.SPONGY_CASTLE,
- ProviderType.CONSCRYPT
- })
@Test
public void testLongMacSha256() throws Exception {
+ testLongMacSha256(false);
+ }
+ @Test
+ @Ignore // StrongBox takes very long time to complete this test and CTS timed out (b/242028608), hence ignoring it.
+ public void testLongMacSha256_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testLongMacSha256(true);
+ }
+ private void testLongMacSha256(boolean isStrongBox) throws Exception {
testLongMac(
"HMACSHA256",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"a",
2147483647L,
- "84f213c9bb5b329d547bc31dabed41939754b1af7482365ec74380c45f6ea0a7");
+ "84f213c9bb5b329d547bc31dabed41939754b1af7482365ec74380c45f6ea0a7",
+ isStrongBox);
testLongMac(
"HMACSHA256",
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"a",
5000000000L,
- "59a75754df7093fa4339aa618b64b104f153a5b42cc85394fdb8735b13ea684a");
+ "59a75754df7093fa4339aa618b64b104f153a5b42cc85394fdb8735b13ea684a",
+ isStrongBox);
}
- @SlowTest(
- providers = {
- ProviderType.OPENJDK,
- ProviderType.BOUNCY_CASTLE,
- ProviderType.SPONGY_CASTLE,
- ProviderType.CONSCRYPT
- })
@Test
public void testLongMacSha384() throws Exception {
testLongMac(
@@ -369,13 +393,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..ed291e0 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.");
}
}
@@ -67,12 +77,23 @@ public class RsaEncryptionTest {
*/
// This is a false positive, since errorprone cannot track values passed into a method.
@SuppressWarnings("InsecureCryptoUsage")
- protected static PrivateKey getPrivateKey(JsonObject object) throws Exception {
+ protected static PrivateKey getPrivateKey(JsonObject object, boolean isStrongBox)
+ throws Exception {
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)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build());
+ return (PrivateKey) keyStore.getKey(KEY_ALIAS_SIG, null);
}
/** Convenience method to get a byte array from a JsonObject */
@@ -124,16 +145,11 @@ 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);
- }
+ testDecryption(filename, false);
+ }
+ @SuppressWarnings("InsecureCryptoUsage")
+ public void testDecryption(String filename, boolean isStrongBox) throws Exception {
+ 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,10 +160,10 @@ 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);
+ PrivateKey key = getPrivateKey(group, isStrongBox);
for (JsonElement t : group.getAsJsonArray("tests")) {
JsonObject testcase = t.getAsJsonObject();
int tcid = testcase.get("tcId").getAsInt();
@@ -178,31 +194,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);
@@ -212,6 +215,11 @@ public class RsaEncryptionTest {
public void testDecryption2048() throws Exception {
testDecryption("rsa_pkcs1_2048_test.json");
}
+ @Test
+ public void testDecryption2048_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testDecryption("rsa_pkcs1_2048_test.json", true);
+ }
@Test
public void testDecryption3072() throws Exception {
diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java
index 201dfbc..5eb47e5 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java
@@ -15,31 +15,63 @@ package com.google.security.wycheproof;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
+import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
+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;
+import android.text.TextUtils;
+import android.util.Log;
/**
* Checks implementations of RSA-OAEP.
*/
-@RunWith(JUnit4.class)
public class RsaOaepTest {
+ private static final String TAG = "RsaOaepTest";
+ 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 saveKeyPairToKeystoreAndReturnPrivateKey(PublicKey pubKey,
+ PrivateKey privKey, String digest, String mgfDigest, boolean isStrongBox)
+ throws Exception {
+ return (PrivateKey) KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey,
+ new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN |
+ KeyProperties.PURPOSE_VERIFY |
+ KeyProperties.PURPOSE_ENCRYPT |
+ KeyProperties.PURPOSE_DECRYPT)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
+ .setDigests(digest, mgfDigest)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build())
+ .getKey(KEY_ALIAS_1, null);
+ }
/**
* A list of algorithm names for RSA-OAEP.
@@ -59,19 +91,19 @@ public class RsaOaepTest {
protected static void printParameters(AlgorithmParameterSpec params) {
if (params instanceof OAEPParameterSpec) {
OAEPParameterSpec oaepParams = (OAEPParameterSpec) params;
- System.out.println("OAEPParameterSpec");
- System.out.println("digestAlgorithm:" + oaepParams.getDigestAlgorithm());
- System.out.println("mgfAlgorithm:" + oaepParams.getMGFAlgorithm());
+ Log.d(TAG, "OAEPParameterSpec");
+ Log.d(TAG, "digestAlgorithm:" + oaepParams.getDigestAlgorithm());
+ Log.d(TAG, "mgfAlgorithm:" + oaepParams.getMGFAlgorithm());
printParameters(oaepParams.getMGFParameters());
} else if (params instanceof MGF1ParameterSpec) {
MGF1ParameterSpec mgf1Params = (MGF1ParameterSpec) params;
- System.out.println("MGF1ParameterSpec");
- System.out.println("digestAlgorithm:" + mgf1Params.getDigestAlgorithm());
+ Log.d(TAG, "MGF1ParameterSpec");
+ Log.d(TAG, "digestAlgorithm:" + mgf1Params.getDigestAlgorithm());
} else {
- System.out.println(params.toString());
- }
+ Log.d(TAG, params.toString());
+ }
}
-
+
/**
* This is not a real test. The JCE algorithm names only specify one hash algorithm. But OAEP
* uses two hases. One hash algorithm is used to hash the labels. The other hash algorithm is
@@ -79,7 +111,7 @@ public class RsaOaepTest {
*
* <p>Different provider use different default values for the hash function that is not specified
* in the algorithm name. Jdk uses mgfsha1 as default. BouncyCastle and Conscrypt use the same
- * hash for labels and mgf. Every provider allows to specify all the parameters using
+ * hash for labels and mgf. Every provider allows to specify all the parameters using
* an OAEPParameterSpec instance.
*
* <p>This test simply tries a number of algorithm names for RSA-OAEP and prints the OAEP
@@ -104,15 +136,11 @@ public class RsaOaepTest {
X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(TestUtil.hexToBytes(pubKey));
PublicKey key = kf.generatePublic(x509keySpec);
for (String oaepName : OaepAlgorithmNames) {
- try {
- Cipher c = Cipher.getInstance(oaepName);
+ Cipher c = Cipher.getInstance(oaepName, EXPECTED_PROVIDER_NAME);
c.init(Cipher.ENCRYPT_MODE, key);
- System.out.println("Algorithm " + oaepName + " uses the following defaults");
+ Log.d(TAG, "Algorithm " + oaepName + " uses the following defaults");
AlgorithmParameters params = c.getParameters();
printParameters(params.getParameterSpec(OAEPParameterSpec.class));
- } catch (NoSuchAlgorithmException ex) {
- continue;
- }
}
}
@@ -135,12 +163,26 @@ public class RsaOaepTest {
*/
// This is a false positive, since errorprone cannot track values passed into a method.
@SuppressWarnings("InsecureCryptoUsage")
- protected static PrivateKey getPrivateKey(JsonObject object) throws Exception {
+ protected static PrivateKey getPrivateKey(JsonObject object, boolean isStrongBox)
+ throws Exception {
KeyFactory kf;
kf = KeyFactory.getInstance("RSA");
byte[] encoded = TestUtil.hexToBytes(getString(object, "privateKeyPkcs8"));
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
- return kf.generatePrivate(keySpec);
+ PrivateKey intermediateKey = kf.generatePrivate(keySpec);
+ BigInteger modulus = new BigInteger(TestUtil.hexToBytes(object.get("n").getAsString()));
+ BigInteger exponent = new BigInteger(TestUtil.hexToBytes(object.get("e").getAsString()));
+ PublicKey pubKey = kf.generatePublic(new RSAPublicKeySpec(modulus, exponent));
+ String digest = getString(object, "sha");
+ String mgfDigest = getString(object, "mgfSha");
+ int keysize = object.get("keysize").getAsInt();
+ if (!KeyStoreUtil.isSupportedDigest(digest, isStrongBox)
+ || !KeyStoreUtil.isSupportedMgfDigest(mgfDigest, isStrongBox)
+ || !KeyStoreUtil.isSupportedRsaKeySize(keysize, isStrongBox)) {
+ throw new UnsupportedKeyParametersException();
+ }
+ return saveKeyPairToKeystoreAndReturnPrivateKey(pubKey, intermediateKey, digest, mgfDigest,
+ isStrongBox);
}
protected static String getOaepAlgorithmName(JsonObject group) throws Exception {
@@ -150,18 +192,24 @@ public class RsaOaepTest {
}
protected static OAEPParameterSpec getOaepParameters(JsonObject group,
- JsonObject test) throws Exception {
+ JsonObject test) throws Exception {
String sha = getString(group, "sha");
String mgf = getString(group, "mgf");
String mgfSha = getString(group, "mgfSha");
+ // mgfDigest other than SHA-1 are supported from KeyMint V1 and above.
+ if (!mgfSha.equalsIgnoreCase("SHA-1")) {
+ assumeTrue("This test is valid for KeyMint version 1 and above.",
+ KeyStoreUtil.getFeatureVersionKeystore() >= KeyStoreUtil.KM_VERSION_KEYMINT_1);
+ }
PSource p = PSource.PSpecified.DEFAULT;
- if (test.has("label")) {
- p = new PSource.PSpecified(getBytes(test, "label"));
+ if (test.has("label") && !TextUtils.isEmpty(getString(test, "label"))) {
+ // p = new PSource.PSpecified(getBytes(test, "label"));
+ throw new UnsupportedKeyParametersException();
}
- return new OAEPParameterSpec(sha, mgf, new MGF1ParameterSpec(mgfSha), p);
+ return new OAEPParameterSpec(sha, mgf, new MGF1ParameterSpec(mgfSha), p);
}
- /**
+ /**
* Tests the signature verification with test vectors in a given JSON file.
*
* <p> Example format for test vectors
@@ -171,7 +219,7 @@ public class RsaOaepTest {
* ...
* "testGroups" : [
* {
- * "d" : "...",
+ * "d" : "...",
* "e" : "10001",
* "n" : "...",
* "keysize" : 2048,
@@ -204,23 +252,32 @@ public class RsaOaepTest {
* specified.
**/
public void testOaep(String filename, boolean allowSkippingKeys)
+ throws Exception {
+ testOaep(filename, allowSkippingKeys, false);
+ }
+
+ private static class UnsupportedKeyParametersException extends Exception { }
+
+ public void testOaep(String filename, boolean allowSkippingKeys, boolean isStrongBox)
throws Exception {
- JsonObject test = JsonUtil.getTestVectors(filename);
+ if (isStrongBox) {
+ KeyStoreUtil.assumeStrongBox();
+ }
+ JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename);
// Compares the expected and actual JSON schema of the test vector file.
// Mismatched JSON schemas will likely lead to a test failure.
String generatorVersion = getString(test, "generatorVersion");
String expectedSchema = "rsaes_oaep_decrypt_schema.json";
String actualSchema = getString(test, "schema");
- if (!expectedSchema.equals(actualSchema)) {
- System.out.println(
+ assertTrue(
"Expecting test vectors with schema "
+ expectedSchema
+ " found vectors with schema "
+ actualSchema
+ " generatorVersion:"
- + generatorVersion);
- }
+ + generatorVersion,
+ expectedSchema.equals(actualSchema));
int numTests = test.get("numberOfTests").getAsInt();
int cntTests = 0;
@@ -228,38 +285,45 @@ public class RsaOaepTest {
int skippedKeys = 0;
for (JsonElement g : test.getAsJsonArray("testGroups")) {
JsonObject group = g.getAsJsonObject();
- PrivateKey key;
+ PrivateKey key = null;
try {
- key = getPrivateKey(group);
- } catch (GeneralSecurityException ex) {
+ key = getPrivateKey(group, isStrongBox);
+ } catch (UnsupportedKeyParametersException e) {
skippedKeys++;
if (!allowSkippingKeys) {
- System.out.printf("Key generation throws:%s\n", ex.toString());
+ throw e;
+ } else {
+ continue;
}
- continue;
}
String algorithm = getOaepAlgorithmName(group);
- Cipher decrypter = Cipher.getInstance(algorithm);
+ Cipher decrypter = Cipher.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
for (JsonElement t : group.getAsJsonArray("tests")) {
cntTests++;
JsonObject testcase = t.getAsJsonObject();
int tcid = testcase.get("tcId").getAsInt();
String messageHex = TestUtil.bytesToHex(getBytes(testcase, "msg"));
- OAEPParameterSpec params = getOaepParameters(group, testcase);
+ OAEPParameterSpec params;
+ try {
+ params = getOaepParameters(group, testcase);
+ } catch (UnsupportedKeyParametersException e) {
+ // TODO This try catch block should be removed once issue b/229183581 is fixed.
+ continue;
+ }
byte[] ciphertext = getBytes(testcase, "ct");
String ciphertextHex = TestUtil.bytesToHex(ciphertext);
String result = getString(testcase, "result");
- decrypter.init(Cipher.DECRYPT_MODE, key, params);
byte[] decrypted = null;
try {
+ decrypter.init(Cipher.DECRYPT_MODE, key, params);
decrypted = decrypter.doFinal(ciphertext);
} catch (GeneralSecurityException ex) {
decrypted = null;
} 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.printf("Decryption throws %s. filename:%s tcId:%d ct:%s\n",
- ex.toString(), filename, tcid, ciphertextHex);
+ Log.e(TAG, String.format("Decryption throws %s. filename:%s tcId:%d ct:%s\n",
+ ex.toString(), filename, tcid, ciphertextHex));
decrypted = null;
// TODO(bleichen): BouncyCastle throws some non-conforming exceptions.
// For the moment we do not count this as a problem to avoid that
@@ -267,21 +331,21 @@ public class RsaOaepTest {
// errors++;
}
if (decrypted == null && result.equals("valid")) {
- System.out.printf(
- "Valid ciphertext not decrypted. filename:%s tcId:%d ct:%s\n",
- filename, tcid, ciphertextHex);
+ Log.e(TAG,
+ String.format("Valid ciphertext not decrypted. filename:%s tcId:%d ct:%s\n",
+ filename, tcid, ciphertextHex));
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);
+ Log.e(TAG,
+ String.format("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);
+ Log.e(TAG,
+ String.format("Incorrect decryption. filename:%s tcId:%d expected:%s" +
+ " decrypted:%s\n", filename, tcid, messageHex, decryptedHex));
errors++;
}
}
@@ -289,7 +353,7 @@ public class RsaOaepTest {
}
assertEquals(0, errors);
if (skippedKeys > 0) {
- System.out.println("RSAES-OAEP: file:" + filename + " skipped key:" + skippedKeys);
+ Log.d(TAG, "RSAES-OAEP: file:" + filename + " skipped key:" + skippedKeys);
assertTrue(allowSkippingKeys);
} else {
assertEquals(numTests, cntTests);
@@ -302,6 +366,11 @@ public class RsaOaepTest {
}
@Test
+ public void testRsaOaep2048Sha1Mgf1Sha1_StrongBox() throws Exception {
+ testOaep("rsa_oaep_2048_sha1_mgf1sha1_test.json", true, true);
+ }
+
+ @Test
public void testRsaOaep2048Sha224Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_2048_sha224_mgf1sha1_test.json", false);
}
@@ -313,12 +382,20 @@ public class RsaOaepTest {
@Test
public void testRsaOaep2048Sha256Mgf1Sha1() throws Exception {
- testOaep("rsa_oaep_2048_sha256_mgf1sha1_test.json", false);
+ testOaep("rsa_oaep_2048_sha256_mgf1sha1_test.json", false);
+ }
+ @Test
+ public void testRsaOaep2048Sha256Mgf1Sha1_StrongBox() throws Exception {
+ testOaep("rsa_oaep_2048_sha256_mgf1sha1_test.json", false, true);
}
@Test
public void testRsaOaep2048Sha256Mgf1Sha256() throws Exception {
- testOaep("rsa_oaep_2048_sha256_mgf1sha256_test.json", false);
+ testOaep("rsa_oaep_2048_sha256_mgf1sha256_test.json", false);
+ }
+ @Test
+ public void testRsaOaep2048Sha256Mgf1Sha256_StrongBox() throws Exception {
+ testOaep("rsa_oaep_2048_sha256_mgf1sha256_test.json", false, true);
}
@Test
@@ -383,8 +460,11 @@ public class RsaOaepTest {
@Test
public void testRsaOaepMisc() throws Exception {
- testOaep("rsa_oaep_misc_test.json", false);
+ testOaep("rsa_oaep_misc_test.json", true);
+ }
+ @Test
+ public void testRsaOaepMisc_StrongBox() throws Exception {
+ testOaep("rsa_oaep_misc_test.json", true, true);
}
-
}
diff --git a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaPssTest.java b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaPssTest.java
index 8868c23..b9a0a72 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaPssTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaPssTest.java
@@ -15,11 +15,10 @@ 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.NoPresubmitTest;
-import com.google.security.wycheproof.WycheproofRunner.ProviderType;
import java.lang.reflect.Constructor;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
@@ -27,6 +26,7 @@ import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
@@ -38,21 +38,23 @@ import java.security.spec.RSAKeyGenParameterSpec;
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.util.Log;
/**
* Tests for RSA-PSS.
*/
-@RunWith(JUnit4.class)
public class RsaPssTest {
+ private static final String TAG = "RsaPssTest";
+ private static final String EXPECTED_PROVIDER_NAME = TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
/**
* Returns an AlgorithmParameterSpec for generating a RSASSA-PSS key,
* which include the PSSParameters.
* Requires jdk11.
- *
+ *
* @param keySizeInBits the size of the modulus in bits.
* @param sha the name of the hash function for hashing the input (e.g. "SHA-256")
* @param mgf the name of the mask generating function (typically "MGF1")
@@ -85,55 +87,37 @@ public class RsaPssTest {
/**
* Tries encoding and decoding of RSASSA-PSS keys generated with RSASSA-PSS.
- *
+ *
* RSASSA-PSS keys contain the PSSParameters, hence their encodings are
* somewhat different than plain RSA keys.
*/
- @NoPresubmitTest(
- providers = {ProviderType.OPENJDK},
- bugs = {"b/120406853"}
- )
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testEncodeDecodePublic() throws Exception {
int keySizeInBits = 2048;
- PublicKey pub;
- try {
- KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSASSA-PSS");
- keyGen.initialize(keySizeInBits);
- KeyPair keypair = keyGen.genKeyPair();
- pub = keypair.getPublic();
- } catch (NoSuchAlgorithmException ex) {
- System.out.println("Key generation for RSASSA-PSS is not supported.");
- return;
- }
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSASSA-PSS", EXPECTED_PROVIDER_NAME);
+ keyGen.initialize(keySizeInBits);
+ KeyPair keypair = keyGen.genKeyPair();
+ PublicKey pub = keypair.getPublic();
byte[] encoded = pub.getEncoded();
assertEquals(
"The test assumes that the public key is in X.509 format", "X.509", pub.getFormat());
- System.out.println("Generated RSA-PSS key");
- System.out.println(TestUtil.bytesToHex(encoded));
KeyFactory kf = KeyFactory.getInstance("RSASSA-PSS");
X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded);
kf.generatePublic(spec);
-
+
// Tries to generate another pair or keys. This time the generator is given an
// RSAKeyGenParameterSpec containing the key size an the PSS parameters.
String sha = "SHA-256";
String mgf = "MGF1";
int saltLength = 20;
- try {
- RSAKeyGenParameterSpec params =
- getPssAlgorithmParameters(keySizeInBits, sha, mgf, sha, saltLength);
- KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSASSA-PSS");
- keyGen.initialize(params);
- KeyPair keypair = keyGen.genKeyPair();
- pub = keypair.getPublic();
- } catch (NoSuchAlgorithmException | NoSuchMethodException ex) {
- System.out.println("Key generation for RSASSA-PSS is not supported.");
- return;
- }
+ RSAKeyGenParameterSpec params =
+ getPssAlgorithmParameters(keySizeInBits, sha, mgf, sha, saltLength);
+ KeyPairGenerator keyGen1 = KeyPairGenerator.getInstance("RSASSA-PSS", EXPECTED_PROVIDER_NAME);
+ keyGen1.initialize(params);
+ KeyPair keypair1 = keyGen1.genKeyPair();
+ pub = keypair1.getPublic();
byte[] encoded2 = pub.getEncoded();
- System.out.println("Generated RSA-PSS key with PSS parameters");
- System.out.println(TestUtil.bytesToHex(encoded2));
X509EncodedKeySpec spec2 = new X509EncodedKeySpec(encoded2);
kf.generatePublic(spec2);
}
@@ -171,14 +155,8 @@ public class RsaPssTest {
kf = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(TestUtil.hexToBytes(pubKey));
PublicKey key = kf.generatePublic(x509keySpec);
- Signature verifier;
- try {
- verifier = Signature.getInstance(algorithm);
- verifier.initVerify(key);
- } catch (NoSuchAlgorithmException ex) {
- System.out.println("Unsupported algorithm:" + algorithm);
- return;
- }
+ Signature verifier = Signature.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ verifier.initVerify(key);
AlgorithmParameters params = verifier.getParameters();
if (params == null) {
// No defaults are specified. This is a good choice since this avoid
@@ -234,6 +212,7 @@ public class RsaPssTest {
* PSSParameters explicitly, and does not default to weak behaviour.
*/
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testDefaults() throws Exception {
testDefaultForAlgorithm("SHA1withRSAandMGF1", "SHA-1", "MGF1", "SHA-1", 20, 1);
testDefaultForAlgorithm("SHA224withRSAandMGF1", "SHA-224", "MGF1", "SHA-224", 28, 1);
@@ -249,7 +228,7 @@ public class RsaPssTest {
testDefaultForAlgorithm("SHA3-384withRSAandMGF1", "SHA3-384", "MGF1", "SHA3-384", 48, 1);
testDefaultForAlgorithm("SHA3-512withRSAandMGF1", "SHA3-512", "MGF1", "SHA3-512", 64, 1);
}
-
+
/** Convenience mehtod to get a String from a JsonObject */
protected static String getString(JsonObject object, String name) throws Exception {
return object.get(name).getAsString();
@@ -265,7 +244,7 @@ public class RsaPssTest {
* Oracle previously specified that algorithm names for RSA-PSS are strings like
* "SHA256WITHRSAandMGF1".
* See http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html
- * These algorithm names fail to specify the hash function for the MGF. A cleaner solution
+ * These algorithm names fail to specify the hash function for the MGF. A cleaner solution
* in jdk11 is to use the algorithm name "RSASSA-PSS" and specify the parameters separately.
* This function simply attempts to return an algorithm name that works.
*
@@ -275,7 +254,7 @@ public class RsaPssTest {
*/
protected static String getAlgorithmName(JsonObject group) throws Exception {
try {
- Signature.getInstance("RSASSA-PSS");
+ Signature.getInstance("RSASSA-PSS", EXPECTED_PROVIDER_NAME);
return "RSASSA-PSS";
} catch (NoSuchAlgorithmException ex) {
// RSASSA-PSS is not known. Try the other option.
@@ -385,15 +364,14 @@ public class RsaPssTest {
// the minor number if only the test vectors (but not the format) changes.
// Versions meant for distribution have no status.
final String expectedVersion = "0.6";
- JsonObject test = JsonUtil.getTestVectors(filename);
+ JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename);
String generatorVersion = getString(test, "generatorVersion");
- if (!generatorVersion.equals(expectedVersion)) {
- System.out.println(
+ assertFalse(
"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;
@@ -407,9 +385,9 @@ public class RsaPssTest {
Signature verifier = null;
try {
key = getPublicKey(group, paramsIncluded);
- verifier = Signature.getInstance(algorithm);
+ verifier = Signature.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
if (!paramsIncluded) {
- PSSParameterSpec pssParams = getPSSParams(group);
+ PSSParameterSpec pssParams = getPSSParams(group);
verifier.setParameter(pssParams);
}
} catch (GeneralSecurityException ex) {
@@ -417,10 +395,10 @@ public class RsaPssTest {
skippedKeys++;
skippedAlgorithms.add(algorithm);
} else {
- System.out.println("Failed to generate verifier for " + algorithm + ex);
+ Log.e(TAG, "Failed to generate verifier for " + algorithm + ex);
errors++;
}
- continue;
+ throw ex;
}
for (JsonElement t : group.getAsJsonArray("tests")) {
cntTests++;
@@ -444,7 +422,7 @@ public class RsaPssTest {
} 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(
+ Log.e(TAG,
"Signature verification throws "
+ ex.toString()
+ " "
@@ -461,7 +439,7 @@ public class RsaPssTest {
if (reason != null) {
comment = " exception:" + reason;
}
- System.out.println(
+ Log.e(TAG,
"Valid signature not verified. "
+ filename
+ " tcId:"
@@ -471,7 +449,7 @@ public class RsaPssTest {
+ comment);
errors++;
} else if (verified && result.equals("invalid")) {
- System.out.println(
+ Log.e(TAG,
"Invalid signature verified. "
+ filename
+ " tcId:"
@@ -488,7 +466,7 @@ public class RsaPssTest {
// Prints some information if tests were skipped. This avoids giving
// the impression that algorithms are supported.
if (skippedKeys > 0 || verifiedTests == 0) {
- System.out.println(
+ Log.d(TAG,
"File:"
+ filename
+ " number of skipped keys:"
@@ -496,7 +474,7 @@ public class RsaPssTest {
+ " verified signatures:"
+ verifiedTests);
for (String s : skippedAlgorithms) {
- System.out.println("Skipped algorithms " + s);
+ Log.d(TAG, "Skipped algorithms " + s);
}
}
@@ -509,47 +487,50 @@ public class RsaPssTest {
}
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testRsaPss2048Sha256() throws Exception {
- testRsaPss("rsa_pss_2048_sha256_mgf1_32_test.json", true, false);
+ testRsaPss("rsa_pss_2048_sha256_mgf1_32_test.json", false, false);
}
- @NoPresubmitTest(
- providers = {ProviderType.BOUNCY_CASTLE},
- bugs = {"b/111634359"}
- )
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testRsaPss3072Sha256() throws Exception {
- testRsaPss("rsa_pss_3072_sha256_mgf1_32_test.json", true, false);
+ testRsaPss("rsa_pss_3072_sha256_mgf1_32_test.json", false, false);
}
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testRsaPss4096Sha256() throws Exception {
- testRsaPss("rsa_pss_4096_sha256_mgf1_32_test.json", true, false);
+ testRsaPss("rsa_pss_4096_sha256_mgf1_32_test.json", false, false);
}
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testRsaPss4096Sha512() throws Exception {
- testRsaPss("rsa_pss_4096_sha512_mgf1_32_test.json", true, false);
+ testRsaPss("rsa_pss_4096_sha512_mgf1_32_test.json", false, false);
}
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testRsaPss2048Sha256NoSalt() throws Exception {
- testRsaPss("rsa_pss_2048_sha256_mgf1_0_test.json", true, false);
+ testRsaPss("rsa_pss_2048_sha256_mgf1_0_test.json", false, false);
}
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testRsaPss2048Sha512_224() throws Exception {
- testRsaPss("rsa_pss_2048_sha512_256_mgf1_28_test.json", true, false);
+ testRsaPss("rsa_pss_2048_sha512_256_mgf1_28_test.json", false, false);
}
@Test
+ @Ignore //TODO Reverify after b/215319125 is fixed.
public void testRsaPss2048Sha512_256() throws Exception {
- testRsaPss("rsa_pss_2048_sha512_256_mgf1_32_test.json", true, false);
+ testRsaPss("rsa_pss_2048_sha512_256_mgf1_32_test.json", false, false);
}
// BouncyCastle and Conscrypt do not support RSA-PSS Parameters in the
// encoding of the key. jdk11 should support this, but as long as
- // testEncodeDecodePublic fails it makes no sense to try this test.
+ // testEncodeDecodePublic fails it makes no sense to try this test.
/*
@ExcludedTest(
providers = {ProviderType.BOUNCY_CASTLE, ProviderType.CONSCRYPT},
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..d41bb98 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,43 @@ 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,
+ boolean isStrongBox) throws Exception {
+ KeyStore keyStore = KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey,
+ new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN |
+ KeyProperties.PURPOSE_VERIFY)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build());
+ return (PrivateKey) keyStore.getKey(KEY_ALIAS_1, null);
+ }
+
static final RSAPublicKeySpec RSA_KEY1 =
new RSAPublicKeySpec(
new BigInteger(
@@ -1083,6 +1111,14 @@ public class RsaSignatureTest {
@Test
public void testBasic() throws Exception {
+ testBasic(false);
+ }
+ @Test
+ public void testBasic_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testBasic(true);
+ }
+ private void testBasic(boolean isStrongBox) throws Exception {
String algorithm = "SHA256WithRSA";
String hashAlgorithm = "SHA-256";
String message = "Hello";
@@ -1095,29 +1131,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, isStrongBox));
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 +1157,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 +1174,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 +1222,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 +1236,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);
}
/**
@@ -1245,6 +1265,14 @@ public class RsaSignatureTest {
*/
@Test
public void testFaultySigner() throws Exception {
+ testFaultySigner(false);
+ }
+ @Test
+ public void testFaultySigner_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testFaultySigner(true);
+ }
+ private void testFaultySigner(boolean isStrongBox) throws Exception {
BigInteger e = new BigInteger("65537");
BigInteger d = new BigInteger(
"1491581187972832788084570222215155297353839087630599492610691218"
@@ -1280,30 +1308,50 @@ 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, isStrongBox));
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)
+ .setIsStrongBoxBacked(isStrongBox)
+ .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 +1359,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"