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