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.security.keystore.KeyGenParameterSpec; 20 import android.security.keystore.KeyInfo; 21 import android.security.keystore.KeyProperties; 22 import android.test.AndroidTestCase; 23 import android.test.MoreAsserts; 24 25 import com.google.common.collect.ObjectArrays; 26 27 import junit.framework.TestCase; 28 29 import java.security.InvalidAlgorithmParameterException; 30 import java.security.NoSuchAlgorithmException; 31 import java.security.NoSuchProviderException; 32 import java.security.Provider; 33 import java.security.Security; 34 import java.security.spec.AlgorithmParameterSpec; 35 import java.security.spec.ECGenParameterSpec; 36 import java.security.Provider.Service; 37 import java.security.SecureRandom; 38 import java.util.Arrays; 39 import java.util.Date; 40 import java.util.HashSet; 41 import java.util.Locale; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.TreeMap; 45 46 import javax.crypto.KeyGenerator; 47 import javax.crypto.SecretKey; 48 49 50 public class KeyGeneratorTest extends AndroidTestCase { 51 private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_PROVIDER_NAME; 52 53 static String[] EXPECTED_ALGORITHMS = { 54 "AES", 55 "HmacSHA1", 56 "HmacSHA224", 57 "HmacSHA256", 58 "HmacSHA384", 59 "HmacSHA512", 60 }; 61 62 static String[] EXPECTED_STRONGBOX_ALGORITHMS = { 63 "AES", 64 "HmacSHA256", 65 }; 66 67 { 68 if (TestUtils.supports3DES()) { 69 EXPECTED_ALGORITHMS = ObjectArrays.concat(EXPECTED_ALGORITHMS, "DESede"); 70 } 71 } 72 73 private static final Map<String, Integer> DEFAULT_KEY_SIZES = 74 new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 75 static { 76 DEFAULT_KEY_SIZES.put("AES", 128); 77 DEFAULT_KEY_SIZES.put("DESede", 168); 78 DEFAULT_KEY_SIZES.put("HmacSHA1", 160); 79 DEFAULT_KEY_SIZES.put("HmacSHA224", 224); 80 DEFAULT_KEY_SIZES.put("HmacSHA256", 256); 81 DEFAULT_KEY_SIZES.put("HmacSHA384", 384); 82 DEFAULT_KEY_SIZES.put("HmacSHA512", 512); 83 } 84 85 static final int[] AES_SUPPORTED_KEY_SIZES = new int[] {128, 192, 256}; 86 static final int[] DES_SUPPORTED_KEY_SIZES = new int[] {168}; 87 testAlgorithmList()88 public void testAlgorithmList() { 89 // Assert that Android Keystore Provider exposes exactly the expected KeyGenerator 90 // algorithms. We don't care whether the algorithms are exposed via aliases, as long as 91 // canonical names of algorithms are accepted. If the Provider exposes extraneous 92 // algorithms, it'll be caught because it'll have to expose at least one Service for such an 93 // algorithm, and this Service's algorithm will not be in the expected set. 94 95 Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME); 96 Set<Service> services = provider.getServices(); 97 Set<String> actualAlgsLowerCase = new HashSet<String>(); 98 Set<String> expectedAlgsLowerCase = new HashSet<String>( 99 Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS))); 100 for (Service service : services) { 101 if ("KeyGenerator".equalsIgnoreCase(service.getType())) { 102 String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US); 103 actualAlgsLowerCase.add(algLowerCase); 104 } 105 } 106 107 TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase, 108 expectedAlgsLowerCase.toArray(new String[0])); 109 } 110 testGenerateWithoutInitThrowsIllegalStateException()111 public void testGenerateWithoutInitThrowsIllegalStateException() throws Exception { 112 for (String algorithm : EXPECTED_ALGORITHMS) { 113 try { 114 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 115 try { 116 keyGenerator.generateKey(); 117 fail(); 118 } catch (IllegalStateException expected) {} 119 } catch (Throwable e) { 120 throw new RuntimeException("Failed for " + algorithm, e); 121 } 122 } 123 } 124 testInitWithKeySizeThrowsUnsupportedOperationException()125 public void testInitWithKeySizeThrowsUnsupportedOperationException() throws Exception { 126 for (String algorithm : EXPECTED_ALGORITHMS) { 127 try { 128 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 129 int keySizeBits = DEFAULT_KEY_SIZES.get(algorithm); 130 try { 131 keyGenerator.init(keySizeBits); 132 fail(); 133 } catch (UnsupportedOperationException expected) {} 134 } catch (Throwable e) { 135 throw new RuntimeException("Failed for " + algorithm, e); 136 } 137 } 138 } 139 testInitWithKeySizeAndSecureRandomThrowsUnsupportedOperationException()140 public void testInitWithKeySizeAndSecureRandomThrowsUnsupportedOperationException() 141 throws Exception { 142 SecureRandom rng = new SecureRandom(); 143 for (String algorithm : EXPECTED_ALGORITHMS) { 144 try { 145 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 146 int keySizeBits = DEFAULT_KEY_SIZES.get(algorithm); 147 try { 148 keyGenerator.init(keySizeBits, rng); 149 fail(); 150 } catch (UnsupportedOperationException expected) {} 151 } catch (Throwable e) { 152 throw new RuntimeException("Failed for " + algorithm, e); 153 } 154 } 155 } 156 testInitWithNullAlgParamsThrowsInvalidAlgorithmParameterException()157 public void testInitWithNullAlgParamsThrowsInvalidAlgorithmParameterException() 158 throws Exception { 159 for (String algorithm : EXPECTED_ALGORITHMS) { 160 try { 161 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 162 try { 163 keyGenerator.init((AlgorithmParameterSpec) null); 164 fail(); 165 } catch (InvalidAlgorithmParameterException expected) {} 166 } catch (Throwable e) { 167 throw new RuntimeException("Failed for " + algorithm, e); 168 } 169 } 170 } 171 testInitWithNullAlgParamsAndSecureRandomThrowsInvalidAlgorithmParameterException()172 public void testInitWithNullAlgParamsAndSecureRandomThrowsInvalidAlgorithmParameterException() 173 throws Exception { 174 SecureRandom rng = new SecureRandom(); 175 for (String algorithm : EXPECTED_ALGORITHMS) { 176 try { 177 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 178 try { 179 keyGenerator.init((AlgorithmParameterSpec) null, rng); 180 fail(); 181 } catch (InvalidAlgorithmParameterException expected) {} 182 } catch (Throwable e) { 183 throw new RuntimeException("Failed for " + algorithm, e); 184 } 185 } 186 } 187 testInitWithAlgParamsAndNullSecureRandom()188 public void testInitWithAlgParamsAndNullSecureRandom() 189 throws Exception { 190 testInitWithAlgParamsAndNullSecureRandomHelper(false /* useStrongbox */); 191 if (TestUtils.hasStrongBox(getContext())) { 192 testInitWithAlgParamsAndNullSecureRandomHelper(true /* useStrongbox */); 193 } 194 } 195 testInitWithAlgParamsAndNullSecureRandomHelper(boolean useStrongbox)196 private void testInitWithAlgParamsAndNullSecureRandomHelper(boolean useStrongbox) 197 throws Exception { 198 for (String algorithm : 199 (useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS)) { 200 try { 201 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 202 keyGenerator.init(getWorkingSpec() 203 .setIsStrongBoxBacked(useStrongbox) 204 .build(), 205 (SecureRandom) null); 206 // Check that generateKey doesn't fail either, just in case null SecureRandom 207 // causes trouble there. 208 keyGenerator.generateKey(); 209 } catch (Throwable e) { 210 throw new RuntimeException("Failed for " + algorithm, e); 211 } 212 } 213 } 214 testInitWithUnsupportedAlgParamsTypeThrowsInvalidAlgorithmParameterException()215 public void testInitWithUnsupportedAlgParamsTypeThrowsInvalidAlgorithmParameterException() 216 throws Exception { 217 for (String algorithm : EXPECTED_ALGORITHMS) { 218 try { 219 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 220 try { 221 keyGenerator.init(new ECGenParameterSpec("secp256r1")); 222 fail(); 223 } catch (InvalidAlgorithmParameterException expected) {} 224 } catch (Throwable e) { 225 throw new RuntimeException("Failed for " + algorithm, e); 226 } 227 } 228 } 229 testDefaultKeySize()230 public void testDefaultKeySize() throws Exception { 231 testDefaultKeySize(false /* useStrongbox */); 232 if (TestUtils.hasStrongBox(getContext())) { 233 testDefaultKeySize(true /* useStrongbox */); 234 } 235 } 236 testDefaultKeySize(boolean useStrongbox)237 private void testDefaultKeySize(boolean useStrongbox) throws Exception { 238 for (String algorithm : 239 (useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS)) { 240 try { 241 int expectedSizeBits = DEFAULT_KEY_SIZES.get(algorithm); 242 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 243 keyGenerator.init(getWorkingSpec().build()); 244 SecretKey key = keyGenerator.generateKey(); 245 assertEquals(expectedSizeBits, TestUtils.getKeyInfo(key).getKeySize()); 246 } catch (Throwable e) { 247 throw new RuntimeException("Failed for " + algorithm, e); 248 } 249 } 250 } 251 testAesKeySupportedSizes()252 public void testAesKeySupportedSizes() throws Exception { 253 testAesKeySupportedSizesHelper(false /* useStrongbox */); 254 if (TestUtils.hasStrongBox(getContext())) { 255 testAesKeySupportedSizesHelper(true /* useStrongbox */); 256 } 257 } 258 testAesKeySupportedSizesHelper(boolean useStrongbox)259 private void testAesKeySupportedSizesHelper(boolean useStrongbox) throws Exception { 260 KeyGenerator keyGenerator = getKeyGenerator("AES"); 261 KeyGenParameterSpec.Builder goodSpec = getWorkingSpec(); 262 CountingSecureRandom rng = new CountingSecureRandom(); 263 for (int i = -16; i <= 512; i++) { 264 try { 265 rng.resetCounters(); 266 KeyGenParameterSpec spec; 267 if (i >= 0) { 268 spec = TestUtils.buildUpon( 269 goodSpec.setKeySize(i)).setIsStrongBoxBacked(useStrongbox).build(); 270 } else { 271 try { 272 spec = TestUtils.buildUpon( 273 goodSpec.setKeySize(i)).setIsStrongBoxBacked(useStrongbox).build(); 274 fail(); 275 } catch (IllegalArgumentException expected) { 276 continue; 277 } 278 } 279 rng.resetCounters(); 280 if (TestUtils.contains(AES_SUPPORTED_KEY_SIZES, i)) { 281 keyGenerator.init(spec, rng); 282 SecretKey key = keyGenerator.generateKey(); 283 assertEquals(i, TestUtils.getKeyInfo(key).getKeySize()); 284 assertEquals((i + 7) / 8, rng.getOutputSizeBytes()); 285 } else { 286 try { 287 keyGenerator.init(spec, rng); 288 fail(); 289 } catch (InvalidAlgorithmParameterException expected) {} 290 assertEquals(0, rng.getOutputSizeBytes()); 291 } 292 } catch (Throwable e) { 293 throw new RuntimeException("Failed for key size " + i, e); 294 } 295 } 296 } 297 298 // TODO: This test will fail until b/117509689 is resolved. testDESKeySupportedSizes()299 public void testDESKeySupportedSizes() throws Exception { 300 if (!TestUtils.supports3DES()) { 301 return; 302 } 303 KeyGenerator keyGenerator = getKeyGenerator("DESede"); 304 KeyGenParameterSpec.Builder goodSpec = getWorkingSpec(); 305 CountingSecureRandom rng = new CountingSecureRandom(); 306 for (int i = -16; i <= 168; i++) { 307 try { 308 rng.resetCounters(); 309 KeyGenParameterSpec spec; 310 if (i >= 0) { 311 spec = TestUtils.buildUpon(goodSpec.setKeySize(i)).build(); 312 } else { 313 try { 314 spec = TestUtils.buildUpon(goodSpec.setKeySize(i)).build(); 315 fail(); 316 } catch (IllegalArgumentException expected) { 317 continue; 318 } 319 } 320 rng.resetCounters(); 321 if (TestUtils.contains(DES_SUPPORTED_KEY_SIZES, i)) { 322 keyGenerator.init(spec, rng); 323 SecretKey key = keyGenerator.generateKey(); 324 assertEquals(i, TestUtils.getKeyInfo(key).getKeySize()); 325 } else { 326 try { 327 keyGenerator.init(spec, rng); 328 fail(); 329 } catch (InvalidAlgorithmParameterException expected) {} 330 assertEquals(0, rng.getOutputSizeBytes()); 331 } 332 } catch (Throwable e) { 333 throw new RuntimeException("Failed for key size " + i + 334 "\n***This test will continue to fail until b/117509689 is resolved***", e); 335 } 336 } 337 } 338 testHmacKeySupportedSizes()339 public void testHmacKeySupportedSizes() throws Exception { 340 testHmacKeySupportedSizesHelper(false /* useStrongbox */); 341 if (TestUtils.hasStrongBox(getContext())) { 342 testHmacKeySupportedSizesHelper(true /* useStrongbox */); 343 } 344 } 345 testHmacKeySupportedSizesHelper(boolean useStrongbox)346 private void testHmacKeySupportedSizesHelper(boolean useStrongbox) throws Exception { 347 CountingSecureRandom rng = new CountingSecureRandom(); 348 for (String algorithm : 349 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 350 if (!TestUtils.isHmacAlgorithm(algorithm)) { 351 continue; 352 } 353 354 for (int i = -16; i <= 1024; i++) { 355 try { 356 rng.resetCounters(); 357 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 358 KeyGenParameterSpec spec; 359 if (i >= 0) { 360 spec = getWorkingSpec() 361 .setKeySize(i) 362 .setIsStrongBoxBacked(useStrongbox) 363 .build(); 364 } else { 365 try { 366 spec = getWorkingSpec() 367 .setKeySize(i) 368 .setIsStrongBoxBacked(useStrongbox) 369 .build(); 370 fail(); 371 } catch (IllegalArgumentException expected) { 372 continue; 373 } 374 } 375 // Strongbox must not support keys larger than 512 bits 376 // TODO: This test currently fails, will be fixed on resolution of b/113525261 377 if (useStrongbox && i > 512) { 378 try { 379 keyGenerator.init(spec, rng); 380 fail(); 381 } catch (InvalidAlgorithmParameterException expected) { 382 assertEquals(0, rng.getOutputSizeBytes()); 383 } 384 } else if ((i >= 64) && ((i % 8 ) == 0)) { 385 keyGenerator.init(spec, rng); 386 SecretKey key = keyGenerator.generateKey(); 387 assertEquals(i, TestUtils.getKeyInfo(key).getKeySize()); 388 assertEquals((i + 7) / 8, rng.getOutputSizeBytes()); 389 } else if (i >= 64) { 390 try { 391 keyGenerator.init(spec, rng); 392 fail(); 393 } catch (InvalidAlgorithmParameterException expected) {} 394 assertEquals(0, rng.getOutputSizeBytes()); 395 } 396 } catch (Throwable e) { 397 throw new RuntimeException( 398 "Failed for " + algorithm + " with key size " + i 399 + ". Use Strongbox: " + useStrongbox, e); 400 } 401 } 402 } 403 } 404 testHmacKeyOnlyOneDigestCanBeAuthorized()405 public void testHmacKeyOnlyOneDigestCanBeAuthorized() throws Exception { 406 testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(false /* useStrongbox */); 407 if (TestUtils.hasStrongBox(getContext())) { 408 testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(true /* useStrongbox */); 409 } 410 } 411 testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(boolean useStrongbox)412 private void testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(boolean useStrongbox) 413 throws Exception { 414 for (String algorithm : 415 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 416 if (!TestUtils.isHmacAlgorithm(algorithm)) { 417 continue; 418 } 419 420 try { 421 String digest = TestUtils.getHmacAlgorithmDigest(algorithm); 422 assertNotNull(digest); 423 424 KeyGenParameterSpec.Builder goodSpec = new KeyGenParameterSpec.Builder( 425 "test1", KeyProperties.PURPOSE_SIGN); 426 427 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 428 429 // Digests authorization not specified in algorithm parameters 430 assertFalse(goodSpec 431 .setIsStrongBoxBacked(useStrongbox) 432 .build() 433 .isDigestsSpecified()); 434 keyGenerator.init(goodSpec.setIsStrongBoxBacked(useStrongbox).build()); 435 SecretKey key = keyGenerator.generateKey(); 436 TestUtils.assertContentsInAnyOrder( 437 Arrays.asList(TestUtils.getKeyInfo(key).getDigests()), digest); 438 439 // The same digest is specified in algorithm parameters 440 keyGenerator.init(TestUtils.buildUpon(goodSpec) 441 .setDigests(digest) 442 .setIsStrongBoxBacked(useStrongbox) 443 .build()); 444 key = keyGenerator.generateKey(); 445 TestUtils.assertContentsInAnyOrder( 446 Arrays.asList(TestUtils.getKeyInfo(key).getDigests()), digest); 447 448 // No digests specified in algorithm parameters 449 try { 450 keyGenerator.init(TestUtils.buildUpon(goodSpec) 451 .setDigests() 452 .setIsStrongBoxBacked(useStrongbox) 453 .build()); 454 fail(); 455 } catch (InvalidAlgorithmParameterException expected) {} 456 457 // A different digest specified in algorithm parameters 458 String anotherDigest = "SHA-256".equalsIgnoreCase(digest) ? "SHA-384" : "SHA-256"; 459 try { 460 keyGenerator.init(TestUtils.buildUpon(goodSpec) 461 .setDigests(anotherDigest) 462 .setIsStrongBoxBacked(useStrongbox) 463 .build()); 464 fail(); 465 } catch (InvalidAlgorithmParameterException expected) {} 466 try { 467 keyGenerator.init(TestUtils.buildUpon(goodSpec) 468 .setDigests(digest, anotherDigest) 469 .setIsStrongBoxBacked(useStrongbox) 470 .build()); 471 fail(); 472 } catch (InvalidAlgorithmParameterException expected) {} 473 } catch (Throwable e) { 474 throw new RuntimeException("Failed for " + algorithm, e); 475 } 476 } 477 } 478 testInitWithUnknownBlockModeFails()479 public void testInitWithUnknownBlockModeFails() { 480 testInitWithUnknownBlockModeFailsHelper(false /* useStrongbox */); 481 if (TestUtils.hasStrongBox(getContext())) { 482 testInitWithUnknownBlockModeFailsHelper(true /* useStrongbox */); 483 } 484 } 485 testInitWithUnknownBlockModeFailsHelper(boolean useStrongbox)486 public void testInitWithUnknownBlockModeFailsHelper(boolean useStrongbox) { 487 for (String algorithm : 488 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 489 try { 490 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 491 try { 492 keyGenerator.init( 493 getWorkingSpec() 494 .setBlockModes("weird") 495 .setIsStrongBoxBacked(useStrongbox) 496 .build()); 497 fail(); 498 } catch (InvalidAlgorithmParameterException expected) {} 499 } catch (Throwable e) { 500 throw new RuntimeException("Failed for " + algorithm, e); 501 } 502 } 503 } 504 testInitWithUnknownEncryptionPaddingFails()505 public void testInitWithUnknownEncryptionPaddingFails() { 506 testInitWithUnknownEncryptionPaddingFailsHelper(false /* useStrongbox */); 507 if (TestUtils.hasStrongBox(getContext())) { 508 testInitWithUnknownEncryptionPaddingFailsHelper(true /* useStrongbox */); 509 } 510 } 511 testInitWithUnknownEncryptionPaddingFailsHelper(boolean useStrongbox)512 private void testInitWithUnknownEncryptionPaddingFailsHelper(boolean useStrongbox) { 513 for (String algorithm : 514 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 515 try { 516 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 517 try { 518 keyGenerator.init( 519 getWorkingSpec() 520 .setEncryptionPaddings("weird") 521 .setIsStrongBoxBacked(useStrongbox) 522 .build()); 523 fail(); 524 } catch (InvalidAlgorithmParameterException expected) {} 525 } catch (Throwable e) { 526 throw new RuntimeException("Failed for " + algorithm, e); 527 } 528 } 529 } 530 testInitWithSignaturePaddingFails()531 public void testInitWithSignaturePaddingFails() { 532 testInitWithSignaturePaddingFailsHelper(false /* useStrongbox */); 533 if (TestUtils.hasStrongBox(getContext())) { 534 testInitWithSignaturePaddingFailsHelper(true /* useStrongbox */); 535 } 536 } 537 testInitWithSignaturePaddingFailsHelper(boolean useStrongbox)538 private void testInitWithSignaturePaddingFailsHelper(boolean useStrongbox) { 539 for (String algorithm : 540 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 541 try { 542 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 543 try { 544 keyGenerator.init(getWorkingSpec() 545 .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) 546 .setIsStrongBoxBacked(useStrongbox) 547 .build()); 548 fail(); 549 } catch (InvalidAlgorithmParameterException expected) {} 550 } catch (Throwable e) { 551 throw new RuntimeException("Failed for " + algorithm, e); 552 } 553 } 554 } 555 testInitWithUnknownDigestFails()556 public void testInitWithUnknownDigestFails() { 557 testInitWithUnknownDigestFailsHelper(false /* useStrongbox */); 558 if (TestUtils.hasStrongBox(getContext())) { 559 testInitWithUnknownDigestFailsHelper(true /* useStrongbox */); 560 } 561 } 562 testInitWithUnknownDigestFailsHelper(boolean useStrongbox)563 public void testInitWithUnknownDigestFailsHelper(boolean useStrongbox) { 564 for (String algorithm : 565 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 566 try { 567 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 568 try { 569 String[] digests; 570 if (TestUtils.isHmacAlgorithm(algorithm)) { 571 // The digest from HMAC key algorithm must be specified in the list of 572 // authorized digests (if the list if provided). 573 digests = new String[] {algorithm, "weird"}; 574 } else { 575 digests = new String[] {"weird"}; 576 } 577 keyGenerator.init( 578 getWorkingSpec() 579 .setDigests(digests) 580 .setIsStrongBoxBacked(useStrongbox) 581 .build()); 582 fail(); 583 } catch (InvalidAlgorithmParameterException expected) {} 584 } catch (Throwable e) { 585 throw new RuntimeException("Failed for " + algorithm, e); 586 } 587 } 588 } 589 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFails()590 public void testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFails() { 591 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper( 592 false /* useStrongbox */); 593 if (TestUtils.hasStrongBox(getContext())) { 594 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper( 595 true /* useStrongbox */); 596 } 597 } 598 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper(boolean useStrongbox)599 private void testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper(boolean useStrongbox) { 600 for (String algorithm : 601 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 602 if (!TestUtils.isHmacAlgorithm(algorithm)) { 603 continue; 604 } 605 try { 606 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 607 608 // Authorized for digest(s) none of which is the one implied by key algorithm. 609 try { 610 String digest = TestUtils.getHmacAlgorithmDigest(algorithm); 611 String anotherDigest = KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest) 612 ? KeyProperties.DIGEST_SHA512 : KeyProperties.DIGEST_SHA256; 613 keyGenerator.init( 614 getWorkingSpec() 615 .setDigests(anotherDigest) 616 .setIsStrongBoxBacked(useStrongbox) 617 .build()); 618 fail(); 619 } catch (InvalidAlgorithmParameterException expected) {} 620 621 // Authorized for empty set of digests 622 try { 623 keyGenerator.init( 624 getWorkingSpec() 625 .setDigests() 626 .setIsStrongBoxBacked(useStrongbox) 627 .build()); 628 fail(); 629 } catch (InvalidAlgorithmParameterException expected) {} 630 } catch (Throwable e) { 631 throw new RuntimeException("Failed for " + algorithm, e); 632 } 633 } 634 } 635 testInitRandomizedEncryptionRequiredButViolatedFails()636 public void testInitRandomizedEncryptionRequiredButViolatedFails() throws Exception { 637 testInitRandomizedEncryptionRequiredButViolatedFailsHelper(false /* useStrongbox */); 638 if (TestUtils.hasStrongBox(getContext())) { 639 testInitRandomizedEncryptionRequiredButViolatedFailsHelper(true /* useStrongbox */); 640 } 641 } 642 testInitRandomizedEncryptionRequiredButViolatedFailsHelper(boolean useStrongbox)643 public void testInitRandomizedEncryptionRequiredButViolatedFailsHelper(boolean useStrongbox) 644 throws Exception { 645 for (String algorithm : 646 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 647 try { 648 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 649 try { 650 keyGenerator.init(getWorkingSpec( 651 KeyProperties.PURPOSE_ENCRYPT) 652 .setBlockModes(KeyProperties.BLOCK_MODE_ECB) 653 .setIsStrongBoxBacked(useStrongbox) 654 .build()); 655 fail(); 656 } catch (InvalidAlgorithmParameterException expected) {} 657 } catch (Throwable e) { 658 throw new RuntimeException("Failed for " + algorithm, e); 659 } 660 } 661 } 662 testGenerateHonorsRequestedAuthorizations()663 public void testGenerateHonorsRequestedAuthorizations() throws Exception { 664 testGenerateHonorsRequestedAuthorizationsHelper(false /* useStrongbox */); 665 if (TestUtils.hasStrongBox(getContext())) { 666 testGenerateHonorsRequestedAuthorizationsHelper(true /* useStrongbox */); 667 } 668 } 669 testGenerateHonorsRequestedAuthorizationsHelper(boolean useStrongbox)670 private void testGenerateHonorsRequestedAuthorizationsHelper(boolean useStrongbox) 671 throws Exception { 672 Date keyValidityStart = new Date(System.currentTimeMillis() - TestUtils.DAY_IN_MILLIS); 673 Date keyValidityForOriginationEnd = 674 new Date(System.currentTimeMillis() + TestUtils.DAY_IN_MILLIS); 675 Date keyValidityForConsumptionEnd = 676 new Date(System.currentTimeMillis() + 3 * TestUtils.DAY_IN_MILLIS); 677 for (String algorithm : 678 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 679 try { 680 String[] blockModes = 681 new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC}; 682 String[] encryptionPaddings = 683 new String[] {KeyProperties.ENCRYPTION_PADDING_PKCS7, 684 KeyProperties.ENCRYPTION_PADDING_NONE}; 685 String[] digests; 686 int purposes; 687 if (TestUtils.isHmacAlgorithm(algorithm)) { 688 // HMAC key can only be authorized for one digest, the one implied by the key's 689 // JCA algorithm name. 690 digests = new String[] {TestUtils.getHmacAlgorithmDigest(algorithm)}; 691 purposes = KeyProperties.PURPOSE_SIGN; 692 } else { 693 digests = new String[] {KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA1}; 694 purposes = KeyProperties.PURPOSE_DECRYPT; 695 } 696 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 697 keyGenerator.init(getWorkingSpec(purposes) 698 .setBlockModes(blockModes) 699 .setEncryptionPaddings(encryptionPaddings) 700 .setDigests(digests) 701 .setKeyValidityStart(keyValidityStart) 702 .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd) 703 .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd) 704 .setIsStrongBoxBacked(useStrongbox) 705 .build()); 706 SecretKey key = keyGenerator.generateKey(); 707 assertEquals(algorithm, key.getAlgorithm()); 708 709 KeyInfo keyInfo = TestUtils.getKeyInfo(key); 710 assertEquals(purposes, keyInfo.getPurposes()); 711 TestUtils.assertContentsInAnyOrder( 712 Arrays.asList(blockModes), keyInfo.getBlockModes()); 713 TestUtils.assertContentsInAnyOrder( 714 Arrays.asList(encryptionPaddings), keyInfo.getEncryptionPaddings()); 715 TestUtils.assertContentsInAnyOrder(Arrays.asList(digests), keyInfo.getDigests()); 716 MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings())); 717 assertEquals(keyValidityStart, keyInfo.getKeyValidityStart()); 718 assertEquals(keyValidityForOriginationEnd, 719 keyInfo.getKeyValidityForOriginationEnd()); 720 assertEquals(keyValidityForConsumptionEnd, 721 keyInfo.getKeyValidityForConsumptionEnd()); 722 assertFalse(keyInfo.isUserAuthenticationRequired()); 723 assertFalse(keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()); 724 } catch (Throwable e) { 725 throw new RuntimeException("Failed for " + algorithm, e); 726 } 727 } 728 } 729 getWorkingSpec()730 private static KeyGenParameterSpec.Builder getWorkingSpec() { 731 return getWorkingSpec(0); 732 } 733 getWorkingSpec(int purposes)734 private static KeyGenParameterSpec.Builder getWorkingSpec(int purposes) { 735 return new KeyGenParameterSpec.Builder("test1", purposes); 736 } 737 getKeyGenerator(String algorithm)738 private static KeyGenerator getKeyGenerator(String algorithm) throws NoSuchAlgorithmException, 739 NoSuchProviderException { 740 return KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME); 741 } 742 } 743