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