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