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