content: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.security.keystore;
  
  import android.annotation.IntDef;
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.StringDef;
  import android.security.keymaster.KeymasterDefs;
  
  import libcore.util.EmptyArray;
  
  import java.lang.annotation.Retention;
  import java.lang.annotation.RetentionPolicy;
  import java.util.Collection;
  import java.util.Locale;
  
  /**
   * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
   */
  public abstract class KeyProperties {
      private KeyProperties() {}
  
      /**
       * @hide
       */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(flag = true, prefix = { "PURPOSE_" }, value = {
              PURPOSE_ENCRYPT,
              PURPOSE_DECRYPT,
              PURPOSE_SIGN,
              PURPOSE_VERIFY,
              PURPOSE_WRAP_KEY,
      })
      public @interface PurposeEnum {}
  
      /**
       * Purpose of key: encryption.
       */
      public static final int PURPOSE_ENCRYPT = 1 << 0;
  
      /**
       * Purpose of key: decryption.
       */
      public static final int PURPOSE_DECRYPT = 1 << 1;
  
      /**
       * Purpose of key: signing or generating a Message Authentication Code (MAC).
       */
      public static final int PURPOSE_SIGN = 1 << 2;
  
      /**
       * Purpose of key: signature or Message Authentication Code (MAC) verification.
       */
      public static final int PURPOSE_VERIFY = 1 << 3;
  
      /**
       * Purpose of key: wrapping and unwrapping wrapped keys for secure import.
       */
      public static final int PURPOSE_WRAP_KEY = 1 << 5;
  
      /**
       * @hide
       */
      public static abstract class Purpose {
          private Purpose() {}
  
          public static int toKeymaster(@PurposeEnum int purpose) {
              switch (purpose) {
                  case PURPOSE_ENCRYPT:
                      return KeymasterDefs.KM_PURPOSE_ENCRYPT;
                  case PURPOSE_DECRYPT:
                      return KeymasterDefs.KM_PURPOSE_DECRYPT;
                  case PURPOSE_SIGN:
                      return KeymasterDefs.KM_PURPOSE_SIGN;
                  case PURPOSE_VERIFY:
                      return KeymasterDefs.KM_PURPOSE_VERIFY;
                  case PURPOSE_WRAP_KEY:
                      return KeymasterDefs.KM_PURPOSE_WRAP;
                  default:
                      throw new IllegalArgumentException("Unknown purpose: " + purpose);
              }
          }
  
          public static @PurposeEnum int fromKeymaster(int purpose) {
              switch (purpose) {
                  case KeymasterDefs.KM_PURPOSE_ENCRYPT:
                      return PURPOSE_ENCRYPT;
                  case KeymasterDefs.KM_PURPOSE_DECRYPT:
                      return PURPOSE_DECRYPT;
                  case KeymasterDefs.KM_PURPOSE_SIGN:
                      return PURPOSE_SIGN;
                  case KeymasterDefs.KM_PURPOSE_VERIFY:
                      return PURPOSE_VERIFY;
                  case KeymasterDefs.KM_PURPOSE_WRAP:
                      return PURPOSE_WRAP_KEY;
                  default:
                      throw new IllegalArgumentException("Unknown purpose: " + purpose);
              }
          }
  
          @NonNull
          public static int[] allToKeymaster(@PurposeEnum int purposes) {
              int[] result = getSetFlags(purposes);
              for (int i = 0; i < result.length; i++) {
                  result[i] = toKeymaster(result[i]);
              }
              return result;
          }
  
          public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
              @PurposeEnum int result = 0;
              for (int keymasterPurpose : purposes) {
                  result |= fromKeymaster(keymasterPurpose);
              }
              return result;
          }
      }
  
      /**
       * @hide
       */
      @Retention(RetentionPolicy.SOURCE)
      @StringDef(prefix = { "KEY_" }, value = {
          KEY_ALGORITHM_RSA,
          KEY_ALGORITHM_EC,
          KEY_ALGORITHM_AES,
          KEY_ALGORITHM_HMAC_SHA1,
          KEY_ALGORITHM_HMAC_SHA224,
          KEY_ALGORITHM_HMAC_SHA256,
          KEY_ALGORITHM_HMAC_SHA384,
          KEY_ALGORITHM_HMAC_SHA512,
          })
      public @interface KeyAlgorithmEnum {}
  
      /** Rivest Shamir Adleman (RSA) key. */
      public static final String KEY_ALGORITHM_RSA = "RSA";
  
      /** Elliptic Curve (EC) Cryptography key. */
      public static final String KEY_ALGORITHM_EC = "EC";
  
      /** Advanced Encryption Standard (AES) key. */
      public static final String KEY_ALGORITHM_AES = "AES";
  
      /**
       * Triple Data Encryption Algorithm (3DES) key.
       *
       * @deprecated Included for interoperability with legacy systems. Prefer {@link
       * KeyProperties#KEY_ALGORITHM_AES} for new development.
       */
      @Deprecated
      public static final String KEY_ALGORITHM_3DES = "DESede";
  
      /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
      public static final String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";
  
      /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
      public static final String KEY_ALGORITHM_HMAC_SHA224 = "HmacSHA224";
  
      /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
      public static final String KEY_ALGORITHM_HMAC_SHA256 = "HmacSHA256";
  
      /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
      public static final String KEY_ALGORITHM_HMAC_SHA384 = "HmacSHA384";
  
      /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
      public static final String KEY_ALGORITHM_HMAC_SHA512 = "HmacSHA512";
  
      /**
       * @hide
       */
      public static abstract class KeyAlgorithm {
          private KeyAlgorithm() {}
  
          public static int toKeymasterAsymmetricKeyAlgorithm(
                  @NonNull @KeyAlgorithmEnum String algorithm) {
              if (KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
                  return KeymasterDefs.KM_ALGORITHM_EC;
              } else if (KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
                  return KeymasterDefs.KM_ALGORITHM_RSA;
              } else {
                  throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
              }
          }
  
          @NonNull
          public static @KeyAlgorithmEnum String fromKeymasterAsymmetricKeyAlgorithm(
                  int keymasterAlgorithm) {
              switch (keymasterAlgorithm) {
                  case KeymasterDefs.KM_ALGORITHM_EC:
                      return KEY_ALGORITHM_EC;
                  case KeymasterDefs.KM_ALGORITHM_RSA:
                      return KEY_ALGORITHM_RSA;
                  default:
                      throw new IllegalArgumentException(
                              "Unsupported key algorithm: " + keymasterAlgorithm);
              }
          }
  
          public static int toKeymasterSecretKeyAlgorithm(
                  @NonNull @KeyAlgorithmEnum String algorithm) {
              if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
                  return KeymasterDefs.KM_ALGORITHM_AES;
              } else if (KEY_ALGORITHM_3DES.equalsIgnoreCase(algorithm)) {
                  return KeymasterDefs.KM_ALGORITHM_3DES;
              } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
                  return KeymasterDefs.KM_ALGORITHM_HMAC;
              } else {
                  throw new IllegalArgumentException(
                          "Unsupported secret key algorithm: " + algorithm);
              }
          }
  
          @NonNull
          public static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
                  int keymasterAlgorithm, int keymasterDigest) {
              switch (keymasterAlgorithm) {
                  case KeymasterDefs.KM_ALGORITHM_AES:
                      return KEY_ALGORITHM_AES;
                  case KeymasterDefs.KM_ALGORITHM_3DES:
                      return KEY_ALGORITHM_3DES;
                  case KeymasterDefs.KM_ALGORITHM_HMAC:
                      switch (keymasterDigest) {
                          case KeymasterDefs.KM_DIGEST_SHA1:
                              return KEY_ALGORITHM_HMAC_SHA1;
                          case KeymasterDefs.KM_DIGEST_SHA_2_224:
                              return KEY_ALGORITHM_HMAC_SHA224;
                          case KeymasterDefs.KM_DIGEST_SHA_2_256:
                              return KEY_ALGORITHM_HMAC_SHA256;
                          case KeymasterDefs.KM_DIGEST_SHA_2_384:
                              return KEY_ALGORITHM_HMAC_SHA384;
                          case KeymasterDefs.KM_DIGEST_SHA_2_512:
                              return KEY_ALGORITHM_HMAC_SHA512;
                          default:
                              throw new IllegalArgumentException("Unsupported HMAC digest: "
                                      + Digest.fromKeymaster(keymasterDigest));
                      }
                  default:
                      throw new IllegalArgumentException(
                              "Unsupported key algorithm: " + keymasterAlgorithm);
              }
          }
  
          /**
           * @hide
           *
           * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
           */
          public static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum String algorithm) {
              String algorithmUpper = algorithm.toUpperCase(Locale.US);
              if (algorithmUpper.startsWith("HMAC")) {
                  String digestUpper = algorithmUpper.substring("HMAC".length());
                  switch (digestUpper) {
                      case "SHA1":
                          return KeymasterDefs.KM_DIGEST_SHA1;
                      case "SHA224":
                          return KeymasterDefs.KM_DIGEST_SHA_2_224;
                      case "SHA256":
                          return KeymasterDefs.KM_DIGEST_SHA_2_256;
                      case "SHA384":
                          return KeymasterDefs.KM_DIGEST_SHA_2_384;
                      case "SHA512":
                          return KeymasterDefs.KM_DIGEST_SHA_2_512;
                      default:
                          throw new IllegalArgumentException(
                                  "Unsupported HMAC digest: " + digestUpper);
                  }
              } else {
                  return -1;
              }
          }
      }
  
      /**
       * @hide
       */
      @Retention(RetentionPolicy.SOURCE)
      @StringDef(prefix = { "BLOCK_MODE_" }, value = {
          BLOCK_MODE_ECB,
          BLOCK_MODE_CBC,
          BLOCK_MODE_CTR,
          BLOCK_MODE_GCM,
          })
      public @interface BlockModeEnum {}
  
      /** Electronic Codebook (ECB) block mode. */
      public static final String BLOCK_MODE_ECB = "ECB";
  
      /** Cipher Block Chaining (CBC) block mode. */
      public static final String BLOCK_MODE_CBC = "CBC";
  
      /** Counter (CTR) block mode. */
      public static final String BLOCK_MODE_CTR = "CTR";
  
      /** Galois/Counter Mode (GCM) block mode. */
      public static final String BLOCK_MODE_GCM = "GCM";
  
      /**
       * @hide
       */
      public static abstract class BlockMode {
          private BlockMode() {}
  
          public static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
              if (BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
                  return KeymasterDefs.KM_MODE_ECB;
              } else if (BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
                  return KeymasterDefs.KM_MODE_CBC;
              } else if (BLOCK_MODE_CTR.equalsIgnoreCase(blockMode)) {
                  return KeymasterDefs.KM_MODE_CTR;
              } else if (BLOCK_MODE_GCM.equalsIgnoreCase(blockMode)) {
                  return KeymasterDefs.KM_MODE_GCM;
              } else {
                  throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
              }
          }
  
          @NonNull
          public static @BlockModeEnum String fromKeymaster(int blockMode) {
              switch (blockMode) {
                  case KeymasterDefs.KM_MODE_ECB:
                      return BLOCK_MODE_ECB;
                  case KeymasterDefs.KM_MODE_CBC:
                      return BLOCK_MODE_CBC;
                  case KeymasterDefs.KM_MODE_CTR:
                      return BLOCK_MODE_CTR;
                  case KeymasterDefs.KM_MODE_GCM:
                      return BLOCK_MODE_GCM;
                  default:
                      throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
              }
          }
  
          @NonNull
          public static @BlockModeEnum String[] allFromKeymaster(
                  @NonNull Collection<Integer> blockModes) {
              if ((blockModes == null) || (blockModes.isEmpty())) {
                  return EmptyArray.STRING;
              }
              @BlockModeEnum String[] result = new String[blockModes.size()];
              int offset = 0;
              for (int blockMode : blockModes) {
                  result[offset] = fromKeymaster(blockMode);
                  offset++;
              }
              return result;
          }
  
          public static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
              if ((blockModes == null) || (blockModes.length == 0)) {
                  return EmptyArray.INT;
              }
              int[] result = new int[blockModes.length];
              for (int i = 0; i < blockModes.length; i++) {
                  result[i] = toKeymaster(blockModes[i]);
              }
              return result;
          }
      }
  
      /**
       * @hide
       */
      @Retention(RetentionPolicy.SOURCE)
      @StringDef(prefix = { "ENCRYPTION_PADDING_" }, value = {
          ENCRYPTION_PADDING_NONE,
          ENCRYPTION_PADDING_PKCS7,
          ENCRYPTION_PADDING_RSA_PKCS1,
          ENCRYPTION_PADDING_RSA_OAEP,
          })
      public @interface EncryptionPaddingEnum {}
  
      /**
       * No encryption padding.
       */
      public static final String ENCRYPTION_PADDING_NONE = "NoPadding";
  
      /**
       * PKCS#7 encryption padding scheme.
       */
      public static final String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
  
      /**
       * RSA PKCS#1 v1.5 padding scheme for encryption.
       */
      public static final String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
  
      /**
       * RSA Optimal Asymmetric Encryption Padding (OAEP) scheme.
       */
      public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
  
      /**
       * @hide
       */
      public static abstract class EncryptionPadding {
          private EncryptionPadding() {}
  
          public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
              if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
                  return KeymasterDefs.KM_PAD_NONE;
              } else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
                  return KeymasterDefs.KM_PAD_PKCS7;
              } else if (ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
                  return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
              } else if (ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
                  return KeymasterDefs.KM_PAD_RSA_OAEP;
              } else {
                  throw new IllegalArgumentException(
                          "Unsupported encryption padding scheme: " + padding);
              }
          }
  
          @NonNull
          public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
              switch (padding) {
                  case KeymasterDefs.KM_PAD_NONE:
                      return ENCRYPTION_PADDING_NONE;
                  case KeymasterDefs.KM_PAD_PKCS7:
                      return ENCRYPTION_PADDING_PKCS7;
                  case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
                      return ENCRYPTION_PADDING_RSA_PKCS1;
                  case KeymasterDefs.KM_PAD_RSA_OAEP:
                      return ENCRYPTION_PADDING_RSA_OAEP;
                  default:
                      throw new IllegalArgumentException(
                              "Unsupported encryption padding: " + padding);
              }
          }
  
          @NonNull
          public static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
              if ((paddings == null) || (paddings.length == 0)) {
                  return EmptyArray.INT;
              }
              int[] result = new int[paddings.length];
              for (int i = 0; i < paddings.length; i++) {
                  result[i] = toKeymaster(paddings[i]);
              }
              return result;
          }
      }
  
      /**
       * @hide
       */
      @Retention(RetentionPolicy.SOURCE)
      @StringDef(prefix = { "SIGNATURE_PADDING_" }, value = {
          SIGNATURE_PADDING_RSA_PKCS1,
          SIGNATURE_PADDING_RSA_PSS,
          })
      public @interface SignaturePaddingEnum {}
  
      /**
       * RSA PKCS#1 v1.5 padding for signatures.
       */
      public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
  
      /**
       * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
       */
      public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
  
      static abstract class SignaturePadding {
          private SignaturePadding() {}
  
          static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
              switch (padding.toUpperCase(Locale.US)) {
                  case SIGNATURE_PADDING_RSA_PKCS1:
                      return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
                  case SIGNATURE_PADDING_RSA_PSS:
                      return KeymasterDefs.KM_PAD_RSA_PSS;
                  default:
                      throw new IllegalArgumentException(
                              "Unsupported signature padding scheme: " + padding);
              }
          }
  
          @NonNull
          static @SignaturePaddingEnum String fromKeymaster(int padding) {
              switch (padding) {
                  case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
                      return SIGNATURE_PADDING_RSA_PKCS1;
                  case KeymasterDefs.KM_PAD_RSA_PSS:
                      return SIGNATURE_PADDING_RSA_PSS;
                  default:
                      throw new IllegalArgumentException("Unsupported signature padding: " + padding);
              }
          }
  
          @NonNull
          static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) {
              if ((paddings == null) || (paddings.length == 0)) {
                  return EmptyArray.INT;
              }
              int[] result = new int[paddings.length];
              for (int i = 0; i < paddings.length; i++) {
                  result[i] = toKeymaster(paddings[i]);
              }
              return result;
          }
      }
  
      /**
       * @hide
       */
      @Retention(RetentionPolicy.SOURCE)
      @StringDef(prefix = { "DIGEST_" }, value = {
          DIGEST_NONE,
          DIGEST_MD5,
          DIGEST_SHA1,
          DIGEST_SHA224,
          DIGEST_SHA256,
          DIGEST_SHA384,
          DIGEST_SHA512,
          })
      public @interface DigestEnum {}
  
      /**
       * No digest: sign/authenticate the raw message.
       */
      public static final String DIGEST_NONE = "NONE";
  
      /**
       * MD5 digest.
       */
      public static final String DIGEST_MD5 = "MD5";
  
      /**
       * SHA-1 digest.
       */
      public static final String DIGEST_SHA1 = "SHA-1";
  
      /**
       * SHA-2 224 (aka SHA-224) digest.
       */
      public static final String DIGEST_SHA224 = "SHA-224";
  
      /**
       * SHA-2 256 (aka SHA-256) digest.
       */
      public static final String DIGEST_SHA256 = "SHA-256";
  
      /**
       * SHA-2 384 (aka SHA-384) digest.
       */
      public static final String DIGEST_SHA384 = "SHA-384";
  
      /**
       * SHA-2 512 (aka SHA-512) digest.
       */
      public static final String DIGEST_SHA512 = "SHA-512";
  
      /**
       * @hide
       */
      public static abstract class Digest {
          private Digest() {}
  
          public static int toKeymaster(@NonNull @DigestEnum String digest) {
              switch (digest.toUpperCase(Locale.US)) {
                  case DIGEST_SHA1:
                      return KeymasterDefs.KM_DIGEST_SHA1;
                  case DIGEST_SHA224:
                      return KeymasterDefs.KM_DIGEST_SHA_2_224;
                  case DIGEST_SHA256:
                      return KeymasterDefs.KM_DIGEST_SHA_2_256;
                  case DIGEST_SHA384:
                      return KeymasterDefs.KM_DIGEST_SHA_2_384;
                  case DIGEST_SHA512:
                      return KeymasterDefs.KM_DIGEST_SHA_2_512;
                  case DIGEST_NONE:
                      return KeymasterDefs.KM_DIGEST_NONE;
                  case DIGEST_MD5:
                      return KeymasterDefs.KM_DIGEST_MD5;
                  default:
                      throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
              }
          }
  
          @NonNull
          public static @DigestEnum String fromKeymaster(int digest) {
              switch (digest) {
                  case KeymasterDefs.KM_DIGEST_NONE:
                      return DIGEST_NONE;
                  case KeymasterDefs.KM_DIGEST_MD5:
                      return DIGEST_MD5;
                  case KeymasterDefs.KM_DIGEST_SHA1:
                      return DIGEST_SHA1;
                  case KeymasterDefs.KM_DIGEST_SHA_2_224:
                      return DIGEST_SHA224;
                  case KeymasterDefs.KM_DIGEST_SHA_2_256:
                      return DIGEST_SHA256;
                  case KeymasterDefs.KM_DIGEST_SHA_2_384:
                      return DIGEST_SHA384;
                  case KeymasterDefs.KM_DIGEST_SHA_2_512:
                      return DIGEST_SHA512;
                  default:
                      throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
              }
          }
  
          @NonNull
          public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
              switch (digest) {
                  case KeymasterDefs.KM_DIGEST_NONE:
                      return "NONE";
                  case KeymasterDefs.KM_DIGEST_MD5:
                      return "MD5";
                  case KeymasterDefs.KM_DIGEST_SHA1:
                      return "SHA1";
                  case KeymasterDefs.KM_DIGEST_SHA_2_224:
                      return "SHA224";
                  case KeymasterDefs.KM_DIGEST_SHA_2_256:
                      return "SHA256";
                  case KeymasterDefs.KM_DIGEST_SHA_2_384:
                      return "SHA384";
                  case KeymasterDefs.KM_DIGEST_SHA_2_512:
                      return "SHA512";
                  default:
                      throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
              }
          }
  
          @NonNull
          public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
              if (digests.isEmpty()) {
                  return EmptyArray.STRING;
              }
              String[] result = new String[digests.size()];
              int offset = 0;
              for (int digest : digests) {
                  result[offset] = fromKeymaster(digest);
                  offset++;
              }
              return result;
          }
  
          @NonNull
          public static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
              if ((digests == null) || (digests.length == 0)) {
                  return EmptyArray.INT;
              }
              int[] result = new int[digests.length];
              int offset = 0;
              for (@DigestEnum String digest : digests) {
                  result[offset] = toKeymaster(digest);
                  offset++;
              }
              return result;
          }
      }
  
      /**
       * @hide
       */
      @Retention(RetentionPolicy.SOURCE)
      @IntDef(prefix = { "ORIGIN_" }, value = {
              ORIGIN_GENERATED,
              ORIGIN_IMPORTED,
              ORIGIN_UNKNOWN,
      })
  
      public @interface OriginEnum {}
  
      /** Key was generated inside AndroidKeyStore. */
      public static final int ORIGIN_GENERATED = 1 << 0;
  
      /** Key was imported into AndroidKeyStore. */
      public static final int ORIGIN_IMPORTED = 1 << 1;
  
      /**
       * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
       * implementation which does not record origin information.
       */
      public static final int ORIGIN_UNKNOWN = 1 << 2;
  
      /**
       * Key was imported into the AndroidKeyStore in an encrypted wrapper. Unlike imported keys,
       * securely imported keys can be imported without appearing as plaintext in the device's host
       * memory.
       */
      public static final int ORIGIN_SECURELY_IMPORTED = 1 << 3;
  
  
      /**
       * @hide
       */
      public static abstract class Origin {
          private Origin() {}
  
          public static @OriginEnum int fromKeymaster(int origin) {
              switch (origin) {
                  case KeymasterDefs.KM_ORIGIN_GENERATED:
                      return ORIGIN_GENERATED;
                  case KeymasterDefs.KM_ORIGIN_IMPORTED:
                      return ORIGIN_IMPORTED;
                  case KeymasterDefs.KM_ORIGIN_UNKNOWN:
                      return ORIGIN_UNKNOWN;
                  case KeymasterDefs.KM_ORIGIN_SECURELY_IMPORTED:
                      return ORIGIN_SECURELY_IMPORTED;
                  default:
                      throw new IllegalArgumentException("Unknown origin: " + origin);
              }
          }
      }
  
      private static int[] getSetFlags(int flags) {
          if (flags == 0) {
              return EmptyArray.INT;
          }
          int result[] = new int[getSetBitCount(flags)];
          int resultOffset = 0;
          int flag = 1;
          while (flags != 0) {
              if ((flags & 1) != 0) {
                  result[resultOffset] = flag;
                  resultOffset++;
              }
              flags >>>= 1;
              flag <<= 1;
          }
          return result;
      }
  
      private static int getSetBitCount(int value) {
          if (value == 0) {
              return 0;
          }
          int result = 0;
          while (value != 0) {
              if ((value & 1) != 0) {
                  result++;
              }
              value >>>= 1;
          }
          return result;
      }
  }
  