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