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.security.keystore;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.StringDef;
23 import android.security.keymaster.KeymasterDefs;
24 
25 import libcore.util.EmptyArray;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.Collection;
30 import java.util.Locale;
31 
32 /**
33  * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
34  */
35 public abstract class KeyProperties {
KeyProperties()36     private KeyProperties() {}
37 
38     /**
39      * @hide
40      */
41     @Retention(RetentionPolicy.SOURCE)
42     @IntDef(flag = true,
43             value = {
44                 PURPOSE_ENCRYPT,
45                 PURPOSE_DECRYPT,
46                 PURPOSE_SIGN,
47                 PURPOSE_VERIFY,
48                 })
49     public @interface PurposeEnum {}
50 
51     /**
52      * Purpose of key: encryption.
53      */
54     public static final int PURPOSE_ENCRYPT = 1 << 0;
55 
56     /**
57      * Purpose of key: decryption.
58      */
59     public static final int PURPOSE_DECRYPT = 1 << 1;
60 
61     /**
62      * Purpose of key: signing or generating a Message Authentication Code (MAC).
63      */
64     public static final int PURPOSE_SIGN = 1 << 2;
65 
66     /**
67      * Purpose of key: signature or Message Authentication Code (MAC) verification.
68      */
69     public static final int PURPOSE_VERIFY = 1 << 3;
70 
71     /**
72      * @hide
73      */
74     public static abstract class Purpose {
Purpose()75         private Purpose() {}
76 
toKeymaster(@urposeEnum int purpose)77         public static int toKeymaster(@PurposeEnum int purpose) {
78             switch (purpose) {
79                 case PURPOSE_ENCRYPT:
80                     return KeymasterDefs.KM_PURPOSE_ENCRYPT;
81                 case PURPOSE_DECRYPT:
82                     return KeymasterDefs.KM_PURPOSE_DECRYPT;
83                 case PURPOSE_SIGN:
84                     return KeymasterDefs.KM_PURPOSE_SIGN;
85                 case PURPOSE_VERIFY:
86                     return KeymasterDefs.KM_PURPOSE_VERIFY;
87                 default:
88                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
89             }
90         }
91 
fromKeymaster(int purpose)92         public static @PurposeEnum int fromKeymaster(int purpose) {
93             switch (purpose) {
94                 case KeymasterDefs.KM_PURPOSE_ENCRYPT:
95                     return PURPOSE_ENCRYPT;
96                 case KeymasterDefs.KM_PURPOSE_DECRYPT:
97                     return PURPOSE_DECRYPT;
98                 case KeymasterDefs.KM_PURPOSE_SIGN:
99                     return PURPOSE_SIGN;
100                 case KeymasterDefs.KM_PURPOSE_VERIFY:
101                     return PURPOSE_VERIFY;
102                 default:
103                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
104             }
105         }
106 
107         @NonNull
allToKeymaster(@urposeEnum int purposes)108         public static int[] allToKeymaster(@PurposeEnum int purposes) {
109             int[] result = getSetFlags(purposes);
110             for (int i = 0; i < result.length; i++) {
111                 result[i] = toKeymaster(result[i]);
112             }
113             return result;
114         }
115 
allFromKeymaster(@onNull Collection<Integer> purposes)116         public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
117             @PurposeEnum int result = 0;
118             for (int keymasterPurpose : purposes) {
119                 result |= fromKeymaster(keymasterPurpose);
120             }
121             return result;
122         }
123     }
124 
125     /**
126      * @hide
127      */
128     @Retention(RetentionPolicy.SOURCE)
129     @StringDef({
130         KEY_ALGORITHM_RSA,
131         KEY_ALGORITHM_EC,
132         KEY_ALGORITHM_AES,
133         KEY_ALGORITHM_HMAC_SHA1,
134         KEY_ALGORITHM_HMAC_SHA224,
135         KEY_ALGORITHM_HMAC_SHA256,
136         KEY_ALGORITHM_HMAC_SHA384,
137         KEY_ALGORITHM_HMAC_SHA512,
138         })
139     public @interface KeyAlgorithmEnum {}
140 
141     /** Rivest Shamir Adleman (RSA) key. */
142     public static final String KEY_ALGORITHM_RSA = "RSA";
143 
144     /** Elliptic Curve (EC) Cryptography key. */
145     public static final String KEY_ALGORITHM_EC = "EC";
146 
147     /** Advanced Encryption Standard (AES) key. */
148     public static final String KEY_ALGORITHM_AES = "AES";
149 
150     /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
151     public static final String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";
152 
153     /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
154     public static final String KEY_ALGORITHM_HMAC_SHA224 = "HmacSHA224";
155 
156     /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
157     public static final String KEY_ALGORITHM_HMAC_SHA256 = "HmacSHA256";
158 
159     /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
160     public static final String KEY_ALGORITHM_HMAC_SHA384 = "HmacSHA384";
161 
162     /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
163     public static final String KEY_ALGORITHM_HMAC_SHA512 = "HmacSHA512";
164 
165     /**
166      * @hide
167      */
168     public static abstract class KeyAlgorithm {
KeyAlgorithm()169         private KeyAlgorithm() {}
170 
toKeymasterAsymmetricKeyAlgorithm( @onNull @eyAlgorithmEnum String algorithm)171         public static int toKeymasterAsymmetricKeyAlgorithm(
172                 @NonNull @KeyAlgorithmEnum String algorithm) {
173             if (KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
174                 return KeymasterDefs.KM_ALGORITHM_EC;
175             } else if (KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
176                 return KeymasterDefs.KM_ALGORITHM_RSA;
177             } else {
178                 throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
179             }
180         }
181 
182         @NonNull
fromKeymasterAsymmetricKeyAlgorithm( int keymasterAlgorithm)183         public static @KeyAlgorithmEnum String fromKeymasterAsymmetricKeyAlgorithm(
184                 int keymasterAlgorithm) {
185             switch (keymasterAlgorithm) {
186                 case KeymasterDefs.KM_ALGORITHM_EC:
187                     return KEY_ALGORITHM_EC;
188                 case KeymasterDefs.KM_ALGORITHM_RSA:
189                     return KEY_ALGORITHM_RSA;
190                 default:
191                     throw new IllegalArgumentException(
192                             "Unsupported key algorithm: " + keymasterAlgorithm);
193             }
194         }
195 
toKeymasterSecretKeyAlgorithm( @onNull @eyAlgorithmEnum String algorithm)196         public static int toKeymasterSecretKeyAlgorithm(
197                 @NonNull @KeyAlgorithmEnum String algorithm) {
198             if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
199                 return KeymasterDefs.KM_ALGORITHM_AES;
200             } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
201                 return KeymasterDefs.KM_ALGORITHM_HMAC;
202             } else {
203                 throw new IllegalArgumentException(
204                         "Unsupported secret key algorithm: " + algorithm);
205             }
206         }
207 
208         @NonNull
fromKeymasterSecretKeyAlgorithm( int keymasterAlgorithm, int keymasterDigest)209         public static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
210                 int keymasterAlgorithm, int keymasterDigest) {
211             switch (keymasterAlgorithm) {
212                 case KeymasterDefs.KM_ALGORITHM_AES:
213                     return KEY_ALGORITHM_AES;
214                 case KeymasterDefs.KM_ALGORITHM_HMAC:
215                     switch (keymasterDigest) {
216                         case KeymasterDefs.KM_DIGEST_SHA1:
217                             return KEY_ALGORITHM_HMAC_SHA1;
218                         case KeymasterDefs.KM_DIGEST_SHA_2_224:
219                             return KEY_ALGORITHM_HMAC_SHA224;
220                         case KeymasterDefs.KM_DIGEST_SHA_2_256:
221                             return KEY_ALGORITHM_HMAC_SHA256;
222                         case KeymasterDefs.KM_DIGEST_SHA_2_384:
223                             return KEY_ALGORITHM_HMAC_SHA384;
224                         case KeymasterDefs.KM_DIGEST_SHA_2_512:
225                             return KEY_ALGORITHM_HMAC_SHA512;
226                         default:
227                             throw new IllegalArgumentException("Unsupported HMAC digest: "
228                                     + Digest.fromKeymaster(keymasterDigest));
229                     }
230                 default:
231                     throw new IllegalArgumentException(
232                             "Unsupported key algorithm: " + keymasterAlgorithm);
233             }
234         }
235 
236         /**
237          * @hide
238          *
239          * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
240          */
toKeymasterDigest(@onNull @eyAlgorithmEnum String algorithm)241         public static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum String algorithm) {
242             String algorithmUpper = algorithm.toUpperCase(Locale.US);
243             if (algorithmUpper.startsWith("HMAC")) {
244                 String digestUpper = algorithmUpper.substring("HMAC".length());
245                 switch (digestUpper) {
246                     case "SHA1":
247                         return KeymasterDefs.KM_DIGEST_SHA1;
248                     case "SHA224":
249                         return KeymasterDefs.KM_DIGEST_SHA_2_224;
250                     case "SHA256":
251                         return KeymasterDefs.KM_DIGEST_SHA_2_256;
252                     case "SHA384":
253                         return KeymasterDefs.KM_DIGEST_SHA_2_384;
254                     case "SHA512":
255                         return KeymasterDefs.KM_DIGEST_SHA_2_512;
256                     default:
257                         throw new IllegalArgumentException(
258                                 "Unsupported HMAC digest: " + digestUpper);
259                 }
260             } else {
261                 return -1;
262             }
263         }
264     }
265 
266     /**
267      * @hide
268      */
269     @Retention(RetentionPolicy.SOURCE)
270     @StringDef({
271         BLOCK_MODE_ECB,
272         BLOCK_MODE_CBC,
273         BLOCK_MODE_CTR,
274         BLOCK_MODE_GCM,
275         })
276     public @interface BlockModeEnum {}
277 
278     /** Electronic Codebook (ECB) block mode. */
279     public static final String BLOCK_MODE_ECB = "ECB";
280 
281     /** Cipher Block Chaining (CBC) block mode. */
282     public static final String BLOCK_MODE_CBC = "CBC";
283 
284     /** Counter (CTR) block mode. */
285     public static final String BLOCK_MODE_CTR = "CTR";
286 
287     /** Galois/Counter Mode (GCM) block mode. */
288     public static final String BLOCK_MODE_GCM = "GCM";
289 
290     /**
291      * @hide
292      */
293     public static abstract class BlockMode {
BlockMode()294         private BlockMode() {}
295 
toKeymaster(@onNull @lockModeEnum String blockMode)296         public static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
297             if (BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
298                 return KeymasterDefs.KM_MODE_ECB;
299             } else if (BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
300                 return KeymasterDefs.KM_MODE_CBC;
301             } else if (BLOCK_MODE_CTR.equalsIgnoreCase(blockMode)) {
302                 return KeymasterDefs.KM_MODE_CTR;
303             } else if (BLOCK_MODE_GCM.equalsIgnoreCase(blockMode)) {
304                 return KeymasterDefs.KM_MODE_GCM;
305             } else {
306                 throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
307             }
308         }
309 
310         @NonNull
fromKeymaster(int blockMode)311         public static @BlockModeEnum String fromKeymaster(int blockMode) {
312             switch (blockMode) {
313                 case KeymasterDefs.KM_MODE_ECB:
314                     return BLOCK_MODE_ECB;
315                 case KeymasterDefs.KM_MODE_CBC:
316                     return BLOCK_MODE_CBC;
317                 case KeymasterDefs.KM_MODE_CTR:
318                     return BLOCK_MODE_CTR;
319                 case KeymasterDefs.KM_MODE_GCM:
320                     return BLOCK_MODE_GCM;
321                 default:
322                     throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
323             }
324         }
325 
326         @NonNull
allFromKeymaster( @onNull Collection<Integer> blockModes)327         public static @BlockModeEnum String[] allFromKeymaster(
328                 @NonNull Collection<Integer> blockModes) {
329             if ((blockModes == null) || (blockModes.isEmpty())) {
330                 return EmptyArray.STRING;
331             }
332             @BlockModeEnum String[] result = new String[blockModes.size()];
333             int offset = 0;
334             for (int blockMode : blockModes) {
335                 result[offset] = fromKeymaster(blockMode);
336                 offset++;
337             }
338             return result;
339         }
340 
allToKeymaster(@ullable @lockModeEnum String[] blockModes)341         public static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
342             if ((blockModes == null) || (blockModes.length == 0)) {
343                 return EmptyArray.INT;
344             }
345             int[] result = new int[blockModes.length];
346             for (int i = 0; i < blockModes.length; i++) {
347                 result[i] = toKeymaster(blockModes[i]);
348             }
349             return result;
350         }
351     }
352 
353     /**
354      * @hide
355      */
356     @Retention(RetentionPolicy.SOURCE)
357     @StringDef({
358         ENCRYPTION_PADDING_NONE,
359         ENCRYPTION_PADDING_PKCS7,
360         ENCRYPTION_PADDING_RSA_PKCS1,
361         ENCRYPTION_PADDING_RSA_OAEP,
362         })
363     public @interface EncryptionPaddingEnum {}
364 
365     /**
366      * No encryption padding.
367      */
368     public static final String ENCRYPTION_PADDING_NONE = "NoPadding";
369 
370     /**
371      * PKCS#7 encryption padding scheme.
372      */
373     public static final String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
374 
375     /**
376      * RSA PKCS#1 v1.5 padding scheme for encryption.
377      */
378     public static final String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
379 
380     /**
381      * RSA Optimal Asymmetric Encryption Padding (OAEP) scheme.
382      */
383     public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
384 
385     /**
386      * @hide
387      */
388     public static abstract class EncryptionPadding {
EncryptionPadding()389         private EncryptionPadding() {}
390 
toKeymaster(@onNull @ncryptionPaddingEnum String padding)391         public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
392             if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
393                 return KeymasterDefs.KM_PAD_NONE;
394             } else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
395                 return KeymasterDefs.KM_PAD_PKCS7;
396             } else if (ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
397                 return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
398             } else if (ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
399                 return KeymasterDefs.KM_PAD_RSA_OAEP;
400             } else {
401                 throw new IllegalArgumentException(
402                         "Unsupported encryption padding scheme: " + padding);
403             }
404         }
405 
406         @NonNull
fromKeymaster(int padding)407         public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
408             switch (padding) {
409                 case KeymasterDefs.KM_PAD_NONE:
410                     return ENCRYPTION_PADDING_NONE;
411                 case KeymasterDefs.KM_PAD_PKCS7:
412                     return ENCRYPTION_PADDING_PKCS7;
413                 case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
414                     return ENCRYPTION_PADDING_RSA_PKCS1;
415                 case KeymasterDefs.KM_PAD_RSA_OAEP:
416                     return ENCRYPTION_PADDING_RSA_OAEP;
417                 default:
418                     throw new IllegalArgumentException(
419                             "Unsupported encryption padding: " + padding);
420             }
421         }
422 
423         @NonNull
allToKeymaster(@ullable @ncryptionPaddingEnum String[] paddings)424         public static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
425             if ((paddings == null) || (paddings.length == 0)) {
426                 return EmptyArray.INT;
427             }
428             int[] result = new int[paddings.length];
429             for (int i = 0; i < paddings.length; i++) {
430                 result[i] = toKeymaster(paddings[i]);
431             }
432             return result;
433         }
434     }
435 
436     /**
437      * @hide
438      */
439     @Retention(RetentionPolicy.SOURCE)
440     @StringDef({
441         SIGNATURE_PADDING_RSA_PKCS1,
442         SIGNATURE_PADDING_RSA_PSS,
443         })
444     public @interface SignaturePaddingEnum {}
445 
446     /**
447      * RSA PKCS#1 v1.5 padding for signatures.
448      */
449     public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
450 
451     /**
452      * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
453      */
454     public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
455 
456     static abstract class SignaturePadding {
SignaturePadding()457         private SignaturePadding() {}
458 
toKeymaster(@onNull @ignaturePaddingEnum String padding)459         static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
460             switch (padding.toUpperCase(Locale.US)) {
461                 case SIGNATURE_PADDING_RSA_PKCS1:
462                     return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
463                 case SIGNATURE_PADDING_RSA_PSS:
464                     return KeymasterDefs.KM_PAD_RSA_PSS;
465                 default:
466                     throw new IllegalArgumentException(
467                             "Unsupported signature padding scheme: " + padding);
468             }
469         }
470 
471         @NonNull
fromKeymaster(int padding)472         static @SignaturePaddingEnum String fromKeymaster(int padding) {
473             switch (padding) {
474                 case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
475                     return SIGNATURE_PADDING_RSA_PKCS1;
476                 case KeymasterDefs.KM_PAD_RSA_PSS:
477                     return SIGNATURE_PADDING_RSA_PSS;
478                 default:
479                     throw new IllegalArgumentException("Unsupported signature padding: " + padding);
480             }
481         }
482 
483         @NonNull
allToKeymaster(@ullable @ignaturePaddingEnum String[] paddings)484         static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) {
485             if ((paddings == null) || (paddings.length == 0)) {
486                 return EmptyArray.INT;
487             }
488             int[] result = new int[paddings.length];
489             for (int i = 0; i < paddings.length; i++) {
490                 result[i] = toKeymaster(paddings[i]);
491             }
492             return result;
493         }
494     }
495 
496     /**
497      * @hide
498      */
499     @Retention(RetentionPolicy.SOURCE)
500     @StringDef({
501         DIGEST_NONE,
502         DIGEST_MD5,
503         DIGEST_SHA1,
504         DIGEST_SHA224,
505         DIGEST_SHA256,
506         DIGEST_SHA384,
507         DIGEST_SHA512,
508         })
509     public @interface DigestEnum {}
510 
511     /**
512      * No digest: sign/authenticate the raw message.
513      */
514     public static final String DIGEST_NONE = "NONE";
515 
516     /**
517      * MD5 digest.
518      */
519     public static final String DIGEST_MD5 = "MD5";
520 
521     /**
522      * SHA-1 digest.
523      */
524     public static final String DIGEST_SHA1 = "SHA-1";
525 
526     /**
527      * SHA-2 224 (aka SHA-224) digest.
528      */
529     public static final String DIGEST_SHA224 = "SHA-224";
530 
531     /**
532      * SHA-2 256 (aka SHA-256) digest.
533      */
534     public static final String DIGEST_SHA256 = "SHA-256";
535 
536     /**
537      * SHA-2 384 (aka SHA-384) digest.
538      */
539     public static final String DIGEST_SHA384 = "SHA-384";
540 
541     /**
542      * SHA-2 512 (aka SHA-512) digest.
543      */
544     public static final String DIGEST_SHA512 = "SHA-512";
545 
546     /**
547      * @hide
548      */
549     public static abstract class Digest {
Digest()550         private Digest() {}
551 
toKeymaster(@onNull @igestEnum String digest)552         public static int toKeymaster(@NonNull @DigestEnum String digest) {
553             switch (digest.toUpperCase(Locale.US)) {
554                 case DIGEST_SHA1:
555                     return KeymasterDefs.KM_DIGEST_SHA1;
556                 case DIGEST_SHA224:
557                     return KeymasterDefs.KM_DIGEST_SHA_2_224;
558                 case DIGEST_SHA256:
559                     return KeymasterDefs.KM_DIGEST_SHA_2_256;
560                 case DIGEST_SHA384:
561                     return KeymasterDefs.KM_DIGEST_SHA_2_384;
562                 case DIGEST_SHA512:
563                     return KeymasterDefs.KM_DIGEST_SHA_2_512;
564                 case DIGEST_NONE:
565                     return KeymasterDefs.KM_DIGEST_NONE;
566                 case DIGEST_MD5:
567                     return KeymasterDefs.KM_DIGEST_MD5;
568                 default:
569                     throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
570             }
571         }
572 
573         @NonNull
fromKeymaster(int digest)574         public static @DigestEnum String fromKeymaster(int digest) {
575             switch (digest) {
576                 case KeymasterDefs.KM_DIGEST_NONE:
577                     return DIGEST_NONE;
578                 case KeymasterDefs.KM_DIGEST_MD5:
579                     return DIGEST_MD5;
580                 case KeymasterDefs.KM_DIGEST_SHA1:
581                     return DIGEST_SHA1;
582                 case KeymasterDefs.KM_DIGEST_SHA_2_224:
583                     return DIGEST_SHA224;
584                 case KeymasterDefs.KM_DIGEST_SHA_2_256:
585                     return DIGEST_SHA256;
586                 case KeymasterDefs.KM_DIGEST_SHA_2_384:
587                     return DIGEST_SHA384;
588                 case KeymasterDefs.KM_DIGEST_SHA_2_512:
589                     return DIGEST_SHA512;
590                 default:
591                     throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
592             }
593         }
594 
595         @NonNull
fromKeymasterToSignatureAlgorithmDigest(int digest)596         public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
597             switch (digest) {
598                 case KeymasterDefs.KM_DIGEST_NONE:
599                     return "NONE";
600                 case KeymasterDefs.KM_DIGEST_MD5:
601                     return "MD5";
602                 case KeymasterDefs.KM_DIGEST_SHA1:
603                     return "SHA1";
604                 case KeymasterDefs.KM_DIGEST_SHA_2_224:
605                     return "SHA224";
606                 case KeymasterDefs.KM_DIGEST_SHA_2_256:
607                     return "SHA256";
608                 case KeymasterDefs.KM_DIGEST_SHA_2_384:
609                     return "SHA384";
610                 case KeymasterDefs.KM_DIGEST_SHA_2_512:
611                     return "SHA512";
612                 default:
613                     throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
614             }
615         }
616 
617         @NonNull
allFromKeymaster(@onNull Collection<Integer> digests)618         public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
619             if (digests.isEmpty()) {
620                 return EmptyArray.STRING;
621             }
622             String[] result = new String[digests.size()];
623             int offset = 0;
624             for (int digest : digests) {
625                 result[offset] = fromKeymaster(digest);
626                 offset++;
627             }
628             return result;
629         }
630 
631         @NonNull
allToKeymaster(@ullable @igestEnum String[] digests)632         public static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
633             if ((digests == null) || (digests.length == 0)) {
634                 return EmptyArray.INT;
635             }
636             int[] result = new int[digests.length];
637             int offset = 0;
638             for (@DigestEnum String digest : digests) {
639                 result[offset] = toKeymaster(digest);
640                 offset++;
641             }
642             return result;
643         }
644     }
645 
646     /**
647      * @hide
648      */
649     @Retention(RetentionPolicy.SOURCE)
650     @IntDef({
651         ORIGIN_GENERATED,
652         ORIGIN_IMPORTED,
653         ORIGIN_UNKNOWN,
654         })
655     public @interface OriginEnum {}
656 
657     /** Key was generated inside AndroidKeyStore. */
658     public static final int ORIGIN_GENERATED = 1 << 0;
659 
660     /** Key was imported into AndroidKeyStore. */
661     public static final int ORIGIN_IMPORTED = 1 << 1;
662 
663     /**
664      * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
665      * implementation which does not record origin information.
666      */
667     public static final int ORIGIN_UNKNOWN = 1 << 2;
668 
669     /**
670      * @hide
671      */
672     public static abstract class Origin {
Origin()673         private Origin() {}
674 
fromKeymaster(int origin)675         public static @OriginEnum int fromKeymaster(int origin) {
676             switch (origin) {
677                 case KeymasterDefs.KM_ORIGIN_GENERATED:
678                     return ORIGIN_GENERATED;
679                 case KeymasterDefs.KM_ORIGIN_IMPORTED:
680                     return ORIGIN_IMPORTED;
681                 case KeymasterDefs.KM_ORIGIN_UNKNOWN:
682                     return ORIGIN_UNKNOWN;
683                 default:
684                     throw new IllegalArgumentException("Unknown origin: " + origin);
685             }
686         }
687     }
688 
getSetFlags(int flags)689     private static int[] getSetFlags(int flags) {
690         if (flags == 0) {
691             return EmptyArray.INT;
692         }
693         int result[] = new int[getSetBitCount(flags)];
694         int resultOffset = 0;
695         int flag = 1;
696         while (flags != 0) {
697             if ((flags & 1) != 0) {
698                 result[resultOffset] = flag;
699                 resultOffset++;
700             }
701             flags >>>= 1;
702             flag <<= 1;
703         }
704         return result;
705     }
706 
getSetBitCount(int value)707     private static int getSetBitCount(int value) {
708         if (value == 0) {
709             return 0;
710         }
711         int result = 0;
712         while (value != 0) {
713             if ((value & 1) != 0) {
714                 result++;
715             }
716             value >>>= 1;
717         }
718         return result;
719     }
720 }
721