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