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