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