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