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