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