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