protected HmacBase(int keymasterDigest) {
   super(
       KeymasterDefs.KM_ALGORITHM_HMAC,
       keymasterDigest,
       KeymasterUtils.getDigestOutputSizeBits(keymasterDigest));
 }
  @Override
  protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
      throws InvalidAlgorithmParameterException {
    resetAll();

    boolean success = false;
    try {
      if ((params == null) || (!(params instanceof KeyGenParameterSpec))) {
        throw new InvalidAlgorithmParameterException(
            "Cannot initialize without a " + KeyGenParameterSpec.class.getName() + " parameter");
      }
      KeyGenParameterSpec spec = (KeyGenParameterSpec) params;
      if (spec.getKeystoreAlias() == null) {
        throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
      }

      mRng = random;
      mSpec = spec;

      mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
      if (mKeySizeBits <= 0) {
        throw new InvalidAlgorithmParameterException("Key size must be positive: " + mKeySizeBits);
      } else if ((mKeySizeBits % 8) != 0) {
        throw new InvalidAlgorithmParameterException(
            "Key size in must be a multiple of 8: " + mKeySizeBits);
      }

      try {
        mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes());
        mKeymasterPaddings =
            KeyProperties.EncryptionPadding.allToKeymaster(spec.getEncryptionPaddings());
        mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
        if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
            && (spec.isRandomizedEncryptionRequired())) {
          for (int keymasterBlockMode : mKeymasterBlockModes) {
            if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
              throw new InvalidAlgorithmParameterException(
                  "Randomized encryption (IND-CPA) required but may be violated"
                      + " by block mode: "
                      + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
                      + ". See "
                      + KeyGenParameterSpec.class.getName()
                      + " documentation.");
            }
          }
        }
        if (spec.isDigestsSpecified()) {
          // Digest(s) explicitly specified in the spec
          mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests());
          if (mKeymasterDigest != -1) {
            // Key algorithm implies a digest -- ensure it's specified in the spec as
            // first digest.
            if (!com.android.internal.util.ArrayUtils.contains(
                mKeymasterDigests, mKeymasterDigest)) {
              throw new InvalidAlgorithmParameterException(
                  "Digests specified in algorithm parameters ("
                      + Arrays.asList(spec.getDigests())
                      + ") must include "
                      + " the digest "
                      + KeyProperties.Digest.fromKeymaster(mKeymasterDigest)
                      + " implied by key algorithm");
            }
            if (mKeymasterDigests[0] != mKeymasterDigest) {
              // The first digest is not the one implied by the key algorithm.
              // Swap the implied digest with the first one.
              for (int i = 0; i < mKeymasterDigests.length; i++) {
                if (mKeymasterDigests[i] == mKeymasterDigest) {
                  mKeymasterDigests[i] = mKeymasterDigests[0];
                  mKeymasterDigests[0] = mKeymasterDigest;
                  break;
                }
              }
            }
          }
        } else {
          // No digest specified in the spec
          if (mKeymasterDigest != -1) {
            // Key algorithm implies a digest -- use that digest
            mKeymasterDigests = new int[] {mKeymasterDigest};
          } else {
            mKeymasterDigests = EmptyArray.INT;
          }
        }
        if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
          if (mKeymasterDigests.length == 0) {
            throw new InvalidAlgorithmParameterException(
                "At least one digest algorithm must be specified");
          }
        }
      } catch (IllegalStateException | IllegalArgumentException e) {
        throw new InvalidAlgorithmParameterException(e);
      }

      success = true;
    } finally {
      if (!success) {
        resetAll();
      }
    }
  }
  @Override
  protected SecretKey engineGenerateKey() {
    KeyGenParameterSpec spec = mSpec;
    if (spec == null) {
      throw new IllegalStateException("Not initialized");
    }

    if ((spec.isEncryptionAtRestRequired()) && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
      throw new IllegalStateException(
          "Requested to import a key which must be encrypted at rest using secure lock"
              + " screen credential, but the credential hasn't yet been entered by the user");
    }

    KeymasterArguments args = new KeymasterArguments();
    args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
    args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
    args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
    args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
    args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
    args.addInts(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
    KeymasterUtils.addUserAuthArgs(
        args,
        spec.isUserAuthenticationRequired(),
        spec.getUserAuthenticationValidityDurationSeconds());
    args.addDate(
        KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
        (spec.getKeyValidityStart() != null) ? spec.getKeyValidityStart() : new Date(0));
    args.addDate(
        KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
        (spec.getKeyValidityForOriginationEnd() != null)
            ? spec.getKeyValidityForOriginationEnd()
            : new Date(Long.MAX_VALUE));
    args.addDate(
        KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
        (spec.getKeyValidityForConsumptionEnd() != null)
            ? spec.getKeyValidityForConsumptionEnd()
            : new Date(Long.MAX_VALUE));

    if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0)
        && (!spec.isRandomizedEncryptionRequired())) {
      // Permit caller-provided IV when encrypting with this key
      args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
    }

    byte[] additionalEntropy =
        KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
            mRng, (mKeySizeBits + 7) / 8);
    int flags = spec.getFlags();
    String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
    KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
    int errorCode =
        mKeyStore.generateKey(
            keyAliasInKeystore, args, additionalEntropy, flags, resultingKeyCharacteristics);
    if (errorCode != KeyStore.NO_ERROR) {
      throw new ProviderException(
          "Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
    }
    @KeyProperties.KeyAlgorithmEnum String keyAlgorithmJCA;
    try {
      keyAlgorithmJCA =
          KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
              mKeymasterAlgorithm, mKeymasterDigest);
    } catch (IllegalArgumentException e) {
      throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
    }
    return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
  }
 protected AndroidKeyStoreHmacSpi(int keymasterDigest) {
   mKeymasterDigest = keymasterDigest;
   mMacSizeBits = KeymasterUtils.getDigestOutputSizeBits(keymasterDigest);
 }