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 result.setBoundToSpecificSecureUserId(spec.getBoundToSpecificSecureUserId()); 503 return result; 504 } 505 buildUpon(KeyGenParameterSpec spec)506 static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec) { 507 return buildUponInternal(spec, null); 508 } 509 buildUpon(KeyGenParameterSpec spec, int newPurposes)510 static KeyGenParameterSpec.Builder buildUpon(KeyGenParameterSpec spec, int newPurposes) { 511 return buildUponInternal(spec, newPurposes); 512 } 513 buildUpon( KeyGenParameterSpec.Builder builder)514 static KeyGenParameterSpec.Builder buildUpon( 515 KeyGenParameterSpec.Builder builder) { 516 return buildUponInternal(builder.build(), null); 517 } 518 buildUpon( KeyGenParameterSpec.Builder builder, int newPurposes)519 static KeyGenParameterSpec.Builder buildUpon( 520 KeyGenParameterSpec.Builder builder, int newPurposes) { 521 return buildUponInternal(builder.build(), newPurposes); 522 } 523 buildUponInternal( KeyGenParameterSpec spec, Integer newPurposes)524 private static KeyGenParameterSpec.Builder buildUponInternal( 525 KeyGenParameterSpec spec, Integer newPurposes) { 526 int purposes = (newPurposes == null) ? spec.getPurposes() : newPurposes; 527 KeyGenParameterSpec.Builder result = 528 new KeyGenParameterSpec.Builder(spec.getKeystoreAlias(), purposes); 529 if (spec.getKeySize() >= 0) { 530 result.setKeySize(spec.getKeySize()); 531 } 532 if (spec.getAlgorithmParameterSpec() != null) { 533 result.setAlgorithmParameterSpec(spec.getAlgorithmParameterSpec()); 534 } 535 result.setCertificateNotBefore(spec.getCertificateNotBefore()); 536 result.setCertificateNotAfter(spec.getCertificateNotAfter()); 537 result.setCertificateSerialNumber(spec.getCertificateSerialNumber()); 538 result.setCertificateSubject(spec.getCertificateSubject()); 539 result.setBlockModes(spec.getBlockModes()); 540 if (spec.isDigestsSpecified()) { 541 result.setDigests(spec.getDigests()); 542 } 543 result.setEncryptionPaddings(spec.getEncryptionPaddings()); 544 result.setSignaturePaddings(spec.getSignaturePaddings()); 545 result.setKeyValidityStart(spec.getKeyValidityStart()); 546 result.setKeyValidityForOriginationEnd(spec.getKeyValidityForOriginationEnd()); 547 result.setKeyValidityForConsumptionEnd(spec.getKeyValidityForConsumptionEnd()); 548 result.setRandomizedEncryptionRequired(spec.isRandomizedEncryptionRequired()); 549 result.setUserAuthenticationRequired(spec.isUserAuthenticationRequired()); 550 result.setUserAuthenticationValidityDurationSeconds( 551 spec.getUserAuthenticationValidityDurationSeconds()); 552 return result; 553 } 554 getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs)555 static KeyPair getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs) { 556 for (KeyPair keyPair : keyPairs) { 557 if (keyAlgorithm.equalsIgnoreCase(keyPair.getPublic().getAlgorithm())) { 558 return keyPair; 559 } 560 } 561 throw new IllegalArgumentException("No KeyPair for key algorithm " + keyAlgorithm); 562 } 563 getKeyForKeyAlgorithm(String keyAlgorithm, Iterable<? extends Key> keys)564 static Key getKeyForKeyAlgorithm(String keyAlgorithm, Iterable<? extends Key> keys) { 565 for (Key key : keys) { 566 if (keyAlgorithm.equalsIgnoreCase(key.getAlgorithm())) { 567 return key; 568 } 569 } 570 throw new IllegalArgumentException("No Key for key algorithm " + keyAlgorithm); 571 } 572 generateLargeKatMsg(byte[] seed, int msgSizeBytes)573 static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception { 574 byte[] result = new byte[msgSizeBytes]; 575 MessageDigest digest = MessageDigest.getInstance("SHA-512"); 576 int resultOffset = 0; 577 int resultRemaining = msgSizeBytes; 578 while (resultRemaining > 0) { 579 seed = digest.digest(seed); 580 int chunkSize = Math.min(seed.length, resultRemaining); 581 System.arraycopy(seed, 0, result, resultOffset, chunkSize); 582 resultOffset += chunkSize; 583 resultRemaining -= chunkSize; 584 } 585 return result; 586 } 587 leftPadWithZeroBytes(byte[] array, int length)588 static byte[] leftPadWithZeroBytes(byte[] array, int length) { 589 if (array.length >= length) { 590 return array; 591 } 592 byte[] result = new byte[length]; 593 System.arraycopy(array, 0, result, result.length - array.length, array.length); 594 return result; 595 } 596 contains(int[] array, int value)597 static boolean contains(int[] array, int value) { 598 for (int element : array) { 599 if (element == value) { 600 return true; 601 } 602 } 603 return false; 604 } 605 isHmacAlgorithm(String algorithm)606 static boolean isHmacAlgorithm(String algorithm) { 607 return algorithm.toUpperCase(Locale.US).startsWith("HMAC"); 608 } 609 getHmacAlgorithmDigest(String algorithm)610 static String getHmacAlgorithmDigest(String algorithm) { 611 String algorithmUpperCase = algorithm.toUpperCase(Locale.US); 612 if (!algorithmUpperCase.startsWith("HMAC")) { 613 return null; 614 } 615 String result = algorithmUpperCase.substring("HMAC".length()); 616 if (result.startsWith("SHA")) { 617 result = "SHA-" + result.substring("SHA".length()); 618 } 619 return result; 620 } 621 getCipherKeyAlgorithm(String transformation)622 static String getCipherKeyAlgorithm(String transformation) { 623 String transformationUpperCase = transformation.toUpperCase(Locale.US); 624 if (transformationUpperCase.startsWith("AES/")) { 625 return KeyProperties.KEY_ALGORITHM_AES; 626 } else if (transformationUpperCase.startsWith("RSA/")) { 627 return KeyProperties.KEY_ALGORITHM_RSA; 628 } else { 629 throw new IllegalArgumentException("Unsupported transformation: " + transformation); 630 } 631 } 632 isCipherSymmetric(String transformation)633 static boolean isCipherSymmetric(String transformation) { 634 String transformationUpperCase = transformation.toUpperCase(Locale.US); 635 if (transformationUpperCase.startsWith("AES/")) { 636 return true; 637 } else if (transformationUpperCase.startsWith("RSA/")) { 638 return false; 639 } else { 640 throw new IllegalArgumentException("Unsupported transformation: " + transformation); 641 } 642 } 643 getCipherDigest(String transformation)644 static String getCipherDigest(String transformation) { 645 String transformationUpperCase = transformation.toUpperCase(Locale.US); 646 if (transformationUpperCase.contains("/OAEP")) { 647 if (transformationUpperCase.endsWith("/OAEPPADDING")) { 648 return KeyProperties.DIGEST_SHA1; 649 } else if (transformationUpperCase.endsWith( 650 "/OAEPWITHSHA-1ANDMGF1PADDING")) { 651 return KeyProperties.DIGEST_SHA1; 652 } else if (transformationUpperCase.endsWith( 653 "/OAEPWITHSHA-224ANDMGF1PADDING")) { 654 return KeyProperties.DIGEST_SHA224; 655 } else if (transformationUpperCase.endsWith( 656 "/OAEPWITHSHA-256ANDMGF1PADDING")) { 657 return KeyProperties.DIGEST_SHA256; 658 } else if (transformationUpperCase.endsWith( 659 "/OAEPWITHSHA-384ANDMGF1PADDING")) { 660 return KeyProperties.DIGEST_SHA384; 661 } else if (transformationUpperCase.endsWith( 662 "/OAEPWITHSHA-512ANDMGF1PADDING")) { 663 return KeyProperties.DIGEST_SHA512; 664 } else { 665 throw new RuntimeException("Unsupported OAEP padding scheme: " 666 + transformation); 667 } 668 } else { 669 return null; 670 } 671 } 672 getCipherEncryptionPadding(String transformation)673 static String getCipherEncryptionPadding(String transformation) { 674 String transformationUpperCase = transformation.toUpperCase(Locale.US); 675 if (transformationUpperCase.endsWith("/NOPADDING")) { 676 return KeyProperties.ENCRYPTION_PADDING_NONE; 677 } else if (transformationUpperCase.endsWith("/PKCS7PADDING")) { 678 return KeyProperties.ENCRYPTION_PADDING_PKCS7; 679 } else if (transformationUpperCase.endsWith("/PKCS1PADDING")) { 680 return KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1; 681 } else if (transformationUpperCase.split("/")[2].startsWith("OAEP")) { 682 return KeyProperties.ENCRYPTION_PADDING_RSA_OAEP; 683 } else { 684 throw new IllegalArgumentException("Unsupported transformation: " + transformation); 685 } 686 } 687 getCipherBlockMode(String transformation)688 static String getCipherBlockMode(String transformation) { 689 return transformation.split("/")[1].toUpperCase(Locale.US); 690 } 691 getSignatureAlgorithmDigest(String algorithm)692 static String getSignatureAlgorithmDigest(String algorithm) { 693 String algorithmUpperCase = algorithm.toUpperCase(Locale.US); 694 int withIndex = algorithmUpperCase.indexOf("WITH"); 695 if (withIndex == -1) { 696 throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); 697 } 698 String digest = algorithmUpperCase.substring(0, withIndex); 699 if (digest.startsWith("SHA")) { 700 digest = "SHA-" + digest.substring("SHA".length()); 701 } 702 return digest; 703 } 704 getSignatureAlgorithmPadding(String algorithm)705 static String getSignatureAlgorithmPadding(String algorithm) { 706 String algorithmUpperCase = algorithm.toUpperCase(Locale.US); 707 if (algorithmUpperCase.endsWith("WITHECDSA")) { 708 return null; 709 } else if (algorithmUpperCase.endsWith("WITHRSA")) { 710 return KeyProperties.SIGNATURE_PADDING_RSA_PKCS1; 711 } else if (algorithmUpperCase.endsWith("WITHRSA/PSS")) { 712 return KeyProperties.SIGNATURE_PADDING_RSA_PSS; 713 } else { 714 throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); 715 } 716 } 717 getSignatureAlgorithmKeyAlgorithm(String algorithm)718 static String getSignatureAlgorithmKeyAlgorithm(String algorithm) { 719 String algorithmUpperCase = algorithm.toUpperCase(Locale.US); 720 if (algorithmUpperCase.endsWith("WITHECDSA")) { 721 return KeyProperties.KEY_ALGORITHM_EC; 722 } else if ((algorithmUpperCase.endsWith("WITHRSA")) 723 || (algorithmUpperCase.endsWith("WITHRSA/PSS"))) { 724 return KeyProperties.KEY_ALGORITHM_RSA; 725 } else { 726 throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); 727 } 728 } 729 isKeyLongEnoughForSignatureAlgorithm(String algorithm, Key key)730 static boolean isKeyLongEnoughForSignatureAlgorithm(String algorithm, Key key) { 731 String keyAlgorithm = key.getAlgorithm(); 732 if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 733 // No length restrictions for ECDSA 734 return true; 735 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 736 // No length restrictions for RSA 737 String digest = getSignatureAlgorithmDigest(algorithm); 738 int digestOutputSizeBits = getDigestOutputSizeBits(digest); 739 if (digestOutputSizeBits == -1) { 740 // No digesting -- assume the key is long enough for the message 741 return true; 742 } 743 String paddingScheme = getSignatureAlgorithmPadding(algorithm); 744 int paddingOverheadBytes; 745 if (KeyProperties.SIGNATURE_PADDING_RSA_PKCS1.equalsIgnoreCase(paddingScheme)) { 746 paddingOverheadBytes = 30; 747 } else if (KeyProperties.SIGNATURE_PADDING_RSA_PSS.equalsIgnoreCase(paddingScheme)) { 748 int saltSizeBytes = (digestOutputSizeBits + 7) / 8; 749 paddingOverheadBytes = saltSizeBytes + 1; 750 } else { 751 throw new IllegalArgumentException( 752 "Unsupported signature padding scheme: " + paddingScheme); 753 } 754 int minKeySizeBytes = paddingOverheadBytes + (digestOutputSizeBits + 7) / 8 + 1; 755 int keySizeBytes = ((RSAKey) key).getModulus().bitLength() / 8; 756 return keySizeBytes >= minKeySizeBytes; 757 } else { 758 throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm); 759 } 760 } 761 getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key)762 static int getMaxSupportedPlaintextInputSizeBytes(String transformation, Key key) { 763 String keyAlgorithm = getCipherKeyAlgorithm(transformation); 764 if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)) { 765 return Integer.MAX_VALUE; 766 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 767 String encryptionPadding = getCipherEncryptionPadding(transformation); 768 int modulusSizeBytes = (getKeySizeBits(key) + 7) / 8; 769 if (KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding)) { 770 return modulusSizeBytes - 1; 771 } else if (KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase( 772 encryptionPadding)) { 773 return modulusSizeBytes - 11; 774 } else if (KeyProperties.ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase( 775 encryptionPadding)) { 776 String digest = getCipherDigest(transformation); 777 int digestOutputSizeBytes = (getDigestOutputSizeBits(digest) + 7) / 8; 778 return modulusSizeBytes - 2 * digestOutputSizeBytes - 2; 779 } else { 780 throw new IllegalArgumentException( 781 "Unsupported encryption padding scheme: " + encryptionPadding); 782 } 783 } else { 784 throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm); 785 } 786 } 787 getDigestOutputSizeBits(String digest)788 static int getDigestOutputSizeBits(String digest) { 789 if (KeyProperties.DIGEST_NONE.equals(digest)) { 790 return -1; 791 } else if (KeyProperties.DIGEST_MD5.equals(digest)) { 792 return 128; 793 } else if (KeyProperties.DIGEST_SHA1.equals(digest)) { 794 return 160; 795 } else if (KeyProperties.DIGEST_SHA224.equals(digest)) { 796 return 224; 797 } else if (KeyProperties.DIGEST_SHA256.equals(digest)) { 798 return 256; 799 } else if (KeyProperties.DIGEST_SHA384.equals(digest)) { 800 return 384; 801 } else if (KeyProperties.DIGEST_SHA512.equals(digest)) { 802 return 512; 803 } else { 804 throw new IllegalArgumentException("Unsupported digest: " + digest); 805 } 806 } 807 concat(byte[] arr1, byte[] arr2)808 static byte[] concat(byte[] arr1, byte[] arr2) { 809 return concat(arr1, 0, (arr1 != null) ? arr1.length : 0, 810 arr2, 0, (arr2 != null) ? arr2.length : 0); 811 } 812 concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2, int len2)813 static byte[] concat(byte[] arr1, int offset1, int len1, 814 byte[] arr2, int offset2, int len2) { 815 if (len1 == 0) { 816 return subarray(arr2, offset2, len2); 817 } else if (len2 == 0) { 818 return subarray(arr1, offset1, len1); 819 } 820 byte[] result = new byte[len1 + len2]; 821 if (len1 > 0) { 822 System.arraycopy(arr1, offset1, result, 0, len1); 823 } 824 if (len2 > 0) { 825 System.arraycopy(arr2, offset2, result, len1, len2); 826 } 827 return result; 828 } 829 subarray(byte[] arr, int offset, int len)830 static byte[] subarray(byte[] arr, int offset, int len) { 831 if (len == 0) { 832 return EmptyArray.BYTE; 833 } 834 if ((offset == 0) && (arr.length == len)) { 835 return arr; 836 } 837 byte[] result = new byte[len]; 838 System.arraycopy(arr, offset, result, 0, len); 839 return result; 840 } 841 getMinimalWorkingImportParametersForSigningingWith( String signatureAlgorithm)842 static KeyProtection getMinimalWorkingImportParametersForSigningingWith( 843 String signatureAlgorithm) { 844 String keyAlgorithm = getSignatureAlgorithmKeyAlgorithm(signatureAlgorithm); 845 String digest = getSignatureAlgorithmDigest(signatureAlgorithm); 846 if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) { 847 return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) 848 .setDigests(digest) 849 .build(); 850 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 851 String padding = getSignatureAlgorithmPadding(signatureAlgorithm); 852 return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN) 853 .setDigests(digest) 854 .setSignaturePaddings(padding) 855 .build(); 856 } else { 857 throw new IllegalArgumentException( 858 "Unsupported signature algorithm: " + signatureAlgorithm); 859 } 860 } 861 getMinimalWorkingImportParametersForCipheringWith( String transformation, int purposes)862 static KeyProtection getMinimalWorkingImportParametersForCipheringWith( 863 String transformation, int purposes) { 864 return getMinimalWorkingImportParametersForCipheringWith(transformation, purposes, false); 865 } 866 getMinimalWorkingImportParametersForCipheringWith( String transformation, int purposes, boolean ivProvidedWhenEncrypting)867 static KeyProtection getMinimalWorkingImportParametersForCipheringWith( 868 String transformation, int purposes, boolean ivProvidedWhenEncrypting) { 869 String keyAlgorithm = TestUtils.getCipherKeyAlgorithm(transformation); 870 if (KeyProperties.KEY_ALGORITHM_AES.equalsIgnoreCase(keyAlgorithm)) { 871 String encryptionPadding = TestUtils.getCipherEncryptionPadding(transformation); 872 String blockMode = TestUtils.getCipherBlockMode(transformation); 873 boolean randomizedEncryptionRequired = true; 874 if (KeyProperties.BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) { 875 randomizedEncryptionRequired = false; 876 } else if ((ivProvidedWhenEncrypting) 877 && ((purposes & KeyProperties.PURPOSE_ENCRYPT) != 0)) { 878 randomizedEncryptionRequired = false; 879 } 880 return new KeyProtection.Builder( 881 purposes) 882 .setBlockModes(blockMode) 883 .setEncryptionPaddings(encryptionPadding) 884 .setRandomizedEncryptionRequired(randomizedEncryptionRequired) 885 .build(); 886 } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) { 887 String digest = TestUtils.getCipherDigest(transformation); 888 String encryptionPadding = TestUtils.getCipherEncryptionPadding(transformation); 889 boolean randomizedEncryptionRequired = 890 !KeyProperties.ENCRYPTION_PADDING_NONE.equalsIgnoreCase(encryptionPadding); 891 return new KeyProtection.Builder( 892 purposes) 893 .setDigests((digest != null) ? new String[] {digest} : EmptyArray.STRING) 894 .setEncryptionPaddings(encryptionPadding) 895 .setRandomizedEncryptionRequired(randomizedEncryptionRequired) 896 .build(); 897 } else { 898 throw new IllegalArgumentException("Unsupported key algorithm: " + keyAlgorithm); 899 } 900 } 901 getBigIntegerMagnitudeBytes(BigInteger value)902 static byte[] getBigIntegerMagnitudeBytes(BigInteger value) { 903 return removeLeadingZeroByteIfPresent(value.toByteArray()); 904 } 905 removeLeadingZeroByteIfPresent(byte[] value)906 private static byte[] removeLeadingZeroByteIfPresent(byte[] value) { 907 if ((value.length < 1) || (value[0] != 0)) { 908 return value; 909 } 910 return TestUtils.subarray(value, 1, value.length - 1); 911 } 912 } 913