KeyProperties.java revision ca7aaeaeee616d9d1d557ee2fb19dd14783be1f0
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                    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         */
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 {
294        private BlockMode() {}
295
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
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
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
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     * <p><b>NOTE</b>: If a key is authorized to be used with no padding, then it can be used with
369     * any padding scheme, both for encryption and signing.
370     */
371    public static final String ENCRYPTION_PADDING_NONE = "NoPadding";
372
373    /**
374     * PKCS#7 encryption padding scheme.
375     */
376    public static final String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
377
378    /**
379     * RSA PKCS#1 v1.5 padding scheme for encryption.
380     */
381    public static final String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
382
383    /**
384     * RSA Optimal Asymmetric Encryption Padding (OAEP) scheme.
385     */
386    public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
387
388    /**
389     * @hide
390     */
391    public static abstract class EncryptionPadding {
392        private EncryptionPadding() {}
393
394        public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
395            if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
396                return KeymasterDefs.KM_PAD_NONE;
397            } else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
398                return KeymasterDefs.KM_PAD_PKCS7;
399            } else if (ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
400                return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
401            } else if (ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
402                return KeymasterDefs.KM_PAD_RSA_OAEP;
403            } else {
404                throw new IllegalArgumentException(
405                        "Unsupported encryption padding scheme: " + padding);
406            }
407        }
408
409        @NonNull
410        public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
411            switch (padding) {
412                case KeymasterDefs.KM_PAD_NONE:
413                    return ENCRYPTION_PADDING_NONE;
414                case KeymasterDefs.KM_PAD_PKCS7:
415                    return ENCRYPTION_PADDING_PKCS7;
416                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
417                    return ENCRYPTION_PADDING_RSA_PKCS1;
418                case KeymasterDefs.KM_PAD_RSA_OAEP:
419                    return ENCRYPTION_PADDING_RSA_OAEP;
420                default:
421                    throw new IllegalArgumentException(
422                            "Unsupported encryption padding: " + padding);
423            }
424        }
425
426        @NonNull
427        public static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
428            if ((paddings == null) || (paddings.length == 0)) {
429                return EmptyArray.INT;
430            }
431            int[] result = new int[paddings.length];
432            for (int i = 0; i < paddings.length; i++) {
433                result[i] = toKeymaster(paddings[i]);
434            }
435            return result;
436        }
437    }
438
439    /**
440     * @hide
441     */
442    @Retention(RetentionPolicy.SOURCE)
443    @StringDef({
444        SIGNATURE_PADDING_RSA_PKCS1,
445        SIGNATURE_PADDING_RSA_PSS,
446        })
447    public @interface SignaturePaddingEnum {}
448
449    /**
450     * RSA PKCS#1 v1.5 padding for signatures.
451     */
452    public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
453
454    /**
455     * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
456     */
457    public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
458
459    static abstract class SignaturePadding {
460        private SignaturePadding() {}
461
462        static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
463            switch (padding.toUpperCase(Locale.US)) {
464                case SIGNATURE_PADDING_RSA_PKCS1:
465                    return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
466                case SIGNATURE_PADDING_RSA_PSS:
467                    return KeymasterDefs.KM_PAD_RSA_PSS;
468                default:
469                    throw new IllegalArgumentException(
470                            "Unsupported signature padding scheme: " + padding);
471            }
472        }
473
474        @NonNull
475        static @SignaturePaddingEnum String fromKeymaster(int padding) {
476            switch (padding) {
477                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
478                    return SIGNATURE_PADDING_RSA_PKCS1;
479                case KeymasterDefs.KM_PAD_RSA_PSS:
480                    return SIGNATURE_PADDING_RSA_PSS;
481                default:
482                    throw new IllegalArgumentException("Unsupported signature padding: " + padding);
483            }
484        }
485
486        @NonNull
487        static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) {
488            if ((paddings == null) || (paddings.length == 0)) {
489                return EmptyArray.INT;
490            }
491            int[] result = new int[paddings.length];
492            for (int i = 0; i < paddings.length; i++) {
493                result[i] = toKeymaster(paddings[i]);
494            }
495            return result;
496        }
497    }
498
499    /**
500     * @hide
501     */
502    @Retention(RetentionPolicy.SOURCE)
503    @StringDef({
504        DIGEST_NONE,
505        DIGEST_MD5,
506        DIGEST_SHA1,
507        DIGEST_SHA224,
508        DIGEST_SHA256,
509        DIGEST_SHA384,
510        DIGEST_SHA512,
511        })
512    public @interface DigestEnum {}
513
514    /**
515     * No digest: sign/authenticate the raw message.
516     *
517     * <p><b>NOTE</b>: If a key is authorized to be used with no digest, then it can be used with
518     * any digest.
519     */
520    public static final String DIGEST_NONE = "NONE";
521
522    /**
523     * MD5 digest.
524     */
525    public static final String DIGEST_MD5 = "MD5";
526
527    /**
528     * SHA-1 digest.
529     */
530    public static final String DIGEST_SHA1 = "SHA-1";
531
532    /**
533     * SHA-2 224 (aka SHA-224) digest.
534     */
535    public static final String DIGEST_SHA224 = "SHA-224";
536
537    /**
538     * SHA-2 256 (aka SHA-256) digest.
539     */
540    public static final String DIGEST_SHA256 = "SHA-256";
541
542    /**
543     * SHA-2 384 (aka SHA-384) digest.
544     */
545    public static final String DIGEST_SHA384 = "SHA-384";
546
547    /**
548     * SHA-2 512 (aka SHA-512) digest.
549     */
550    public static final String DIGEST_SHA512 = "SHA-512";
551
552    /**
553     * @hide
554     */
555    public static abstract class Digest {
556        private Digest() {}
557
558        public static int toKeymaster(@NonNull @DigestEnum String digest) {
559            switch (digest.toUpperCase(Locale.US)) {
560                case DIGEST_SHA1:
561                    return KeymasterDefs.KM_DIGEST_SHA1;
562                case DIGEST_SHA224:
563                    return KeymasterDefs.KM_DIGEST_SHA_2_224;
564                case DIGEST_SHA256:
565                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
566                case DIGEST_SHA384:
567                    return KeymasterDefs.KM_DIGEST_SHA_2_384;
568                case DIGEST_SHA512:
569                    return KeymasterDefs.KM_DIGEST_SHA_2_512;
570                case DIGEST_NONE:
571                    return KeymasterDefs.KM_DIGEST_NONE;
572                case DIGEST_MD5:
573                    return KeymasterDefs.KM_DIGEST_MD5;
574                default:
575                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
576            }
577        }
578
579        @NonNull
580        public static @DigestEnum String fromKeymaster(int digest) {
581            switch (digest) {
582                case KeymasterDefs.KM_DIGEST_NONE:
583                    return DIGEST_NONE;
584                case KeymasterDefs.KM_DIGEST_MD5:
585                    return DIGEST_MD5;
586                case KeymasterDefs.KM_DIGEST_SHA1:
587                    return DIGEST_SHA1;
588                case KeymasterDefs.KM_DIGEST_SHA_2_224:
589                    return DIGEST_SHA224;
590                case KeymasterDefs.KM_DIGEST_SHA_2_256:
591                    return DIGEST_SHA256;
592                case KeymasterDefs.KM_DIGEST_SHA_2_384:
593                    return DIGEST_SHA384;
594                case KeymasterDefs.KM_DIGEST_SHA_2_512:
595                    return DIGEST_SHA512;
596                default:
597                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
598            }
599        }
600
601        @NonNull
602        public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
603            switch (digest) {
604                case KeymasterDefs.KM_DIGEST_NONE:
605                    return "NONE";
606                case KeymasterDefs.KM_DIGEST_MD5:
607                    return "MD5";
608                case KeymasterDefs.KM_DIGEST_SHA1:
609                    return "SHA1";
610                case KeymasterDefs.KM_DIGEST_SHA_2_224:
611                    return "SHA224";
612                case KeymasterDefs.KM_DIGEST_SHA_2_256:
613                    return "SHA256";
614                case KeymasterDefs.KM_DIGEST_SHA_2_384:
615                    return "SHA384";
616                case KeymasterDefs.KM_DIGEST_SHA_2_512:
617                    return "SHA512";
618                default:
619                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
620            }
621        }
622
623        @NonNull
624        public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
625            if (digests.isEmpty()) {
626                return EmptyArray.STRING;
627            }
628            String[] result = new String[digests.size()];
629            int offset = 0;
630            for (int digest : digests) {
631                result[offset] = fromKeymaster(digest);
632                offset++;
633            }
634            return result;
635        }
636
637        @NonNull
638        public static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
639            if ((digests == null) || (digests.length == 0)) {
640                return EmptyArray.INT;
641            }
642            int[] result = new int[digests.length];
643            int offset = 0;
644            for (@DigestEnum String digest : digests) {
645                result[offset] = toKeymaster(digest);
646                offset++;
647            }
648            return result;
649        }
650    }
651
652    /**
653     * @hide
654     */
655    @Retention(RetentionPolicy.SOURCE)
656    @IntDef({
657        ORIGIN_GENERATED,
658        ORIGIN_IMPORTED,
659        ORIGIN_UNKNOWN,
660        })
661    public @interface OriginEnum {}
662
663    /** Key was generated inside AndroidKeyStore. */
664    public static final int ORIGIN_GENERATED = 1 << 0;
665
666    /** Key was imported into AndroidKeyStore. */
667    public static final int ORIGIN_IMPORTED = 1 << 1;
668
669    /**
670     * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
671     * implementation which does not record origin information.
672     */
673    public static final int ORIGIN_UNKNOWN = 1 << 2;
674
675    /**
676     * @hide
677     */
678    public static abstract class Origin {
679        private Origin() {}
680
681        public static @OriginEnum int fromKeymaster(int origin) {
682            switch (origin) {
683                case KeymasterDefs.KM_ORIGIN_GENERATED:
684                    return ORIGIN_GENERATED;
685                case KeymasterDefs.KM_ORIGIN_IMPORTED:
686                    return ORIGIN_IMPORTED;
687                case KeymasterDefs.KM_ORIGIN_UNKNOWN:
688                    return ORIGIN_UNKNOWN;
689                default:
690                    throw new IllegalArgumentException("Unknown origin: " + origin);
691            }
692        }
693    }
694
695    private static int[] getSetFlags(int flags) {
696        if (flags == 0) {
697            return EmptyArray.INT;
698        }
699        int result[] = new int[getSetBitCount(flags)];
700        int resultOffset = 0;
701        int flag = 1;
702        while (flags != 0) {
703            if ((flags & 1) != 0) {
704                result[resultOffset] = flag;
705                resultOffset++;
706            }
707            flags >>>= 1;
708            flag <<= 1;
709        }
710        return result;
711    }
712
713    private static int getSetBitCount(int value) {
714        if (value == 0) {
715            return 0;
716        }
717        int result = 0;
718        while (value != 0) {
719            if ((value & 1) != 0) {
720                result++;
721            }
722            value >>>= 1;
723        }
724        return result;
725    }
726}
727