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         return result;
503     }
504 
buildUpon(KeyGenParameterSpec spec)505     static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec) {
506         return buildUponInternal(spec, null);
507     }
508 
buildUpon(KeyGenParameterSpec spec, int newPurposes)509     static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec, int newPurposes) {
510         return buildUponInternal(spec, newPurposes);
511     }
512 
buildUpon( KeyGenParameterSpec.Builder builder)513     static KeyGenParameterSpec.Builder buildUpon(
514             KeyGenParameterSpec.Builder builder) {
515         return buildUponInternal(builder.build(), null);
516     }
517 
buildUpon( KeyGenParameterSpec.Builder builder, int newPurposes)518     static KeyGenParameterSpec.Builder buildUpon(
519             KeyGenParameterSpec.Builder builder, int newPurposes) {
520         return buildUponInternal(builder.build(), newPurposes);
521     }
522 
buildUponInternal( KeyGenParameterSpec spec, Integer newPurposes)523     private static KeyGenParameterSpec.Builder buildUponInternal(
524             KeyGenParameterSpec spec, Integer newPurposes) {
525         int purposes = (newPurposes == null) ? spec.getPurposes() : newPurposes;
526         KeyGenParameterSpec.Builder result =
527                 new KeyGenParameterSpec.Builder(spec.getKeystoreAlias(), purposes);
528         if (spec.getKeySize() >= 0) {
529             result.setKeySize(spec.getKeySize());
530         }
531         if (spec.getAlgorithmParameterSpec() != null) {
532             result.setAlgorithmParameterSpec(spec.getAlgorithmParameterSpec());
533         }
534         result.setCertificateNotBefore(spec.getCertificateNotBefore());
535         result.setCertificateNotAfter(spec.getCertificateNotAfter());
536         result.setCertificateSerialNumber(spec.getCertificateSerialNumber());
537         result.setCertificateSubject(spec.getCertificateSubject());
538         result.setBlockModes(spec.getBlockModes());
539         if (spec.isDigestsSpecified()) {
540             result.setDigests(spec.getDigests());
541         }
542         result.setEncryptionPaddings(spec.getEncryptionPaddings());
543         result.setSignaturePaddings(spec.getSignaturePaddings());
544         result.setKeyValidityStart(spec.getKeyValidityStart());
545         result.setKeyValidityForOriginationEnd(spec.getKeyValidityForOriginationEnd());
546         result.setKeyValidityForConsumptionEnd(spec.getKeyValidityForConsumptionEnd());
547         result.setRandomizedEncryptionRequired(spec.isRandomizedEncryptionRequired());
548         result.setUserAuthenticationRequired(spec.isUserAuthenticationRequired());
549         result.setUserAuthenticationValidityDurationSeconds(
550                 spec.getUserAuthenticationValidityDurationSeconds());
551         return result;
552     }
553 
getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs)554     static KeyPair getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs) {
555         for (KeyPair keyPair : keyPairs) {
556             if (keyAlgorithm.equalsIgnoreCase(keyPair.getPublic().getAlgorithm())) {
557                 return keyPair;
558             }
559         }
560         throw new IllegalArgumentException("No KeyPair for key algorithm " + keyAlgorithm);
561     }
562 
getKeyForKeyAlgorithm(String keyAlgorithm, Iterable<? extends Key> keys)563     static Key getKeyForKeyAlgorithm(String keyAlgorithm, Iterable<? extends Key> keys) {
564         for (Key key : keys) {
565             if (keyAlgorithm.equalsIgnoreCase(key.getAlgorithm())) {
566                 return key;
567             }
568         }
569         throw new IllegalArgumentException("No Key for key algorithm " + keyAlgorithm);
570     }
571 
generateLargeKatMsg(byte[] seed, int msgSizeBytes)572     static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception {
573         byte[] result = new byte[msgSizeBytes];
574         MessageDigest digest = MessageDigest.getInstance("SHA-512");
575         int resultOffset = 0;
576         int resultRemaining = msgSizeBytes;
577         while (resultRemaining > 0) {
578             seed = digest.digest(seed);
579             int chunkSize = Math.min(seed.length, resultRemaining);
580             System.arraycopy(seed, 0, result, resultOffset, chunkSize);
581             resultOffset += chunkSize;
582             resultRemaining -= chunkSize;
583         }
584         return result;
585     }
586 
leftPadWithZeroBytes(byte[] array, int length)587     static byte[] leftPadWithZeroBytes(byte[] array, int length) {
588         if (array.length >= length) {
589             return array;
590         }
591         byte[] result = new byte[length];
592         System.arraycopy(array, 0, result, result.length - array.length, array.length);
593         return result;
594     }
595 
contains(int[] array, int value)596     static boolean contains(int[] array, int value) {
597         for (int element : array) {
598             if (element == value) {
599                 return true;
600             }
601         }
602         return false;
603     }
604 
isHmacAlgorithm(String algorithm)605     static boolean isHmacAlgorithm(String algorithm) {
606         return algorithm.toUpperCase(Locale.US).startsWith("HMAC");
607     }
608 
getHmacAlgorithmDigest(String algorithm)609     static String getHmacAlgorithmDigest(String algorithm) {
610         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
611         if (!algorithmUpperCase.startsWith("HMAC")) {
612             return null;
613         }
614         String result = algorithmUpperCase.substring("HMAC".length());
615         if (result.startsWith("SHA")) {
616             result = "SHA-" + result.substring("SHA".length());
617         }
618         return result;
619     }
620 
getCipherKeyAlgorithm(String transformation)621     static String getCipherKeyAlgorithm(String transformation) {
622         String transformationUpperCase = transformation.toUpperCase(Locale.US);
623         if (transformationUpperCase.startsWith("AES/")) {
624             return KeyProperties.KEY_ALGORITHM_AES;
625         } else if (transformationUpperCase.startsWith("RSA/")) {
626             return KeyProperties.KEY_ALGORITHM_RSA;
627         } else {
628             throw new IllegalArgumentException("Unsupported transformation: " + transformation);
629         }
630     }
631 
isCipherSymmetric(String transformation)632     static boolean isCipherSymmetric(String transformation) {
633         String transformationUpperCase = transformation.toUpperCase(Locale.US);
634         if (transformationUpperCase.startsWith("AES/")) {
635             return true;
636         } else if (transformationUpperCase.startsWith("RSA/")) {
637             return false;
638         } else {
639             throw new IllegalArgumentException("Unsupported transformation: " + transformation);
640         }
641     }
642 
getCipherDigest(String transformation)643     static String getCipherDigest(String transformation) {
644         String transformationUpperCase = transformation.toUpperCase(Locale.US);
645         if (transformationUpperCase.contains("/OAEP")) {
646             if (transformationUpperCase.endsWith("/OAEPPADDING")) {
647                 return KeyProperties.DIGEST_SHA1;
648             } else if (transformationUpperCase.endsWith(
649                     "/OAEPWITHSHA-1ANDMGF1PADDING")) {
650                 return KeyProperties.DIGEST_SHA1;
651             } else if (transformationUpperCase.endsWith(
652                     "/OAEPWITHSHA-224ANDMGF1PADDING")) {
653                 return KeyProperties.DIGEST_SHA224;
654             } else if (transformationUpperCase.endsWith(
655                     "/OAEPWITHSHA-256ANDMGF1PADDING")) {
656                 return KeyProperties.DIGEST_SHA256;
657             } else if (transformationUpperCase.endsWith(
658                     "/OAEPWITHSHA-384ANDMGF1PADDING")) {
659                 return KeyProperties.DIGEST_SHA384;
660             } else if (transformationUpperCase.endsWith(
661                     "/OAEPWITHSHA-512ANDMGF1PADDING")) {
662                 return KeyProperties.DIGEST_SHA512;
663             } else {
664                 throw new RuntimeException("Unsupported OAEP padding scheme: "
665                         + transformation);
666             }
667         } else {
668             return null;
669         }
670     }
671 
getCipherEncryptionPadding(String transformation)672     static String getCipherEncryptionPadding(String transformation) {
673         String transformationUpperCase = transformation.toUpperCase(Locale.US);
674         if (transformationUpperCase.endsWith("/NOPADDING")) {
675             return KeyProperties.ENCRYPTION_PADDING_NONE;
676         } else if (transformationUpperCase.endsWith("/PKCS7PADDING")) {
677             return KeyProperties.ENCRYPTION_PADDING_PKCS7;
678         } else if (transformationUpperCase.endsWith("/PKCS1PADDING")) {
679             return KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1;
680         } else if (transformationUpperCase.split("/")[2].startsWith("OAEP")) {
681             return KeyProperties.ENCRYPTION_PADDING_RSA_OAEP;
682         } else {
683             throw new IllegalArgumentException("Unsupported transformation: " + transformation);
684         }
685     }
686 
getCipherBlockMode(String transformation)687     static String getCipherBlockMode(String transformation) {
688         return transformation.split("/")[1].toUpperCase(Locale.US);
689     }
690 
getSignatureAlgorithmDigest(String algorithm)691     static String getSignatureAlgorithmDigest(String algorithm) {
692         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
693         int withIndex = algorithmUpperCase.indexOf("WITH");
694         if (withIndex == -1) {
695             throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
696         }
697         String digest = algorithmUpperCase.substring(0, withIndex);
698         if (digest.startsWith("SHA")) {
699             digest = "SHA-" + digest.substring("SHA".length());
700         }
701         return digest;
702     }
703 
getSignatureAlgorithmPadding(String algorithm)704     static String getSignatureAlgorithmPadding(String algorithm) {
705         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
706         if (algorithmUpperCase.endsWith("WITHECDSA")) {
707             return null;
708         } else if (algorithmUpperCase.endsWith("WITHRSA")) {
709             return KeyProperties.SIGNATURE_PADDING_RSA_PKCS1;
710         } else if (algorithmUpperCase.endsWith("WITHRSA/PSS")) {
711             return KeyProperties.SIGNATURE_PADDING_RSA_PSS;
712         } else {
713             throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
714         }
715     }
716 
getSignatureAlgorithmKeyAlgorithm(String algorithm)717     static String getSignatureAlgorithmKeyAlgorithm(String algorithm) {
718         String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
719         if (algorithmUpperCase.endsWith("WITHECDSA")) {
720             return KeyProperties.KEY_ALGORITHM_EC;
721         } else if ((algorithmUpperCase.endsWith("WITHRSA"))
722                 || (algorithmUpperCase.endsWith("WITHRSA/PSS"))) {
723             return KeyProperties.KEY_ALGORITHM_RSA;
724         } else {
725             throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
726         }
727     }
728 
isKeyLongEnoughForSignatureAlgorithm(String algorithm, Key key)729     static boolean isKeyLongEnoughForSignatureAlgorithm(String algorithm, Key key) {
730         String keyAlgorithm = key.getAlgorithm();
731         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
732             // No length restrictions for ECDSA
733             return true;
734         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
735             // No length restrictions for RSA
736             String digest = getSignatureAlgorithmDigest(algorithm);
737             int digestOutputSizeBits = getDigestOutputSizeBits(digest);
738             if (digestOutputSizeBits == -1) {
739                 // No digesting -- assume the key is long enough for the message
740                 return true;
741             }
742             String paddingScheme = getSignatureAlgorithmPadding(algorithm);
743             int paddingOverheadBytes;
744             if (KeyProperties.SIGNATURE_PADDING_RSA_PKCS1.equalsIgnoreCase(paddingScheme)) {
745                 paddingOverheadBytes = 30;
746             } else if (KeyProperties.SIGNATURE_PADDING_RSA_PSS.equalsIgnoreCase(paddingScheme)) {
747                 int saltSizeBytes = (digestOutputSizeBits + 7) / 8;
748                 paddingOverheadBytes = saltSizeBytes + 1;
749             } else {
750                 throw new IllegalArgumentException(
751                         "Unsupported signature padding scheme: " + paddingScheme);
752             }
753             int minKeySizeBytes = paddingOverheadBytes + (digestOutputSizeBits + 7) / 8 + 1;
754             int keySizeBytes = ((RSAKey) key).getModulus().bitLength() / 8;
755             return keySizeBytes >= minKeySizeBytes;
756         } else {
757             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
758         }
759     }
760 
getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key)761     static int getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key) {
762         String keyAlgorithm = getCipherKeyAlgorithm(transformation);
763         if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)) {
764             return Integer.MAX_VALUE;
765         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
766             String encryptionPadding = getCipherEncryptionPadding(transformation);
767             int modulusSizeBytes = (getKeySizeBits(key) + 7) / 8;
768             if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding)) {
769                 return modulusSizeBytes - 1;
770             } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(
771                     encryptionPadding)) {
772                 return modulusSizeBytes - 11;
773             } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(
774                     encryptionPadding)) {
775                 String digest = getCipherDigest(transformation);
776                 int digestOutputSizeBytes = (getDigestOutputSizeBits(digest) + 7) / 8;
777                 return modulusSizeBytes - 2 * digestOutputSizeBytes - 2;
778             } else {
779                 throw new IllegalArgumentException(
780                         "Unsupported encryption padding scheme: " + encryptionPadding);
781             }
782         } else {
783             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
784         }
785     }
786 
getDigestOutputSizeBits(String digest)787     static int getDigestOutputSizeBits(String digest) {
788         if (KeyProperties.DIGEST_NONE.equals(digest)) {
789             return -1;
790         } else if (KeyProperties.DIGEST_MD5.equals(digest)) {
791             return 128;
792         } else if (KeyProperties.DIGEST_SHA1.equals(digest)) {
793             return 160;
794         } else if (KeyProperties.DIGEST_SHA224.equals(digest)) {
795             return 224;
796         } else if (KeyProperties.DIGEST_SHA256.equals(digest)) {
797             return 256;
798         } else if (KeyProperties.DIGEST_SHA384.equals(digest)) {
799             return 384;
800         } else if (KeyProperties.DIGEST_SHA512.equals(digest)) {
801             return 512;
802         } else {
803             throw new IllegalArgumentException("Unsupported digest: " + digest);
804         }
805     }
806 
concat(byte[] arr1, byte[] arr2)807     static byte[] concat(byte[] arr1, byte[] arr2) {
808         return concat(arr1, 0, (arr1 != null) ? arr1.length : 0,
809                 arr2, 0, (arr2 != null) ? arr2.length : 0);
810     }
811 
concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, int len2)812     static byte[] concat(byte[] arr1, int offset1, int len1,
813             byte[] arr2, int offset2, int len2) {
814         if (len1 == 0) {
815             return subarray(arr2, offset2, len2);
816         } else if (len2 == 0) {
817             return subarray(arr1, offset1, len1);
818         }
819         byte[] result = new byte[len1 + len2];
820         if (len1 > 0) {
821             System.arraycopy(arr1, offset1, result, 0, len1);
822         }
823         if (len2 > 0) {
824             System.arraycopy(arr2, offset2, result, len1, len2);
825         }
826         return result;
827     }
828 
subarray(byte[] arr, int offset, int len)829     static byte[] subarray(byte[] arr, int offset, int len) {
830         if (len == 0) {
831             return EmptyArray.BYTE;
832         }
833         if ((offset == 0) && (arr.length == len)) {
834             return arr;
835         }
836         byte[] result = new byte[len];
837         System.arraycopy(arr, offset, result, 0, len);
838         return result;
839     }
840 
getMinimalWorkingImportParametersForSigningingWith( String signatureAlgorithm)841     static KeyProtection getMinimalWorkingImportParametersForSigningingWith(
842             String signatureAlgorithm) {
843         String keyAlgorithm = getSignatureAlgorithmKeyAlgorithm(signatureAlgorithm);
844         String digest = getSignatureAlgorithmDigest(signatureAlgorithm);
845         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
846             return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
847                     .setDigests(digest)
848                     .build();
849         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
850             String padding = getSignatureAlgorithmPadding(signatureAlgorithm);
851             return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
852                     .setDigests(digest)
853                     .setSignaturePaddings(padding)
854                     .build();
855         } else {
856             throw new IllegalArgumentException(
857                     "Unsupported signature algorithm: " + signatureAlgorithm);
858         }
859     }
860 
getMinimalWorkingImportParametersForCipheringWith( String transformation, int purposes)861     static KeyProtection getMinimalWorkingImportParametersForCipheringWith(
862             String transformation, int purposes) {
863         return getMinimalWorkingImportParametersForCipheringWith(transformation, purposes, false);
864     }
865 
getMinimalWorkingImportParametersForCipheringWith( String transformation, int purposes, boolean ivProvidedWhenEncrypting)866     static KeyProtection getMinimalWorkingImportParametersForCipheringWith(
867             String transformation, int purposes, boolean ivProvidedWhenEncrypting) {
868         String keyAlgorithm = TestUtils.getCipherKeyAlgorithm(transformation);
869         if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)) {
870             String encryptionPadding = TestUtils.getCipherEncryptionPadding(transformation);
871             String blockMode = TestUtils.getCipherBlockMode(transformation);
872             boolean randomizedEncryptionRequired = true;
873             if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
874                 randomizedEncryptionRequired = false;
875             } else if ((ivProvidedWhenEncrypting)
876                     && ((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)) {
877                 randomizedEncryptionRequired = false;
878             }
879             return new KeyProtection.Builder(
880                     purposes)
881                     .setBlockModes(blockMode)
882                     .setEncryptionPaddings(encryptionPadding)
883                     .setRandomizedEncryptionRequired(randomizedEncryptionRequired)
884                     .build();
885         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
886             String digest = TestUtils.getCipherDigest(transformation);
887             String encryptionPadding = TestUtils.getCipherEncryptionPadding(transformation);
888             boolean randomizedEncryptionRequired =
889                     !KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding);
890             return new KeyProtection.Builder(
891                     purposes)
892                     .setDigests((digest != null) ? new String[] {digest} : EmptyArray.STRING)
893                     .setEncryptionPaddings(encryptionPadding)
894                     .setRandomizedEncryptionRequired(randomizedEncryptionRequired)
895                     .build();
896         } else {
897             throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm);
898         }
899     }
900 
getBigIntegerMagnitudeBytes(BigInteger value)901     static byte[] getBigIntegerMagnitudeBytes(BigInteger value) {
902         return removeLeadingZeroByteIfPresent(value.toByteArray());
903     }
904 
removeLeadingZeroByteIfPresent(byte[] value)905     private static byte[] removeLeadingZeroByteIfPresent(byte[] value) {
906         if ((value.length < 1) || (value[0] != 0)) {
907             return value;
908         }
909         return TestUtils.subarray(value, 1, value.length - 1);
910     }
911 }
912