aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-02-01 11:05:09 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-02-01 11:05:09 +0000
commite6f6c55b64d61e176809e6df121a5aaaeb26160f (patch)
treeac677ca5b94f464f7a98786ab625d1fe6e6d489b
parentce01dc9a6d7932f5e3778ad053566ae9a31b65be (diff)
parente93c839d9f7988b54a3f5e1e33809e69f14c0163 (diff)
downloadwycheproof-busytown-mac-infra-release.tar.gz
Snap for 9550355 from e93c839d9f7988b54a3f5e1e33809e69f14c0163 to sdk-releaseplatform-tools-34.0.1platform-tools-34.0.0platform-tools-33.0.4busytown-mac-infra-release
Change-Id: I126e2be26376683809ee836241dcd5a63f00e3bb
-rw-r--r--Android.bp1
-rw-r--r--java/com/google/security/wycheproof/testcases/EcdhTest.java2
-rw-r--r--keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java25
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/AesGcmTest.java292
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java49
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java38
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java91
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java237
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java17
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java23
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java18
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java23
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java37
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java46
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java15
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java114
-rw-r--r--keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java28
17 files changed, 832 insertions, 224 deletions
diff --git a/Android.bp b/Android.bp
index 8f8b1a6..a68f02f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -76,6 +76,7 @@ java_import {
name: "wycheproof-gson",
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
index 8bb774e..d174cbf 100644
--- a/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java
+++ b/keystore-cts/java/android/keystore/cts/util/KeyStoreUtil.java
@@ -13,7 +13,10 @@
*/
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;
@@ -84,6 +87,28 @@ public class KeyStoreUtil {
}
}
+ 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 isStrongBoxSupportDigest(String digest) {
+ return digest.equalsIgnoreCase("sha-1")
+ || digest.equalsIgnoreCase("sha-256");
+ }
+
+ public static boolean isStrongBoxSupportKeySize(int keySize) {
+ return keySize == 2048;
+ }
+
public static X509Certificate createCertificate(
KeyPair keyPair, X500Principal subject, X500Principal issuer)
throws OperatorCreationException, CertificateException, IOException {
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 8a574f6..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;
@@ -41,9 +46,6 @@ 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.
@@ -70,8 +72,12 @@ public class AesGcmTest {
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);
+ }
}
}
@@ -80,7 +86,7 @@ public class AesGcmTest {
KeyStoreUtil.cleanUpKeyStore();
}
- private SecretKey setKeystoreEntry(String alias, SecretKeySpec key)
+ private SecretKey setKeystoreEntry(String alias, SecretKeySpec key, boolean isStrongBox)
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
keyStore.setEntry(
alias,
@@ -89,15 +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;
@@ -110,6 +127,7 @@ public class AesGcmTest {
final int nonceLengthInBits;
final int tagLengthInBits;
final String alias;
+ final String alias_sb;
public GcmTestVector(
String message,
@@ -129,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";
}
};
@@ -215,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, getKey(test.alias), test.parameters);
+ getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.ENCRYPT_MODE,
+ test, isStrongBox);
} catch (InvalidKeyException | InvalidAlgorithmParameterException ex) {
// Not supported
continue;
@@ -236,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, getKey(test.alias), 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));
@@ -248,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, getKey(test.alias), 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);
@@ -275,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, getKey(test.alias), 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);
@@ -299,7 +342,8 @@ public class AesGcmTest {
// Simple test that a modified ciphertext fails.
ptBuffer.clear();
- cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters);
+ cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE, test,
+ isStrongBox);
cipher.updateAAD(empty);
cipher.updateAAD(test.aad);
cipher.updateAAD(new byte[1]);
@@ -333,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, getKey(test.alias), 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);
@@ -366,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, getKey(test.alias), 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 {
@@ -392,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, getKey(test.alias), 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, getKey(test.alias), 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);
}
@@ -408,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, getKey(test.alias), 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);
@@ -421,7 +497,8 @@ public class AesGcmTest {
// Decryption
ctBuffer.flip();
- cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), 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);
@@ -433,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, getKey(test.alias), 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);
@@ -450,7 +535,8 @@ public class AesGcmTest {
// Decryption
ByteBuffer decrypted = ByteBuffer.wrap(backingArray);
ctBuffer.flip();
- cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), 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));
@@ -460,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.
@@ -470,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.");
}
@@ -542,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++) {
@@ -552,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.");
}
@@ -656,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, getKey(test.alias), 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);
@@ -670,7 +780,8 @@ public class AesGcmTest {
// Decryption
ctBuffer.flip();
ctBuffer = ctBuffer.asReadOnlyBuffer();
- cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), 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);
@@ -686,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();
@@ -698,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, getKey(test.alias), test.parameters);
cipher.updateAAD(test.aad);
cipher.doFinal(ptBuffer, ctBuffer);
assertEquals(test.ctHex, TestUtil.byteBufferToHex(ctBuffer));
@@ -708,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, getKey(test.alias), 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));
@@ -717,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, getKey(test.alias), test.parameters);
cipher.updateAAD(test.aad);
try {
cipher.doFinal(ptBuffer, ctBuffer);
@@ -734,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, getKey(test.alias), test.parameters);
+ cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
cipher.updateAAD(test.aad);
try {
cipher.doFinal(ctBuffer, decrypted);
@@ -751,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, getKey(test.alias), test.parameters);
int outputSize = cipher.getOutputSize(test.pt.length);
ByteBuffer ctBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(empty);
@@ -770,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, getKey(test.alias), test.parameters);
int outputSize = cipher.getOutputSize(test.ct.length);
ByteBuffer ptBuffer = ByteBuffer.allocate(outputSize);
cipher.updateAAD(empty);
@@ -787,7 +932,8 @@ public class AesGcmTest {
// Simple test that a modified ciphertext fails.
ctBuffer.flip();
ptBuffer.clear();
- cipher.init(Cipher.DECRYPT_MODE, getKey(test.alias), test.parameters);
+ cipher = getInitializedCipherInstance("AES/GCM/NoPadding", Cipher.DECRYPT_MODE,
+ test, isStrongBox);
cipher.updateAAD(empty);
cipher.updateAAD(test.aad);
cipher.updateAAD(new byte[1]);
@@ -822,7 +968,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*/ false);
} catch (Exception e) {
fail("Failed to set secret key entry in KeyStore.");
}
@@ -851,7 +997,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*/ false);
} catch (Exception e) {
fail("Failed to set secret key entry in KeyStore.");
}
@@ -907,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.");
}
@@ -939,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");
@@ -946,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.");
}
@@ -967,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 d0f85d4..85d84d7 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/CipherInputStreamTest.java
@@ -71,13 +71,15 @@ public class CipherInputStreamTest {
return bytes;
}
- static SecretKey randomKey(String algorithm, String alias, int keySizeInBytes) throws Exception {
+ 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);
@@ -103,9 +105,9 @@ public class CipherInputStreamTest {
@SuppressWarnings("InsecureCryptoUsage")
public TestVector(
String algorithm, String alias, int keySize,
- int ivSize, int tagSize, int ptSize, int aadSize) throws Exception {
+ int ivSize, int tagSize, int ptSize, int aadSize, boolean isStrongBox) throws Exception {
this.algorithm = algorithm;
- this.key = randomKey(algorithm, alias, keySize);
+ this.key = randomKey(algorithm, alias, keySize, isStrongBox);
this.params = randomParameters(algorithm, ivSize, tagSize);
this.pt = randomBytes(ptSize);
this.aad = randomBytes(aadSize);
@@ -122,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) {
@@ -133,7 +136,7 @@ public class CipherInputStreamTest {
String keyAlias = "Key-" + keySize + "-" + ivSize + "-" + tagSize +
"-" + ptSize + "-" + aadSize;
result.add(new TestVector(algorithm, keyAlias, keySize,
- ivSize, tagSize, ptSize, aadSize));
+ ivSize, tagSize, ptSize, aadSize, isStrongBox));
}
}
}
@@ -240,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);
}
@@ -271,13 +292,22 @@ 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);
}
@@ -293,7 +323,8 @@ public class CipherInputStreamTest {
final int[] ptSizes = {0, 8, 16, 65, 8100};
final int[] aadSizes = {0, 8, 24};
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 bf863fe..39a6044 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/CipherOutputStreamTest.java
@@ -71,13 +71,15 @@ public class CipherOutputStreamTest {
return bytes;
}
- static SecretKey randomKey(String algorithm, String alias, int keySizeInBytes) throws Exception{
+ 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);
@@ -103,9 +105,9 @@ public class CipherOutputStreamTest {
public TestVector(
String algorithm, String alias, int keySize,
- int ivSize, int tagSize, int ptSize, int aadSize) throws Exception {
+ int ivSize, int tagSize, int ptSize, int aadSize, boolean isStrongBox) throws Exception {
this.algorithm = algorithm;
- this.key = randomKey(algorithm, alias, keySize);
+ this.key = randomKey(algorithm, alias, keySize, isStrongBox);
this.params = randomParameters(algorithm, ivSize, tagSize);
this.pt = randomBytes(ptSize);
this.aad = randomBytes(aadSize);
@@ -122,7 +124,8 @@ 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>();
@@ -133,7 +136,7 @@ public class CipherOutputStreamTest {
for (int aadSize : aadSizes) {
String keyAlias = "Key" + counter++;
result.add(new TestVector(algorithm, keyAlias, keySize,
- ivSize, tagSize, ptSize, aadSize));
+ ivSize, tagSize, ptSize, aadSize, isStrongBox));
}
}
}
@@ -213,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;
@@ -233,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;
@@ -258,7 +279,8 @@ public class CipherOutputStreamTest {
final int[] ptSizes = {8, 16, 65, 8100};
final int[] aadSizes = {0, 8, 24};
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 dae5bde..f57ad29 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdhTest.java
@@ -48,7 +48,6 @@ import android.security.keystore.KeyProtection;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyGenParameterSpec;
import android.keystore.cts.util.KeyStoreUtil;
-import android.keystore.cts.util.TestUtils;
import androidx.test.InstrumentationRegistry;
@@ -104,21 +103,25 @@ public class EcdhTest {
KeyStoreUtil.cleanUpKeyStore();
}
- private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey)
+ 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) throws Exception {
+ 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)
- .build();
+ .setAlgorithmParameterSpec(ecSpec)
+ .setIsStrongBoxBacked(isStrongBox)
+ .build();
keyGen.initialize(ecKeySpec);
return keyGen.generateKeyPair();
@@ -540,8 +543,18 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
/** Checks that key agreement using ECDH works. */
@Test
public void testBasic() throws Exception {
- KeyPair keyPairA = generateECKeyPair(KEY_ALIAS_2, new ECGenParameterSpec("secp256r1"));
- KeyPair keyPairB = generateECKeyPair(KEY_ALIAS_3, new ECGenParameterSpec("secp256r1"));
+ 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", EXPECTED_PROVIDER_NAME);
KeyAgreement kaB = KeyAgreement.getInstance("ECDH", EXPECTED_PROVIDER_NAME);
@@ -583,8 +596,13 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
*/
@SuppressWarnings("InsecureCryptoUsage")
public void testModifiedPublic(String algorithm) throws Exception {
+ 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"));
+ 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(pair.getPrivate());
@@ -619,8 +637,13 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
*/
@SuppressWarnings("InsecureCryptoUsage")
public void testModifiedPublicSpec(String algorithm) throws Exception {
+ 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"));
+ 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(pair.getPrivate());
@@ -658,6 +681,11 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
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
@@ -669,6 +697,11 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
public void testEcdhModifiedPublicSpec() throws Exception {
testModifiedPublicSpec("ECDH");
}
+ @Test
+ public void testEcdhModifiedPublicSpec_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testModifiedPublicSpec("ECDH", true);
+ }
@Test
@Ignore // ECDHC algorithm is not supported in AndroidKeyStore
@@ -684,12 +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 {
+ 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")).getPrivate();
+ new ECGenParameterSpec("secp256r1"), isStrongBox)
+ .getPrivate();
ECPublicKey pub = (ECPublicKey) generateECKeyPair(KEY_ALIAS_3,
- new ECGenParameterSpec("secp256r1")).getPublic();
+ new ECGenParameterSpec("secp256r1"), isStrongBox)
+ .getPublic();
// Get the shared secret for the unmodified keys.
ka.init(priv);
ka.doPhase(pub, true);
@@ -728,6 +769,11 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
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.
@@ -750,6 +796,9 @@ 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");
keyGen.initialize(spec);
@@ -764,11 +813,11 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
// This Public key is not pair of priv1, but it is required to create KeyPair to import into
// AndroidKeyStore, So using dummy public key.
PublicKey pub1 = kf.generatePublic(EC_VALID_PUBLIC_KEY.getX509EncodedKeySpec());
- ka.init(getKeystorePrivateKey(pub1, priv1));
+ ka.init(getKeystorePrivateKey(pub1, priv1, isStrongBox));
ka.doPhase(pub, true);
byte[] shared1 = ka.generateSecret();
PrivateKey priv2 = kf.generatePrivate(spec2);
- ka.init(getKeystorePrivateKey(pub1, priv2));
+ 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
@@ -779,21 +828,17 @@ public static final EcPublicKeyTestVector EC_VALID_PUBLIC_KEY =
@Test
public void testNistCurveLargePrivateKey() throws Exception {
- Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- /**
- * Software emulation of ECDH / AGREE_KEY function is performed by Keystore when the underlying
- * device is a Keymaster implementation rather than KeyMint. However, this emulated support does
- * not (yet) support imported ECDH keys, so skip the test if this is the case (b/216434270).
- */
- assumeTrue("This test can only test with keymint version 1 and above",
- TestUtils.getFeatureVersionKeystore(context) >= KeyStoreUtil.KM_VERSION_KEYMINT_1);
-
testLargePrivateKey(EcUtil.getNistP224Params());
testLargePrivateKey(EcUtil.getNistP256Params());
testLargePrivateKey(EcUtil.getNistP384Params());
// This test failed before CVE-2017-10176 was fixed.
testLargePrivateKey(EcUtil.getNistP521Params());
}
+ @Test
+ public void testNistCurveLargePrivateKey_StrongBox() throws Exception {
+ KeyStoreUtil.assumeStrongBox();
+ testLargePrivateKey(EcUtil.getNistP256Params(), true);
+ }
@Test
@Ignore // Brainpool curves are not supported in AndroidKeyStore.
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 3da6d83..3a758f3 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/EcdsaTest.java
@@ -17,6 +17,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.math.BigInteger;
+import java.nio.ByteBuffer;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
@@ -31,6 +32,7 @@ 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.Ignore;
@@ -49,21 +51,24 @@ import android.keystore.cts.util.KeyStoreUtil;
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)
- throws Exception {
+ 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);
+ KeyStore keyStore = KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey,
+ keyProtection);
return (PrivateKey) keyStore.getKey(KEY_ALIAS_1, null);
}
@@ -86,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.
*/
@@ -116,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) {
@@ -134,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);
}
@@ -151,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.
@@ -181,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(getKeystorePrivateKey(pub, priv));
+ signer.initSign(getKeystorePrivateKey(pub, priv, isStrongBox));
signer.update(messageBytes);
byte[] signature = signer.sign();
verifier.initVerify(pub);
@@ -200,13 +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 {
+ 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(ecParams);
+ 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
@@ -214,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, EXPECTED_PROVIDER_NAME);
- signer.initSign(getKeystorePrivateKey(keyPair.getPublic(), keyPair.getPrivate()));
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);
}
@@ -238,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++;
@@ -270,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);
}
@@ -289,16 +348,116 @@ public class EcdsaTest {
}
@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());
+ public void testBiasSecp224r1() throws Exception {
+ testBias("SHA224WithECDSA", "secp224r1");
+ }
+
+ @Test
+ public void testBiasSecp256r1() throws Exception {
+ testBias("SHA256WithECDSA", "secp256r1");
+ }
+
+ @Test
+ public void testBiasSecp384r1() throws Exception {
+ testBias("SHA384WithECDSA", "secp384r1");
+ }
+
+ @Test
+ public void testBiasSecp521r1() throws Exception {
+ testBias("SHA512WithECDSA", "secp521r1");
+ }
+
+ @Test
+ public void testBiasSecp521r1_StrongBox() throws Exception {
+ testBias("SHA256WithECDSA", "secp256r1", true);
}
@Test
@Ignore // Brainpool curve are not supported in AndroidKeyStore
- public void testBiasBrainpoolCurve() throws Exception {
- testBias("SHA256WithECDSA", "brainpoolP256r1", EcUtil.getBrainpoolP256r1Params());
+ 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 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.
+ */
+ 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");
+ keyGen.initialize(new ECGenParameterSpec(curve));
+ KeyPair keyPair = keyGen.generateKeyPair();
+ 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++) {
+ // This is the function call that is tested by this test.
+ signer.initSign(priv, null);
+ signer.update(message[i]);
+ byte[] signature = signer.sign();
+ BigInteger r = extractR(signature);
+ assertTrue("Same r computed twice", rSet.add(r));
+ }
+ }
+
+ @Test
+ 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 91a0d2c..c4a6b63 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonAeadTest.java
@@ -89,7 +89,7 @@ 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")) {
@@ -99,6 +99,7 @@ public class JsonAeadTest {
.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(KEY_ALIAS_1, null);
@@ -152,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
@@ -203,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.
@@ -234,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;
@@ -265,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 e0c5b27..d6dae08 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonCipherTest.java
@@ -74,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");
@@ -89,6 +90,7 @@ public class JsonCipherTest {
.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);
@@ -124,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.
@@ -164,7 +170,7 @@ 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.
@@ -195,7 +201,7 @@ 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) {
errors++;
continue;
@@ -238,9 +244,16 @@ public class JsonCipherTest {
}
@Test
- public void testAesCbcPkcs5() throws Exception {
+ 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 f5b97f8..779dc42 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonEcdhTest.java
@@ -88,6 +88,9 @@ public class JsonEcdhTest {
* ...
**/
public void testEcdhComp(String filename) throws Exception {
+ 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.
@@ -122,6 +125,7 @@ public class JsonEcdhTest {
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);
@@ -156,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")) {
@@ -172,29 +178,31 @@ public class JsonEcdhTest {
}
}
assertEquals(0, errors);
- assertEquals(numTests, passedTests);
+ assertEquals(numTests, passedTests + rejectedTests);
}
@Test
- @Ignore //TODO Reverify after bug b/215175472 is fixed.
public void testSecp224r1() throws Exception {
testEcdhComp("ecdh_secp224r1_test.json");
}
@Test
- @Ignore //TODO Reverify after bug b/215175472 is fixed.
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 {
testEcdhComp("ecdh_secp384r1_test.json");
}
@Test
- @Ignore //TODO Reverify after bug b/215175472 is fixed.
public void testSecp521r1() throws Exception {
testEcdhComp("ecdh_secp521r1_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 c1dddc5..d813d02 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonMacTest.java
@@ -72,8 +72,8 @@ 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 Exception {
+ 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")) {
@@ -91,7 +91,9 @@ public class JsonMacTest {
// full length tag and truncates it. The drawback of having to truncate tags is that
// the caller has to compare truncated tags during verification.
KeyStore keyStore = KeyStoreUtil.saveSecretKeyToKeystore(KEY_ALIAS_1, keySpec,
- new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build());
+ 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);
@@ -110,6 +112,9 @@ 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(this.getClass(), filename);
String algorithm = test.get("algorithm").getAsString();
@@ -130,14 +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;
- computedTag = computeMac(algorithm, key, msg, tagSize);
+ computedTag = computeMac(algorithm, key, msg, tagSize, isStrongBox);
boolean eq = arrayEquals(expectedTag, computedTag);
if (result.equals("invalid")) {
@@ -264,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 {
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 58da530..bbea28a 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/JsonSignatureTest.java
@@ -54,13 +54,12 @@ public class JsonSignatureTest {
KeyStoreUtil.cleanUpKeyStore();
}
- private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey)
- throws Exception {
+ private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey, String digest,
+ boolean isStrongBox) throws Exception {
KeyProtection keyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
- .setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512)
+ .setDigests(digest)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setIsStrongBoxBacked(isStrongBox)
.build();
KeyStore keyStore =
KeyStoreUtil.saveKeysToKeystore(KEY_ALIAS_1, pubKey, privKey, keyProtection);
@@ -263,16 +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);
PrivateKey intermediateKey = kf.generatePrivate(keySpec);
X509EncodedKeySpec x509keySpec = new X509EncodedKeySpec(pubEncoded);
PublicKey pubKey = kf.generatePublic(x509keySpec);
- return getKeystorePrivateKey(pubKey, intermediateKey);
+ return getKeystorePrivateKey(pubKey, intermediateKey, digest, isStrongBox);
} else {
throw new NoSuchAlgorithmException("Algorithm " + algorithm + " is not supported");
}
@@ -432,8 +433,13 @@ 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 {
+ boolean allowSkippingKeys, boolean isStrongBox) throws Exception {
JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename);
int cntTests = 0;
int errors = 0;
@@ -442,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;
@@ -466,13 +472,17 @@ public class JsonSignatureTest {
signer.update(message);
String sig = TestUtil.bytesToHex(signer.sign());
if (!sig.equals(expectedSig)) {
+ android.util.Log.e("JsonSignatureTest", "Signature mismatch error for test id " + tcid);
errors++;
} else {
cntTests++;
}
} catch (InvalidKeyException | SignatureException ex) {
if (result.equals("valid")) {
- errors++;
+ android.util.Log.e("JsonSignatureTest", "Unexpected exception for test id " + tcid, ex);
+ if (!isStrongBox) {
+ errors++;
+ }
}
}
}
@@ -725,9 +735,14 @@ public class JsonSignatureTest {
// 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 {
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 171af58..3a42761 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/MacTest.java
@@ -46,10 +46,12 @@ public class MacTest {
KeyStoreUtil.cleanUpKeyStore();
}
- private static Key getKeyStoreSecretKey(byte[] keyMaterial, String algorithm) throws Exception {
+ 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).build());
+ new SecretKeySpec(keyMaterial, algorithm),
+ new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
+ .setIsStrongBoxBacked(isStrongBox).build());
return keyStore.getKey(KEY_ALIAS_1, null);
}
@@ -192,6 +194,9 @@ 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, EXPECTED_PROVIDER_NAME);
} catch (NoSuchAlgorithmException ex) {
@@ -200,7 +205,7 @@ public class MacTest {
byte[] key = new byte[keySize];
SecureRandom rand = new SecureRandom();
rand.nextBytes(key);
- testUpdate(algorithm, getKeyStoreSecretKey(key, algorithm));
+ testUpdate(algorithm, getKeyStoreSecretKey(key, algorithm, isStrongBox));
}
@Test
@@ -219,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);
}
@@ -298,10 +310,15 @@ 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 = getKeyStoreSecretKey(TestUtil.hexToBytes(keyhex), algorithm);
+ Key key = getKeyStoreSecretKey(TestUtil.hexToBytes(keyhex), algorithm, isStrongBox);
byte[] bytes = message.getBytes(UTF_8);
byte[] mac = null;
try {
@@ -331,18 +348,29 @@ public class MacTest {
@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);
}
@Test
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 32a78b3..ed291e0 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaEncryptionTest.java
@@ -77,7 +77,8 @@ 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());
@@ -90,6 +91,7 @@ public class RsaEncryptionTest {
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);
}
@@ -143,6 +145,10 @@ public class RsaEncryptionTest {
*/
@SuppressWarnings("InsecureCryptoUsage")
public void testDecryption(String filename) throws Exception {
+ 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
@@ -157,7 +163,7 @@ public class RsaEncryptionTest {
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();
@@ -209,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 4fd12cb..ed4987c 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaOaepTest.java
@@ -15,6 +15,7 @@ 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;
@@ -57,7 +58,8 @@ public class RsaOaepTest {
}
private static PrivateKey saveKeyPairToKeystoreAndReturnPrivateKey(PublicKey pubKey,
- PrivateKey privKey) throws Exception {
+ 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 |
@@ -65,9 +67,8 @@ public class RsaOaepTest {
KeyProperties.PURPOSE_DECRYPT)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
- .setDigests(KeyProperties.DIGEST_SHA1, KeyProperties.DIGEST_SHA224,
- KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA384,
- KeyProperties.DIGEST_SHA512)
+ .setDigests(digest, mgfDigest)
+ .setIsStrongBoxBacked(isStrongBox)
.build())
.getKey(KEY_ALIAS_1, null);
}
@@ -162,7 +163,8 @@ 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"));
@@ -171,7 +173,17 @@ public class RsaOaepTest {
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));
- return saveKeyPairToKeystoreAndReturnPrivateKey(pubKey, intermediateKey);
+ String digest = getString(object, "sha");
+ String mgfDigest = getString(object, "mgfSha");
+ int keysize = object.get("keysize").getAsInt();
+ if (isStrongBox
+ && (!KeyStoreUtil.isStrongBoxSupportDigest(digest)
+ || !KeyStoreUtil.isStrongBoxSupportDigest(mgfDigest)
+ || !KeyStoreUtil.isStrongBoxSupportKeySize(keysize))) {
+ throw new UnsupportedKeyParametersException();
+ }
+ return saveKeyPairToKeystoreAndReturnPrivateKey(pubKey, intermediateKey, digest, mgfDigest,
+ isStrongBox);
}
protected static String getOaepAlgorithmName(JsonObject group) throws Exception {
@@ -185,9 +197,15 @@ public class RsaOaepTest {
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") && !TextUtils.isEmpty(getString(test, "label"))) {
- p = new PSource.PSpecified(getBytes(test, "label"));
+ // p = new PSource.PSpecified(getBytes(test, "label"));
+ throw new UnsupportedKeyParametersException();
}
return new OAEPParameterSpec(sha, mgf, new MGF1ParameterSpec(mgfSha), p);
}
@@ -235,7 +253,17 @@ 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 {
+ if (isStrongBox) {
+ KeyStoreUtil.assumeStrongBox();
+ }
JsonObject test = JsonUtil.getTestVectors(this.getClass(), filename);
// Compares the expected and actual JSON schema of the test vector file.
@@ -258,7 +286,18 @@ public class RsaOaepTest {
int skippedKeys = 0;
for (JsonElement g : test.getAsJsonArray("testGroups")) {
JsonObject group = g.getAsJsonObject();
- PrivateKey key = getPrivateKey(group);
+ PrivateKey key = null;
+ try {
+ key = getPrivateKey(group, isStrongBox);
+ } catch (UnsupportedKeyParametersException e) {
+ skippedKeys++;
+ if (isStrongBox) {
+ continue;
+ }
+ if (!allowSkippingKeys) {
+ throw e;
+ }
+ }
String algorithm = getOaepAlgorithmName(group);
Cipher decrypter = Cipher.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
for (JsonElement t : group.getAsJsonArray("tests")) {
@@ -266,7 +305,13 @@ public class RsaOaepTest {
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");
@@ -296,13 +341,13 @@ public class RsaOaepTest {
String decryptedHex = TestUtil.bytesToHex(decrypted);
if (result.equals("invalid")) {
Log.e(TAG,
- String.format("Invalid ciphertext decrypted. filename:%s tcId:%d expected:%s decrypted:%s\n",
- filename, tcid, messageHex, decryptedHex));
+ String.format("Invalid ciphertext decrypted. filename:%s tcId:%d expected:%s" +
+ " decrypted:%s\n", filename, tcid, messageHex, decryptedHex));
errors++;
} else if (!decryptedHex.equals(messageHex)) {
Log.e(TAG,
- String.format("Incorrect decryption. filename:%s tcId:%d expected:%s decrypted:%s\n",
- filename, tcid, messageHex, decryptedHex));
+ String.format("Incorrect decryption. filename:%s tcId:%d expected:%s" +
+ " decrypted:%s\n", filename, tcid, messageHex, decryptedHex));
errors++;
}
}
@@ -311,118 +356,117 @@ public class RsaOaepTest {
assertEquals(0, errors);
if (skippedKeys > 0) {
Log.d(TAG, "RSAES-OAEP: file:" + filename + " skipped key:" + skippedKeys);
- assertTrue(allowSkippingKeys);
+ assertTrue(!allowSkippingKeys);
} else {
assertEquals(numTests, cntTests);
}
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep2048Sha1Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_2048_sha1_mgf1sha1_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
+ 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);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep2048Sha224Mgf1Sha224() throws Exception {
testOaep("rsa_oaep_2048_sha224_mgf1sha224_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
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
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
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
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep2048Sha384Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_2048_sha384_mgf1sha1_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep2048Sha384Mgf1Sha384() throws Exception {
testOaep("rsa_oaep_2048_sha384_mgf1sha384_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep2048Sha512Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_2048_sha512_mgf1sha1_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep2048Sha512Mgf1Sha512() throws Exception {
testOaep("rsa_oaep_2048_sha512_mgf1sha512_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep3072Sha256Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_3072_sha256_mgf1sha1_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep3072Sha256Mgf1Sha256() throws Exception {
testOaep("rsa_oaep_3072_sha256_mgf1sha256_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep3072Sha512Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_3072_sha512_mgf1sha1_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep3072Sha512Mgf1Sha512() throws Exception {
testOaep("rsa_oaep_3072_sha512_mgf1sha512_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep4096Sha256Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_4096_sha256_mgf1sha1_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep4096Sha256Mgf1Sha256() throws Exception {
testOaep("rsa_oaep_4096_sha256_mgf1sha256_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep4096Sha512Mgf1Sha1() throws Exception {
testOaep("rsa_oaep_4096_sha512_mgf1sha1_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaep4096Sha512Mgf1Sha512() throws Exception {
testOaep("rsa_oaep_4096_sha512_mgf1sha512_test.json", false);
}
@Test
- @Ignore //TODO Reverify after bugs b/229182999 and b/229183581 are fixed.
public void testRsaOaepMisc() throws Exception {
- testOaep("rsa_oaep_misc_test.json", false);
+ testOaep("rsa_oaep_misc_test.json", false);
+ }
+ @Test
+ public void testRsaOaepMisc_StrongBox() throws Exception {
+ testOaep("rsa_oaep_misc_test.json", false, true);
}
}
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 57382f6..d41bb98 100644
--- a/keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java
+++ b/keystore-cts/java/com/google/security/wycheproof/testcases/RsaSignatureTest.java
@@ -57,13 +57,14 @@ public class RsaSignatureTest {
KeyStoreUtil.cleanUpKeyStore();
}
- private static PrivateKey getKeystorePrivateKey(PublicKey pubKey, PrivateKey privKey)
- throws Exception {
+ 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, KeyProperties.DIGEST_SHA512)
+ .setDigests(KeyProperties.DIGEST_SHA256)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .setIsStrongBoxBacked(isStrongBox)
.build());
return (PrivateKey) keyStore.getKey(KEY_ALIAS_1, null);
}
@@ -1110,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";
@@ -1125,7 +1134,7 @@ public class RsaSignatureTest {
Signature signer = Signature.getInstance(algorithm, TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME);
Signature verifier = Signature.getInstance(algorithm,
TestUtil.EXPECTED_CRYPTO_OP_PROVIDER_NAME);
- signer.initSign(getKeystorePrivateKey(pub, priv));
+ signer.initSign(getKeystorePrivateKey(pub, priv, isStrongBox));
signer.update(messageBytes);
byte[] signature = signer.sign();
verifier.initVerify(pub);
@@ -1256,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"
@@ -1296,7 +1313,7 @@ public class RsaSignatureTest {
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(n, e);
PublicKey pubKey = kf.generatePublic(pubKeySpec);
- signer.initSign(getKeystorePrivateKey(pubKey, validPrivKey));
+ signer.initSign(getKeystorePrivateKey(pubKey, validPrivKey, isStrongBox));
signer.update(message);
byte[] signature = signer.sign();
PrivateKey invalidPrivKey = null;
@@ -1318,6 +1335,7 @@ public class RsaSignatureTest {
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.