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 
TestUtils()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      */
supports3DES()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      */
hasStrongBox(Context context)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      */
assertKeyPairSelfConsistent(KeyPair keyPair)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      */
assertKeyPairSelfConsistent(PublicKey publicKey, PrivateKey privateKey)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 
getKeySizeBits(Key key)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 
assertKeySize(int expectedSizeBits, KeyPair keyPair)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      */
assertKeyStoreKeyPair(KeyStore keyStore, String alias, KeyPair keyPair)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 
assertKeyMaterialExportable(Key key)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 
assertKeyMaterialNotExportable(Key key)213     private static void assertKeyMaterialNotExportable(Key key) {
214         assertEquals(null, key.getFormat());
215         assertEquals(null, key.getEncoded());
216     }
217 
assertOpaqueKey(Key key)218     private static void assertOpaqueKey(Key key) {
219         assertFalse(key.getClass().getName() + " is a transparent key", isTransparentKey(key));
220     }
221 
assertTransparentKey(Key key)222     private static void assertTransparentKey(Key key) {
223         assertTrue(key.getClass().getName() + " is not a transparent key", isTransparentKey(key));
224     }
225 
isTransparentKey(Key key)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 
assertECParameterSpecEqualsIgnoreSeedIfNotPresent( ECParameterSpec expected, ECParameterSpec actual)238     static void assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
239             ECParameterSpec expected, ECParameterSpec actual) {
240         assertECParameterSpecEqualsIgnoreSeedIfNotPresent(null, expected, actual);
241     }
242 
assertECParameterSpecEqualsIgnoreSeedIfNotPresent(String message, ECParameterSpec expected, ECParameterSpec actual)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 
getKeyInfo(Key key)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 
assertContentsInAnyOrder(Iterable<T> actual, T... expected)277     static <T> void assertContentsInAnyOrder(Iterable<T> actual, T... expected) {
278         assertContentsInAnyOrder(null, actual, expected);
279     }
280 
assertContentsInAnyOrder(String message, Iterable<T> actual, T... expected)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 
getFrequencyTable(Iterable<T> values)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 
getFrequencyTable(T... values)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")
frequencyTableToValues(Map<T, Integer> table)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"})
sortAssumingComparable(List<?> values)380     private static void sortAssumingComparable(List<?> values) {
381         Collections.sort((List<Comparable>)values);
382     }
383 
toLowerCase(String... values)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 
getRawResPrivateKey(Context context, int resId)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 
getRawResX509Certificate(Context context, int resId)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 
importIntoAndroidKeyStore( String alias, PrivateKey privateKey, Certificate certificate, KeyProtection keyProtection)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 
importIntoAndroidKeyStore( String alias, SecretKey key, KeyProtection keyProtection)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 
importIntoAndroidKeyStore( String alias, Context context, int privateResId, int certResId, KeyProtection params)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 
drain(InputStream in)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 
buildUpon(KeyProtection params)486     static KeyProtection.Builder buildUpon(KeyProtection params) {
487         return buildUponInternal(params, null);
488     }
489 
buildUpon(KeyProtection params, int newPurposes)490     static KeyProtection.Builder buildUpon(KeyProtection params, int newPurposes) {
491         return buildUponInternal(params, newPurposes);
492     }
493 
buildUpon( KeyProtection.Builder builder)494     static KeyProtection.Builder buildUpon(
495             KeyProtection.Builder builder) {
496         return buildUponInternal(builder.build(), null);
497     }
498 
buildUpon( KeyProtection.Builder builder, int newPurposes)499     static KeyProtection.Builder buildUpon(
500             KeyProtection.Builder builder, int newPurposes) {
501         return buildUponInternal(builder.build(), newPurposes);
502     }
503 
buildUponInternal( KeyProtection spec, Integer newPurposes)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 
buildUpon(KeyGenParameterSpec spec)525     static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec) {
526         return buildUponInternal(spec, null);
527     }
528 
buildUpon(KeyGenParameterSpec spec, int newPurposes)529     static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec, int newPurposes) {
530         return buildUponInternal(spec, newPurposes);
531     }
532 
buildUpon( KeyGenParameterSpec.Builder builder)533     static KeyGenParameterSpec.Builder buildUpon(
534             KeyGenParameterSpec.Builder builder) {
535         return buildUponInternal(builder.build(), null);
536     }
537 
buildUpon( KeyGenParameterSpec.Builder builder, int newPurposes)538     static KeyGenParameterSpec.Builder buildUpon(
539             KeyGenParameterSpec.Builder builder, int newPurposes) {
540         return buildUponInternal(builder.build(), newPurposes);
541     }
542 
buildUponInternal( KeyGenParameterSpec spec, Integer newPurposes)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 
getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs)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 
getKeyForKeyAlgorithm(String keyAlgorithm, Iterable<? extends Key> keys)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 
generateLargeKatMsg(byte[] seed, int msgSizeBytes)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 
leftPadWithZeroBytes(byte[] array, int length)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 
contains(int[] array, int value)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 
isHmacAlgorithm(String algorithm)625     static boolean isHmacAlgorithm(String algorithm) {
626         return algorithm.toUpperCase(Locale.US).startsWith("HMAC");
627     }
628 
getHmacAlgorithmDigest(String algorithm)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 
getCipherKeyAlgorithm(String transformation)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 
isCipherSymmetric(String transformation)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 
getCipherDigest(String transformation)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 
getCipherEncryptionPadding(String transformation)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 
getCipherBlockMode(String transformation)710     static String getCipherBlockMode(String transformation) {
711         return transformation.split("/")[1].toUpperCase(Locale.US);
712     }
713 
getSignatureAlgorithmDigest(String algorithm)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 
getSignatureAlgorithmPadding(String algorithm)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 
getSignatureAlgorithmKeyAlgorithm(String algorithm)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 
isKeyLongEnoughForSignatureAlgorithm(String algorithm, Key key)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 
getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key)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 
getDigestOutputSizeBits(String digest)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 
concat(byte[] arr1, byte[] arr2)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 
concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, int len2)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 
subarray(byte[] arr, int offset, int len)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 
getMinimalWorkingImportParametersForSigningingWith( String signatureAlgorithm)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 
getMinimalWorkingImportParametersForCipheringWith( String transformation, int purposes)885     static KeyProtection getMinimalWorkingImportParametersForCipheringWith(
886             String transformation, int purposes) {
887         return getMinimalWorkingImportParametersForCipheringWith(transformation, purposes, false);
888     }
889 
getMinimalWorkingImportParametersForCipheringWith( String transformation, int purposes, boolean ivProvidedWhenEncrypting)890     static KeyProtection getMinimalWorkingImportParametersForCipheringWith(
891             String transformation, int purposes, boolean ivProvidedWhenEncrypting) {
892         return getMinimalWorkingImportParametersForCipheringWith(transformation, purposes,
893             ivProvidedWhenEncrypting, false, false);
894     }
895 
getMinimalWorkingImportParametersForCipheringWith( String transformation, int purposes, boolean ivProvidedWhenEncrypting, boolean isUnlockedDeviceRequired, boolean isUserAuthRequired)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 
getBigIntegerMagnitudeBytes(BigInteger value)939     static byte[] getBigIntegerMagnitudeBytes(BigInteger value) {
940         return removeLeadingZeroByteIfPresent(value.toByteArray());
941     }
942 
removeLeadingZeroByteIfPresent(byte[] value)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