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