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