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