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