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