Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.keystore.cts;
     18 
     19 import android.content.Context;
     20 import android.content.pm.PackageManager;
     21 import android.os.SystemProperties;
     22 import android.security.keystore.KeyGenParameterSpec;
     23 import android.security.keystore.KeyInfo;
     24 import android.security.keystore.KeyProperties;
     25 import android.security.keystore.KeyProtection;
     26 import android.test.MoreAsserts;
     27 import junit.framework.Assert;
     28 
     29 import java.io.ByteArrayOutputStream;
     30 import java.io.IOException;
     31 import java.io.InputStream;
     32 import java.math.BigInteger;
     33 import java.security.Key;
     34 import java.security.KeyFactory;
     35 import java.security.KeyPair;
     36 import java.security.KeyStore;
     37 import java.security.KeyStoreException;
     38 import java.security.MessageDigest;
     39 import java.security.NoSuchAlgorithmException;
     40 import java.security.NoSuchProviderException;
     41 import java.security.PrivateKey;
     42 import java.security.ProviderException;
     43 import java.security.PublicKey;
     44 import java.security.UnrecoverableEntryException;
     45 import java.security.cert.Certificate;
     46 import java.security.cert.CertificateFactory;
     47 import java.security.cert.X509Certificate;
     48 import java.security.interfaces.ECKey;
     49 import java.security.interfaces.ECPrivateKey;
     50 import java.security.interfaces.ECPublicKey;
     51 import java.security.interfaces.RSAKey;
     52 import java.security.interfaces.RSAPrivateKey;
     53 import java.security.interfaces.RSAPublicKey;
     54 import java.security.spec.ECParameterSpec;
     55 import java.security.spec.EllipticCurve;
     56 import java.security.spec.InvalidKeySpecException;
     57 import java.security.spec.PKCS8EncodedKeySpec;
     58 import java.util.ArrayList;
     59 import java.util.Collections;
     60 import java.util.HashMap;
     61 import java.util.List;
     62 import java.util.Locale;
     63 import java.util.Map;
     64 
     65 import javax.crypto.SecretKey;
     66 import javax.crypto.SecretKeyFactory;
     67 import javax.crypto.spec.SecretKeySpec;
     68 
     69 abstract class TestUtils extends Assert {
     70 
     71     static final String EXPECTED_CRYPTO_OP_PROVIDER_NAME = "AndroidKeyStoreBCWorkaround";
     72     static final String EXPECTED_PROVIDER_NAME = "AndroidKeyStore";
     73 
     74     static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
     75 
     76 
     77     private TestUtils() {}
     78 
     79     /**
     80      * Returns whether 3DES KeyStore tests should run on this device. 3DES support was added in
     81      * KeyMaster 4.0 and there should be no software fallback on earlier KeyMaster versions.
     82      */
     83     static boolean supports3DES() {
     84         return "true".equals(SystemProperties.get("ro.hardware.keystore_desede"));
     85     }
     86 
     87     /**
     88      * Returns whether the device has a StrongBox backed KeyStore.
     89      */
     90     static boolean hasStrongBox(Context context) {
     91         return context.getPackageManager()
     92             .hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE);
     93     }
     94 
     95     /**
     96      * Asserts the the key algorithm and algorithm-specific parameters of the two keys in the
     97      * provided pair match.
     98      */
     99     static void assertKeyPairSelfConsistent(KeyPair keyPair) {
    100         assertKeyPairSelfConsistent(keyPair.getPublic(), keyPair.getPrivate());
    101     }
    102 
    103     /**
    104      * Asserts the the key algorithm and public algorithm-specific parameters of the two provided
    105      * keys match.
    106      */
    107     static void assertKeyPairSelfConsistent(PublicKey publicKey, PrivateKey privateKey) {
    108         assertNotNull(publicKey);
    109         assertNotNull(privateKey);
    110         assertEquals(publicKey.getAlgorithm(), privateKey.getAlgorithm());
    111         String keyAlgorithm = publicKey.getAlgorithm();
    112         if ("EC".equalsIgnoreCase(keyAlgorithm)) {
    113             assertTrue("EC public key must be instanceof ECKey: "
    114                     + publicKey.getClass().getName(),
    115                     publicKey instanceof ECKey);
    116             assertTrue("EC private key must be instanceof ECKey: "
    117                     + privateKey.getClass().getName(),
    118                     privateKey instanceof ECKey);
    119             assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
    120                     "Private key must have the same EC parameters as public key",
    121                     ((ECKey) publicKey).getParams(), ((ECKey) privateKey).getParams());
    122         } else if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
    123             assertTrue("RSA public key must be instance of RSAKey: "
    124                     + publicKey.getClass().getName(),
    125                     publicKey instanceof RSAKey);
    126             assertTrue("RSA private key must be instance of RSAKey: "
    127                     + privateKey.getClass().getName(),
    128                     privateKey instanceof RSAKey);
    129             assertEquals("Private and public key must have the same RSA modulus",
    130                     ((RSAKey) publicKey).getModulus(), ((RSAKey) privateKey).getModulus());
    131         } else {
    132             fail("Unsuported key algorithm: " + keyAlgorithm);
    133         }
    134     }
    135 
    136     static int getKeySizeBits(Key key) {
    137         if (key instanceof ECKey) {
    138             return ((ECKey) key).getParams().getCurve().getField().getFieldSize();
    139         } else if (key instanceof RSAKey) {
    140             return ((RSAKey) key).getModulus().bitLength();
    141         } else {
    142             throw new IllegalArgumentException("Unsupported key type: " + key.getClass());
    143         }
    144     }
    145 
    146     static void assertKeySize(int expectedSizeBits, KeyPair keyPair) {
    147         assertEquals(expectedSizeBits, getKeySizeBits(keyPair.getPrivate()));
    148         assertEquals(expectedSizeBits, getKeySizeBits(keyPair.getPublic()));
    149     }
    150 
    151     /**
    152      * Asserts that the provided key pair is an Android Keystore key pair stored under the provided
    153      * alias.
    154      */
    155     static void assertKeyStoreKeyPair(KeyStore keyStore, String alias, KeyPair keyPair) {
    156         assertKeyMaterialExportable(keyPair.getPublic());
    157         assertKeyMaterialNotExportable(keyPair.getPrivate());
    158         assertTransparentKey(keyPair.getPublic());
    159         assertOpaqueKey(keyPair.getPrivate());
    160 
    161         KeyStore.Entry entry;
    162         Certificate cert;
    163         try {
    164             entry = keyStore.getEntry(alias, null);
    165             cert = keyStore.getCertificate(alias);
    166         } catch (KeyStoreException | UnrecoverableEntryException | NoSuchAlgorithmException e) {
    167             throw new RuntimeException("Failed to load entry: " + alias, e);
    168         }
    169         assertNotNull(entry);
    170 
    171         assertTrue(entry instanceof KeyStore.PrivateKeyEntry);
    172         KeyStore.PrivateKeyEntry privEntry = (KeyStore.PrivateKeyEntry) entry;
    173         assertEquals(cert, privEntry.getCertificate());
    174         assertTrue("Certificate must be an X.509 certificate: " + cert.getClass(),
    175                 cert instanceof X509Certificate);
    176         final X509Certificate x509Cert = (X509Certificate) cert;
    177 
    178         PrivateKey keystorePrivateKey = privEntry.getPrivateKey();
    179         PublicKey keystorePublicKey = cert.getPublicKey();
    180         assertEquals(keyPair.getPrivate(), keystorePrivateKey);
    181         assertEquals(keyPair.getPublic(), keystorePublicKey);
    182 
    183         assertEquals(
    184                 "Public key used to sign certificate should have the same algorithm as in KeyPair",
    185                 keystorePublicKey.getAlgorithm(), x509Cert.getPublicKey().getAlgorithm());
    186 
    187         Certificate[] chain = privEntry.getCertificateChain();
    188         if (chain.length == 0) {
    189             fail("Empty certificate chain");
    190             return;
    191         }
    192         assertEquals(cert, chain[0]);
    193     }
    194 
    195 
    196     private static void assertKeyMaterialExportable(Key key) {
    197         if (key instanceof PublicKey) {
    198             assertEquals("X.509", key.getFormat());
    199         } else if (key instanceof PrivateKey) {
    200             assertEquals("PKCS#8", key.getFormat());
    201         } else if (key instanceof SecretKey) {
    202             assertEquals("RAW", key.getFormat());
    203         } else {
    204             fail("Unsupported key type: " + key.getClass().getName());
    205         }
    206         byte[] encodedForm = key.getEncoded();
    207         assertNotNull(encodedForm);
    208         if (encodedForm.length == 0) {
    209             fail("Empty encoded form");
    210         }
    211     }
    212 
    213     private static void assertKeyMaterialNotExportable(Key key) {
    214         assertEquals(null, key.getFormat());
    215         assertEquals(null, key.getEncoded());
    216     }
    217 
    218     private static void assertOpaqueKey(Key key) {
    219         assertFalse(key.getClass().getName() + " is a transparent key", isTransparentKey(key));
    220     }
    221 
    222     private static void assertTransparentKey(Key key) {
    223         assertTrue(key.getClass().getName() + " is not a transparent key", isTransparentKey(key));
    224     }
    225 
    226     private static boolean isTransparentKey(Key key) {
    227         if (key instanceof PrivateKey) {
    228             return (key instanceof ECPrivateKey) || (key instanceof RSAPrivateKey);
    229         } else if (key instanceof PublicKey) {
    230             return (key instanceof ECPublicKey) || (key instanceof RSAPublicKey);
    231         } else if (key instanceof SecretKey) {
    232             return (key instanceof SecretKeySpec);
    233         } else {
    234             throw new IllegalArgumentException("Unsupported key type: " + key.getClass().getName());
    235         }
    236     }
    237 
    238     static void assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
    239             ECParameterSpec expected, ECParameterSpec actual) {
    240         assertECParameterSpecEqualsIgnoreSeedIfNotPresent(null, expected, actual);
    241     }
    242 
    243     static void assertECParameterSpecEqualsIgnoreSeedIfNotPresent(String message,
    244             ECParameterSpec expected, ECParameterSpec actual) {
    245         EllipticCurve expectedCurve = expected.getCurve();
    246         EllipticCurve actualCurve = actual.getCurve();
    247         String msgPrefix = (message != null) ? message + ": " : "";
    248         assertEquals(msgPrefix + "curve field", expectedCurve.getField(), actualCurve.getField());
    249         assertEquals(msgPrefix + "curve A", expectedCurve.getA(), actualCurve.getA());
    250         assertEquals(msgPrefix + "curve B", expectedCurve.getB(), actualCurve.getB());
    251         assertEquals(msgPrefix + "order", expected.getOrder(), actual.getOrder());
    252         assertEquals(msgPrefix + "generator",
    253                 expected.getGenerator(), actual.getGenerator());
    254         assertEquals(msgPrefix + "cofactor", expected.getCofactor(), actual.getCofactor());
    255 
    256         // If present, the seed must be the same
    257         byte[] expectedSeed = expectedCurve.getSeed();
    258         byte[] actualSeed = expectedCurve.getSeed();
    259         if ((expectedSeed != null) && (actualSeed != null)) {
    260             MoreAsserts.assertEquals(expectedSeed, actualSeed);
    261         }
    262     }
    263 
    264     static KeyInfo getKeyInfo(Key key) throws InvalidKeySpecException, NoSuchAlgorithmException,
    265             NoSuchProviderException {
    266         if ((key instanceof PrivateKey) || (key instanceof PublicKey)) {
    267             return KeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore")
    268                     .getKeySpec(key, KeyInfo.class);
    269         } else if (key instanceof SecretKey) {
    270             return (KeyInfo) SecretKeyFactory.getInstance(key.getAlgorithm(), "AndroidKeyStore")
    271                     .getKeySpec((SecretKey) key, KeyInfo.class);
    272         } else {
    273             throw new IllegalArgumentException("Unexpected key type: " + key.getClass());
    274         }
    275     }
    276 
    277     static <T> void assertContentsInAnyOrder(Iterable<T> actual, T... expected) {
    278         assertContentsInAnyOrder(null, actual, expected);
    279     }
    280 
    281     static <T> void assertContentsInAnyOrder(String message, Iterable<T> actual, T... expected) {
    282         Map<T, Integer> actualFreq = getFrequencyTable(actual);
    283         Map<T, Integer> expectedFreq = getFrequencyTable(expected);
    284         if (actualFreq.equals(expectedFreq)) {
    285             return;
    286         }
    287 
    288         Map<T, Integer> extraneousFreq = new HashMap<T, Integer>();
    289         for (Map.Entry<T, Integer> actualEntry : actualFreq.entrySet()) {
    290             int actualCount = actualEntry.getValue();
    291             Integer expectedCount = expectedFreq.get(actualEntry.getKey());
    292             int diff = actualCount - ((expectedCount != null) ? expectedCount : 0);
    293             if (diff > 0) {
    294                 extraneousFreq.put(actualEntry.getKey(), diff);
    295             }
    296         }
    297 
    298         Map<T, Integer> missingFreq = new HashMap<T, Integer>();
    299         for (Map.Entry<T, Integer> expectedEntry : expectedFreq.entrySet()) {
    300             int expectedCount = expectedEntry.getValue();
    301             Integer actualCount = actualFreq.get(expectedEntry.getKey());
    302             int diff = expectedCount - ((actualCount != null) ? actualCount : 0);
    303             if (diff > 0) {
    304                 missingFreq.put(expectedEntry.getKey(), diff);
    305             }
    306         }
    307 
    308         List<T> extraneous = frequencyTableToValues(extraneousFreq);
    309         List<T> missing = frequencyTableToValues(missingFreq);
    310         StringBuilder result = new StringBuilder();
    311         String delimiter = "";
    312         if (message != null) {
    313             result.append(message).append(".");
    314             delimiter = " ";
    315         }
    316         if (!missing.isEmpty()) {
    317             result.append(delimiter).append("missing: " + missing);
    318             delimiter = ", ";
    319         }
    320         if (!extraneous.isEmpty()) {
    321             result.append(delimiter).append("extraneous: " + extraneous);
    322         }
    323         fail(result.toString());
    324     }
    325 
    326     private static <T> Map<T, Integer> getFrequencyTable(Iterable<T> values) {
    327         Map<T, Integer> result = new HashMap<T, Integer>();
    328         for (T value : values) {
    329             Integer count = result.get(value);
    330             if (count == null) {
    331                 count = 1;
    332             } else {
    333                 count++;
    334             }
    335             result.put(value, count);
    336         }
    337         return result;
    338     }
    339 
    340     private static <T> Map<T, Integer> getFrequencyTable(T... values) {
    341         Map<T, Integer> result = new HashMap<T, Integer>();
    342         for (T value : values) {
    343             Integer count = result.get(value);
    344             if (count == null) {
    345                 count = 1;
    346             } else {
    347                 count++;
    348             }
    349             result.put(value, count);
    350         }
    351         return result;
    352     }
    353 
    354     @SuppressWarnings("rawtypes")
    355     private static <T> List<T> frequencyTableToValues(Map<T, Integer> table) {
    356         if (table.isEmpty()) {
    357             return Collections.emptyList();
    358         }
    359 
    360         List<T> result = new ArrayList<T>();
    361         boolean comparableValues = true;
    362         for (Map.Entry<T, Integer> entry : table.entrySet()) {
    363             T value = entry.getKey();
    364             if (!(value instanceof Comparable)) {
    365                 comparableValues = false;
    366             }
    367             int frequency = entry.getValue();
    368             for (int i = 0; i < frequency; i++) {
    369                 result.add(value);
    370             }
    371         }
    372 
    373         if (comparableValues) {
    374             sortAssumingComparable(result);
    375         }
    376         return result;
    377     }
    378 
    379     @SuppressWarnings({"rawtypes", "unchecked"})
    380     private static void sortAssumingComparable(List<?> values) {
    381         Collections.sort((List<Comparable>)values);
    382     }
    383 
    384     static String[] toLowerCase(String... values) {
    385         if (values == null) {
    386             return null;
    387         }
    388         String[] result = new String[values.length];
    389         for (int i = 0; i < values.length; i++) {
    390             String value = values[i];
    391             result[i] = (value != null) ? value.toLowerCase() : null;
    392         }
    393         return result;
    394     }
    395 
    396     static PrivateKey getRawResPrivateKey(Context context, int resId) throws Exception {
    397         byte[] pkcs8EncodedForm;
    398         try (InputStream in = context.getResources().openRawResource(resId)) {
    399             pkcs8EncodedForm = drain(in);
    400         }
    401         PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(pkcs8EncodedForm);
    402 
    403         try {
    404             return KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
    405         } catch (InvalidKeySpecException e) {
    406             try {
    407                 return KeyFactory.getInstance("RSA").generatePrivate(privateKeySpec);
    408             } catch (InvalidKeySpecException e2) {
    409                 throw new InvalidKeySpecException("The key is neither EC nor RSA", e);
    410             }
    411         }
    412     }
    413 
    414     static X509Certificate getRawResX509Certificate(Context context, int resId) throws Exception {
    415         try (InputStream in = context.getResources().openRawResource(resId)) {
    416             return (X509Certificate) CertificateFactory.getInstance("X.509")
    417                     .generateCertificate(in);
    418         }
    419     }
    420 
    421     static KeyPair importIntoAndroidKeyStore(
    422             String alias,
    423             PrivateKey privateKey,
    424             Certificate certificate,
    425             KeyProtection keyProtection) throws Exception {
    426         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
    427         keyStore.load(null);
    428         keyStore.setEntry(alias,
    429                 new KeyStore.PrivateKeyEntry(privateKey, new Certificate[] {certificate}),
    430                 keyProtection);
    431         return new KeyPair(
    432                 keyStore.getCertificate(alias).getPublicKey(),
    433                 (PrivateKey) keyStore.getKey(alias, null));
    434     }
    435 
    436     static ImportedKey importIntoAndroidKeyStore(
    437             String alias,
    438             SecretKey key,
    439             KeyProtection keyProtection) throws Exception {
    440         KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
    441         keyStore.load(null);
    442         keyStore.setEntry(alias,
    443                 new KeyStore.SecretKeyEntry(key),
    444                 keyProtection);
    445         return new ImportedKey(alias, key, (SecretKey) keyStore.getKey(alias, null));
    446     }
    447 
    448     static ImportedKey importIntoAndroidKeyStore(
    449             String alias, Context context, int privateResId, int certResId, KeyProtection params)
    450                     throws Exception {
    451         Certificate originalCert = TestUtils.getRawResX509Certificate(context, certResId);
    452         PublicKey originalPublicKey = originalCert.getPublicKey();
    453         PrivateKey originalPrivateKey = TestUtils.getRawResPrivateKey(context, privateResId);
    454 
    455         // Check that the domain parameters match between the private key and the public key. This
    456         // is to catch accidental errors where a test provides the wrong resource ID as one of the
    457         // parameters.
    458         if (!originalPublicKey.getAlgorithm().equalsIgnoreCase(originalPrivateKey.getAlgorithm())) {
    459             throw new IllegalArgumentException("Key algorithm mismatch."
    460                     + " Public: " + originalPublicKey.getAlgorithm()
    461                     + ", private: " + originalPrivateKey.getAlgorithm());
    462         }
    463         assertKeyPairSelfConsistent(originalPublicKey, originalPrivateKey);
    464 
    465         KeyPair keystoreBacked = TestUtils.importIntoAndroidKeyStore(
    466                 alias, originalPrivateKey, originalCert,
    467                 params);
    468         assertKeyPairSelfConsistent(keystoreBacked);
    469         assertKeyPairSelfConsistent(keystoreBacked.getPublic(), originalPrivateKey);
    470         return new ImportedKey(
    471                 alias,
    472                 new KeyPair(originalCert.getPublicKey(), originalPrivateKey),
    473                 keystoreBacked);
    474     }
    475 
    476     static byte[] drain(InputStream in) throws IOException {
    477         ByteArrayOutputStream result = new ByteArrayOutputStream();
    478         byte[] buffer = new byte[16 * 1024];
    479         int chunkSize;
    480         while ((chunkSize = in.read(buffer)) != -1) {
    481             result.write(buffer, 0, chunkSize);
    482         }
    483         return result.toByteArray();
    484     }
    485 
    486     static KeyProtection.Builder buildUpon(KeyProtection params) {
    487         return buildUponInternal(params, null);
    488     }
    489 
    490     static KeyProtection.Builder buildUpon(KeyProtection params, int newPurposes) {
    491         return buildUponInternal(params, newPurposes);
    492     }
    493 
    494     static KeyProtection.Builder buildUpon(
    495             KeyProtection.Builder builder) {
    496         return buildUponInternal(builder.build(), null);
    497     }
    498 
    499     static KeyProtection.Builder buildUpon(
    500             KeyProtection.Builder builder, int newPurposes) {
    501         return buildUponInternal(builder.build(), newPurposes);
    502     }
    503 
    504     private static KeyProtection.Builder buildUponInternal(
    505             KeyProtection spec, Integer newPurposes) {
    506         int purposes = (newPurposes == null) ? spec.getPurposes() : newPurposes;
    507         KeyProtection.Builder result = new KeyProtection.Builder(purposes);
    508         result.setBlockModes(spec.getBlockModes());
    509         if (spec.isDigestsSpecified()) {
    510             result.setDigests(spec.getDigests());
    511         }
    512         result.setEncryptionPaddings(spec.getEncryptionPaddings());
    513         result.setSignaturePaddings(spec.getSignaturePaddings());
    514         result.setKeyValidityStart(spec.getKeyValidityStart());
    515         result.setKeyValidityForOriginationEnd(spec.getKeyValidityForOriginationEnd());
    516         result.setKeyValidityForConsumptionEnd(spec.getKeyValidityForConsumptionEnd());
    517         result.setRandomizedEncryptionRequired(spec.isRandomizedEncryptionRequired());
    518         result.setUserAuthenticationRequired(spec.isUserAuthenticationRequired());
    519         result.setUserAuthenticationValidityDurationSeconds(
    520                 spec.getUserAuthenticationValidityDurationSeconds());
    521         result.setBoundToSpecificSecureUserId(spec.getBoundToSpecificSecureUserId());
    522         return result;
    523     }
    524 
    525     static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec) {
    526         return buildUponInternal(spec, null);
    527     }
    528 
    529     static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec, int newPurposes) {
    530         return buildUponInternal(spec, newPurposes);
    531     }
    532 
    533     static KeyGenParameterSpec.Builder buildUpon(
    534             KeyGenParameterSpec.Builder builder) {
    535         return buildUponInternal(builder.build(), null);
    536     }
    537 
    538     static KeyGenParameterSpec.Builder buildUpon(
    539             KeyGenParameterSpec.Builder builder, int newPurposes) {
    540         return buildUponInternal(builder.build(), newPurposes);
    541     }
    542 
    543     private static KeyGenParameterSpec.Builder buildUponInternal(
    544             KeyGenParameterSpec spec, Integer newPurposes) {
    545         int purposes = (newPurposes == null) ? spec.getPurposes() : newPurposes;
    546         KeyGenParameterSpec.Builder result =
    547                 new KeyGenParameterSpec.Builder(spec.getKeystoreAlias(), purposes);
    548         if (spec.getKeySize() >= 0) {
    549             result.setKeySize(spec.getKeySize());
    550         }
    551         if (spec.getAlgorithmParameterSpec() != null) {
    552             result.setAlgorithmParameterSpec(spec.getAlgorithmParameterSpec());
    553         }
    554         result.setCertificateNotBefore(spec.getCertificateNotBefore());
    555         result.setCertificateNotAfter(spec.getCertificateNotAfter());
    556         result.setCertificateSerialNumber(spec.getCertificateSerialNumber());
    557         result.setCertificateSubject(spec.getCertificateSubject());
    558         result.setBlockModes(spec.getBlockModes());
    559         if (spec.isDigestsSpecified()) {
    560             result.setDigests(spec.getDigests());
    561         }
    562         result.setEncryptionPaddings(spec.getEncryptionPaddings());
    563         result.setSignaturePaddings(spec.getSignaturePaddings());
    564         result.setKeyValidityStart(spec.getKeyValidityStart());
    565         result.setKeyValidityForOriginationEnd(spec.getKeyValidityForOriginationEnd());
    566         result.setKeyValidityForConsumptionEnd(spec.getKeyValidityForConsumptionEnd());
    567         result.setRandomizedEncryptionRequired(spec.isRandomizedEncryptionRequired());
    568         result.setUserAuthenticationRequired(spec.isUserAuthenticationRequired());
    569         result.setUserAuthenticationValidityDurationSeconds(
    570                 spec.getUserAuthenticationValidityDurationSeconds());
    571         return result;
    572     }
    573 
    574     static KeyPair getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs) {
    575         for (KeyPair keyPair : keyPairs) {
    576             if (keyAlgorithm.equalsIgnoreCase(keyPair.getPublic().getAlgorithm())) {
    577                 return keyPair;
    578             }
    579         }
    580         throw new IllegalArgumentException("No KeyPair for key algorithm " + keyAlgorithm);
    581     }
    582 
    583     static Key getKeyForKeyAlgorithm(String keyAlgorithm, Iterable<? extends Key> keys) {
    584         for (Key key : keys) {
    585             if (keyAlgorithm.equalsIgnoreCase(key.getAlgorithm())) {
    586                 return key;
    587             }
    588         }
    589         throw new IllegalArgumentException("No Key for key algorithm " + keyAlgorithm);
    590     }
    591 
    592     static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception {
    593         byte[] result = new byte[msgSizeBytes];
    594         MessageDigest digest = MessageDigest.getInstance("SHA-512");
    595         int resultOffset = 0;
    596         int resultRemaining = msgSizeBytes;
    597         while (resultRemaining > 0) {
    598             seed = digest.digest(seed);
    599             int chunkSize = Math.min(seed.length, resultRemaining);
    600             System.arraycopy(seed, 0, result, resultOffset, chunkSize);
    601             resultOffset += chunkSize;
    602             resultRemaining -= chunkSize;
    603         }
    604         return result;
    605     }
    606 
    607     static byte[] leftPadWithZeroBytes(byte[] array, int length) {
    608         if (array.length >= length) {
    609             return array;
    610         }
    611         byte[] result = new byte[length];
    612         System.arraycopy(array, 0, result, result.length - array.length, array.length);
    613         return result;
    614     }
    615 
    616     static boolean contains(int[] array, int value) {
    617         for (int element : array) {
    618             if (element == value) {
    619                 return true;
    620             }
    621         }
    622         return false;
    623     }
    624 
    625     static boolean isHmacAlgorithm(String algorithm) {
    626         return algorithm.toUpperCase(Locale.US).startsWith("HMAC");
    627     }
    628 
    629     static String getHmacAlgorithmDigest(String algorithm) {
    630         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
    631         if (!algorithmUpperCase.startsWith("HMAC")) {
    632             return null;
    633         }
    634         String result = algorithmUpperCase.substring("HMAC".length());
    635         if (result.startsWith("SHA")) {
    636             result = "SHA-" + result.substring("SHA".length());
    637         }
    638         return result;
    639     }
    640 
    641     static String getCipherKeyAlgorithm(String transformation) {
    642         String transformationUpperCase = transformation.toUpperCase(Locale.US);
    643         if (transformationUpperCase.startsWith("AES/")) {
    644             return KeyProperties.KEY_ALGORITHM_AES;
    645         } else if (transformationUpperCase.startsWith("DESEDE/")) {
    646             return KeyProperties.KEY_ALGORITHM_3DES;
    647         } else if (transformationUpperCase.startsWith("RSA/")) {
    648             return KeyProperties.KEY_ALGORITHM_RSA;
    649         } else {
    650             throw new IllegalArgumentException("Unsupported transformation: " + transformation);
    651         }
    652     }
    653 
    654     static boolean isCipherSymmetric(String transformation) {
    655         String transformationUpperCase = transformation.toUpperCase(Locale.US);
    656         if (transformationUpperCase.startsWith("AES/") || transformationUpperCase.startsWith(
    657                 "DESEDE/")) {
    658             return true;
    659         } else if (transformationUpperCase.startsWith("RSA/")) {
    660             return false;
    661         } else {
    662             throw new IllegalArgumentException("YYZ: Unsupported transformation: " + transformation);
    663         }
    664     }
    665 
    666     static String getCipherDigest(String transformation) {
    667         String transformationUpperCase = transformation.toUpperCase(Locale.US);
    668         if (transformationUpperCase.contains("/OAEP")) {
    669             if (transformationUpperCase.endsWith("/OAEPPADDING")) {
    670                 return KeyProperties.DIGEST_SHA1;
    671             } else if (transformationUpperCase.endsWith(
    672                     "/OAEPWITHSHA-1ANDMGF1PADDING")) {
    673                 return KeyProperties.DIGEST_SHA1;
    674             } else if (transformationUpperCase.endsWith(
    675                     "/OAEPWITHSHA-224ANDMGF1PADDING")) {
    676                 return KeyProperties.DIGEST_SHA224;
    677             } else if (transformationUpperCase.endsWith(
    678                     "/OAEPWITHSHA-256ANDMGF1PADDING")) {
    679                 return KeyProperties.DIGEST_SHA256;
    680             } else if (transformationUpperCase.endsWith(
    681                     "/OAEPWITHSHA-384ANDMGF1PADDING")) {
    682                 return KeyProperties.DIGEST_SHA384;
    683             } else if (transformationUpperCase.endsWith(
    684                     "/OAEPWITHSHA-512ANDMGF1PADDING")) {
    685                 return KeyProperties.DIGEST_SHA512;
    686             } else {
    687                 throw new RuntimeException("Unsupported OAEP padding scheme: "
    688                         + transformation);
    689             }
    690         } else {
    691             return null;
    692         }
    693     }
    694 
    695     static String getCipherEncryptionPadding(String transformation) {
    696         String transformationUpperCase = transformation.toUpperCase(Locale.US);
    697         if (transformationUpperCase.endsWith("/NOPADDING")) {
    698             return KeyProperties.ENCRYPTION_PADDING_NONE;
    699         } else if (transformationUpperCase.endsWith("/PKCS7PADDING")) {
    700             return KeyProperties.ENCRYPTION_PADDING_PKCS7;
    701         } else if (transformationUpperCase.endsWith("/PKCS1PADDING")) {
    702             return KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
    703         } else if (transformationUpperCase.split("/")[2].startsWith("OAEP")) {
    704             return KeyProperties.ENCRYPTION_PADDING_RSA_OAEP;
    705         } else {
    706             throw new IllegalArgumentException("Unsupported transformation: " + transformation);
    707         }
    708     }
    709 
    710     static String getCipherBlockMode(String transformation) {
    711         return transformation.split("/")[1].toUpperCase(Locale.US);
    712     }
    713 
    714     static String getSignatureAlgorithmDigest(String algorithm) {
    715         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
    716         int withIndex = algorithmUpperCase.indexOf("WITH");
    717         if (withIndex == -1) {
    718             throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
    719         }
    720         String digest = algorithmUpperCase.substring(0, withIndex);
    721         if (digest.startsWith("SHA")) {
    722             digest = "SHA-" + digest.substring("SHA".length());
    723         }
    724         return digest;
    725     }
    726 
    727     static String getSignatureAlgorithmPadding(String algorithm) {
    728         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
    729         if (algorithmUpperCase.endsWith("WITHECDSA")) {
    730             return null;
    731         } else if (algorithmUpperCase.endsWith("WITHRSA")) {
    732             return KeyProperties.SIGNATURE_PADDING_RSA_PKCS1;
    733         } else if (algorithmUpperCase.endsWith("WITHRSA/PSS")) {
    734             return KeyProperties.SIGNATURE_PADDING_RSA_PSS;
    735         } else {
    736             throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
    737         }
    738     }
    739 
    740     static String getSignatureAlgorithmKeyAlgorithm(String algorithm) {
    741         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
    742         if (algorithmUpperCase.endsWith("WITHECDSA")) {
    743             return KeyProperties.KEY_ALGORITHM_EC;
    744         } else if ((algorithmUpperCase.endsWith("WITHRSA"))
    745                 || (algorithmUpperCase.endsWith("WITHRSA/PSS"))) {
    746             return KeyProperties.KEY_ALGORITHM_RSA;
    747         } else {
    748             throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
    749         }
    750     }
    751 
    752     static boolean isKeyLongEnoughForSignatureAlgorithm(String algorithm, Key key) {
    753         String keyAlgorithm = key.getAlgorithm();
    754         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
    755             // No length restrictions for ECDSA
    756             return true;
    757         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
    758             // No length restrictions for RSA
    759             String digest = getSignatureAlgorithmDigest(algorithm);
    760             int digestOutputSizeBits = getDigestOutputSizeBits(digest);
    761             if (digestOutputSizeBits == -1) {
    762                 // No digesting -- assume the key is long enough for the message
    763                 return true;
    764             }
    765             String paddingScheme = getSignatureAlgorithmPadding(algorithm);
    766             int paddingOverheadBytes;
    767             if (KeyProperties.SIGNATURE_PADDING_RSA_PKCS1.equalsIgnoreCase(paddingScheme)) {
    768                 paddingOverheadBytes = 30;
    769             } else if (KeyProperties.SIGNATURE_PADDING_RSA_PSS.equalsIgnoreCase(paddingScheme)) {
    770                 int saltSizeBytes = (digestOutputSizeBits + 7) / 8;
    771                 paddingOverheadBytes = saltSizeBytes + 1;
    772             } else {
    773                 throw new IllegalArgumentException(
    774                         "Unsupported signature padding scheme: " + paddingScheme);
    775             }
    776             int minKeySizeBytes = paddingOverheadBytes + (digestOutputSizeBits + 7) / 8 + 1;
    777             int keySizeBytes = ((RSAKey) key).getModulus().bitLength() / 8;
    778             return keySizeBytes >= minKeySizeBytes;
    779         } else {
    780             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
    781         }
    782     }
    783 
    784     static int getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key) {
    785         String keyAlgorithm = getCipherKeyAlgorithm(transformation);
    786         if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)
    787                 || KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(keyAlgorithm)) {
    788             return Integer.MAX_VALUE;
    789         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
    790             String encryptionPadding = getCipherEncryptionPadding(transformation);
    791             int modulusSizeBytes = (getKeySizeBits(key) + 7) / 8;
    792             if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding)) {
    793                 return modulusSizeBytes - 1;
    794             } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(
    795                     encryptionPadding)) {
    796                 return modulusSizeBytes - 11;
    797             } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(
    798                     encryptionPadding)) {
    799                 String digest = getCipherDigest(transformation);
    800                 int digestOutputSizeBytes = (getDigestOutputSizeBits(digest) + 7) / 8;
    801                 return modulusSizeBytes - 2 * digestOutputSizeBytes - 2;
    802             } else {
    803                 throw new IllegalArgumentException(
    804                         "Unsupported encryption padding scheme: " + encryptionPadding);
    805             }
    806         } else {
    807             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
    808         }
    809     }
    810 
    811     static int getDigestOutputSizeBits(String digest) {
    812         if (KeyProperties.DIGEST_NONE.equals(digest)) {
    813             return -1;
    814         } else if (KeyProperties.DIGEST_MD5.equals(digest)) {
    815             return 128;
    816         } else if (KeyProperties.DIGEST_SHA1.equals(digest)) {
    817             return 160;
    818         } else if (KeyProperties.DIGEST_SHA224.equals(digest)) {
    819             return 224;
    820         } else if (KeyProperties.DIGEST_SHA256.equals(digest)) {
    821             return 256;
    822         } else if (KeyProperties.DIGEST_SHA384.equals(digest)) {
    823             return 384;
    824         } else if (KeyProperties.DIGEST_SHA512.equals(digest)) {
    825             return 512;
    826         } else {
    827             throw new IllegalArgumentException("Unsupported digest: " + digest);
    828         }
    829     }
    830 
    831     static byte[] concat(byte[] arr1, byte[] arr2) {
    832         return concat(arr1, 0, (arr1 != null) ? arr1.length : 0,
    833                 arr2, 0, (arr2 != null) ? arr2.length : 0);
    834     }
    835 
    836     static byte[] concat(byte[] arr1, int offset1, int len1,
    837             byte[] arr2, int offset2, int len2) {
    838         if (len1 == 0) {
    839             return subarray(arr2, offset2, len2);
    840         } else if (len2 == 0) {
    841             return subarray(arr1, offset1, len1);
    842         }
    843         byte[] result = new byte[len1 + len2];
    844         if (len1 > 0) {
    845             System.arraycopy(arr1, offset1, result, 0, len1);
    846         }
    847         if (len2 > 0) {
    848             System.arraycopy(arr2, offset2, result, len1, len2);
    849         }
    850         return result;
    851     }
    852 
    853     static byte[] subarray(byte[] arr, int offset, int len) {
    854         if (len == 0) {
    855             return EmptyArray.BYTE;
    856         }
    857         if ((offset == 0) && (arr.length == len)) {
    858             return arr;
    859         }
    860         byte[] result = new byte[len];
    861         System.arraycopy(arr, offset, result, 0, len);
    862         return result;
    863     }
    864 
    865     static KeyProtection getMinimalWorkingImportParametersForSigningingWith(
    866             String signatureAlgorithm) {
    867         String keyAlgorithm = getSignatureAlgorithmKeyAlgorithm(signatureAlgorithm);
    868         String digest = getSignatureAlgorithmDigest(signatureAlgorithm);
    869         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
    870             return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
    871                     .setDigests(digest)
    872                     .build();
    873         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
    874             String padding = getSignatureAlgorithmPadding(signatureAlgorithm);
    875             return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
    876                     .setDigests(digest)
    877                     .setSignaturePaddings(padding)
    878                     .build();
    879         } else {
    880             throw new IllegalArgumentException(
    881                     "Unsupported signature algorithm: " + signatureAlgorithm);
    882         }
    883     }
    884 
    885     static KeyProtection getMinimalWorkingImportParametersForCipheringWith(
    886             String transformation, int purposes) {
    887         return getMinimalWorkingImportParametersForCipheringWith(transformation, purposes, false);
    888     }
    889 
    890     static KeyProtection getMinimalWorkingImportParametersForCipheringWith(
    891             String transformation, int purposes, boolean ivProvidedWhenEncrypting) {
    892         return getMinimalWorkingImportParametersForCipheringWith(transformation, purposes,
    893             ivProvidedWhenEncrypting, false, false);
    894     }
    895 
    896     static KeyProtection getMinimalWorkingImportParametersForCipheringWith(
    897             String transformation, int purposes, boolean ivProvidedWhenEncrypting,
    898             boolean isUnlockedDeviceRequired, boolean isUserAuthRequired) {
    899         String keyAlgorithm = TestUtils.getCipherKeyAlgorithm(transformation);
    900         if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)
    901             || KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(keyAlgorithm)) {
    902             String encryptionPadding = TestUtils.getCipherEncryptionPadding(transformation);
    903             String blockMode = TestUtils.getCipherBlockMode(transformation);
    904             boolean randomizedEncryptionRequired = true;
    905             if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
    906                 randomizedEncryptionRequired = false;
    907             } else if ((ivProvidedWhenEncrypting)
    908                     && ((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)) {
    909                 randomizedEncryptionRequired = false;
    910             }
    911             return new KeyProtection.Builder(
    912                     purposes)
    913                     .setBlockModes(blockMode)
    914                     .setEncryptionPaddings(encryptionPadding)
    915                     .setRandomizedEncryptionRequired(randomizedEncryptionRequired)
    916                     .setUnlockedDeviceRequired(isUnlockedDeviceRequired)
    917                     .setUserAuthenticationRequired(isUserAuthRequired)
    918                     .setUserAuthenticationValidityDurationSeconds(3600)
    919                     .build();
    920         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
    921             String digest = TestUtils.getCipherDigest(transformation);
    922             String encryptionPadding = TestUtils.getCipherEncryptionPadding(transformation);
    923             boolean randomizedEncryptionRequired =
    924                     !KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding);
    925             return new KeyProtection.Builder(
    926                     purposes)
    927                     .setDigests((digest != null) ? new String[] {digest} : EmptyArray.STRING)
    928                     .setEncryptionPaddings(encryptionPadding)
    929                     .setRandomizedEncryptionRequired(randomizedEncryptionRequired)
    930                     .setUserAuthenticationRequired(isUserAuthRequired)
    931                     .setUserAuthenticationValidityDurationSeconds(3600)
    932                     .setUnlockedDeviceRequired(isUnlockedDeviceRequired)
    933                     .build();
    934         } else {
    935             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
    936         }
    937     }
    938 
    939     static byte[] getBigIntegerMagnitudeBytes(BigInteger value) {
    940         return removeLeadingZeroByteIfPresent(value.toByteArray());
    941     }
    942 
    943     private static byte[] removeLeadingZeroByteIfPresent(byte[] value) {
    944         if ((value.length < 1) || (value[0] != 0)) {
    945             return value;
    946         }
    947         return TestUtils.subarray(value, 1, value.length - 1);
    948     }
    949 }
    950