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