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