CTS-test Android Keystore import supports required key sizes.

This explicitly tests that Android Keystore can import keys of all
required sizes:
* AES: 128, 192, 256.
* HMAC: 8 -- 1024 (must be multiple of 8).
* EC: P-224, P-256, P-384, P-521.
* RSA: 512, 768, 1024, 2048, 3072, 4096.

For AES, this also tests that other sizes are not supported.

Bug: 21936191
Change-Id: I923437c86b438e77a2a9027bb9f9fb4742138881
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
index 6cd64b8..5fe3505 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
@@ -62,7 +62,6 @@
 import javax.crypto.Cipher;
 import javax.crypto.Mac;
 import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
 import javax.security.auth.x500.X500Principal;
 
 public class AndroidKeyStoreTest extends AndroidTestCase {
@@ -1888,7 +1887,7 @@
                 0x00, 0x05, (byte) 0xAA, (byte) 0x0A5, (byte) 0xFF, 0x55, 0x0A
         };
 
-        SecretKey expectedSecret = new SecretKeySpec(expectedKey, "AES");
+        SecretKey expectedSecret = new TransparentSecretKey(expectedKey, "AES");
 
         byte[] wrappedExpected = c.wrap(expectedSecret);
 
@@ -2231,15 +2230,15 @@
 
         long testStartTimeMillis = System.currentTimeMillis();
 
-        SecretKey key1 =
-                new SecretKeySpec(HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "AES");
+        SecretKey key1 = new TransparentSecretKey(
+                HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "AES");
         String entryName1 = "test0";
 
-        SecretKey key2 =
-                new SecretKeySpec(HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "AES");
+        SecretKey key2 = new TransparentSecretKey(
+                HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "AES");
 
-        SecretKey key3 =
-                new SecretKeySpec(HexEncoding.decode("33333333333333333333777777777777"), "AES");
+        SecretKey key3 = new TransparentSecretKey(
+                HexEncoding.decode("33333333333333333333777777777777"), "AES");
 
         mKeyStore.load(null);
         int latestImportedEntryNumber = 0;
@@ -2322,14 +2321,14 @@
 
         long testStartTimeMillis = System.currentTimeMillis();
 
-        SecretKey key1 = new SecretKeySpec(
+        SecretKey key1 = new TransparentSecretKey(
                 HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "HmacSHA256");
         String entryName1 = "test0";
 
-        SecretKey key2 = new SecretKeySpec(
+        SecretKey key2 = new TransparentSecretKey(
                 HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "HmacSHA256");
 
-        SecretKey key3 = new SecretKeySpec(
+        SecretKey key3 = new TransparentSecretKey(
                 HexEncoding.decode("33333333333333333333777777777777"), "HmacSHA256");
 
         mKeyStore.load(null);
@@ -2406,7 +2405,7 @@
             try {
                 String digest = TestUtils.getHmacAlgorithmDigest(algorithm);
                 assertNotNull(digest);
-                SecretKeySpec keyBeingImported = new SecretKeySpec(new byte[16], algorithm);
+                SecretKey keyBeingImported = new TransparentSecretKey(new byte[16], algorithm);
 
                 KeyProtection.Builder goodSpec =
                         new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN);
@@ -2457,4 +2456,121 @@
             }
         }
     }
+
+    public void testKeyStore_ImportSupportedSizes_AES() throws Exception {
+        mKeyStore.load(null);
+
+        KeyProtection params = new KeyProtection.Builder(
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .build();
+        String alias = "test1";
+        mKeyStore.deleteEntry(alias);
+        assertFalse(mKeyStore.containsAlias(alias));
+        for (int keySizeBytes = 0; keySizeBytes <= 512 / 8; keySizeBytes++) {
+            int keySizeBits = keySizeBytes * 8;
+            try {
+                KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(
+                        new TransparentSecretKey(new byte[keySizeBytes], "AES"));
+                if (TestUtils.contains(KeyGeneratorTest.AES_SUPPORTED_KEY_SIZES, keySizeBits)) {
+                    mKeyStore.setEntry(alias, entry, params);
+                    SecretKey key = (SecretKey) mKeyStore.getKey(alias, null);
+                    assertEquals("AES", key.getAlgorithm());
+                    assertEquals(keySizeBits, TestUtils.getKeyInfo(key).getKeySize());
+                } else {
+                    mKeyStore.deleteEntry(alias);
+                    assertFalse(mKeyStore.containsAlias(alias));
+                    try {
+                        mKeyStore.setEntry(alias, entry, params);
+                        fail();
+                    } catch (KeyStoreException expected) {}
+                    assertFalse(mKeyStore.containsAlias(alias));
+                }
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for key size " + keySizeBits, e);
+            }
+        }
+    }
+
+    public void testKeyStore_ImportSupportedSizes_HMAC() throws Exception {
+        mKeyStore.load(null);
+
+        KeyProtection params = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build();
+        String alias = "test1";
+        mKeyStore.deleteEntry(alias);
+        assertFalse(mKeyStore.containsAlias(alias));
+        for (String algorithm : KeyGeneratorTest.EXPECTED_ALGORITHMS) {
+            if (!TestUtils.isHmacAlgorithm(algorithm)) {
+                continue;
+            }
+            for (int keySizeBytes = 0; keySizeBytes <= 1024 / 8; keySizeBytes++) {
+                try {
+                    KeyStore.SecretKeyEntry entry = new KeyStore.SecretKeyEntry(
+                            new TransparentSecretKey(new byte[keySizeBytes], algorithm));
+                    if (keySizeBytes > 0) {
+                        mKeyStore.setEntry(alias, entry, params);
+                        SecretKey key = (SecretKey) mKeyStore.getKey(alias, null);
+                        assertEquals(algorithm, key.getAlgorithm());
+                        assertEquals(keySizeBytes * 8, TestUtils.getKeyInfo(key).getKeySize());
+                    } else {
+                        mKeyStore.deleteEntry(alias);
+                        assertFalse(mKeyStore.containsAlias(alias));
+                        try {
+                            mKeyStore.setEntry(alias, entry, params);
+                            fail();
+                        } catch (KeyStoreException expected) {}
+                    }
+                } catch (Throwable e) {
+                    throw new RuntimeException(
+                            "Failed for " + algorithm + " with key size " + (keySizeBytes * 8), e);
+                }
+            }
+        }
+    }
+
+    public void testKeyStore_ImportSupportedSizes_EC() throws Exception {
+        mKeyStore.load(null);
+        KeyProtection params =
+                TestUtils.getMinimalWorkingImportParametersForSigningingWith("SHA256withECDSA");
+        checkKeyPairImportSucceeds(
+                "secp224r1", R.raw.ec_key3_secp224r1_pkcs8, R.raw.ec_key3_secp224r1_cert, params);
+        checkKeyPairImportSucceeds(
+                "secp256r1", R.raw.ec_key4_secp256r1_pkcs8, R.raw.ec_key4_secp256r1_cert, params);
+        checkKeyPairImportSucceeds(
+                "secp384r1", R.raw.ec_key5_secp384r1_pkcs8, R.raw.ec_key5_secp384r1_cert, params);
+        checkKeyPairImportSucceeds(
+                "secp512r1", R.raw.ec_key6_secp521r1_pkcs8, R.raw.ec_key6_secp521r1_cert, params);
+    }
+
+    public void testKeyStore_ImportSupportedSizes_RSA() throws Exception {
+        mKeyStore.load(null);
+        KeyProtection params =
+                TestUtils.getMinimalWorkingImportParametersForSigningingWith("SHA256withRSA");
+        checkKeyPairImportSucceeds(
+                "512", R.raw.rsa_key5_512_pkcs8, R.raw.rsa_key5_512_cert, params);
+        checkKeyPairImportSucceeds(
+                "768", R.raw.rsa_key6_768_pkcs8, R.raw.rsa_key6_768_cert, params);
+        checkKeyPairImportSucceeds(
+                "1024", R.raw.rsa_key3_1024_pkcs8, R.raw.rsa_key3_1024_cert, params);
+        checkKeyPairImportSucceeds(
+                "2048", R.raw.rsa_key8_2048_pkcs8, R.raw.rsa_key8_2048_cert, params);
+        checkKeyPairImportSucceeds(
+                "3072", R.raw.rsa_key7_3072_pksc8, R.raw.rsa_key7_3072_cert, params);
+        checkKeyPairImportSucceeds(
+                "4096", R.raw.rsa_key4_4096_pkcs8, R.raw.rsa_key4_4096_cert, params);
+    }
+
+    private void checkKeyPairImportSucceeds(
+            String alias, int privateResId, int certResId, KeyProtection params) throws Exception {
+        try {
+            mKeyStore.deleteEntry(alias);
+            TestUtils.importIntoAndroidKeyStore(
+                    alias, getContext(), privateResId, certResId, params);
+        } catch (Throwable e) {
+            throw new RuntimeException("Failed for " + alias, e);
+        } finally {
+            try {
+                mKeyStore.deleteEntry(alias);
+            } catch (Exception ignored) {}
+        }
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
index 88d5421..6deaed4 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
@@ -66,7 +66,7 @@
         DEFAULT_KEY_SIZES.put("HmacSHA512", 512);
     }
 
-    private static final int[] AES_SUPPORTED_KEY_SIZES = new int[] {128, 192, 256};
+    static final int[] AES_SUPPORTED_KEY_SIZES = new int[] {128, 192, 256};
 
     public void testAlgorithmList() {
         // Assert that Android Keystore Provider exposes exactly the expected KeyGenerator
@@ -244,7 +244,7 @@
                     assertEquals(0, rng.getOutputSizeBytes());
                 }
             } catch (Throwable e) {
-                throw new RuntimeException("Failed to key size " + i, e);
+                throw new RuntimeException("Failed for key size " + i, e);
             }
         }
     }
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 38905bf..1c4f6ad 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -27,7 +27,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigInteger;
 import java.security.Key;
 import java.security.KeyFactory;
 import java.security.KeyPair;
@@ -53,7 +52,6 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -442,29 +440,13 @@
                     + " Public: " + originalPublicKey.getAlgorithm()
                     + ", private: " + originalPrivateKey.getAlgorithm());
         }
-        String keyAlgorithm = originalPublicKey.getAlgorithm();
-        if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
-            ECParameterSpec publicKeyParams = ((ECKey) originalPublicKey).getParams();
-            ECParameterSpec privateKeyParams = ((ECKey) originalPrivateKey).getParams();
-            assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
-                    publicKeyParams, privateKeyParams);
-        } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
-            BigInteger publicKeyModulus = ((RSAKey) originalPublicKey).getModulus();
-            BigInteger privateKeyModulus = ((RSAKey) originalPrivateKey).getModulus();
-            if (!publicKeyModulus.equals(privateKeyModulus)) {
-                throw new IllegalArgumentException("RSA key pair modulus mismatch."
-                        + " Public (" + publicKeyModulus.bitLength() + " bit): "
-                        + publicKeyModulus.toString(16)
-                        + ", private (" + privateKeyModulus.bitLength() + " bit): "
-                        + privateKeyModulus.toString(16));
-            }
-        } else {
-            throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
-        }
+        assertKeyPairSelfConsistent(originalPublicKey, originalPrivateKey);
 
         KeyPair keystoreBacked = TestUtils.importIntoAndroidKeyStore(
                 alias, originalPrivateKey, originalCert,
                 params);
+        assertKeyPairSelfConsistent(keystoreBacked);
+        assertKeyPairSelfConsistent(keystoreBacked.getPublic(), originalPrivateKey);
         return new ImportedKey(
                 alias,
                 new KeyPair(originalCert.getPublicKey(), originalPrivateKey),
diff --git a/tests/tests/keystore/src/android/keystore/cts/TransparentSecretKey.java b/tests/tests/keystore/src/android/keystore/cts/TransparentSecretKey.java
new file mode 100644
index 0000000..6e74dc0
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/TransparentSecretKey.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.keystore.cts;
+
+import javax.crypto.SecretKey;
+
+/**
+ * {@link SecretKey} which exposes its key material. The two reasons for the existence of this class
+ * are: (1) to help test that classes under test don't assume that all transparent secret keys are
+ * instances of {@link SecretKeySpec}, and (2) because {@code SecretKeySpec} rejects zero-length
+ * key material which is needed in some tests.
+ */
+public class TransparentSecretKey implements SecretKey {
+    private final String mAlgorithm;
+    private final byte[] mKeyMaterial;
+
+    public TransparentSecretKey(byte[] keyMaterial, String algorithm) {
+        mAlgorithm = algorithm;
+        mKeyMaterial = keyMaterial.clone();
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        return mKeyMaterial;
+    }
+
+    @Override
+    public String getFormat() {
+        return "RAW";
+    }
+}