KeyProperties.java revision acb7efd0d6dbde2506bb333e400a281f422df3fc
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
17package android.security.keystore;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.annotation.StringDef;
23import android.security.keymaster.KeymasterDefs;
24
25import libcore.util.EmptyArray;
26
27import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29import java.util.Collection;
30import java.util.Locale;
31
32/**
33 * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
34 */
35public abstract class 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 {
75        private Purpose() {}
76
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
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
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
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 {
169        private KeyAlgorithm() {}
170
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
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
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
209        public static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
210                int keymasterAlgorithm, int keymasterDigest) {
211            switch (keymasterAlgorithm) {
212                case KeymasterDefs.KM_ALGORITHM_AES:
213                    if (keymasterDigest != -1) {
214                        throw new IllegalArgumentException("Digest not supported for AES key: "
215                                + Digest.fromKeymaster(keymasterDigest));
216                    }
217                    return KEY_ALGORITHM_AES;
218                case KeymasterDefs.KM_ALGORITHM_HMAC:
219                    switch (keymasterDigest) {
220                        case KeymasterDefs.KM_DIGEST_SHA1:
221                            return KEY_ALGORITHM_HMAC_SHA1;
222                        case KeymasterDefs.KM_DIGEST_SHA_2_224:
223                            return KEY_ALGORITHM_HMAC_SHA224;
224                        case KeymasterDefs.KM_DIGEST_SHA_2_256:
225                            return KEY_ALGORITHM_HMAC_SHA256;
226                        case KeymasterDefs.KM_DIGEST_SHA_2_384:
227                            return KEY_ALGORITHM_HMAC_SHA384;
228                        case KeymasterDefs.KM_DIGEST_SHA_2_512:
229                            return KEY_ALGORITHM_HMAC_SHA512;
230                        default:
231                            throw new IllegalArgumentException("Unsupported HMAC digest: "
232                                    + Digest.fromKeymaster(keymasterDigest));
233                    }
234                default:
235                    throw new IllegalArgumentException(
236                            "Unsupported key algorithm: " + keymasterAlgorithm);
237            }
238        }
239
240        /**
241         * @hide
242         *
243         * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
244         */
245        public static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum String algorithm) {
246            String algorithmUpper = algorithm.toUpperCase(Locale.US);
247            if (algorithmUpper.startsWith("HMAC")) {
248                String digestUpper = algorithmUpper.substring("HMAC".length());
249                switch (digestUpper) {
250                    case "SHA1":
251                        return KeymasterDefs.KM_DIGEST_SHA1;
252                    case "SHA224":
253                        return KeymasterDefs.KM_DIGEST_SHA_2_224;
254                    case "SHA256":
255                        return KeymasterDefs.KM_DIGEST_SHA_2_256;
256                    case "SHA384":
257                        return KeymasterDefs.KM_DIGEST_SHA_2_384;
258                    case "SHA512":
259                        return KeymasterDefs.KM_DIGEST_SHA_2_512;
260                    default:
261                        throw new IllegalArgumentException(
262                                "Unsupported HMAC digest: " + digestUpper);
263                }
264            } else {
265                return -1;
266            }
267        }
268    }
269
270    /**
271     * @hide
272     */
273    @Retention(RetentionPolicy.SOURCE)
274    @StringDef({
275        BLOCK_MODE_ECB,
276        BLOCK_MODE_CBC,
277        BLOCK_MODE_CTR,
278        BLOCK_MODE_GCM,
279        })
280    public @interface BlockModeEnum {}
281
282    /** Electronic Codebook (ECB) block mode. */
283    public static final String BLOCK_MODE_ECB = "ECB";
284
285    /** Cipher Block Chaining (CBC) block mode. */
286    public static final String BLOCK_MODE_CBC = "CBC";
287
288    /** Counter (CTR) block mode. */
289    public static final String BLOCK_MODE_CTR = "CTR";
290
291    /** Galois/Counter Mode (GCM) block mode. */
292    public static final String BLOCK_MODE_GCM = "GCM";
293
294    /**
295     * @hide
296     */
297    public static abstract class BlockMode {
298        private BlockMode() {}
299
300        public static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
301            if (BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
302                return KeymasterDefs.KM_MODE_ECB;
303            } else if (BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
304                return KeymasterDefs.KM_MODE_CBC;
305            } else if (BLOCK_MODE_CTR.equalsIgnoreCase(blockMode)) {
306                return KeymasterDefs.KM_MODE_CTR;
307            } else if (BLOCK_MODE_GCM.equalsIgnoreCase(blockMode)) {
308                return KeymasterDefs.KM_MODE_GCM;
309            } else {
310                throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
311            }
312        }
313
314        @NonNull
315        public static @BlockModeEnum String fromKeymaster(int blockMode) {
316            switch (blockMode) {
317                case KeymasterDefs.KM_MODE_ECB:
318                    return BLOCK_MODE_ECB;
319                case KeymasterDefs.KM_MODE_CBC:
320                    return BLOCK_MODE_CBC;
321                case KeymasterDefs.KM_MODE_CTR:
322                    return BLOCK_MODE_CTR;
323                case KeymasterDefs.KM_MODE_GCM:
324                    return BLOCK_MODE_GCM;
325                default:
326                    throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
327            }
328        }
329
330        @NonNull
331        public static @BlockModeEnum String[] allFromKeymaster(
332                @NonNull Collection<Integer> blockModes) {
333            if ((blockModes == null) || (blockModes.isEmpty())) {
334                return EmptyArray.STRING;
335            }
336            @BlockModeEnum String[] result = new String[blockModes.size()];
337            int offset = 0;
338            for (int blockMode : blockModes) {
339                result[offset] = fromKeymaster(blockMode);
340                offset++;
341            }
342            return result;
343        }
344
345        public static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
346            if ((blockModes == null) || (blockModes.length == 0)) {
347                return EmptyArray.INT;
348            }
349            int[] result = new int[blockModes.length];
350            for (int i = 0; i < blockModes.length; i++) {
351                result[i] = toKeymaster(blockModes[i]);
352            }
353            return result;
354        }
355    }
356
357    /**
358     * @hide
359     */
360    @Retention(RetentionPolicy.SOURCE)
361    @StringDef({
362        ENCRYPTION_PADDING_NONE,
363        ENCRYPTION_PADDING_PKCS7,
364        ENCRYPTION_PADDING_RSA_PKCS1,
365        ENCRYPTION_PADDING_RSA_OAEP,
366        })
367    public @interface EncryptionPaddingEnum {}
368
369    /**
370     * No encryption padding.
371     *
372     * <p><b>NOTE</b>: If a key is authorized to be used with no padding, then it can be used with
373     * any padding scheme, both for encryption and signing.
374     */
375    public static final String ENCRYPTION_PADDING_NONE = "NoPadding";
376
377    /**
378     * PKCS#7 encryption padding scheme.
379     */
380    public static final String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
381
382    /**
383     * RSA PKCS#1 v1.5 padding scheme for encryption.
384     */
385    public static final String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
386
387    /**
388     * RSA Optimal Asymmetric Encryption Padding (OAEP) scheme.
389     */
390    public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
391
392    /**
393     * @hide
394     */
395    public static abstract class EncryptionPadding {
396        private EncryptionPadding() {}
397
398        public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
399            if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
400                return KeymasterDefs.KM_PAD_NONE;
401            } else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
402                return KeymasterDefs.KM_PAD_PKCS7;
403            } else if (ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
404                return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
405            } else if (ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
406                return KeymasterDefs.KM_PAD_RSA_OAEP;
407            } else {
408                throw new IllegalArgumentException(
409                        "Unsupported encryption padding scheme: " + padding);
410            }
411        }
412
413        @NonNull
414        public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
415            switch (padding) {
416                case KeymasterDefs.KM_PAD_NONE:
417                    return ENCRYPTION_PADDING_NONE;
418                case KeymasterDefs.KM_PAD_PKCS7:
419                    return ENCRYPTION_PADDING_PKCS7;
420                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
421                    return ENCRYPTION_PADDING_RSA_PKCS1;
422                case KeymasterDefs.KM_PAD_RSA_OAEP:
423                    return ENCRYPTION_PADDING_RSA_OAEP;
424                default:
425                    throw new IllegalArgumentException(
426                            "Unsupported encryption padding: " + padding);
427            }
428        }
429
430        @NonNull
431        public static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
432            if ((paddings == null) || (paddings.length == 0)) {
433                return EmptyArray.INT;
434            }
435            int[] result = new int[paddings.length];
436            for (int i = 0; i < paddings.length; i++) {
437                result[i] = toKeymaster(paddings[i]);
438            }
439            return result;
440        }
441    }
442
443    /**
444     * @hide
445     */
446    @Retention(RetentionPolicy.SOURCE)
447    @StringDef({
448        SIGNATURE_PADDING_RSA_PKCS1,
449        SIGNATURE_PADDING_RSA_PSS,
450        })
451    public @interface SignaturePaddingEnum {}
452
453    /**
454     * RSA PKCS#1 v1.5 padding for signatures.
455     */
456    public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
457
458    /**
459     * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
460     */
461    public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
462
463    static abstract class SignaturePadding {
464        private SignaturePadding() {}
465
466        static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
467            switch (padding.toUpperCase(Locale.US)) {
468                case SIGNATURE_PADDING_RSA_PKCS1:
469                    return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
470                case SIGNATURE_PADDING_RSA_PSS:
471                    return KeymasterDefs.KM_PAD_RSA_PSS;
472                default:
473                    throw new IllegalArgumentException(
474                            "Unsupported signature padding scheme: " + padding);
475            }
476        }
477
478        @NonNull
479        static @SignaturePaddingEnum String fromKeymaster(int padding) {
480            switch (padding) {
481                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
482                    return SIGNATURE_PADDING_RSA_PKCS1;
483                case KeymasterDefs.KM_PAD_RSA_PSS:
484                    return SIGNATURE_PADDING_RSA_PSS;
485                default:
486                    throw new IllegalArgumentException("Unsupported signature padding: " + padding);
487            }
488        }
489
490        @NonNull
491        static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) {
492            if ((paddings == null) || (paddings.length == 0)) {
493                return EmptyArray.INT;
494            }
495            int[] result = new int[paddings.length];
496            for (int i = 0; i < paddings.length; i++) {
497                result[i] = toKeymaster(paddings[i]);
498            }
499            return result;
500        }
501    }
502
503    /**
504     * @hide
505     */
506    @Retention(RetentionPolicy.SOURCE)
507    @StringDef({
508        DIGEST_NONE,
509        DIGEST_MD5,
510        DIGEST_SHA1,
511        DIGEST_SHA224,
512        DIGEST_SHA256,
513        DIGEST_SHA384,
514        DIGEST_SHA512,
515        })
516    public @interface DigestEnum {}
517
518    /**
519     * No digest: sign/authenticate the raw message.
520     *
521     * <p><b>NOTE</b>: If a key is authorized to be used with no digest, then it can be used with
522     * any digest.
523     */
524    public static final String DIGEST_NONE = "NONE";
525
526    /**
527     * MD5 digest.
528     */
529    public static final String DIGEST_MD5 = "MD5";
530
531    /**
532     * SHA-1 digest.
533     */
534    public static final String DIGEST_SHA1 = "SHA-1";
535
536    /**
537     * SHA-2 224 (aka SHA-224) digest.
538     */
539    public static final String DIGEST_SHA224 = "SHA-224";
540
541    /**
542     * SHA-2 256 (aka SHA-256) digest.
543     */
544    public static final String DIGEST_SHA256 = "SHA-256";
545
546    /**
547     * SHA-2 384 (aka SHA-384) digest.
548     */
549    public static final String DIGEST_SHA384 = "SHA-384";
550
551    /**
552     * SHA-2 512 (aka SHA-512) digest.
553     */
554    public static final String DIGEST_SHA512 = "SHA-512";
555
556    /**
557     * @hide
558     */
559    public static abstract class Digest {
560        private Digest() {}
561
562        public static int toKeymaster(@NonNull @DigestEnum String digest) {
563            switch (digest.toUpperCase(Locale.US)) {
564                case DIGEST_SHA1:
565                    return KeymasterDefs.KM_DIGEST_SHA1;
566                case DIGEST_SHA224:
567                    return KeymasterDefs.KM_DIGEST_SHA_2_224;
568                case DIGEST_SHA256:
569                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
570                case DIGEST_SHA384:
571                    return KeymasterDefs.KM_DIGEST_SHA_2_384;
572                case DIGEST_SHA512:
573                    return KeymasterDefs.KM_DIGEST_SHA_2_512;
574                case DIGEST_NONE:
575                    return KeymasterDefs.KM_DIGEST_NONE;
576                case DIGEST_MD5:
577                    return KeymasterDefs.KM_DIGEST_MD5;
578                default:
579                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
580            }
581        }
582
583        @NonNull
584        public static @DigestEnum String fromKeymaster(int digest) {
585            switch (digest) {
586                case KeymasterDefs.KM_DIGEST_NONE:
587                    return DIGEST_NONE;
588                case KeymasterDefs.KM_DIGEST_MD5:
589                    return DIGEST_MD5;
590                case KeymasterDefs.KM_DIGEST_SHA1:
591                    return DIGEST_SHA1;
592                case KeymasterDefs.KM_DIGEST_SHA_2_224:
593                    return DIGEST_SHA224;
594                case KeymasterDefs.KM_DIGEST_SHA_2_256:
595                    return DIGEST_SHA256;
596                case KeymasterDefs.KM_DIGEST_SHA_2_384:
597                    return DIGEST_SHA384;
598                case KeymasterDefs.KM_DIGEST_SHA_2_512:
599                    return DIGEST_SHA512;
600                default:
601                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
602            }
603        }
604
605        @NonNull
606        public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
607            switch (digest) {
608                case KeymasterDefs.KM_DIGEST_NONE:
609                    return "NONE";
610                case KeymasterDefs.KM_DIGEST_MD5:
611                    return "MD5";
612                case KeymasterDefs.KM_DIGEST_SHA1:
613                    return "SHA1";
614                case KeymasterDefs.KM_DIGEST_SHA_2_224:
615                    return "SHA224";
616                case KeymasterDefs.KM_DIGEST_SHA_2_256:
617                    return "SHA256";
618                case KeymasterDefs.KM_DIGEST_SHA_2_384:
619                    return "SHA384";
620                case KeymasterDefs.KM_DIGEST_SHA_2_512:
621                    return "SHA512";
622                default:
623                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
624            }
625        }
626
627        @NonNull
628        public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
629            if (digests.isEmpty()) {
630                return EmptyArray.STRING;
631            }
632            String[] result = new String[digests.size()];
633            int offset = 0;
634            for (int digest : digests) {
635                result[offset] = fromKeymaster(digest);
636                offset++;
637            }
638            return result;
639        }
640
641        @NonNull
642        public static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
643            if ((digests == null) || (digests.length == 0)) {
644                return EmptyArray.INT;
645            }
646            int[] result = new int[digests.length];
647            int offset = 0;
648            for (@DigestEnum String digest : digests) {
649                result[offset] = toKeymaster(digest);
650                offset++;
651            }
652            return result;
653        }
654    }
655
656    /**
657     * @hide
658     */
659    @Retention(RetentionPolicy.SOURCE)
660    @IntDef({
661        ORIGIN_GENERATED,
662        ORIGIN_IMPORTED,
663        ORIGIN_UNKNOWN,
664        })
665    public @interface OriginEnum {}
666
667    /** Key was generated inside AndroidKeyStore. */
668    public static final int ORIGIN_GENERATED = 1 << 0;
669
670    /** Key was imported into AndroidKeyStore. */
671    public static final int ORIGIN_IMPORTED = 1 << 1;
672
673    /**
674     * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
675     * implementation which does not record origin information.
676     */
677    public static final int ORIGIN_UNKNOWN = 1 << 2;
678
679    /**
680     * @hide
681     */
682    public static abstract class Origin {
683        private Origin() {}
684
685        public static @OriginEnum int fromKeymaster(int origin) {
686            switch (origin) {
687                case KeymasterDefs.KM_ORIGIN_GENERATED:
688                    return ORIGIN_GENERATED;
689                case KeymasterDefs.KM_ORIGIN_IMPORTED:
690                    return ORIGIN_IMPORTED;
691                case KeymasterDefs.KM_ORIGIN_UNKNOWN:
692                    return ORIGIN_UNKNOWN;
693                default:
694                    throw new IllegalArgumentException("Unknown origin: " + origin);
695            }
696        }
697    }
698
699    private static int[] getSetFlags(int flags) {
700        if (flags == 0) {
701            return EmptyArray.INT;
702        }
703        int result[] = new int[getSetBitCount(flags)];
704        int resultOffset = 0;
705        int flag = 1;
706        while (flags != 0) {
707            if ((flags & 1) != 0) {
708                result[resultOffset] = flag;
709                resultOffset++;
710            }
711            flags >>>= 1;
712            flag <<= 1;
713        }
714        return result;
715    }
716
717    private static int getSetBitCount(int value) {
718        if (value == 0) {
719            return 0;
720        }
721        int result = 0;
722        while (value != 0) {
723            if ((value & 1) != 0) {
724                result++;
725            }
726            value >>>= 1;
727        }
728        return result;
729    }
730}
731