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 if (i > 512) { 376 try { 377 keyGenerator.init(spec, rng); 378 fail(); 379 } catch (InvalidAlgorithmParameterException expected) { 380 assertEquals(0, rng.getOutputSizeBytes()); 381 } 382 } else if ((i >= 64) && ((i % 8 ) == 0)) { 383 keyGenerator.init(spec, rng); 384 SecretKey key = keyGenerator.generateKey(); 385 assertEquals(i, TestUtils.getKeyInfo(key).getKeySize()); 386 assertEquals((i + 7) / 8, rng.getOutputSizeBytes()); 387 } else if (i >= 64) { 388 try { 389 keyGenerator.init(spec, rng); 390 fail(); 391 } catch (InvalidAlgorithmParameterException expected) {} 392 assertEquals(0, rng.getOutputSizeBytes()); 393 } 394 } catch (Throwable e) { 395 throw new RuntimeException( 396 "Failed for " + algorithm + " with key size " + i 397 + ". Use Strongbox: " + useStrongbox, e); 398 } 399 } 400 } 401 } 402 testHmacKeyOnlyOneDigestCanBeAuthorized()403 public void testHmacKeyOnlyOneDigestCanBeAuthorized() throws Exception { 404 testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(false /* useStrongbox */); 405 if (TestUtils.hasStrongBox(getContext())) { 406 testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(true /* useStrongbox */); 407 } 408 } 409 testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(boolean useStrongbox)410 private void testHmacKeyOnlyOneDigestCanBeAuthorizedHelper(boolean useStrongbox) 411 throws Exception { 412 for (String algorithm : 413 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 414 if (!TestUtils.isHmacAlgorithm(algorithm)) { 415 continue; 416 } 417 418 try { 419 String digest = TestUtils.getHmacAlgorithmDigest(algorithm); 420 assertNotNull(digest); 421 422 KeyGenParameterSpec.Builder goodSpec = new KeyGenParameterSpec.Builder( 423 "test1", KeyProperties.PURPOSE_SIGN); 424 425 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 426 427 // Digests authorization not specified in algorithm parameters 428 assertFalse(goodSpec 429 .setIsStrongBoxBacked(useStrongbox) 430 .build() 431 .isDigestsSpecified()); 432 keyGenerator.init(goodSpec.setIsStrongBoxBacked(useStrongbox).build()); 433 SecretKey key = keyGenerator.generateKey(); 434 TestUtils.assertContentsInAnyOrder( 435 Arrays.asList(TestUtils.getKeyInfo(key).getDigests()), digest); 436 437 // The same digest is specified in algorithm parameters 438 keyGenerator.init(TestUtils.buildUpon(goodSpec) 439 .setDigests(digest) 440 .setIsStrongBoxBacked(useStrongbox) 441 .build()); 442 key = keyGenerator.generateKey(); 443 TestUtils.assertContentsInAnyOrder( 444 Arrays.asList(TestUtils.getKeyInfo(key).getDigests()), digest); 445 446 // No digests specified in algorithm parameters 447 try { 448 keyGenerator.init(TestUtils.buildUpon(goodSpec) 449 .setDigests() 450 .setIsStrongBoxBacked(useStrongbox) 451 .build()); 452 fail(); 453 } catch (InvalidAlgorithmParameterException expected) {} 454 455 // A different digest specified in algorithm parameters 456 String anotherDigest = "SHA-256".equalsIgnoreCase(digest) ? "SHA-384" : "SHA-256"; 457 try { 458 keyGenerator.init(TestUtils.buildUpon(goodSpec) 459 .setDigests(anotherDigest) 460 .setIsStrongBoxBacked(useStrongbox) 461 .build()); 462 fail(); 463 } catch (InvalidAlgorithmParameterException expected) {} 464 try { 465 keyGenerator.init(TestUtils.buildUpon(goodSpec) 466 .setDigests(digest, anotherDigest) 467 .setIsStrongBoxBacked(useStrongbox) 468 .build()); 469 fail(); 470 } catch (InvalidAlgorithmParameterException expected) {} 471 } catch (Throwable e) { 472 throw new RuntimeException("Failed for " + algorithm, e); 473 } 474 } 475 } 476 testInitWithUnknownBlockModeFails()477 public void testInitWithUnknownBlockModeFails() { 478 testInitWithUnknownBlockModeFailsHelper(false /* useStrongbox */); 479 if (TestUtils.hasStrongBox(getContext())) { 480 testInitWithUnknownBlockModeFailsHelper(true /* useStrongbox */); 481 } 482 } 483 testInitWithUnknownBlockModeFailsHelper(boolean useStrongbox)484 public void testInitWithUnknownBlockModeFailsHelper(boolean useStrongbox) { 485 for (String algorithm : 486 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 487 try { 488 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 489 try { 490 keyGenerator.init( 491 getWorkingSpec() 492 .setBlockModes("weird") 493 .setIsStrongBoxBacked(useStrongbox) 494 .build()); 495 fail(); 496 } catch (InvalidAlgorithmParameterException expected) {} 497 } catch (Throwable e) { 498 throw new RuntimeException("Failed for " + algorithm, e); 499 } 500 } 501 } 502 testInitWithUnknownEncryptionPaddingFails()503 public void testInitWithUnknownEncryptionPaddingFails() { 504 testInitWithUnknownEncryptionPaddingFailsHelper(false /* useStrongbox */); 505 if (TestUtils.hasStrongBox(getContext())) { 506 testInitWithUnknownEncryptionPaddingFailsHelper(true /* useStrongbox */); 507 } 508 } 509 testInitWithUnknownEncryptionPaddingFailsHelper(boolean useStrongbox)510 private void testInitWithUnknownEncryptionPaddingFailsHelper(boolean useStrongbox) { 511 for (String algorithm : 512 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 513 try { 514 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 515 try { 516 keyGenerator.init( 517 getWorkingSpec() 518 .setEncryptionPaddings("weird") 519 .setIsStrongBoxBacked(useStrongbox) 520 .build()); 521 fail(); 522 } catch (InvalidAlgorithmParameterException expected) {} 523 } catch (Throwable e) { 524 throw new RuntimeException("Failed for " + algorithm, e); 525 } 526 } 527 } 528 testInitWithSignaturePaddingFails()529 public void testInitWithSignaturePaddingFails() { 530 testInitWithSignaturePaddingFailsHelper(false /* useStrongbox */); 531 if (TestUtils.hasStrongBox(getContext())) { 532 testInitWithSignaturePaddingFailsHelper(true /* useStrongbox */); 533 } 534 } 535 testInitWithSignaturePaddingFailsHelper(boolean useStrongbox)536 private void testInitWithSignaturePaddingFailsHelper(boolean useStrongbox) { 537 for (String algorithm : 538 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 539 try { 540 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 541 try { 542 keyGenerator.init(getWorkingSpec() 543 .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1) 544 .setIsStrongBoxBacked(useStrongbox) 545 .build()); 546 fail(); 547 } catch (InvalidAlgorithmParameterException expected) {} 548 } catch (Throwable e) { 549 throw new RuntimeException("Failed for " + algorithm, e); 550 } 551 } 552 } 553 testInitWithUnknownDigestFails()554 public void testInitWithUnknownDigestFails() { 555 testInitWithUnknownDigestFailsHelper(false /* useStrongbox */); 556 if (TestUtils.hasStrongBox(getContext())) { 557 testInitWithUnknownDigestFailsHelper(true /* useStrongbox */); 558 } 559 } 560 testInitWithUnknownDigestFailsHelper(boolean useStrongbox)561 public void testInitWithUnknownDigestFailsHelper(boolean useStrongbox) { 562 for (String algorithm : 563 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 564 try { 565 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 566 try { 567 String[] digests; 568 if (TestUtils.isHmacAlgorithm(algorithm)) { 569 // The digest from HMAC key algorithm must be specified in the list of 570 // authorized digests (if the list if provided). 571 digests = new String[] {algorithm, "weird"}; 572 } else { 573 digests = new String[] {"weird"}; 574 } 575 keyGenerator.init( 576 getWorkingSpec() 577 .setDigests(digests) 578 .setIsStrongBoxBacked(useStrongbox) 579 .build()); 580 fail(); 581 } catch (InvalidAlgorithmParameterException expected) {} 582 } catch (Throwable e) { 583 throw new RuntimeException("Failed for " + algorithm, e); 584 } 585 } 586 } 587 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFails()588 public void testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFails() { 589 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper( 590 false /* useStrongbox */); 591 if (TestUtils.hasStrongBox(getContext())) { 592 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper( 593 true /* useStrongbox */); 594 } 595 } 596 testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper(boolean useStrongbox)597 private void testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFailsHelper(boolean useStrongbox) { 598 for (String algorithm : 599 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 600 if (!TestUtils.isHmacAlgorithm(algorithm)) { 601 continue; 602 } 603 try { 604 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 605 606 // Authorized for digest(s) none of which is the one implied by key algorithm. 607 try { 608 String digest = TestUtils.getHmacAlgorithmDigest(algorithm); 609 String anotherDigest = KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest) 610 ? KeyProperties.DIGEST_SHA512 : KeyProperties.DIGEST_SHA256; 611 keyGenerator.init( 612 getWorkingSpec() 613 .setDigests(anotherDigest) 614 .setIsStrongBoxBacked(useStrongbox) 615 .build()); 616 fail(); 617 } catch (InvalidAlgorithmParameterException expected) {} 618 619 // Authorized for empty set of digests 620 try { 621 keyGenerator.init( 622 getWorkingSpec() 623 .setDigests() 624 .setIsStrongBoxBacked(useStrongbox) 625 .build()); 626 fail(); 627 } catch (InvalidAlgorithmParameterException expected) {} 628 } catch (Throwable e) { 629 throw new RuntimeException("Failed for " + algorithm, e); 630 } 631 } 632 } 633 testInitRandomizedEncryptionRequiredButViolatedFails()634 public void testInitRandomizedEncryptionRequiredButViolatedFails() throws Exception { 635 testInitRandomizedEncryptionRequiredButViolatedFailsHelper(false /* useStrongbox */); 636 if (TestUtils.hasStrongBox(getContext())) { 637 testInitRandomizedEncryptionRequiredButViolatedFailsHelper(true /* useStrongbox */); 638 } 639 } 640 testInitRandomizedEncryptionRequiredButViolatedFailsHelper(boolean useStrongbox)641 public void testInitRandomizedEncryptionRequiredButViolatedFailsHelper(boolean useStrongbox) 642 throws Exception { 643 for (String algorithm : 644 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 645 try { 646 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 647 try { 648 keyGenerator.init(getWorkingSpec( 649 KeyProperties.PURPOSE_ENCRYPT) 650 .setBlockModes(KeyProperties.BLOCK_MODE_ECB) 651 .setIsStrongBoxBacked(useStrongbox) 652 .build()); 653 fail(); 654 } catch (InvalidAlgorithmParameterException expected) {} 655 } catch (Throwable e) { 656 throw new RuntimeException("Failed for " + algorithm, e); 657 } 658 } 659 } 660 testGenerateHonorsRequestedAuthorizations()661 public void testGenerateHonorsRequestedAuthorizations() throws Exception { 662 testGenerateHonorsRequestedAuthorizationsHelper(false /* useStrongbox */); 663 if (TestUtils.hasStrongBox(getContext())) { 664 testGenerateHonorsRequestedAuthorizationsHelper(true /* useStrongbox */); 665 } 666 } 667 testGenerateHonorsRequestedAuthorizationsHelper(boolean useStrongbox)668 private void testGenerateHonorsRequestedAuthorizationsHelper(boolean useStrongbox) 669 throws Exception { 670 Date keyValidityStart = new Date(System.currentTimeMillis() - TestUtils.DAY_IN_MILLIS); 671 Date keyValidityForOriginationEnd = 672 new Date(System.currentTimeMillis() + TestUtils.DAY_IN_MILLIS); 673 Date keyValidityForConsumptionEnd = 674 new Date(System.currentTimeMillis() + 3 * TestUtils.DAY_IN_MILLIS); 675 for (String algorithm : 676 useStrongbox ? EXPECTED_STRONGBOX_ALGORITHMS : EXPECTED_ALGORITHMS) { 677 try { 678 String[] blockModes = 679 new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC}; 680 String[] encryptionPaddings = 681 new String[] {KeyProperties.ENCRYPTION_PADDING_PKCS7, 682 KeyProperties.ENCRYPTION_PADDING_NONE}; 683 String[] digests; 684 int purposes; 685 if (TestUtils.isHmacAlgorithm(algorithm)) { 686 // HMAC key can only be authorized for one digest, the one implied by the key's 687 // JCA algorithm name. 688 digests = new String[] {TestUtils.getHmacAlgorithmDigest(algorithm)}; 689 purposes = KeyProperties.PURPOSE_SIGN; 690 } else { 691 digests = new String[] {KeyProperties.DIGEST_SHA384, KeyProperties.DIGEST_SHA1}; 692 purposes = KeyProperties.PURPOSE_DECRYPT; 693 } 694 KeyGenerator keyGenerator = getKeyGenerator(algorithm); 695 keyGenerator.init(getWorkingSpec(purposes) 696 .setBlockModes(blockModes) 697 .setEncryptionPaddings(encryptionPaddings) 698 .setDigests(digests) 699 .setKeyValidityStart(keyValidityStart) 700 .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd) 701 .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd) 702 .setIsStrongBoxBacked(useStrongbox) 703 .build()); 704 SecretKey key = keyGenerator.generateKey(); 705 assertEquals(algorithm, key.getAlgorithm()); 706 707 KeyInfo keyInfo = TestUtils.getKeyInfo(key); 708 assertEquals(purposes, keyInfo.getPurposes()); 709 TestUtils.assertContentsInAnyOrder( 710 Arrays.asList(blockModes), keyInfo.getBlockModes()); 711 TestUtils.assertContentsInAnyOrder( 712 Arrays.asList(encryptionPaddings), keyInfo.getEncryptionPaddings()); 713 TestUtils.assertContentsInAnyOrder(Arrays.asList(digests), keyInfo.getDigests()); 714 MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings())); 715 assertEquals(keyValidityStart, keyInfo.getKeyValidityStart()); 716 assertEquals(keyValidityForOriginationEnd, 717 keyInfo.getKeyValidityForOriginationEnd()); 718 assertEquals(keyValidityForConsumptionEnd, 719 keyInfo.getKeyValidityForConsumptionEnd()); 720 assertFalse(keyInfo.isUserAuthenticationRequired()); 721 assertFalse(keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware()); 722 } catch (Throwable e) { 723 throw new RuntimeException("Failed for " + algorithm, e); 724 } 725 } 726 } 727 getWorkingSpec()728 private static KeyGenParameterSpec.Builder getWorkingSpec() { 729 return getWorkingSpec(0); 730 } 731 getWorkingSpec(int purposes)732 private static KeyGenParameterSpec.Builder getWorkingSpec(int purposes) { 733 return new KeyGenParameterSpec.Builder("test1", purposes); 734 } 735 getKeyGenerator(String algorithm)736 private static KeyGenerator getKeyGenerator(String algorithm) throws NoSuchAlgorithmException, 737 NoSuchProviderException { 738 return KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME); 739 } 740 } 741