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