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