Merge "Added android reay_se implementation of KeyMint Applet" am: e04d71a0fe am: 21020d96c5 am: 9bfa27bb74

Original change: https://android-review.googlesource.com/c/platform/external/libese/+/2082578

Change-Id: Iec1642cd082f1881709468d9efdd3e5ee54baccf
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
new file mode 100644
index 0000000..27428ca
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
@@ -0,0 +1,601 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMAndroidSEProvider;
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.APDU;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import org.globalplatform.upgrade.Element;
+import org.globalplatform.upgrade.OnUpgradeListener;
+import org.globalplatform.upgrade.UpgradeManager;
+
+public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener {
+  // Magic number version
+  private static final byte KM_MAGIC_NUMBER = (byte) 0x82;
+  // MSB byte is for Major version and LSB byte is for Minor version.
+  public static final short KM_APPLET_PACKAGE_VERSION = 0x0300;
+
+  private static final byte KM_BEGIN_STATE = 0x00;
+  private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1;
+  private static final short POWER_RESET_MASK_FLAG = (short) 0x4000;
+
+  // Provider specific Commands
+  private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00;
+  private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3;
+  // Commands 4, 5 and 6 are reserved for vendor usage.
+  private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7;
+  // 0x08 was reserved for INS_INIT_STRONGBOX_CMD
+  // 0x09 was reserved for INS_SET_BOOT_ENDED_CMD earlier. it is unused now.
+  private static final byte INS_SE_FACTORY_PROVISIONING_LOCK_CMD =
+      INS_KEYMINT_PROVIDER_APDU_START + 10;
+  private static final byte INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD =
+      INS_KEYMINT_PROVIDER_APDU_START + 11;
+  private static final byte INS_OEM_UNLOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 12;
+  private static final byte INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD =
+      INS_KEYMINT_PROVIDER_APDU_START + 13;
+  private static final byte INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD =
+      INS_KEYMINT_PROVIDER_APDU_START + 14;
+  private static final byte INS_PROVISION_PRESHARED_SECRET_CMD =
+      INS_KEYMINT_PROVIDER_APDU_START + 15;
+  private static final byte INS_SET_BOOT_PARAMS_CMD =
+      INS_KEYMINT_PROVIDER_APDU_START + 16; // Unused
+  private static final byte INS_OEM_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 17;
+  private static final byte INS_PROVISION_SECURE_BOOT_MODE_CMD =
+      INS_KEYMINT_PROVIDER_APDU_START + 18;
+
+  private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F;
+  public static final byte BOOT_KEY_MAX_SIZE = 32;
+  public static final byte BOOT_HASH_MAX_SIZE = 32;
+  public static final byte SHARED_SECRET_KEY_SIZE = 32;
+
+  // Package version.
+  protected short packageVersion;
+
+  KMAndroidSEApplet() {
+    super(new KMAndroidSEProvider());
+    packageVersion = KM_APPLET_PACKAGE_VERSION;
+  }
+
+  /**
+   * Installs this applet.
+   *
+   * @param bArray the array containing installation parameters
+   * @param bOffset the starting offset in bArray
+   * @param bLength the length in bytes of the parameter data in bArray
+   */
+  public static void install(byte[] bArray, short bOffset, byte bLength) {
+    new KMAndroidSEApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
+  }
+
+  public void handleDeviceBooted() {
+    if (seProvider.isBootSignalEventSupported() && seProvider.isDeviceRebooted()) {
+      kmDataStore.clearDeviceBootStatus();
+      super.reboot();
+      seProvider.clearDeviceBooted(true);
+    }
+  }
+
+  @Override
+  public void process(APDU apdu) {
+    try {
+      handleDeviceBooted();
+      // If this is select applet apdu which is selecting this applet then return
+      if (apdu.isISOInterindustryCLA()) {
+        if (selectingApplet()) {
+          return;
+        }
+      }
+      short apduIns = validateApdu(apdu);
+      if (apduIns == KMType.INVALID_VALUE) {
+        return;
+      }
+      if (((KMAndroidSEProvider) seProvider).isPowerReset()) {
+        super.powerReset();
+      }
+
+      if (isCommandAllowed(apduIns)) {
+        switch (apduIns) {
+          case INS_PROVISION_ATTEST_IDS_CMD:
+            processProvisionAttestIdsCmd(apdu);
+            kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTEST_IDS);
+            sendResponse(apdu, KMError.OK);
+            break;
+
+          case INS_PROVISION_PRESHARED_SECRET_CMD:
+            processProvisionPreSharedSecretCmd(apdu);
+            kmDataStore.setProvisionStatus(PROVISION_STATUS_PRESHARED_SECRET);
+            sendResponse(apdu, KMError.OK);
+            break;
+
+          case INS_GET_PROVISION_STATUS_CMD:
+            processGetProvisionStatusCmd(apdu);
+            break;
+
+          case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD:
+            processProvisionRkpDeviceUniqueKeyPair(apdu);
+            break;
+
+          case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD:
+            processProvisionRkpAdditionalCertChain(apdu);
+            break;
+
+          case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+            kmDataStore.setProvisionStatus(PROVISION_STATUS_SE_LOCKED);
+            sendResponse(apdu, KMError.OK);
+            break;
+
+          case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD:
+            processProvisionOEMRootPublicKeyCmd(apdu);
+            kmDataStore.setProvisionStatus(PROVISION_STATUS_OEM_PUBLIC_KEY);
+            sendResponse(apdu, KMError.OK);
+            break;
+
+          case INS_OEM_LOCK_PROVISIONING_CMD:
+            processOEMLockProvisionCmd(apdu);
+            break;
+
+          case INS_OEM_UNLOCK_PROVISIONING_CMD:
+            processOEMUnlockProvisionCmd(apdu);
+            break;
+
+          case INS_PROVISION_SECURE_BOOT_MODE_CMD:
+            processSecureBootCmd(apdu);
+            break;
+
+          default:
+            super.process(apdu);
+            break;
+        }
+      } else {
+        ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
+      }
+    } catch (KMException exception) {
+      sendResponse(apdu, KMException.reason());
+    } catch (ISOException exp) {
+      sendResponse(apdu, mapISOErrorToKMError(exp.getReason()));
+    } catch (CryptoException e) {
+      sendResponse(apdu, mapCryptoErrorToKMError(e.getReason()));
+    } catch (Exception e) {
+      sendResponse(apdu, KMError.GENERIC_UNKNOWN_ERROR);
+    } finally {
+      repository.clean();
+    }
+  }
+
+  private boolean isCommandAllowed(short apduIns) {
+    boolean result = true;
+    switch (apduIns) {
+      case INS_PROVISION_ATTEST_IDS_CMD:
+      case INS_PROVISION_PRESHARED_SECRET_CMD:
+      case INS_PROVISION_SECURE_BOOT_MODE_CMD:
+      case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD:
+        if (kmDataStore.isProvisionLocked()) {
+          result = false;
+        }
+        break;
+
+      case INS_OEM_UNLOCK_PROVISIONING_CMD:
+        if (!kmDataStore.isProvisionLocked()) {
+          result = false;
+        }
+        break;
+
+      case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+        if (isSeFactoryProvisioningLocked() || !isSeFactoryProvisioningComplete()) {
+          result = false;
+        }
+        break;
+
+      case INS_OEM_LOCK_PROVISIONING_CMD:
+        // Allow lock only when
+        // 1. All the necessary provisioning commands are succcessfully executed
+        // 2. SE provision is locked
+        // 3. OEM Root Public is provisioned.
+        if (kmDataStore.isProvisionLocked()
+            || !(isProvisioningComplete() && isSeFactoryProvisioningLocked())) {
+          result = false;
+        }
+        break;
+
+      case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD:
+      case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD:
+        if (isSeFactoryProvisioningLocked()) {
+          result = false;
+        }
+        break;
+
+      case INS_GET_PROVISION_STATUS_CMD:
+        break;
+
+      default:
+        // Allow other commands only if provision is completed.
+        if (!isProvisioningComplete()) {
+          result = false;
+        }
+    }
+    return result;
+  }
+
+  private boolean isSeFactoryProvisioningLocked() {
+    short pStatus = kmDataStore.getProvisionStatus();
+    boolean result = false;
+    if ((0 != (pStatus & PROVISION_STATUS_SE_LOCKED))) {
+      result = true;
+    }
+    return result;
+  }
+
+  private boolean isSeFactoryProvisioningComplete() {
+    short pStatus = kmDataStore.getProvisionStatus();
+    if (PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR
+        == (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) {
+      return true;
+    }
+    return false;
+  }
+
+  private void processSecureBootCmd(APDU apdu) {
+    short argsProto = KMArray.instance((short) 1);
+    KMArray.cast(argsProto).add((short) 0, KMInteger.exp());
+    short args = receiveIncoming(apdu, argsProto);
+    short val = KMInteger.cast(KMArray.cast(args).get((short) 0)).getShort();
+    if (val != 1 && val != 0) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    // Store secure boot mode value.
+    JCSystem.beginTransaction();
+    kmDataStore.secureBootMode = (byte) val;
+    JCSystem.commitTransaction();
+    kmDataStore.setProvisionStatus(PROVISION_STATUS_SECURE_BOOT_MODE);
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void processOEMUnlockProvisionCmd(APDU apdu) {
+    authenticateOEM(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu);
+    kmDataStore.unlockProvision();
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void processOEMLockProvisionCmd(APDU apdu) {
+    authenticateOEM(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu);
+    // Enable the lock bit in provision status.
+    kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED);
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void authenticateOEM(byte[] plainMsg, APDU apdu) {
+
+    tmpVariables[0] = KMArray.instance((short) 1);
+    KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp());
+    short args = receiveIncoming(apdu, tmpVariables[0]);
+    // Get the signature input.
+    short signature = KMArray.cast(args).get((short) 0);
+    byte[] oemPublicKey = kmDataStore.getOEMRootPublicKey();
+
+    if (!seProvider.ecVerify256(
+        oemPublicKey,
+        (short) 0,
+        (short) oemPublicKey.length,
+        plainMsg,
+        (short) 0,
+        (short) plainMsg.length,
+        KMByteBlob.cast(signature).getBuffer(),
+        KMByteBlob.cast(signature).getStartOff(),
+        KMByteBlob.cast(signature).length())) {
+      KMException.throwIt(KMError.VERIFICATION_FAILED);
+    }
+  }
+
+  private void processProvisionOEMRootPublicKeyCmd(APDU apdu) {
+    // Re-purpose the apdu buffer as scratch pad.
+    byte[] scratchPad = apdu.getBuffer();
+    // Arguments
+    short keyparams = KMKeyParameters.exp();
+    short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT);
+    short blob = KMByteBlob.exp();
+    short argsProto = KMArray.instance((short) 3);
+    KMArray.cast(argsProto).add((short) 0, keyparams);
+    KMArray.cast(argsProto).add((short) 1, keyFormatPtr);
+    KMArray.cast(argsProto).add((short) 2, blob);
+    short args = receiveIncoming(apdu, argsProto);
+
+    // key params should have os patch, os version and verified root of trust
+    data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0);
+    tmpVariables[0] = KMArray.cast(args).get((short) 1);
+    // Key format must be RAW format
+    byte keyFormat = KMEnum.cast(tmpVariables[0]).getVal();
+    if (keyFormat != KMType.RAW) {
+      KMException.throwIt(KMError.UNIMPLEMENTED);
+    }
+
+    // get algorithm - only EC keys expected
+    tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+    if (tmpVariables[0] != KMType.EC) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    // get digest - only SHA256 supported
+    tmpVariables[0] =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]);
+    if (tmpVariables[0] != KMType.INVALID_VALUE) {
+      if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) {
+        KMException.throwIt(KMError.INVALID_ARGUMENT);
+      }
+      tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0);
+      if (tmpVariables[0] != KMType.SHA2_256) {
+        KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+      }
+    } else {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    // Purpose should be VERIFY
+    tmpVariables[0] =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]);
+    if (tmpVariables[0] != KMType.INVALID_VALUE) {
+      if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) {
+        KMException.throwIt(KMError.INVALID_ARGUMENT);
+      }
+      tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0);
+      if (tmpVariables[0] != KMType.VERIFY) {
+        KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+      }
+    } else {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+
+    tmpVariables[0] = KMArray.cast(args).get((short) 2);
+    // persist OEM Root Public Key.
+    kmDataStore.persistOEMRootPublicKey(
+        KMByteBlob.cast(tmpVariables[0]).getBuffer(),
+        KMByteBlob.cast(tmpVariables[0]).getStartOff(),
+        KMByteBlob.cast(tmpVariables[0]).length());
+  }
+
+  private static void processProvisionRkpDeviceUniqueKeyPair(APDU apdu) {
+    // Re-purpose the apdu buffer as scratch pad.
+    byte[] scratchPad = apdu.getBuffer();
+    short arr = KMArray.instance((short) 1);
+    short coseKeyExp = KMCoseKey.exp();
+    KMArray.cast(arr).add((short) 0, coseKeyExp); // [ CoseKey ]
+    arr = receiveIncoming(apdu, arr);
+    // Get cose key.
+    short coseKey = KMArray.cast(arr).get((short) 0);
+    short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+    short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen);
+    // Store the Device unique Key.
+    kmDataStore.createRkpDeviceUniqueKeyPair(
+        scratchPad, (short) 0, pubKeyLen, scratchPad, pubKeyLen, privKeyLen);
+    short bcc = generateBcc(false, scratchPad);
+    short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, MAX_COSE_BUF_SIZE);
+    kmDataStore.persistBootCertificateChain(scratchPad, (short) 0, len);
+    kmDataStore.setProvisionStatus(PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR);
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void processProvisionRkpAdditionalCertChain(APDU apdu) {
+    // X509 certificate chain is received as shown below:
+    /**
+     * x509CertChain = bstr .cbor UdsCerts
+     *
+     * <p>AdditionalDKSignatures = { * SignerName => DKCertChain } ; SignerName is a string
+     * identifier that indicates both the signing authority as ; well as the format of the
+     * DKCertChain SignerName = tstr
+     *
+     * <p>DKCertChain = [ 2* X509Certificate ; Root -> ... -> Leaf. "Root" is the vendor self-signed
+     * ; cert, "Leaf" contains DK_pub. There may also be ; intermediate certificates between Root
+     * and Leaf. ] ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA)
+     * X509Certificate = bstr
+     */
+    // Store the cbor encoded UdsCerts as it is in the persistent memory so cbor decoding is
+    // required here.
+    byte[] srcBuffer = apdu.getBuffer();
+    short recvLen = apdu.setIncomingAndReceive();
+    short srcOffset = apdu.getOffsetCdata();
+    short bufferLength = apdu.getIncomingLength();
+    short bufferStartOffset = repository.allocReclaimableMemory(bufferLength);
+    short index = bufferStartOffset;
+    byte[] buffer = repository.getHeap();
+    while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) {
+      Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen);
+      index += recvLen;
+      recvLen = apdu.receiveBytes(srcOffset);
+    }
+    short byteHeaderLen =
+        decoder.readCertificateChainHeaderLen(buffer, bufferStartOffset, bufferLength);
+    kmDataStore.persistAdditionalCertChain(
+        buffer,
+        (short) (bufferStartOffset + byteHeaderLen),
+        (short) (bufferLength - byteHeaderLen));
+    kmDataStore.setProvisionStatus(PROVISION_STATUS_ADDITIONAL_CERT_CHAIN);
+    // reclaim memory
+    repository.reclaimMemory(bufferLength);
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void processProvisionAttestIdsCmd(APDU apdu) {
+    short keyparams = KMKeyParameters.exp();
+    short cmd = KMArray.instance((short) 1);
+    KMArray.cast(cmd).add((short) 0, keyparams);
+    short args = receiveIncoming(apdu, cmd);
+
+    short attData = KMArray.cast(args).get((short) 0);
+    // persist attestation Ids - if any is missing then exception occurs
+    setAttestationIds(attData);
+  }
+
+  public void setAttestationIds(short attIdVals) {
+    KMKeyParameters instParam = KMKeyParameters.cast(attIdVals);
+    KMArray vals = KMArray.cast(instParam.getVals());
+    short index = 0;
+    short length = vals.length();
+    short key;
+    short type;
+    short obj;
+    while (index < length) {
+      obj = vals.get(index);
+      key = KMTag.getKey(obj);
+      type = KMTag.getTagType(obj);
+
+      if (KMType.BYTES_TAG != type) {
+        KMException.throwIt(KMError.INVALID_ARGUMENT);
+      }
+      obj = KMByteTag.cast(obj).getValue();
+      if (KMByteBlob.cast(obj).length() > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) {
+        KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+      }
+      kmDataStore.setAttestationId(
+          key,
+          KMByteBlob.cast(obj).getBuffer(),
+          KMByteBlob.cast(obj).getStartOff(),
+          KMByteBlob.cast(obj).length());
+      index++;
+    }
+  }
+
+  private void processProvisionPreSharedSecretCmd(APDU apdu) {
+    short blob = KMByteBlob.exp();
+    short argsProto = KMArray.instance((short) 1);
+    KMArray.cast(argsProto).add((short) 0, blob);
+    short args = receiveIncoming(apdu, argsProto);
+
+    short val = KMArray.cast(args).get((short) 0);
+
+    if (val != KMType.INVALID_VALUE && KMByteBlob.cast(val).length() != SHARED_SECRET_KEY_SIZE) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    // Persist shared Hmac.
+    kmDataStore.createPresharedKey(
+        KMByteBlob.cast(val).getBuffer(),
+        KMByteBlob.cast(val).getStartOff(),
+        KMByteBlob.cast(val).length());
+  }
+
+  // This function masks the error code with POWER_RESET_MASK_FLAG
+  // in case if card reset event occurred. The clients of the Applet
+  // has to extract the power reset status from the error code and
+  // process accordingly.
+  private static short buildErrorStatus(short err) {
+    short int32Ptr = KMInteger.instance((short) 4);
+    short powerResetStatus = 0;
+    if (((KMAndroidSEProvider) seProvider).isPowerReset()) {
+      powerResetStatus = POWER_RESET_MASK_FLAG;
+    }
+
+    Util.setShort(
+        KMInteger.cast(int32Ptr).getBuffer(),
+        KMInteger.cast(int32Ptr).getStartOff(),
+        powerResetStatus);
+
+    Util.setShort(
+        KMInteger.cast(int32Ptr).getBuffer(),
+        (short) (KMInteger.cast(int32Ptr).getStartOff() + 2),
+        err);
+    // reset power reset status flag to its default value.
+    // repository.restorePowerResetStatus(); //TODO
+    return int32Ptr;
+  }
+
+  private void processGetProvisionStatusCmd(APDU apdu) {
+    byte[] scratchpad = apdu.getBuffer();
+    short pStatus = kmDataStore.getProvisionStatus();
+    Util.setShort(scratchpad, (short) 0, pStatus);
+    short resp = KMArray.instance((short) 2);
+    KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK));
+    KMArray.cast(resp).add((short) 1, KMInteger.instance(scratchpad, (short) 0, (short) 2));
+    sendOutgoing(apdu, resp);
+  }
+
+  private boolean isProvisioningComplete() {
+    short pStatus = kmDataStore.getProvisionStatus();
+    short pCompleteStatus =
+        PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR
+            | PROVISION_STATUS_PRESHARED_SECRET
+            | PROVISION_STATUS_ATTEST_IDS
+            | PROVISION_STATUS_OEM_PUBLIC_KEY
+            | PROVISION_STATUS_SECURE_BOOT_MODE;
+    if (kmDataStore.isProvisionLocked() || (pCompleteStatus == (pStatus & pCompleteStatus))) {
+      return true;
+    }
+    return false;
+  }
+
+  @Override
+  public void onCleanup() {}
+
+  @Override
+  public void onConsolidate() {}
+
+  private boolean isUpgradeAllowed(short oldVersion) {
+    boolean upgradeAllowed = false;
+    // Downgrade of the Applet is not allowed.
+    if (KM_APPLET_PACKAGE_VERSION >= oldVersion) {
+      upgradeAllowed = true;
+    }
+    return upgradeAllowed;
+  }
+
+  @Override
+  public void onRestore(Element element) {
+    element.initRead();
+    byte magicNumber = element.readByte();
+    if (magicNumber != KM_MAGIC_NUMBER) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short oldPackageVersion = element.readShort();
+    // Validate version.
+    if (!isUpgradeAllowed(oldPackageVersion)) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    kmDataStore.onRestore(element, oldPackageVersion, KM_APPLET_PACKAGE_VERSION);
+  }
+
+  @Override
+  public Element onSave() {
+    short primitiveCount = 3;
+    primitiveCount += kmDataStore.getBackupPrimitiveByteCount();
+    short objectCount = kmDataStore.getBackupObjectCount();
+    // Create element.
+    Element element =
+        UpgradeManager.createElement(Element.TYPE_SIMPLE, primitiveCount, objectCount);
+
+    element.write(KM_MAGIC_NUMBER);
+    element.write(packageVersion);
+    kmDataStore.onSave(element);
+    return element;
+  }
+
+  private short validateApdu(APDU apdu) {
+    // Read the apdu header and buffer.
+    byte[] apduBuffer = apdu.getBuffer();
+    short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1);
+
+    // Validate CLA
+    if (!apdu.isValidCLA()) {
+      ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
+    }
+
+    // Validate P1P2.
+    if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) {
+      sendResponse(apdu, KMError.INVALID_P1P2);
+      return KMType.INVALID_VALUE;
+    }
+    return apduBuffer[ISO7816.OFFSET_INS];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
new file mode 100644
index 0000000..90bc3fe
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java
@@ -0,0 +1,1038 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMAESKey;
+import com.android.javacard.seprovider.KMAttestationCert;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMMasterKey;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+// The class encodes strongbox generated amd signed attestation certificate. This only encodes
+// required fields of the certificates. It is not meant to be generic X509 cert encoder.
+// Whatever fields that are fixed are added as byte arrays. The Extensions are encoded as per
+// the values.
+// The certificate is assembled with leafs first and then the sequences.
+
+public class KMAttestationCertImpl implements KMAttestationCert {
+
+  private static final byte MAX_PARAMS = 30;
+  // DER encoded object identifiers required by the cert.
+  // rsaEncryption - 1.2.840.113549.1.1.1
+  private static final byte[] rsaEncryption = {
+    0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01
+  };
+  // ecPublicKey -  1.2.840.10045.2.1
+  private static final byte[] eccPubKey = {
+    0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01
+  };
+  // prime256v1 curve - 1.2.840.10045.3.1.7
+  private static final byte[] prime256v1 = {
+    0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07
+  };
+  // Key Usage Extn - 2.5.29.15
+  private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F};
+  // Android Extn - 1.3.6.1.4.1.11129.2.1.17
+  private static final byte[] androidExtn = {
+    0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11
+  };
+  private static final short RSA_SIG_LEN = 256;
+  private static final byte ECDSA_MAX_SIG_LEN = 72;
+  // Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2
+  // SEQUENCE of alg OBJ ID and parameters = NULL.
+  private static final byte[] X509EcdsaSignAlgIdentifier = {
+    0x30, 0x0A, 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, (byte) 0x3D, 0x04, 0x03, 0x02
+  };
+  // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11
+  // SEQUENCE of alg OBJ ID and parameters = NULL.
+  private static final byte[] X509RsaSignAlgIdentifier = {
+    0x30, 0x0D, 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01,
+    0x0B, 0x05, 0x00
+  };
+
+  // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's
+  // extension.
+  private static final short[] swTagIds = {
+    KMType.ATTESTATION_APPLICATION_ID,
+    KMType.CREATION_DATETIME,
+    KMType.ALLOW_WHILE_ON_BODY,
+    KMType.USAGE_COUNT_LIMIT,
+    KMType.USAGE_EXPIRE_DATETIME,
+    KMType.ORIGINATION_EXPIRE_DATETIME,
+    KMType.ACTIVE_DATETIME,
+  };
+
+  // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's
+  // extension.
+  private static final short[] hwTagIds = {
+    KMType.BOOT_PATCH_LEVEL,
+    KMType.VENDOR_PATCH_LEVEL,
+    KMType.ATTESTATION_ID_MODEL,
+    KMType.ATTESTATION_ID_MANUFACTURER,
+    KMType.ATTESTATION_ID_MEID,
+    KMType.ATTESTATION_ID_IMEI,
+    KMType.ATTESTATION_ID_SERIAL,
+    KMType.ATTESTATION_ID_PRODUCT,
+    KMType.ATTESTATION_ID_DEVICE,
+    KMType.ATTESTATION_ID_BRAND,
+    KMType.OS_PATCH_LEVEL,
+    KMType.OS_VERSION,
+    KMType.ROOT_OF_TRUST,
+    KMType.ORIGIN,
+    KMType.UNLOCKED_DEVICE_REQUIRED,
+    KMType.TRUSTED_CONFIRMATION_REQUIRED,
+    KMType.AUTH_TIMEOUT,
+    KMType.USER_AUTH_TYPE,
+    KMType.NO_AUTH_REQUIRED,
+    KMType.EARLY_BOOT_ONLY,
+    KMType.ROLLBACK_RESISTANCE,
+    KMType.RSA_OAEP_MGF_DIGEST,
+    KMType.RSA_PUBLIC_EXPONENT,
+    KMType.ECCURVE,
+    KMType.PADDING,
+    KMType.DIGEST,
+    KMType.KEYSIZE,
+    KMType.ALGORITHM,
+    KMType.PURPOSE
+  };
+
+  private static final byte keyUsageSign = (byte) 0x80; // 0 bit
+  private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit
+  private static final byte keyUsageDataEncipher = (byte) 0x10; // 3rd- bit
+  private static final byte keyUsageKeyAgreement = (byte) 0x08; // 4th- bit
+  private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit
+
+  private static final short KEYMINT_VERSION = 200;
+  private static final short ATTESTATION_VERSION = 200;
+  private static final byte[] pubExponent = {0x01, 0x00, 0x01};
+  private static final byte X509_VERSION = (byte) 0x02;
+
+  // Buffer indexes in transient array
+  private static final byte NUM_INDEX_ENTRIES = 21;
+  private static final byte CERT_START = (byte) 0;
+  private static final byte CERT_LENGTH = (byte) 1;
+  private static final byte TBS_START = (byte) 2;
+  private static final byte TBS_LENGTH = (byte) 3;
+  private static final byte BUF_START = (byte) 4;
+  private static final byte BUF_LENGTH = (byte) 5;
+  private static final byte SW_PARAM_INDEX = (byte) 6;
+  private static final byte HW_PARAM_INDEX = (byte) 7;
+  // Data indexes in transient array
+  private static final byte STACK_PTR = (byte) 8;
+  private static final byte UNIQUE_ID = (byte) 9;
+  private static final byte ATT_CHALLENGE = (byte) 10;
+  private static final byte NOT_BEFORE = (byte) 11;
+  private static final byte NOT_AFTER = (byte) 12;
+  private static final byte PUB_KEY = (byte) 13;
+  private static final byte VERIFIED_BOOT_KEY = (byte) 14;
+  private static final byte VERIFIED_HASH = (byte) 15;
+  private static final byte ISSUER = (byte) 16;
+  private static final byte SUBJECT_NAME = (byte) 17;
+  private static final byte SERIAL_NUMBER = (byte) 18;
+  private static final byte CERT_ATT_KEY_SECRET = (byte) 19;
+  private static final byte CERT_ATT_KEY_RSA_PUB_MOD = (byte) 20;
+  // State indexes in transient array
+  private static final byte NUM_STATE_ENTRIES = 7;
+  private static final byte KEY_USAGE = (byte) 0;
+  private static final byte UNUSED_BITS = (byte) 1;
+  private static final byte DEVICE_LOCKED = (byte) 2;
+  private static final byte VERIFIED_STATE = (byte) 3;
+  private static final byte CERT_MODE = (byte) 4;
+  private static final byte RSA_CERT = (byte) 5;
+  private static final byte CERT_RSA_SIGN = (byte) 6;
+
+  private static KMAttestationCert inst;
+  private static KMSEProvider seProvider;
+
+  private static short[] indexes;
+  private static byte[] states;
+
+  private static byte[] stack;
+  private static short[] swParams;
+  private static short[] hwParams;
+
+  private static final byte SERIAL_NUM_MAX_LEN = 20;
+
+  private KMAttestationCertImpl() {}
+
+  public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) {
+    if (inst == null) {
+      inst = new KMAttestationCertImpl();
+      seProvider = provider;
+
+      // Allocate transient memory
+      indexes = JCSystem.makeTransientShortArray(NUM_INDEX_ENTRIES, JCSystem.CLEAR_ON_RESET);
+      states = JCSystem.makeTransientByteArray(NUM_STATE_ENTRIES, JCSystem.CLEAR_ON_RESET);
+      swParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+      hwParams = JCSystem.makeTransientShortArray(MAX_PARAMS, JCSystem.CLEAR_ON_RESET);
+    }
+    init(rsaCert);
+    return inst;
+  }
+
+  private static void init(boolean rsaCert) {
+    for (short i = 0; i < NUM_INDEX_ENTRIES; i++) {
+      indexes[i] = 0;
+    }
+    Util.arrayFillNonAtomic(states, (short) 0, NUM_STATE_ENTRIES, (byte) 0);
+    stack = null;
+    states[CERT_MODE] = KMType.NO_CERT;
+    states[UNUSED_BITS] = 8;
+    states[RSA_CERT] = rsaCert ? (byte) 1 : (byte) 0;
+    states[CERT_RSA_SIGN] = 1;
+    indexes[CERT_ATT_KEY_SECRET] = KMType.INVALID_VALUE;
+    indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+    indexes[ISSUER] = KMType.INVALID_VALUE;
+    indexes[SUBJECT_NAME] = KMType.INVALID_VALUE;
+    indexes[SERIAL_NUMBER] = KMType.INVALID_VALUE;
+  }
+
+  @Override
+  public KMAttestationCert verifiedBootHash(short obj) {
+    indexes[VERIFIED_HASH] = obj;
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert verifiedBootKey(short obj) {
+    indexes[VERIFIED_BOOT_KEY] = obj;
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert verifiedBootState(byte val) {
+    states[VERIFIED_STATE] = val;
+    return this;
+  }
+
+  private KMAttestationCert uniqueId(short obj) {
+    indexes[UNIQUE_ID] = obj;
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad) {
+    if (!derEncoded) {
+      // convert milliseconds to UTC date
+      indexes[NOT_BEFORE] = KMUtils.convertToDate(obj, scratchpad, true);
+    } else {
+      indexes[NOT_BEFORE] =
+          KMByteBlob.instance(
+              KMByteBlob.cast(obj).getBuffer(),
+              KMByteBlob.cast(obj).getStartOff(),
+              KMByteBlob.cast(obj).length());
+    }
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert notAfter(
+      short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) {
+    if (!derEncoded) {
+      if (usageExpiryTimeObj != KMType.INVALID_VALUE) {
+        // compare if the expiry time is greater then 2050 then use generalized
+        // time format else use utc time format.
+        short tmpVar = KMInteger.uint_64(KMUtils.firstJan2050, (short) 0);
+        if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) {
+          usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, false);
+        } else {
+          usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, true);
+        }
+        indexes[NOT_AFTER] = usageExpiryTimeObj;
+      } else {
+        // notAfter = certExpirtyTimeObj;
+      }
+    } else {
+      indexes[NOT_AFTER] = usageExpiryTimeObj;
+    }
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert deviceLocked(boolean val) {
+    if (val) {
+      states[DEVICE_LOCKED] = (byte) 0xFF;
+    } else {
+      states[DEVICE_LOCKED] = 0;
+    }
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert publicKey(short obj) {
+    indexes[PUB_KEY] = obj;
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert attestationChallenge(short obj) {
+    indexes[ATT_CHALLENGE] = obj;
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert extensionTag(short tag, boolean hwEnforced) {
+    if (hwEnforced) {
+      hwParams[indexes[HW_PARAM_INDEX]] = tag;
+      indexes[HW_PARAM_INDEX]++;
+    } else {
+      swParams[indexes[SW_PARAM_INDEX]] = tag;
+      indexes[SW_PARAM_INDEX]++;
+    }
+    if (KMTag.getKey(tag) == KMType.PURPOSE) {
+      createKeyUsage(tag);
+    }
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert issuer(short obj) {
+    indexes[ISSUER] = obj;
+    return this;
+  }
+
+  private void createKeyUsage(short tag) {
+    short len = KMEnumArrayTag.cast(tag).length();
+    byte index = 0;
+    while (index < len) {
+      if (KMEnumArrayTag.cast(tag).get(index) == KMType.SIGN) {
+        states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageSign);
+      } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.WRAP_KEY) {
+        states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyEncipher);
+      } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) {
+        states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageDataEncipher);
+      } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.AGREE_KEY) {
+        states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageKeyAgreement);
+      } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.ATTEST_KEY) {
+        states[KEY_USAGE] = (byte) (states[KEY_USAGE] | keyUsageCertSign);
+      }
+      index++;
+    }
+    index = states[KEY_USAGE];
+    while (index != 0) {
+      index = (byte) (index << 1);
+      states[UNUSED_BITS]--;
+    }
+  }
+
+  private static void pushTbsCert(boolean rsaCert, boolean rsa) {
+    short last = indexes[STACK_PTR];
+    pushExtensions();
+    // subject public key info
+    if (rsaCert) {
+      pushRsaSubjectKeyInfo();
+    } else {
+      pushEccSubjectKeyInfo();
+    }
+    // subject
+    pushBytes(
+        KMByteBlob.cast(indexes[SUBJECT_NAME]).getBuffer(),
+        KMByteBlob.cast(indexes[SUBJECT_NAME]).getStartOff(),
+        KMByteBlob.cast(indexes[SUBJECT_NAME]).length());
+    pushValidity();
+    // issuer - der encoded
+    pushBytes(
+        KMByteBlob.cast(indexes[ISSUER]).getBuffer(),
+        KMByteBlob.cast(indexes[ISSUER]).getStartOff(),
+        KMByteBlob.cast(indexes[ISSUER]).length());
+    // Algorithm Id
+    if (rsa) {
+      pushAlgorithmId(X509RsaSignAlgIdentifier);
+    } else {
+      pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+    }
+    // Serial Number
+    pushBytes(
+        KMByteBlob.cast(indexes[SERIAL_NUMBER]).getBuffer(),
+        KMByteBlob.cast(indexes[SERIAL_NUMBER]).getStartOff(),
+        KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+    pushIntegerHeader(KMByteBlob.cast(indexes[SERIAL_NUMBER]).length());
+    // Version
+    pushByte(X509_VERSION);
+    pushIntegerHeader((short) 1);
+    pushByte((byte) 0x03);
+    pushByte((byte) 0xA0);
+    // Finally sequence header.
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushExtensions() {
+    short last = indexes[STACK_PTR];
+    // Push KeyUsage extension
+    if (states[KEY_USAGE] != 0) {
+      pushKeyUsage(states[KEY_USAGE], states[UNUSED_BITS]);
+    }
+    if (states[CERT_MODE] == KMType.ATTESTATION_CERT) {
+      pushKeyDescription();
+    }
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+    // Extensions have explicit tag of [3]
+    pushLength((short) (last - indexes[STACK_PTR]));
+    pushByte((byte) 0xA3);
+  }
+
+  // Time SEQUENCE{UTCTime, UTC or Generalized Time)
+  private static void pushValidity() {
+    short last = indexes[STACK_PTR];
+    if (indexes[NOT_AFTER] != 0) {
+      pushBytes(
+          KMByteBlob.cast(indexes[NOT_AFTER]).getBuffer(),
+          KMByteBlob.cast(indexes[NOT_AFTER]).getStartOff(),
+          KMByteBlob.cast(indexes[NOT_AFTER]).length());
+    } else {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    pushTimeHeader(KMByteBlob.cast(indexes[NOT_AFTER]).length());
+    pushBytes(
+        KMByteBlob.cast(indexes[NOT_BEFORE]).getBuffer(),
+        KMByteBlob.cast(indexes[NOT_BEFORE]).getStartOff(),
+        KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+    pushTimeHeader(KMByteBlob.cast(indexes[NOT_BEFORE]).length());
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushTimeHeader(short len) {
+    if (len == 13) { // UTC Time
+      pushLength((short) 0x0D);
+      pushByte((byte) 0x17);
+    } else if (len == 15) { // Generalized Time
+      pushLength((short) 0x0F);
+      pushByte((byte) 0x18);
+    } else {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+  }
+
+  // SEQUENCE{SEQUENCE{algId, NULL}, bitString{SEQUENCE{ modulus as positive integer, public
+  // exponent
+  // as positive integer}
+  private static void pushRsaSubjectKeyInfo() {
+    short last = indexes[STACK_PTR];
+    pushBytes(pubExponent, (short) 0, (short) pubExponent.length);
+    pushIntegerHeader((short) pubExponent.length);
+    pushBytes(
+        KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+        KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+        KMByteBlob.cast(indexes[PUB_KEY]).length());
+
+    // encode modulus as positive if the MSB is 1.
+    if (KMByteBlob.cast(indexes[PUB_KEY]).get((short) 0) < 0) {
+      pushByte((byte) 0x00);
+      pushIntegerHeader((short) (KMByteBlob.cast(indexes[PUB_KEY]).length() + 1));
+    } else {
+      pushIntegerHeader(KMByteBlob.cast(indexes[PUB_KEY]).length());
+    }
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+    pushBitStringHeader((byte) 0x00, (short) (last - indexes[STACK_PTR]));
+    pushRsaEncryption();
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  // SEQUENCE{SEQUENCE{ecPubKey, prime256v1}, bitString{pubKey}}
+  private static void pushEccSubjectKeyInfo() {
+    short last = indexes[STACK_PTR];
+    pushBytes(
+        KMByteBlob.cast(indexes[PUB_KEY]).getBuffer(),
+        KMByteBlob.cast(indexes[PUB_KEY]).getStartOff(),
+        KMByteBlob.cast(indexes[PUB_KEY]).length());
+    pushBitStringHeader((byte) 0x00, KMByteBlob.cast(indexes[PUB_KEY]).length());
+    pushEcDsa();
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushEcDsa() {
+    short last = indexes[STACK_PTR];
+    pushBytes(prime256v1, (short) 0, (short) prime256v1.length);
+    pushBytes(eccPubKey, (short) 0, (short) eccPubKey.length);
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushRsaEncryption() {
+    short last = indexes[STACK_PTR];
+    pushNullHeader();
+    pushBytes(rsaEncryption, (short) 0, (short) rsaEncryption.length);
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  // KeyDescription ::= SEQUENCE {
+  //         attestationVersion         INTEGER, # Value 200
+  //         attestationSecurityLevel   SecurityLevel, # See below
+  //         keymasterVersion           INTEGER, # Value 200
+  //         keymasterSecurityLevel     SecurityLevel, # See below
+  //         attestationChallenge       OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
+  //         uniqueId                   OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
+  //         softwareEnforced           AuthorizationList, # See below
+  //         hardwareEnforced           AuthorizationList, # See below
+  //     }
+  private static void pushKeyDescription() {
+    short last = indexes[STACK_PTR];
+    pushHWParams();
+    pushSWParams();
+    if (indexes[UNIQUE_ID] != 0) {
+      pushOctetString(
+          KMByteBlob.cast(indexes[UNIQUE_ID]).getBuffer(),
+          KMByteBlob.cast(indexes[UNIQUE_ID]).getStartOff(),
+          KMByteBlob.cast(indexes[UNIQUE_ID]).length());
+    } else {
+      pushOctetStringHeader((short) 0);
+    }
+    pushOctetString(
+        KMByteBlob.cast(indexes[ATT_CHALLENGE]).getBuffer(),
+        KMByteBlob.cast(indexes[ATT_CHALLENGE]).getStartOff(),
+        KMByteBlob.cast(indexes[ATT_CHALLENGE]).length());
+    pushEnumerated(KMType.STRONGBOX);
+    pushShort(KEYMINT_VERSION);
+    pushIntegerHeader((short) 2);
+    pushEnumerated(KMType.STRONGBOX);
+    pushShort(ATTESTATION_VERSION);
+    pushIntegerHeader((short) 2);
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+    pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+    pushBytes(androidExtn, (short) 0, (short) androidExtn.length);
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushSWParams() {
+    short last = indexes[STACK_PTR];
+    byte index = 0;
+    short length = (short) swTagIds.length;
+    do {
+      pushParams(swParams, indexes[SW_PARAM_INDEX], swTagIds[index]);
+    } while (++index < length);
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushHWParams() {
+    short last = indexes[STACK_PTR];
+    byte index = 0;
+    short length = (short) hwTagIds.length;
+    do {
+      if (hwTagIds[index] == KMType.ROOT_OF_TRUST) {
+        pushRoT();
+        continue;
+      }
+      if (pushParams(hwParams, indexes[HW_PARAM_INDEX], hwTagIds[index])) {
+        continue;
+      }
+    } while (++index < length);
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static boolean pushParams(short[] params, short len, short tagId) {
+    short index = 0;
+    while (index < len) {
+      if (tagId == KMTag.getKey(params[index])) {
+        pushTag(params[index]);
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  private static void pushTag(short tag) {
+    short type = KMTag.getTagType(tag);
+    short tagId = KMTag.getKey(tag);
+    short val;
+    switch (type) {
+      case KMType.BYTES_TAG:
+        val = KMByteTag.cast(tag).getValue();
+        pushBytesTag(
+            tagId,
+            KMByteBlob.cast(val).getBuffer(),
+            KMByteBlob.cast(val).getStartOff(),
+            KMByteBlob.cast(val).length());
+        break;
+      case KMType.ENUM_TAG:
+        val = KMEnumTag.cast(tag).getValue();
+        pushEnumTag(tagId, (byte) val);
+        break;
+      case KMType.ENUM_ARRAY_TAG:
+        val = KMEnumArrayTag.cast(tag).getValues();
+        pushEnumArrayTag(
+            tagId,
+            KMByteBlob.cast(val).getBuffer(),
+            KMByteBlob.cast(val).getStartOff(),
+            KMByteBlob.cast(val).length());
+        break;
+      case KMType.UINT_TAG:
+      case KMType.ULONG_TAG:
+      case KMType.DATE_TAG:
+        val = KMIntegerTag.cast(tag).getValue();
+        pushIntegerTag(
+            tagId,
+            KMInteger.cast(val).getBuffer(),
+            KMInteger.cast(val).getStartOff(),
+            KMInteger.cast(val).length());
+        break;
+      case KMType.UINT_ARRAY_TAG:
+      case KMType.ULONG_ARRAY_TAG:
+        // According to KeyMint hal only one user secure id is used but this conflicts with
+        //  tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS
+        val = KMIntegerArrayTag.cast(tag).getValues();
+        pushIntegerArrayTag(tagId, val);
+        break;
+      case KMType.BOOL_TAG:
+        val = KMBoolTag.cast(tag).getVal();
+        pushBoolTag(tagId);
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_TAG);
+        break;
+    }
+  }
+
+  // RootOfTrust ::= SEQUENCE {
+  //          verifiedBootKey            OCTET_STRING,
+  //          deviceLocked               BOOLEAN,
+  //          verifiedBootState          VerifiedBootState,
+  //          verifiedBootHash           OCTET_STRING,
+  //      }
+  // VerifiedBootState ::= ENUMERATED {
+  //          Verified                   (0),
+  //          SelfSigned                 (1),
+  //          Unverified                 (2),
+  //          Failed                     (3),
+  //      }
+  private static void pushRoT() {
+    short last = indexes[STACK_PTR];
+    // verified boot hash
+    pushOctetString(
+        KMByteBlob.cast(indexes[VERIFIED_HASH]).getBuffer(),
+        KMByteBlob.cast(indexes[VERIFIED_HASH]).getStartOff(),
+        KMByteBlob.cast(indexes[VERIFIED_HASH]).length());
+
+    pushEnumerated(states[VERIFIED_STATE]);
+
+    pushBoolean(states[DEVICE_LOCKED]);
+    // verified boot Key
+    pushOctetString(
+        KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getBuffer(),
+        KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).getStartOff(),
+        KMByteBlob.cast(indexes[VERIFIED_BOOT_KEY]).length());
+
+    // Finally sequence header
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+    // ... and tag Id
+    pushTagIdHeader(KMType.ROOT_OF_TRUST, (short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushOctetString(byte[] buf, short start, short len) {
+    pushBytes(buf, start, len);
+    pushOctetStringHeader(len);
+  }
+
+  private static void pushBoolean(byte val) {
+    pushByte(val);
+    pushBooleanHeader((short) 1);
+  }
+
+  private static void pushBooleanHeader(short len) {
+    pushLength(len);
+    pushByte((byte) 0x01);
+  }
+
+  // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+  // All of these are enum array tags i.e. byte long values
+  private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short len) {
+    short last = indexes[STACK_PTR];
+    short index = 0;
+    while (index < len) {
+      pushByte(buf[(short) (start + index)]);
+      pushIntegerHeader((short) 1);
+      index++;
+    }
+    pushSetHeader((short) (last - indexes[STACK_PTR]));
+    pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+  }
+
+  // Only SET of INTEGERS supported are padding, digest, purpose and blockmode
+  // All of these are enum array tags i.e. byte long values
+  private static void pushIntegerArrayTag(short tagId, short arr) {
+    short last = indexes[STACK_PTR];
+    short index = 0;
+    short len = KMArray.cast(arr).length();
+    short ptr;
+    while (index < len) {
+      ptr = KMArray.cast(arr).get(index);
+      pushInteger(
+          KMInteger.cast(ptr).getBuffer(),
+          KMInteger.cast(ptr).getStartOff(),
+          KMInteger.cast(ptr).length());
+      index++;
+    }
+    pushSetHeader((short) (last - indexes[STACK_PTR]));
+    pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushSetHeader(short len) {
+    pushLength(len);
+    pushByte((byte) 0x31);
+  }
+
+  private static void pushEnumerated(byte val) {
+    short last = indexes[STACK_PTR];
+    pushByte(val);
+    pushEnumeratedHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushEnumeratedHeader(short len) {
+    pushLength(len);
+    pushByte((byte) 0x0A);
+  }
+
+  private static void pushBoolTag(short tagId) {
+    short last = indexes[STACK_PTR];
+    pushNullHeader();
+    pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushNullHeader() {
+    pushByte((byte) 0);
+    pushByte((byte) 0x05);
+  }
+
+  private static void pushEnumTag(short tagId, byte val) {
+    short last = indexes[STACK_PTR];
+    pushByte(val);
+    pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+    pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) {
+    short last = indexes[STACK_PTR];
+    pushInteger(buf, start, len);
+    pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+  }
+
+  // Ignore leading zeros. Only Unsigned Integers are required hence if MSB is set then add 0x00
+  // as most significant byte.
+  private static void pushInteger(byte[] buf, short start, short len) {
+    short last = indexes[STACK_PTR];
+    byte index = 0;
+    while (index < (byte) len) {
+      if (buf[(short) (start + index)] != 0) {
+        break;
+      }
+      index++;
+    }
+    if (index == (byte) len) {
+      pushByte((byte) 0x00);
+    } else {
+      pushBytes(buf, (short) (start + index), (short) (len - index));
+      if (buf[(short) (start + index)] < 0) { // MSB is 1
+        pushByte((byte) 0x00); // always unsigned int
+      }
+    }
+    pushIntegerHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  // Bytes Tag is a octet string and tag id is added explicitly
+  private static void pushBytesTag(short tagId, byte[] buf, short start, short len) {
+    short last = indexes[STACK_PTR];
+    pushBytes(buf, start, len);
+    pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+    pushTagIdHeader(tagId, (short) (last - indexes[STACK_PTR]));
+  }
+
+  // tag id <= 30 ---> 0xA0 | {tagId}
+  // 30 < tagId < 128 ---> 0xBF 0x{tagId}
+  // tagId >= 128 ---> 0xBF 0x80+(tagId/128) 0x{tagId - (128*(tagId/128))}
+  private static void pushTagIdHeader(short tagId, short len) {
+    pushLength(len);
+    short count = (short) (tagId / 128);
+    if (count > 0) {
+      pushByte((byte) (tagId - (128 * count)));
+      pushByte((byte) (0x80 + count));
+      pushByte((byte) 0xBF);
+    } else if (tagId > 30) {
+      pushByte((byte) tagId);
+      pushByte((byte) 0xBF);
+    } else {
+      pushByte((byte) (0xA0 | (byte) tagId));
+    }
+  }
+
+  // SEQUENCE {ObjId, OCTET STRING{BIT STRING{keyUsage}}}
+  private static void pushKeyUsage(byte keyUsage, byte unusedBits) {
+    short last = indexes[STACK_PTR];
+    pushByte(keyUsage);
+    pushBitStringHeader(unusedBits, (short) (last - indexes[STACK_PTR]));
+    pushOctetStringHeader((short) (last - indexes[STACK_PTR]));
+    pushBytes(keyUsageExtn, (short) 0, (short) keyUsageExtn.length);
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+  }
+
+  private static void pushAlgorithmId(byte[] algId) {
+    pushBytes(algId, (short) 0, (short) algId.length);
+  }
+
+  private static void pushIntegerHeader(short len) {
+    pushLength(len);
+    pushByte((byte) 0x02);
+  }
+
+  private static void pushOctetStringHeader(short len) {
+    pushLength(len);
+    pushByte((byte) 0x04);
+  }
+
+  private static void pushSequenceHeader(short len) {
+    pushLength(len);
+    pushByte((byte) 0x30);
+  }
+
+  private static void pushBitStringHeader(byte unusedBits, short len) {
+    pushByte(unusedBits);
+    pushLength((short) (len + 1)); // 1 extra byte for unused bits byte
+    pushByte((byte) 0x03);
+  }
+
+  private static void pushLength(short len) {
+    if (len < 128) {
+      pushByte((byte) len);
+    } else if (len < 256) {
+      pushByte((byte) len);
+      pushByte((byte) 0x81);
+    } else {
+      pushShort(len);
+      pushByte((byte) 0x82);
+    }
+  }
+
+  private static void pushShort(short val) {
+    decrementStackPtr((short) 2);
+    Util.setShort(stack, indexes[STACK_PTR], val);
+  }
+
+  private static void pushByte(byte val) {
+    decrementStackPtr((short) 1);
+    stack[indexes[STACK_PTR]] = val;
+  }
+
+  private static void pushBytes(byte[] buf, short start, short len) {
+    decrementStackPtr(len);
+    if (buf != null) {
+      Util.arrayCopyNonAtomic(buf, start, stack, indexes[STACK_PTR], len);
+    }
+  }
+
+  private static void decrementStackPtr(short cnt) {
+    indexes[STACK_PTR] = (short) (indexes[STACK_PTR] - cnt);
+    if (indexes[BUF_START] > indexes[STACK_PTR]) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+  }
+
+  @Override
+  public KMAttestationCert buffer(byte[] buf, short start, short maxLen) {
+    stack = buf;
+    indexes[BUF_START] = start;
+    indexes[BUF_LENGTH] = maxLen;
+    indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+    return this;
+  }
+
+  @Override
+  public short getCertStart() {
+    return indexes[CERT_START];
+  }
+
+  @Override
+  public short getCertLength() {
+    return indexes[CERT_LENGTH];
+  }
+
+  public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) {
+    indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]);
+    short last = indexes[STACK_PTR];
+    short sigLen = 0;
+    if (fakeCert) {
+      rsaSign = true;
+      pushByte((byte) 0);
+      sigLen = 1;
+    }
+    // Push placeholder signature Bit string header
+    // This will potentially change at the end
+    else if (rsaSign) {
+      decrementStackPtr(RSA_SIG_LEN);
+    } else {
+      decrementStackPtr(ECDSA_MAX_SIG_LEN);
+    }
+    short signatureOffset = indexes[STACK_PTR];
+    pushBitStringHeader((byte) 0, (short) (last - indexes[STACK_PTR]));
+    if (rsaSign) {
+      pushAlgorithmId(X509RsaSignAlgIdentifier);
+    } else {
+      pushAlgorithmId(X509EcdsaSignAlgIdentifier);
+    }
+    indexes[TBS_LENGTH] = indexes[STACK_PTR];
+    pushTbsCert((states[RSA_CERT] == 0 ? false : true), rsaSign);
+    indexes[TBS_START] = indexes[STACK_PTR];
+    indexes[TBS_LENGTH] = (short) (indexes[TBS_LENGTH] - indexes[TBS_START]);
+    if (attSecret != KMType.INVALID_VALUE) {
+      // Sign with the attestation key
+      // The pubKey is the modulus.
+      if (rsaSign) {
+        sigLen =
+            seProvider.rsaSign256Pkcs1(
+                KMByteBlob.cast(attSecret).getBuffer(),
+                KMByteBlob.cast(attSecret).getStartOff(),
+                KMByteBlob.cast(attSecret).length(),
+                KMByteBlob.cast(attMod).getBuffer(),
+                KMByteBlob.cast(attMod).getStartOff(),
+                KMByteBlob.cast(attMod).length(),
+                stack,
+                indexes[TBS_START],
+                indexes[TBS_LENGTH],
+                stack,
+                signatureOffset);
+        if (sigLen > RSA_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+      } else {
+        sigLen =
+            seProvider.ecSign256(
+                KMByteBlob.cast(attSecret).getBuffer(),
+                KMByteBlob.cast(attSecret).getStartOff(),
+                KMByteBlob.cast(attSecret).length(),
+                stack,
+                indexes[TBS_START],
+                indexes[TBS_LENGTH],
+                stack,
+                signatureOffset);
+        if (sigLen > ECDSA_MAX_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR);
+      }
+      // Adjust signature length
+      indexes[STACK_PTR] = signatureOffset;
+      pushBitStringHeader((byte) 0, sigLen);
+    } else if (!fakeCert) { // No attestation key provisioned in the factory
+      KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED);
+    }
+    last = (short) (signatureOffset + sigLen);
+    // Add certificate sequence header
+    indexes[STACK_PTR] = indexes[TBS_START];
+    pushSequenceHeader((short) (last - indexes[STACK_PTR]));
+    indexes[CERT_START] = indexes[STACK_PTR];
+    indexes[CERT_LENGTH] = (short) (last - indexes[CERT_START]);
+  }
+
+  @Override
+  public void build() {
+    if (states[CERT_MODE] == KMType.FAKE_CERT) {
+      build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true);
+    } else {
+      build(
+          indexes[CERT_ATT_KEY_SECRET],
+          indexes[CERT_ATT_KEY_RSA_PUB_MOD],
+          (states[CERT_RSA_SIGN] == 0 ? false : true),
+          false);
+    }
+  }
+
+  @Override
+  public KMAttestationCert makeUniqueId(
+      byte[] scratchPad,
+      short scratchPadOff,
+      byte[] creationTime,
+      short timeOffset,
+      short creationTimeLen,
+      byte[] attestAppId,
+      short appIdOff,
+      short attestAppIdLen,
+      byte resetSinceIdRotation,
+      KMMasterKey masterKey) {
+    // Concatenate T||C||R
+    // temporal count T
+    short temp =
+        KMUtils.countTemporalCount(
+            creationTime, timeOffset, creationTimeLen, scratchPad, scratchPadOff);
+    Util.setShort(scratchPad, (short) scratchPadOff, temp);
+    temp = scratchPadOff;
+    scratchPadOff += 2;
+
+    // Application Id C
+    Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, attestAppIdLen);
+    scratchPadOff += attestAppIdLen;
+
+    // Reset After Rotation R
+    scratchPad[scratchPadOff] = resetSinceIdRotation;
+    scratchPadOff++;
+
+    // Get the key data from the master key
+    KMAESKey aesKey = (KMAESKey) masterKey;
+    short mKeyData = KMByteBlob.instance((short) (aesKey.aesKey.getSize() / 8));
+    aesKey.aesKey.getKey(
+        KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+        KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/
+    timeOffset = KMByteBlob.instance((short) 32);
+    appIdOff =
+        seProvider.hmacSign(
+            KMByteBlob.cast(mKeyData).getBuffer(), /* Key */
+            KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/
+            KMByteBlob.cast(mKeyData).length(), /* Key length*/
+            scratchPad, /* data */
+            temp, /* data start */
+            scratchPadOff, /* data length */
+            KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */
+            KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */
+    if (appIdOff != 32) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    return uniqueId(timeOffset);
+  }
+
+  @Override
+  public boolean serialNumber(short number) {
+    // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.2
+    short length = KMByteBlob.cast(number).length();
+    if (length > SERIAL_NUM_MAX_LEN) {
+      return false;
+    }
+    // The serial number Must be a positive integer.
+    byte msb = KMByteBlob.cast(number).get((short) 0);
+    if (msb < 0 && length > (SERIAL_NUM_MAX_LEN - 1)) {
+      return false;
+    }
+    indexes[SERIAL_NUMBER] = number;
+    return true;
+  }
+
+  @Override
+  public boolean subjectName(short sub) {
+    if (sub == KMType.INVALID_VALUE || KMByteBlob.cast(sub).length() == 0) return false;
+    indexes[SUBJECT_NAME] = sub;
+    return true;
+  }
+
+  @Override
+  public KMAttestationCert ecAttestKey(short attestKey, byte mode) {
+    states[CERT_MODE] = mode;
+    indexes[CERT_ATT_KEY_SECRET] = attestKey;
+    indexes[CERT_ATT_KEY_RSA_PUB_MOD] = KMType.INVALID_VALUE;
+    states[CERT_RSA_SIGN] = 0;
+    return this;
+  }
+
+  @Override
+  public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode) {
+    states[CERT_MODE] = mode;
+    indexes[CERT_ATT_KEY_SECRET] = attestPrivExp;
+    indexes[CERT_ATT_KEY_RSA_PUB_MOD] = attestMod;
+    states[CERT_RSA_SIGN] = 1;
+    return this;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java
new file mode 100644
index 0000000..3fb3653
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+public class KMConfigurations {
+  // Machine types
+  public static final byte LITTLE_ENDIAN = 0x00;
+  public static final byte BIG_ENDIAN = 0x01;
+  public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN;
+  // If the size of the attestation ids is known and lesser than 64
+  // then reduce the size here. It reduces the heap memory usage.
+  public static final byte MAX_ATTESTATION_IDS_SIZE = 64;
+  public static final short MAX_SUBJECT_DER_LEN = 1095;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java
new file mode 100644
index 0000000..bed3ba7
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.Util;
+
+public class KMUtils {
+
+  // 64 bit unsigned calculations for time
+  public static final byte[] oneSecMsec = {0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec
+  public static final byte[] oneMinMsec = {0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec
+  public static final byte[] oneHourMsec = {
+    0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80
+  }; // 3600000 msec
+  public static final byte[] oneDayMsec = {0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec
+  public static final byte[] oneMonthMsec = {
+    0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50
+  }; // 2629746000 msec
+  public static final byte[] leapYearMsec = {
+    0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00
+  }; // 31622400000;
+  public static final byte[] yearMsec = {
+    0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00
+  }; // 31536000000
+  // Leap year(366) + 3 * 365
+  public static final byte[] fourYrsMsec = {
+    0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00
+  }; // 126230400000
+  public static final byte[] firstJan2020 = {
+    0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00
+  }; // 1577836800000 msec
+  public static final byte[] firstJan2050 = {
+    0, 0, 0x02, 0x4b, (byte) 0xCE, 0x5C, (byte) 0xF0, 0x00
+  }; // 2524608000000
+  // msec
+  public static final byte[] febMonthLeapMSec = {
+    0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00
+  }; // 2505600000
+  public static final byte[] febMonthMsec = {
+    0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00
+  }; // 2419200000
+  public static final byte[] ThirtyOneDaysMonthMsec = {
+    0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00
+  }; // 2678400000
+  public static final byte[] ThirtDaysMonthMsec = {
+    0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00
+  }; // 2592000000
+  public static final short year2051 = 2051;
+  public static final short year2020 = 2020;
+  // Convert to milliseconds constants
+  public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3};
+
+  // --------------------------------------
+  public static short convertToDate(short time, byte[] scratchPad, boolean utcFlag) {
+
+    short yrsCount = 0;
+    short monthCount = 1;
+    short dayCount = 1;
+    short hhCount = 0;
+    short mmCount = 0;
+    short ssCount = 0;
+    byte Z = 0x5A;
+    boolean from2020 = true;
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+    Util.arrayCopyNonAtomic(
+        KMInteger.cast(time).getBuffer(),
+        KMInteger.cast(time).getStartOff(),
+        scratchPad,
+        (short) (8 - KMInteger.cast(time).length()),
+        KMInteger.cast(time).length());
+    // If the time is less then 1 Jan 2020 then it is an error
+    if (KMInteger.unsignedByteArrayCompare(
+            scratchPad, (short) 0, firstJan2020, (short) 0, (short) 8)
+        < 0) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    if (utcFlag
+        && KMInteger.unsignedByteArrayCompare(
+                scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8)
+            >= 0) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+
+    if (KMInteger.unsignedByteArrayCompare(
+            scratchPad, (short) 0, firstJan2050, (short) 0, (short) 8)
+        < 0) {
+      Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8);
+      subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+      Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+    } else {
+      from2020 = false;
+      Util.arrayCopyNonAtomic(firstJan2050, (short) 0, scratchPad, (short) 8, (short) 8);
+      subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+      Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+    }
+    // divide the given time with four yrs msec count
+    if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, (short) 8)
+        >= 0) {
+      Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+      // quotient is multiple of 4
+      yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+      yrsCount = (short) (yrsCount * 4); // number of yrs.
+      // copy reminder as new dividend
+      Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+    }
+
+    // Get the leap year index starting from the (base Year + yrsCount) Year.
+    short leapYrIdx = getLeapYrIndex(from2020, yrsCount);
+
+    // if leap year index is 0, then the number of days for the 1st year will be 366 days.
+    // if leap year index is not 0, then the number of days for the 1st year will be 365 days.
+    if (((leapYrIdx == 0)
+            && (KMInteger.unsignedByteArrayCompare(
+                    scratchPad, (short) 0, leapYearMsec, (short) 0, (short) 8)
+                >= 0))
+        || ((leapYrIdx != 0)
+            && (KMInteger.unsignedByteArrayCompare(
+                    scratchPad, (short) 0, yearMsec, (short) 0, (short) 8)
+                >= 0))) {
+      for (short i = 0; i < 4; i++) {
+        yrsCount++;
+        if (i == leapYrIdx) {
+          Util.arrayCopyNonAtomic(leapYearMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+        } else {
+          Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+        }
+        subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+        Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+        if (((short) (i + 1) == leapYrIdx)) {
+          if (KMInteger.unsignedByteArrayCompare(
+                  scratchPad, (short) 0, leapYearMsec, (short) 0, (short) 8)
+              < 0) {
+            break;
+          }
+        } else {
+          if (KMInteger.unsignedByteArrayCompare(
+                  scratchPad, (short) 0, yearMsec, (short) 0, (short) 8)
+              < 0) {
+            break;
+          }
+        }
+      }
+    }
+
+    // total yrs from 1970
+    if (from2020) {
+      yrsCount = (short) (year2020 + yrsCount);
+    } else {
+      yrsCount = (short) (year2051 + yrsCount);
+    }
+
+    // divide the given time with one month msec count
+    if (KMInteger.unsignedByteArrayCompare(
+            scratchPad, (short) 0, oneMonthMsec, (short) 0, (short) 8)
+        >= 0) {
+      for (short i = 0; i < 12; i++) {
+        if (i == 1) {
+          // Feb month
+          if (isLeapYear(yrsCount)) {
+            // Leap year 29 days
+            Util.arrayCopyNonAtomic(febMonthLeapMSec, (short) 0, scratchPad, (short) 8, (short) 8);
+          } else {
+            // 28 days
+            Util.arrayCopyNonAtomic(febMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+          }
+        } else if (((i <= 6) && ((i % 2 == 0))) || ((i > 6) && ((i % 2 == 1)))) {
+          Util.arrayCopyNonAtomic(
+              ThirtyOneDaysMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+        } else {
+          // 30 Days
+          Util.arrayCopyNonAtomic(ThirtDaysMonthMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+        }
+
+        if (KMInteger.unsignedByteArrayCompare(
+                scratchPad, (short) 0, scratchPad, (short) 8, (short) 8)
+            >= 0) {
+          subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8);
+          Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+        } else {
+          break;
+        }
+        monthCount++;
+      }
+    }
+
+    // divide the given time with one day msec count
+    if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, (short) 8)
+        >= 0) {
+      Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+      dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+      dayCount++;
+      Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+    }
+
+    // divide the given time with one hour msec count
+    if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, (short) 8)
+        >= 0) {
+      Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+      hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+      Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+    }
+
+    // divide the given time with one minute msec count
+    if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, (short) 8)
+        >= 0) {
+      Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+      mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+      Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+    }
+
+    // divide the given time with one second msec count
+    if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, (short) 8)
+        >= 0) {
+      Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, (short) 8);
+      ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16);
+      Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8);
+    }
+
+    // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+    short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY
+    len += numberToString(monthCount, scratchPad, len);
+    len += numberToString(dayCount, scratchPad, len);
+    len += numberToString(hhCount, scratchPad, len);
+    len += numberToString(mmCount, scratchPad, len);
+    len += numberToString(ssCount, scratchPad, len);
+    scratchPad[len] = Z;
+    len++;
+    if (utcFlag) {
+      return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY
+    } else {
+      return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY
+    }
+  }
+
+  public static short numberToString(short number, byte[] scratchPad, short offset) {
+    byte zero = 0x30;
+    byte len = 2;
+    byte digit;
+    if (number > 999) {
+      len = 4;
+    }
+    byte index = len;
+    while (index > 0) {
+      digit = (byte) (number % 10);
+      number = (short) (number / 10);
+      scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero);
+      index--;
+    }
+    return len;
+  }
+
+  // Use Euclid's formula: dividend = quotient*divisor + remainder
+  // i.e. dividend - quotient*divisor = remainder where remainder < divisor.
+  // so this is division by subtraction until remainder remains.
+  public static short divide(byte[] buf, short dividend, short divisor, short remainder) {
+    short expCnt = 1;
+    short q = 0;
+    // first increase divisor so that it becomes greater then dividend.
+    while (compare(buf, divisor, dividend) < 0) {
+      shiftLeft(buf, divisor);
+      expCnt = (short) (expCnt << 1);
+    }
+    // Now subtract divisor from dividend if dividend is greater then divisor.
+    // Copy remainder in the dividend and repeat.
+    while (expCnt != 0) {
+      if (compare(buf, dividend, divisor) >= 0) {
+        subtract(buf, dividend, divisor, remainder, (byte) 8);
+        copy(buf, remainder, dividend);
+        q = (short) (q + expCnt);
+      }
+      expCnt = (short) (expCnt >> 1);
+      shiftRight(buf, divisor);
+    }
+    return q;
+  }
+
+  public static void copy(byte[] buf, short from, short to) {
+    Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8);
+  }
+
+  public static byte compare(byte[] buf, short lhs, short rhs) {
+    return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8);
+  }
+
+  public static void shiftLeft(byte[] buf, short start, short count) {
+    short index = 0;
+    while (index < count) {
+      shiftLeft(buf, start);
+      index++;
+    }
+  }
+
+  public static void shiftLeft(byte[] buf, short start) {
+    byte index = 7;
+    byte carry = 0;
+    byte tmp;
+    while (index >= 0) {
+      tmp = buf[(short) (start + index)];
+      buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] << 1);
+      buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] + carry);
+      if (tmp < 0) {
+        carry = 1;
+      } else {
+        carry = 0;
+      }
+      index--;
+    }
+  }
+
+  public static void shiftRight(byte[] buf, short start) {
+    byte index = 0;
+    byte carry = 0;
+    byte tmp;
+    while (index < 8) {
+      tmp = (byte) (buf[(short) (start + index)] & 0x01);
+      buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] >> 1);
+      buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] & 0x7F);
+      buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] | carry);
+      if (tmp == 1) {
+        carry = (byte) 0x80;
+      } else {
+        carry = 0;
+      }
+      index++;
+    }
+  }
+
+  public static void add(byte[] buf, short op1, short op2, short result) {
+    byte index = 7;
+    byte carry = 0;
+    short tmp;
+    short val1 = 0;
+    short val2 = 0;
+    while (index >= 0) {
+      val1 = (short) (buf[(short) (op1 + index)] & 0x00FF);
+      val2 = (short) (buf[(short) (op2 + index)] & 0x00FF);
+      tmp = (short) (val1 + val2 + carry);
+      carry = 0;
+      if (tmp > 255) {
+        carry = 1; // max unsigned byte value is 255
+      }
+      buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF);
+      index--;
+    }
+  }
+
+  // subtraction by borrowing.
+  public static void subtract(byte[] buf, short op1, short op2, short result, byte sizeBytes) {
+    byte borrow = 0;
+    byte index = (byte) (sizeBytes - 1);
+    short r;
+    short x;
+    short y;
+    while (index >= 0) {
+      x = (short) (buf[(short) (op1 + index)] & 0xFF);
+      y = (short) (buf[(short) (op2 + index)] & 0xFF);
+      r = (short) (x - y - borrow);
+      borrow = 0;
+      if (r < 0) {
+        borrow = 1;
+        r = (short) (r + 256); // max unsigned byte value is 255
+      }
+      buf[(short) (result + index)] = (byte) (r & 0xFF);
+      index--;
+    }
+  }
+
+  public static short countTemporalCount(
+      byte[] bufTime, short timeOff, short timeLen, byte[] scratchPad, short offset) {
+    Util.arrayFillNonAtomic(scratchPad, (short) offset, (short) 24, (byte) 0);
+    Util.arrayCopyNonAtomic(bufTime, timeOff, scratchPad, (short) (offset + 8 - timeLen), timeLen);
+    Util.arrayCopyNonAtomic(
+        ThirtDaysMonthMsec, (short) 0, scratchPad, (short) (offset + 8), (short) 8);
+    return divide(scratchPad, (short) 0, (short) 8, (short) 16);
+  }
+
+  public static boolean isLeapYear(short year) {
+    if ((short) (year % 4) == (short) 0) {
+      if (((short) (year % 100) == (short) 0) && ((short) (year % 400)) != (short) 0) {
+        return false;
+      }
+      return true;
+    }
+    return false;
+  }
+
+  public static short getLeapYrIndex(boolean from2020, short yrsCount) {
+    short newBaseYr = (short) (from2020 ? (year2020 + yrsCount) : (year2051 + yrsCount));
+    for (short i = 0; i < 4; i++) {
+      if (isLeapYear((short) (newBaseYr + i))) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  public static void computeOnesCompliment(byte[] buf, short offset, short len) {
+    short index = offset;
+    // Compute 1s compliment
+    while (index < (short) (len + offset)) {
+      buf[index] = (byte) ~buf[index];
+      index++;
+    }
+  }
+
+  // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3)
+  public static void convertToMilliseconds(
+      byte[] buf, short inputOff, short outputOff, short scratchPadOff) {
+    short index = 0;
+    short length = (short) SEC_TO_MILLIS_SHIFT_POS.length;
+    while (index < length) {
+      Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8);
+      shiftLeft(buf, scratchPadOff, SEC_TO_MILLIS_SHIFT_POS[index]);
+      Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8);
+      add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff));
+      Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8);
+      Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0);
+      index++;
+    }
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java
new file mode 100644
index 0000000..06f1eaf
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.security.AESKey;
+import org.globalplatform.upgrade.Element;
+
+public class KMAESKey implements KMMasterKey {
+
+  public AESKey aesKey;
+
+  public KMAESKey(AESKey key) {
+    aesKey = key;
+  }
+
+  public static void onSave(Element element, KMAESKey kmKey) {
+    element.write(kmKey.aesKey);
+  }
+
+  public static KMAESKey onRestore(AESKey aesKey) {
+    if (aesKey == null) {
+      return null;
+    }
+    return new KMAESKey(aesKey);
+  }
+
+  public static short getBackupPrimitiveByteCount() {
+    return (short) 0;
+  }
+
+  public static short getBackupObjectCount() {
+    return (short) 1;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java
new file mode 100644
index 0000000..a3198dd
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java
@@ -0,0 +1,1574 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.AESKey;
+import javacard.security.CryptoException;
+import javacard.security.DESKey;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.HMACKey;
+import javacard.security.Key;
+import javacard.security.KeyAgreement;
+import javacard.security.KeyBuilder;
+import javacard.security.KeyPair;
+import javacard.security.MessageDigest;
+import javacard.security.RSAPrivateKey;
+import javacard.security.RandomData;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+import org.globalplatform.upgrade.Element;
+import org.globalplatform.upgrade.UpgradeManager;
+
+public class KMAndroidSEProvider implements KMSEProvider {
+
+  public static final byte AES_GCM_TAG_LENGTH = 16;
+  public static final byte AES_GCM_NONCE_LENGTH = 12;
+  public static final byte KEYSIZE_128_OFFSET = 0x00;
+  public static final byte KEYSIZE_256_OFFSET = 0x01;
+  public static final short TMP_ARRAY_SIZE = 300;
+  private static final short RSA_KEY_SIZE = 256;
+  public static final short CERT_CHAIN_MAX_SIZE = 2500; // First 2 bytes for length.
+  public static final byte SHARED_SECRET_KEY_SIZE = 32;
+  public static final byte POWER_RESET_FALSE = (byte) 0xAA;
+  public static final byte POWER_RESET_TRUE = (byte) 0x00;
+  private static final byte COMPUTED_HMAC_KEY_SIZE = 32;
+  private static byte[] CMAC_KDF_CONSTANT_L;
+  private static byte[] CMAC_KDF_CONSTANT_ZERO;
+
+  private static KeyAgreement keyAgreement;
+
+  // AESKey
+  private AESKey aesKeys[];
+  // DES3Key
+  private DESKey triDesKey;
+  // HMACKey
+  private HMACKey hmacKey;
+  // RSA Key Pair
+  private KeyPair rsaKeyPair;
+  // EC Key Pair.
+  private KeyPair ecKeyPair;
+  // Temporary array.
+  public byte[] tmpArray;
+  // This is used for internal encryption/decryption operations.
+  private static AEADCipher aesGcmCipher;
+
+  private Signature kdf;
+  public static byte[] resetFlag;
+
+  private Signature hmacSignature;
+  // For ImportwrappedKey operations.
+  private KMRsaOAEPEncoding rsaOaepDecipher;
+  private KMPoolManager poolMgr;
+
+  private KMOperationImpl globalOperation;
+  // Entropy
+  private RandomData rng;
+
+  private static KMAndroidSEProvider androidSEProvider = null;
+
+  public static KMAndroidSEProvider getInstance() {
+    return androidSEProvider;
+  }
+
+  public KMAndroidSEProvider() {
+    initStatics();
+    // Re-usable AES,DES and HMAC keys in persisted memory.
+    aesKeys = new AESKey[2];
+    aesKeys[KEYSIZE_128_OFFSET] =
+        (AESKey)
+            KeyBuilder.buildKey(
+                KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
+    aesKeys[KEYSIZE_256_OFFSET] =
+        (AESKey)
+            KeyBuilder.buildKey(
+                KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false);
+    triDesKey =
+        (DESKey)
+            KeyBuilder.buildKey(
+                KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false);
+    hmacKey =
+        (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false);
+    rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048);
+    ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+    keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false);
+    poolMgr = KMPoolManager.getInstance();
+    poolMgr.initECKey(ecKeyPair);
+    // RsaOAEP Decipher
+    rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1);
+
+    kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false);
+    hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false);
+
+    globalOperation = new KMOperationImpl();
+
+    // Temporary transient array created to use locally inside functions.
+    tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+    Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0);
+    // Random number generator initialisation.
+    rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION);
+    androidSEProvider = this;
+    resetFlag = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET);
+    resetFlag[0] = (byte) POWER_RESET_FALSE;
+  }
+
+  void initStatics() {
+    CMAC_KDF_CONSTANT_L = new byte[] {0x00, 0x00, 0x01, 0x00};
+    CMAC_KDF_CONSTANT_ZERO = new byte[] {0x00};
+  }
+
+  public void clean() {
+    Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0);
+  }
+
+  public AESKey createAESKey(short keysize) {
+    try {
+      newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8));
+      return createAESKey(tmpArray, (short) 0, (short) (keysize / 8));
+    } finally {
+      clean();
+    }
+  }
+
+  public AESKey createAESKey(byte[] buf, short startOff, short length) {
+    AESKey key = null;
+    short keysize = (short) (length * 8);
+    if (keysize == 128) {
+      key = (AESKey) aesKeys[KEYSIZE_128_OFFSET];
+      key.setKey(buf, (short) startOff);
+    } else if (keysize == 256) {
+      key = (AESKey) aesKeys[KEYSIZE_256_OFFSET];
+      key.setKey(buf, (short) startOff);
+    }
+    return key;
+  }
+
+  public DESKey createTDESKey() {
+    try {
+      newRandomNumber(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8));
+      return createTDESKey(tmpArray, (short) 0, (short) (KeyBuilder.LENGTH_DES3_3KEY / 8));
+    } finally {
+      clean();
+    }
+  }
+
+  public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) {
+    triDesKey.setKey(secretBuffer, secretOff);
+    return triDesKey;
+  }
+
+  public HMACKey createHMACKey(short keysize) {
+    if ((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    try {
+      newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8));
+      return createHMACKey(tmpArray, (short) 0, (short) (keysize / 8));
+    } finally {
+      clean();
+    }
+  }
+
+  public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) {
+    hmacKey.setKey(secretBuffer, secretOff, secretLength);
+    return hmacKey;
+  }
+
+  public KeyPair createRsaKeyPair() {
+    rsaKeyPair.genKeyPair();
+    return rsaKeyPair;
+  }
+
+  public RSAPrivateKey createRsaKey(
+      byte[] modBuffer,
+      short modOff,
+      short modLength,
+      byte[] privBuffer,
+      short privOff,
+      short privLength) {
+    RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate();
+    privKey.setExponent(privBuffer, privOff, privLength);
+    privKey.setModulus(modBuffer, modOff, modLength);
+    return privKey;
+  }
+
+  public KeyPair createECKeyPair() {
+    ecKeyPair.genKeyPair();
+    return ecKeyPair;
+  }
+
+  public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) {
+    ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate();
+    privKey.setS(privBuffer, privOff, privLength);
+    return privKey;
+  }
+
+  @Override
+  public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) {
+    switch (alg) {
+      case KMType.AES:
+        AESKey aesKey = createAESKey(keysize);
+        return aesKey.getKey(buf, startOff);
+      case KMType.DES:
+        DESKey desKey = createTDESKey();
+        return desKey.getKey(buf, startOff);
+      case KMType.HMAC:
+        HMACKey hmacKey = createHMACKey(keysize);
+        return hmacKey.getKey(buf, startOff);
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+    return 0;
+  }
+
+  @Override
+  public void createAsymmetricKey(
+      byte alg,
+      byte[] privKeyBuf,
+      short privKeyStart,
+      short privKeyLength,
+      byte[] pubModBuf,
+      short pubModStart,
+      short pubModLength,
+      short[] lengths) {
+    switch (alg) {
+      case KMType.RSA:
+        if (RSA_KEY_SIZE != privKeyLength || RSA_KEY_SIZE != pubModLength) {
+          CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+        }
+        KeyPair rsaKey = createRsaKeyPair();
+        RSAPrivateKey privKey = (RSAPrivateKey) rsaKey.getPrivate();
+        // Copy exponent.
+        Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0);
+        lengths[0] = privKey.getExponent(tmpArray, (short) 0);
+        if (lengths[0] > privKeyLength) {
+          CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+        }
+        Util.arrayFillNonAtomic(privKeyBuf, privKeyStart, privKeyLength, (byte) 0);
+        Util.arrayCopyNonAtomic(
+            tmpArray,
+            (short) 0,
+            privKeyBuf,
+            (short) (privKeyStart + privKeyLength - lengths[0]),
+            lengths[0]);
+        // Copy modulus
+        Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0);
+        lengths[1] = privKey.getModulus(tmpArray, (short) 0);
+        if (lengths[1] > pubModLength) {
+          CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+        }
+        Util.arrayFillNonAtomic(pubModBuf, pubModStart, pubModLength, (byte) 0);
+        Util.arrayCopyNonAtomic(
+            tmpArray,
+            (short) 0,
+            pubModBuf,
+            (short) (pubModStart + pubModLength - lengths[1]),
+            lengths[1]);
+        break;
+      case KMType.EC:
+        KeyPair ecKey = createECKeyPair();
+        ECPublicKey ecPubKey = (ECPublicKey) ecKey.getPublic();
+        ECPrivateKey ecPrivKey = (ECPrivateKey) ecKey.getPrivate();
+        lengths[0] = ecPrivKey.getS(privKeyBuf, privKeyStart);
+        lengths[1] = ecPubKey.getW(pubModBuf, pubModStart);
+        if (lengths[0] > privKeyLength || lengths[1] > pubModLength) {
+          CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+        }
+        break;
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+  }
+
+  @Override
+  public boolean importSymmetricKey(
+      byte alg, short keysize, byte[] buf, short startOff, short length) {
+    switch (alg) {
+      case KMType.AES:
+        createAESKey(buf, startOff, length);
+        break;
+      case KMType.DES:
+        createTDESKey(buf, startOff, length);
+        break;
+      case KMType.HMAC:
+        createHMACKey(buf, startOff, length);
+        break;
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+    return true;
+  }
+
+  @Override
+  public boolean importAsymmetricKey(
+      byte alg,
+      byte[] privKeyBuf,
+      short privKeyStart,
+      short privKeyLength,
+      byte[] pubModBuf,
+      short pubModStart,
+      short pubModLength) {
+    switch (alg) {
+      case KMType.RSA:
+        createRsaKey(pubModBuf, pubModStart, pubModLength, privKeyBuf, privKeyStart, privKeyLength);
+        break;
+      case KMType.EC:
+        createEcKey(privKeyBuf, privKeyStart, privKeyLength);
+        break;
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+    return true;
+  }
+
+  @Override
+  public void getTrueRandomNumber(byte[] buf, short start, short length) {
+    newRandomNumber(buf, start, length);
+  }
+
+  @Override
+  public void newRandomNumber(byte[] num, short startOff, short length) {
+    rng.nextBytes(num, startOff, length);
+  }
+
+  @Override
+  public void addRngEntropy(byte[] num, short offset, short length) {
+    rng.setSeed(num, offset, length);
+  }
+
+  public short aesGCMEncrypt(
+      AESKey key,
+      byte[] secret,
+      short secretStart,
+      short secretLen,
+      byte[] encSecret,
+      short encSecretStart,
+      byte[] nonce,
+      short nonceStart,
+      short nonceLen,
+      byte[] authData,
+      short authDataStart,
+      short authDataLen,
+      byte[] authTag,
+      short authTagStart,
+      short authTagLen) {
+    if (authTagLen != AES_GCM_TAG_LENGTH) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    if (nonceLen != AES_GCM_NONCE_LENGTH) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    if (aesGcmCipher == null) {
+      aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+    }
+    aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen);
+    if (authDataLen != 0) {
+      aesGcmCipher.updateAAD(authData, authDataStart, authDataLen);
+    }
+    short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, encSecret, encSecretStart);
+    aesGcmCipher.retrieveTag(authTag, authTagStart, authTagLen);
+    return ciphLen;
+  }
+
+  @Override
+  public short aesGCMEncrypt(
+      byte[] aesKey,
+      short aesKeyStart,
+      short aesKeyLen,
+      byte[] secret,
+      short secretStart,
+      short secretLen,
+      byte[] encSecret,
+      short encSecretStart,
+      byte[] nonce,
+      short nonceStart,
+      short nonceLen,
+      byte[] authData,
+      short authDataStart,
+      short authDataLen,
+      byte[] authTag,
+      short authTagStart,
+      short authTagLen) {
+
+    AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen);
+    return aesGCMEncrypt(
+        key,
+        secret,
+        secretStart,
+        secretLen,
+        encSecret,
+        encSecretStart,
+        nonce,
+        nonceStart,
+        nonceLen,
+        authData,
+        authDataStart,
+        authDataLen,
+        authTag,
+        authTagStart,
+        authTagLen);
+  }
+
+  @Override
+  public boolean aesGCMDecrypt(
+      byte[] aesKey,
+      short aesKeyStart,
+      short aesKeyLen,
+      byte[] encSecret,
+      short encSecretStart,
+      short encSecretLen,
+      byte[] secret,
+      short secretStart,
+      byte[] nonce,
+      short nonceStart,
+      short nonceLen,
+      byte[] authData,
+      short authDataStart,
+      short authDataLen,
+      byte[] authTag,
+      short authTagStart,
+      short authTagLen) {
+    if (aesGcmCipher == null) {
+      aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+    }
+    boolean verification = false;
+    AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen);
+    aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen);
+    if (authDataLen != 0) {
+      aesGcmCipher.updateAAD(authData, authDataStart, authDataLen);
+    }
+    // encrypt the secret
+    aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, secretStart);
+    verification =
+        aesGcmCipher.verifyTag(
+            authTag, authTagStart, (short) authTagLen, (short) AES_GCM_TAG_LENGTH);
+    return verification;
+  }
+
+  public HMACKey cmacKdf(
+      KMPreSharedKey preSharedKey,
+      byte[] label,
+      short labelStart,
+      short labelLen,
+      byte[] context,
+      short contextStart,
+      short contextLength) {
+    try {
+      // This is hardcoded to requirement - 32 byte output with two concatenated
+      // 16 bytes K1 and K2.
+      final byte n = 2; // hardcoded
+
+      // [i] counter - 32 bits
+      short iBufLen = 4;
+      short keyOutLen = n * 16;
+      // Convert Hmackey to AES Key as the algorithm is ALG_AES_CMAC_128.
+      KMHmacKey hmacKey = ((KMHmacKey) preSharedKey);
+      hmacKey.hmacKey.getKey(tmpArray, (short) 0);
+      aesKeys[KEYSIZE_256_OFFSET].setKey(tmpArray, (short) 0);
+      // Initialize the key derivation function.
+      kdf.init(aesKeys[KEYSIZE_256_OFFSET], Signature.MODE_SIGN);
+      // Clear the tmpArray buffer.
+      Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0);
+
+      Util.arrayFillNonAtomic(tmpArray, (short) 0, iBufLen, (byte) 0);
+      Util.arrayFillNonAtomic(tmpArray, (short) iBufLen, keyOutLen, (byte) 0);
+
+      byte i = 1;
+      short pos = 0;
+      while (i <= n) {
+        tmpArray[3] = i;
+        // 4 bytes of iBuf with counter in it
+        kdf.update(tmpArray, (short) 0, (short) iBufLen);
+        kdf.update(label, labelStart, (short) labelLen); // label
+        kdf.update(
+            CMAC_KDF_CONSTANT_ZERO,
+            (short) 0,
+            (short) CMAC_KDF_CONSTANT_ZERO.length); // 1 byte of 0x00
+        kdf.update(context, contextStart, contextLength); // context
+        // 4 bytes of L - signature of 16 bytes
+        pos =
+            kdf.sign(
+                CMAC_KDF_CONSTANT_L,
+                (short) 0,
+                (short) CMAC_KDF_CONSTANT_L.length,
+                tmpArray,
+                (short) (iBufLen + pos));
+        i++;
+      }
+      return createHMACKey(tmpArray, (short) iBufLen, (short) keyOutLen);
+    } finally {
+      clean();
+    }
+  }
+
+  public short hmacSign(
+      HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) {
+    hmacSignature.init(key, Signature.MODE_SIGN);
+    return hmacSignature.sign(data, dataStart, dataLength, mac, macStart);
+  }
+
+  @Override
+  public short hmacSign(
+      byte[] keyBuf,
+      short keyStart,
+      short keyLength,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] mac,
+      short macStart) {
+    HMACKey key = createHMACKey(keyBuf, keyStart, keyLength);
+    return hmacSign(key, data, dataStart, dataLength, mac, macStart);
+  }
+
+  @Override
+  public short hmacSign(
+      Object key, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) {
+    if (!(key instanceof KMHmacKey)) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    KMHmacKey hmacKey = (KMHmacKey) key;
+    return hmacSign(hmacKey.hmacKey, data, dataStart, dataLength, mac, macStart);
+  }
+
+  @Override
+  public short hmacKDF(
+      KMMasterKey masterkey,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] signature,
+      short signatureStart) {
+    try {
+      KMAESKey aesKey = (KMAESKey) masterkey;
+      short keyLen = (short) (aesKey.aesKey.getSize() / 8);
+      aesKey.aesKey.getKey(tmpArray, (short) 0);
+      return hmacSign(
+          tmpArray, (short) 0, keyLen, data, dataStart, dataLength, signature, signatureStart);
+    } finally {
+      clean();
+    }
+  }
+
+  @Override
+  public boolean hmacVerify(
+      KMComputedHmacKey key,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] mac,
+      short macStart,
+      short macLength) {
+    KMHmacKey hmacKey = (KMHmacKey) key;
+    hmacSignature.init(hmacKey.hmacKey, Signature.MODE_VERIFY);
+    return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, macLength);
+  }
+
+  @Override
+  public short rsaDecipherOAEP256(
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] modBuffer,
+      short modOff,
+      short modLength,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+    RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate();
+    key.setExponent(secret, (short) secretStart, (short) secretLength);
+    key.setModulus(modBuffer, (short) modOff, (short) modLength);
+    rsaOaepDecipher.init(key, Cipher.MODE_DECRYPT);
+    return rsaOaepDecipher.doFinal(
+        inputDataBuf,
+        (short) inputDataStart,
+        (short) inputDataLength,
+        outputDataBuf,
+        (short) outputDataStart);
+  }
+
+  private byte mapSignature256Alg(byte alg, byte padding, byte digest) {
+    switch (alg) {
+      case KMType.RSA:
+        switch (padding) {
+          case KMType.RSA_PKCS1_1_5_SIGN:
+            {
+              if (digest == KMType.DIGEST_NONE) {
+                return KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST;
+              } else {
+                return Signature.ALG_RSA_SHA_256_PKCS1;
+              }
+            }
+          case KMType.RSA_PSS:
+            return Signature.ALG_RSA_SHA_256_PKCS1_PSS;
+          case KMType.PADDING_NONE:
+            return KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD;
+        }
+        break;
+      case KMType.EC:
+        if (digest == KMType.DIGEST_NONE) {
+          return KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST;
+        } else {
+          return Signature.ALG_ECDSA_SHA_256;
+        }
+      case KMType.HMAC:
+        return Signature.ALG_HMAC_SHA_256;
+    }
+    return -1;
+  }
+
+  private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) {
+    switch (alg) {
+      case KMType.AES:
+        switch (blockmode) {
+          case KMType.ECB:
+            return Cipher.ALG_AES_BLOCK_128_ECB_NOPAD;
+          case KMType.CBC:
+            return Cipher.ALG_AES_BLOCK_128_CBC_NOPAD;
+          case KMType.CTR:
+            return Cipher.ALG_AES_CTR;
+          case KMType.GCM:
+            return AEADCipher.ALG_AES_GCM;
+        }
+        break;
+      case KMType.DES:
+        switch (blockmode) {
+          case KMType.ECB:
+            return Cipher.ALG_DES_ECB_NOPAD;
+          case KMType.CBC:
+            return Cipher.ALG_DES_CBC_NOPAD;
+        }
+        break;
+      case KMType.RSA:
+        switch (padding) {
+          case KMType.PADDING_NONE:
+            return Cipher.ALG_RSA_NOPAD;
+          case KMType.RSA_PKCS1_1_5_ENCRYPT:
+            return Cipher.ALG_RSA_PKCS1;
+          case KMType.RSA_OAEP:
+            {
+              if (digest == KMType.SHA1) {
+                  /* MGF Digest is SHA1 */
+                return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1;
+              } else if (digest == KMType.SHA2_256) {
+                  /* MGF Digest is SHA256 */
+                return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256;
+              } else {
+                KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+              }
+            }
+        }
+        break;
+    }
+    return -1;
+  }
+
+  public KMOperation createSymmetricCipher(
+      short alg,
+      short purpose,
+      short macLength,
+      short blockMode,
+      short padding,
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] ivBuffer,
+      short ivStart,
+      short ivLength,
+      boolean isRkp) {
+
+    short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0);
+    KMOperation operation = null;
+    if (isRkp) {
+      operation = poolMgr.getRKpOperation(purpose, cipherAlg, alg, padding, blockMode, macLength);
+    } else {
+      operation =
+          poolMgr.getOperationImpl(
+              purpose, cipherAlg, alg, padding, blockMode, macLength, secretLength, false);
+    }
+    // Get the KeyObject from the operation and update the key with the secret key material.
+    KMKeyObject keyObj = operation.getKeyObject();
+    Key key = (Key) keyObj.keyObjectInst;
+    switch (secretLength) {
+      case 32:
+      case 16:
+        ((AESKey) key).setKey(secret, secretStart);
+        break;
+      case 24:
+        ((DESKey) key).setKey(secret, secretStart);
+        break;
+      default:
+        CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+        break;
+    }
+    ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength);
+    return operation;
+  }
+
+  public KMOperation createHmacSignerVerifier(
+      short purpose,
+      short digest,
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      boolean isRkp) {
+    KMOperation operation = null;
+    if (digest != KMType.SHA2_256) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    if (isRkp) {
+      operation =
+          poolMgr.getRKpOperation(
+              purpose,
+              Signature.ALG_HMAC_SHA_256,
+              KMType.HMAC,
+              KMType.INVALID_VALUE,
+              KMType.INVALID_VALUE,
+              KMType.INVALID_VALUE);
+    } else {
+      operation =
+          poolMgr.getOperationImpl(
+              purpose,
+              Signature.ALG_HMAC_SHA_256,
+              KMType.HMAC,
+              KMType.INVALID_VALUE,
+              KMType.INVALID_VALUE,
+              KMType.INVALID_VALUE,
+              (short) 0,
+              false);
+    }
+    // Get the KeyObject from the operation and update the key with the secret key material.
+    KMKeyObject keyObj = operation.getKeyObject();
+    HMACKey key = (HMACKey) keyObj.keyObjectInst;
+    key.setKey(secret, secretStart, secretLength);
+    ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+    return operation;
+  }
+
+  private KMOperation createHmacSignerVerifier(
+      short purpose, short digest, HMACKey hmacKey, boolean isTrustedConf) {
+    if (digest != KMType.SHA2_256) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    KMOperation operation =
+        poolMgr.getOperationImpl(
+            purpose,
+            Signature.ALG_HMAC_SHA_256,
+            KMType.HMAC,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            (short) 0,
+            isTrustedConf);
+    // Get the KeyObject from the operation and update the key with the secret key material.
+    KMKeyObject keyObj = operation.getKeyObject();
+    HMACKey key = (HMACKey) keyObj.keyObjectInst;
+    short len = hmacKey.getKey(tmpArray, (short) 0);
+    key.setKey(tmpArray, (short) 0, len);
+    ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+    return operation;
+  }
+
+  @Override
+  public KMOperation getRkpOperation(
+      byte purpose,
+      byte alg,
+      byte digest,
+      byte padding,
+      byte blockMode,
+      byte[] keyBuf,
+      short keyStart,
+      short keyLength,
+      byte[] ivBuf,
+      short ivStart,
+      short ivLength,
+      short macLength) {
+    KMOperation opr = null;
+    switch (alg) {
+      case KMType.AES:
+        // Convert macLength to bytes
+        macLength = (short) (macLength / 8);
+        opr =
+            createSymmetricCipher(
+                alg,
+                purpose,
+                macLength,
+                blockMode,
+                padding,
+                keyBuf,
+                keyStart,
+                keyLength,
+                ivBuf,
+                ivStart,
+                ivLength,
+                true /* isRKP */);
+        break;
+      case KMType.HMAC:
+        opr =
+            createHmacSignerVerifier(
+                purpose, digest, keyBuf, keyStart, keyLength, true /* isRKP */);
+        break;
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+    return opr;
+  }
+
+  @Override
+  public KMOperation initSymmetricOperation(
+      byte purpose,
+      byte alg,
+      byte digest,
+      byte padding,
+      byte blockMode,
+      byte[] keyBuf,
+      short keyStart,
+      short keyLength,
+      byte[] ivBuf,
+      short ivStart,
+      short ivLength,
+      short macLength) {
+    KMOperation opr = null;
+    switch (alg) {
+      case KMType.AES:
+      case KMType.DES:
+        // Convert macLength to bytes
+        macLength = (short) (macLength / 8);
+        opr =
+            createSymmetricCipher(
+                alg,
+                purpose,
+                macLength,
+                blockMode,
+                padding,
+                keyBuf,
+                keyStart,
+                keyLength,
+                ivBuf,
+                ivStart,
+                ivLength,
+                false /* isRKP */);
+        break;
+      case KMType.HMAC:
+        opr =
+            createHmacSignerVerifier(
+                purpose, digest, keyBuf, keyStart, keyLength, false /* isRKP */);
+        break;
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+    return opr;
+  }
+
+  @Override
+  public KMOperation initSymmetricOperation(
+      byte purpose,
+      byte alg,
+      byte digest,
+      byte padding,
+      byte blockMode,
+      Object key,
+      byte interfaceType,
+      byte[] ivBuf,
+      short ivStart,
+      short ivLength,
+      short macLength,
+      boolean oneShot) {
+    short keyLen = 0;
+    globalOperation.setPurpose(purpose);
+    globalOperation.setAlgorithmType(alg);
+    globalOperation.setPaddingAlgorithm(padding);
+    globalOperation.setBlockMode(blockMode);
+    try {
+      switch (interfaceType) {
+        case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+          KMAESKey aesKey = (KMAESKey) key;
+          keyLen = (short) (aesKey.aesKey.getSize() / 8);
+          aesKey.aesKey.getKey(tmpArray, (short) 0);
+          break;
+
+        default:
+          KMException.throwIt(KMError.INVALID_ARGUMENT);
+      }
+
+      switch (alg) {
+        case KMType.HMAC:
+          HMACKey hmackey = createHMACKey(tmpArray, (short) 0, keyLen);
+          globalOperation.setSignature(hmacSignature);
+          globalOperation.init(hmackey, digest, null, (short) 0, (short) 0);
+          break;
+
+        default:
+          KMException.throwIt(KMError.INVALID_ARGUMENT);
+      }
+    } finally {
+      clean();
+    }
+    return globalOperation;
+  }
+
+  @Override
+  public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) {
+    KMHmacKey key = (KMHmacKey) computedHmacKey;
+    return createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.hmacKey, true);
+  }
+
+  public KMOperation createRsaSigner(
+      short digest,
+      short padding,
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] modBuffer,
+      short modOff,
+      short modLength) {
+    byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest);
+    KMOperation operation =
+        poolMgr.getOperationImpl(
+            KMType.SIGN,
+            alg,
+            KMType.RSA,
+            padding,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            secretLength,
+            false);
+    // Get the KeyObject from the operation and update the key with the secret key material.
+    KMKeyObject keyObj = operation.getKeyObject();
+    RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+    key.setExponent(secret, secretStart, secretLength);
+    key.setModulus(modBuffer, modOff, modLength);
+    ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+    return operation;
+  }
+
+  public KMOperation createRsaDecipher(
+      short padding,
+      short mgfDigest,
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] modBuffer,
+      short modOff,
+      short modLength) {
+    byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest);
+    KMOperation operation =
+        poolMgr.getOperationImpl(
+            KMType.DECRYPT,
+            cipherAlg,
+            KMType.RSA,
+            padding,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            secretLength,
+            false);
+    // Get the KeyObject from the operation and update the key with the secret key material.
+    KMKeyObject keyObj = operation.getKeyObject();
+    RSAPrivateKey key = (RSAPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+    key.setExponent(secret, secretStart, secretLength);
+    key.setModulus(modBuffer, modOff, modLength);
+    ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0);
+    return operation;
+  }
+
+  public KMOperation createEcSigner(
+      short digest, byte[] secret, short secretStart, short secretLength) {
+    byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest);
+    KMOperation operation =
+        poolMgr.getOperationImpl(
+            KMType.SIGN,
+            alg,
+            KMType.EC,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            secretLength,
+            false);
+    KMKeyObject keyObj = operation.getKeyObject();
+    ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+    key.setS(secret, secretStart, secretLength);
+    ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0);
+    return operation;
+  }
+
+  public KMOperation createKeyAgreement(byte[] secret, short secretStart, short secretLength) {
+    KMOperation operation =
+        poolMgr.getOperationImpl(
+            KMType.AGREE_KEY,
+            KeyAgreement.ALG_EC_SVDP_DH_PLAIN,
+            KMType.EC,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            (short) 0,
+            false);
+    KMKeyObject keyObj = operation.getKeyObject();
+    ECPrivateKey key = (ECPrivateKey) ((KeyPair) (keyObj.keyObjectInst)).getPrivate();
+    key.setS(secret, secretStart, secretLength);
+    ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0);
+    return operation;
+  }
+
+  @Override
+  public KMOperation initAsymmetricOperation(
+      byte purpose,
+      byte alg,
+      byte padding,
+      byte digest,
+      byte mgfDigest,
+      byte[] privKeyBuf,
+      short privKeyStart,
+      short privKeyLength,
+      byte[] pubModBuf,
+      short pubModStart,
+      short pubModLength) {
+    KMOperation opr = null;
+    if (alg == KMType.RSA) {
+      switch (purpose) {
+        case KMType.SIGN:
+          opr =
+              createRsaSigner(
+                  digest,
+                  padding,
+                  privKeyBuf,
+                  privKeyStart,
+                  privKeyLength,
+                  pubModBuf,
+                  pubModStart,
+                  pubModLength);
+          break;
+        case KMType.DECRYPT:
+          opr =
+              createRsaDecipher(
+                  padding,
+                  mgfDigest,
+                  privKeyBuf,
+                  privKeyStart,
+                  privKeyLength,
+                  pubModBuf,
+                  pubModStart,
+                  pubModLength);
+          break;
+        default:
+          KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+          break;
+      }
+    } else if (alg == KMType.EC) {
+      switch (purpose) {
+        case KMType.SIGN:
+          opr = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength);
+          break;
+
+        case KMType.AGREE_KEY:
+          opr = createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength);
+          break;
+        default:
+          KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+          break;
+      }
+    } else {
+      CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+    }
+    return opr;
+  }
+
+  @Override
+  public short cmacKDF(
+      KMPreSharedKey pSharedKey,
+      byte[] label,
+      short labelStart,
+      short labelLen,
+      byte[] context,
+      short contextStart,
+      short contextLength,
+      byte[] keyBuf,
+      short keyStart) {
+    HMACKey key =
+        cmacKdf(pSharedKey, label, labelStart, labelLen, context, contextStart, contextLength);
+    return key.getKey(keyBuf, keyStart);
+  }
+
+  @Override
+  public boolean isUpgrading() {
+    return UpgradeManager.isUpgrading();
+  }
+
+  @Override
+  public KMMasterKey createMasterKey(KMMasterKey masterKey, short keySizeBits) {
+    try {
+      if (masterKey == null) {
+        AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, keySizeBits, false);
+        masterKey = new KMAESKey(key);
+        short keyLen = (short) (keySizeBits / 8);
+        getTrueRandomNumber(tmpArray, (short) 0, keyLen);
+        ((KMAESKey) masterKey).aesKey.setKey(tmpArray, (short) 0);
+      }
+      return (KMMasterKey) masterKey;
+    } finally {
+      clean();
+    }
+  }
+
+  @Override
+  public KMPreSharedKey createPreSharedKey(
+      KMPreSharedKey preSharedKey, byte[] keyData, short offset, short length) {
+    short lengthInBits = (short) (length * 8);
+    if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    if (preSharedKey == null) {
+      HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, lengthInBits, false);
+      preSharedKey = new KMHmacKey(key);
+    }
+    ((KMHmacKey) preSharedKey).hmacKey.setKey(keyData, offset, length);
+    return (KMPreSharedKey) preSharedKey;
+  }
+
+  @Override
+  public KMComputedHmacKey createComputedHmacKey(
+      KMComputedHmacKey computedHmacKey, byte[] keyData, short offset, short length) {
+    if (length != COMPUTED_HMAC_KEY_SIZE) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    if (computedHmacKey == null) {
+      HMACKey key =
+          (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false);
+      computedHmacKey = new KMHmacKey(key);
+    }
+    ((KMHmacKey) computedHmacKey).hmacKey.setKey(keyData, offset, length);
+    return (KMComputedHmacKey) computedHmacKey;
+  }
+
+  @Override
+  public short ecSign256(
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+
+    ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate();
+    key.setS(secret, secretStart, secretLength);
+
+    Signature.OneShot signer = null;
+    try {
+
+      signer =
+          Signature.OneShot.open(
+              MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+      signer.init(key, Signature.MODE_SIGN);
+      return signer.sign(
+          inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+    } finally {
+      if (signer != null) {
+        signer.close();
+      }
+    }
+  }
+
+  @Override
+  public short ecSign256(
+      KMAttestationKey ecPrivKey,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+    Signature.OneShot signer = null;
+    try {
+
+      signer =
+          Signature.OneShot.open(
+              MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+      signer.init(((KMECPrivateKey) ecPrivKey).ecKeyPair.getPrivate(), Signature.MODE_SIGN);
+      return signer.sign(
+          inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+    } finally {
+      if (signer != null) {
+        signer.close();
+      }
+    }
+  }
+
+  @Override
+  public short rsaSign256Pkcs1(
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] modBuf,
+      short modStart,
+      short modLength,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+
+    Signature.OneShot signer = null;
+    try {
+
+      signer =
+          Signature.OneShot.open(
+              MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_RSA, Cipher.PAD_PKCS1);
+
+      RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate();
+      ;
+      key.setExponent(secret, secretStart, secretLength);
+      key.setModulus(modBuf, modStart, modLength);
+
+      signer.init(key, Signature.MODE_SIGN);
+      return signer.sign(
+          inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+    } finally {
+      if (signer != null) {
+        signer.close();
+      }
+    }
+  }
+
+  @Override
+  public boolean isAttestationKeyProvisioned() {
+    return false;
+  }
+
+  @Override
+  public short getAttestationKeyAlgorithm() {
+    return KMType.INVALID_VALUE;
+  }
+
+  @Override
+  public short hkdf(
+      byte[] ikm,
+      short ikmOff,
+      short ikmLen,
+      byte[] salt,
+      short saltOff,
+      short saltLen,
+      byte[] info,
+      short infoOff,
+      short infoLen,
+      byte[] out,
+      short outOff,
+      short outLen) {
+    // HMAC_extract
+    hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, tmpArray, (short) 0);
+    // HMAC_expand
+    return hkdfExpand(tmpArray, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen);
+  }
+
+  private short hkdfExtract(
+      byte[] ikm,
+      short ikmOff,
+      short ikmLen,
+      byte[] salt,
+      short saltOff,
+      short saltLen,
+      byte[] out,
+      short off) {
+    // https://tools.ietf.org/html/rfc5869#section-2.2
+    HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen);
+    hmacSignature.init(hmacKey, Signature.MODE_SIGN);
+    return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off);
+  }
+
+  private short hkdfExpand(
+      byte[] prk,
+      short prkOff,
+      short prkLen,
+      byte[] info,
+      short infoOff,
+      short infoLen,
+      byte[] out,
+      short outOff,
+      short outLen) {
+    // https://tools.ietf.org/html/rfc5869#section-2.3
+    short digestLen = (short) 32; // SHA256 digest length.
+    // Calculate no of iterations N.
+    short n = (short) ((short) (outLen + digestLen - 1) / digestLen);
+    if (n > 255) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen);
+    Util.arrayFill(tmpArray, (short) 0, (short) 33, (byte) 0);
+    short bytesCopied = 0;
+    short len = 0;
+    for (short i = 0; i < n; i++) {
+      tmpArray[0]++;
+      hmacSignature.init(hmacKey, Signature.MODE_SIGN);
+      if (i != 0) {
+        hmacSignature.update(tmpArray, (short) 1, (short) 32);
+      }
+      hmacSignature.update(info, infoOff, infoLen);
+      len = hmacSignature.sign(tmpArray, (short) 0, (short) 1, tmpArray, (short) 1);
+      if ((short) (bytesCopied + len) > outLen) {
+        len = (short) (outLen - bytesCopied);
+      }
+      Util.arrayCopyNonAtomic(tmpArray, (short) 1, out, (short) (outOff + bytesCopied), len);
+      bytesCopied += len;
+    }
+    return outLen;
+  }
+
+  @Override
+  public short ecdhKeyAgreement(
+      byte[] privKey,
+      short privKeyOff,
+      short privKeyLen,
+      byte[] publicKey,
+      short publicKeyOff,
+      short publicKeyLen,
+      byte[] secret,
+      short secretOff) {
+    keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen));
+    return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff);
+  }
+
+  @Override
+  public boolean ecVerify256(
+      byte[] pubKey,
+      short pubKeyOffset,
+      short pubKeyLen,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] signatureDataBuf,
+      short signatureDataStart,
+      short signatureDataLen) {
+    Signature.OneShot signer = null;
+    try {
+      signer =
+          Signature.OneShot.open(
+              MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+      ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic();
+      key.setW(pubKey, pubKeyOffset, pubKeyLen);
+      signer.init(key, Signature.MODE_VERIFY);
+      return signer.verify(
+          inputDataBuf,
+          inputDataStart,
+          inputDataLength,
+          signatureDataBuf,
+          signatureDataStart,
+          (short) (signatureDataBuf[(short) (signatureDataStart + 1)] + 2));
+    } finally {
+      if (signer != null) {
+        signer.close();
+      }
+    }
+  }
+
+  @Override
+  public short ecSign256(
+      KMDeviceUniqueKeyPair ecPrivKey,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+    Signature.OneShot signer = null;
+    try {
+      signer =
+          Signature.OneShot.open(
+              MessageDigest.ALG_SHA_256, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL);
+      signer.init(((KMECDeviceUniqueKey) ecPrivKey).ecKeyPair.getPrivate(), Signature.MODE_SIGN);
+      return signer.sign(
+          inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+    } finally {
+      if (signer != null) {
+        signer.close();
+      }
+    }
+  }
+
+  @Override
+  public KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(
+      KMDeviceUniqueKeyPair key,
+      byte[] pubKey,
+      short pubKeyOff,
+      short pubKeyLen,
+      byte[] privKey,
+      short privKeyOff,
+      short privKeyLen) {
+    if (key == null) {
+      KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+      poolMgr.initECKey(ecKeyPair);
+      key = new KMECDeviceUniqueKey(ecKeyPair);
+    }
+    ECPrivateKey ecKeyPair = (ECPrivateKey) ((KMECDeviceUniqueKey) key).ecKeyPair.getPrivate();
+    ECPublicKey ecPublicKey = (ECPublicKey) ((KMECDeviceUniqueKey) key).ecKeyPair.getPublic();
+    ecKeyPair.setS(privKey, privKeyOff, privKeyLen);
+    ecPublicKey.setW(pubKey, pubKeyOff, pubKeyLen);
+    return (KMDeviceUniqueKeyPair) key;
+  }
+
+  @Override
+  public KMRkpMacKey createRkpMacKey(
+      KMRkpMacKey rkpMacKey, byte[] keyData, short offset, short length) {
+    if (rkpMacKey == null) {
+      HMACKey key =
+          (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), false);
+      rkpMacKey = new KMHmacKey(key);
+    }
+    ((KMHmacKey) rkpMacKey).hmacKey.setKey(keyData, offset, length);
+    return rkpMacKey;
+  }
+
+  @Override
+  public short messageDigest256(
+      byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) {
+    MessageDigest.OneShot mDigest = null;
+    short len = 0;
+    try {
+      mDigest = MessageDigest.OneShot.open(MessageDigest.ALG_SHA_256);
+      len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset);
+    } finally {
+      if (mDigest != null) {
+        mDigest.close();
+        mDigest = null;
+      }
+    }
+    return len;
+  }
+
+  public boolean isPowerReset() {
+    boolean flag = false;
+    if (resetFlag[0] == POWER_RESET_TRUE) {
+      resetFlag[0] = POWER_RESET_FALSE;
+      flag = true;
+      if (poolMgr != null) {
+        poolMgr.powerReset();
+      }
+    }
+    return flag;
+  }
+
+  @Override
+  public void onSave(Element element, byte interfaceType, Object object) {
+    element.write(interfaceType);
+    if (object == null) {
+      element.write(null);
+      return;
+    }
+    switch (interfaceType) {
+      case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+        KMAESKey.onSave(element, (KMAESKey) object);
+        break;
+      case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+        KMHmacKey.onSave(element, (KMHmacKey) object);
+        break;
+      case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+        KMECDeviceUniqueKey.onSave(element, (KMECDeviceUniqueKey) object);
+        break;
+      case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+        KMHmacKey.onSave(element, (KMHmacKey) object);
+        break;
+      default:
+        ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+  }
+
+  @Override
+  public Object onRestore(Element element) {
+    if (element == null) {
+      return null;
+    }
+    byte interfaceType = element.readByte();
+    switch (interfaceType) {
+      case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY:
+        return KMHmacKey.onRestore((HMACKey) element.readObject());
+      case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+        return KMAESKey.onRestore((AESKey) element.readObject());
+      case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+        return KMHmacKey.onRestore((HMACKey) element.readObject());
+      case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+        return KMECDeviceUniqueKey.onRestore((KeyPair) element.readObject());
+      case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+        return KMHmacKey.onRestore((HMACKey) element.readObject());
+      default:
+        ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return null;
+  }
+
+  @Override
+  public short getBackupPrimitiveByteCount(byte interfaceType) {
+    short primitiveCount = 1; // interface type
+    switch (interfaceType) {
+      case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+        primitiveCount += KMAESKey.getBackupPrimitiveByteCount();
+        break;
+      case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+        primitiveCount += KMHmacKey.getBackupPrimitiveByteCount();
+        break;
+      case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+        primitiveCount += KMECDeviceUniqueKey.getBackupPrimitiveByteCount();
+        break;
+      case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+        primitiveCount += KMHmacKey.getBackupPrimitiveByteCount();
+        break;
+      default:
+        ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return primitiveCount;
+  }
+
+  @Override
+  public short getBackupObjectCount(byte interfaceType) {
+    switch (interfaceType) {
+      case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY:
+        return KMAESKey.getBackupObjectCount();
+      case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY:
+        return KMHmacKey.getBackupObjectCount();
+      case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR:
+        return KMECDeviceUniqueKey.getBackupObjectCount();
+      case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY:
+        return KMHmacKey.getBackupObjectCount();
+      default:
+        ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return 0;
+  }
+
+  @Override
+  public boolean isBootSignalEventSupported() {
+    return false;
+  }
+
+  @Override
+  public boolean isDeviceRebooted() {
+    return false;
+  }
+
+  @Override
+  public void clearDeviceBooted(boolean resetBootFlag) {
+    // To be filled
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java
new file mode 100644
index 0000000..da60794
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.seprovider;
+
+/**
+ * The KMAttestationCert interface represents a X509 compliant attestation certificate required to
+ * support keymaster's attestKey function. This cert will be created according to the specifications
+ * given in android keymaster hal documentation. KMSeProvider has to provide the instance of this
+ * certificate. This interface is designed based on builder pattern and hence each method returns
+ * instance of cert.
+ */
+public interface KMAttestationCert {
+
+  /**
+   * Set verified boot hash.
+   *
+   * @param obj This is a KMByteBlob containing hash
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert verifiedBootHash(short obj);
+
+  /**
+   * Set verified boot key received during booting up.
+   *
+   * @param obj This is a KMByteBlob containing verified boot key.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert verifiedBootKey(short obj);
+
+  /**
+   * Set verified boot state received during booting up.
+   *
+   * @param val This is a byte containing verified boot state value.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert verifiedBootState(byte val);
+
+  /**
+   * Set uniqueId received from CA certificate during provisioning.
+   *
+   * @param scratchpad Buffer to store intermediate results.
+   * @param scratchPadOff Start offset of the scratchpad buffer.
+   * @param creationTime This buffer contains the CREATION_TIME value.
+   * @param creationTimeOff Start offset of creattionTime buffer.
+   * @param creationTimeLen Length of the creationTime buffer.
+   * @param attestAppId This buffer contains the ATTESTATION_APPLICATION_ID value.
+   * @param attestAppIdOff Start offset of the attestAppId buffer.
+   * @param attestAppIdLen Length of the attestAppId buffer.
+   * @param resetSinceIdRotation This holds the information of RESET_SINCE_ID_ROTATION.
+   * @param masterKey
+   * @return instance of KMAttestationCert.
+   */
+  KMAttestationCert makeUniqueId(
+      byte[] scratchpad,
+      short scratchPadOff,
+      byte[] creationTime,
+      short creationTimeOff,
+      short creationTimeLen,
+      byte[] attestAppId,
+      short attestAppIdOff,
+      short attestAppIdLen,
+      byte resetSinceIdRotation,
+      KMMasterKey masterKey);
+
+  /**
+   * Set start time received from creation/activation time tag. Used for certificate's valid period.
+   *
+   * @param obj This is a KMByteBlob object containing start time.
+   * @param scratchpad Buffer to store intermediate results.
+   * @return instance of KMAttestationCert.
+   */
+  KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad);
+
+  /**
+   * Set expiry time received from expiry time tag or ca certificates expiry time. Used for
+   * certificate's valid period.
+   *
+   * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. certificate.
+   * @param scratchPad Buffer to store intermediate results.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad);
+
+  /**
+   * Set device lock status received during booting time or due to device lock command.
+   *
+   * @param val This is true if device is locked.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert deviceLocked(boolean val);
+
+  /**
+   * Set public key to be attested received from attestKey command.
+   *
+   * @param obj This is KMByteBlob containing the public key.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert publicKey(short obj);
+
+  /**
+   * Set attestation challenge received from attestKey command.
+   *
+   * @param obj This is KMByteBlob containing the attestation challenge.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert attestationChallenge(short obj);
+
+  /**
+   * Set extension tag received from key characteristics which needs to be added to android
+   * extension. This method will called once for each tag.
+   *
+   * @param tag is the KMByteBlob containing KMTag.
+   * @param hwEnforced is true if the tag has to be added to hw enforced list or else added to sw
+   *     enforced list.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert extensionTag(short tag, boolean hwEnforced);
+
+  /**
+   * Set ASN.1 encoded X509 issuer field received from attestation key CA cert.
+   *
+   * @param obj This is KMByteBlob containing the issuer.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert issuer(short obj);
+
+  /**
+   * Set byte buffer to be used to generate certificate.
+   *
+   * @param buf This is byte[] buffer.
+   * @param bufStart This is short start offset.
+   * @param maxLen This is short length of the buffer.
+   * @return instance of KMAttestationCert
+   */
+  KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen);
+
+  /**
+   * Get the start of the certificate
+   *
+   * @return start of the attestation cert.
+   */
+  short getCertStart();
+
+  /**
+   * Get the length of the certificate
+   *
+   * @return length of the attestation cert.
+   */
+  short getCertLength();
+
+  /**
+   * Build a fake signed certificate. After this method executes the certificate is ready with the
+   * signature equal to 1 byte which is 0 and with rsa signature algorithm.
+   */
+  void build();
+
+  /**
+   * Set the Serial number in the certificate. If no serial number is set then serial number is 1.
+   *
+   * @param serialNumber
+   */
+  boolean serialNumber(short serialNumber);
+
+  /**
+   * Set the Subject Name in the certificate.
+   *
+   * @param subject
+   */
+  boolean subjectName(short subject);
+
+  /**
+   * Set attestation key and mode.
+   *
+   * @param attestKey KMByteBlob of the key
+   * @param mode
+   */
+  KMAttestationCert ecAttestKey(short attestKey, byte mode);
+  /**
+   * Set attestation key and mode.
+   *
+   * @param attestKey KMByteBlob of the key
+   * @param mode
+   */
+  KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode);
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java
new file mode 100644
index 0000000..d941306
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+/**
+ * KMAttestationKey is a marker interface and the SE Provider has to implement this interface.
+ * Internally attestation key is stored as a Javacard EC key pair object, which will provide
+ * additional security. The attestation key is maintained by the SEProvider.
+ */
+public interface KMAttestationKey {}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java
new file mode 100644
index 0000000..8d406b4
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMComputedHmacKey.java
@@ -0,0 +1,3 @@
+package com.android.javacard.seprovider;
+
+public interface KMComputedHmacKey {}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java
new file mode 100644
index 0000000..a1d6454
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java
@@ -0,0 +1,11 @@
+package com.android.javacard.seprovider;
+
+public class KMDataStoreConstants {
+  // INTERFACE Types
+  public static final byte INTERFACE_TYPE_COMPUTED_HMAC_KEY = 0x01;
+  public static final byte INTERFACE_TYPE_ATTESTATION_KEY = 0x02;
+  public static final byte INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR = 0x03;
+  public static final byte INTERFACE_TYPE_MASTER_KEY = 0x04;
+  public static final byte INTERFACE_TYPE_PRE_SHARED_KEY = 0x05;
+  public static final byte INTERFACE_TYPE_RKP_MAC_KEY = 0x06;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKeyPair.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKeyPair.java
new file mode 100644
index 0000000..9bbccd8
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKeyPair.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright(C) 2021 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+public interface KMDeviceUniqueKeyPair {
+
+  short getPublicKey(byte[] buf, short offset);
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java
new file mode 100644
index 0000000..8adb1fb
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright(C) 2021 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.security.ECPublicKey;
+import javacard.security.KeyPair;
+import org.globalplatform.upgrade.Element;
+
+public class KMECDeviceUniqueKey implements KMDeviceUniqueKeyPair {
+
+  public KeyPair ecKeyPair;
+
+  @Override
+  public short getPublicKey(byte[] buf, short offset) {
+    ECPublicKey publicKey = (ECPublicKey) ecKeyPair.getPublic();
+    return publicKey.getW(buf, offset);
+  }
+
+  public KMECDeviceUniqueKey(KeyPair ecPair) {
+    ecKeyPair = ecPair;
+  }
+
+  public static void onSave(Element element, KMECDeviceUniqueKey kmKey) {
+    element.write(kmKey.ecKeyPair);
+  }
+
+  public static KMECDeviceUniqueKey onRestore(KeyPair ecKey) {
+    if (ecKey == null) {
+      return null;
+    }
+    return new KMECDeviceUniqueKey(ecKey);
+  }
+
+  public static short getBackupPrimitiveByteCount() {
+    return (short) 0;
+  }
+
+  public static short getBackupObjectCount() {
+    return (short) 1;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java
new file mode 100644
index 0000000..35c687b
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.security.KeyPair;
+import org.globalplatform.upgrade.Element;
+
+public class KMECPrivateKey implements KMAttestationKey {
+
+  public KeyPair ecKeyPair;
+
+  public KMECPrivateKey(KeyPair ecPair) {
+    ecKeyPair = ecPair;
+  }
+
+  public static void onSave(Element element, KMECPrivateKey kmKey) {
+    element.write(kmKey.ecKeyPair);
+  }
+
+  public static KMECPrivateKey onRestore(KeyPair ecKey) {
+    if (ecKey == null) {
+      return null;
+    }
+    return new KMECPrivateKey(ecKey);
+  }
+
+  public static short getBackupPrimitiveByteCount() {
+    return (short) 0;
+  }
+
+  public static short getBackupObjectCount() {
+    return (short) 1;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java
new file mode 100644
index 0000000..e9b95ee
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.MessageDigest;
+import javacard.security.Signature;
+import javacardx.crypto.Cipher;
+
+public class KMEcdsa256NoDigestSignature extends Signature {
+
+  public static final byte ALG_ECDSA_NODIGEST = (byte) 0x67;
+  public static final byte MAX_NO_DIGEST_MSG_LEN = 32;
+  private byte algorithm;
+  private Signature inst;
+
+  public KMEcdsa256NoDigestSignature(byte alg) {
+    algorithm = alg;
+    inst = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
+  }
+
+  @Override
+  public void init(Key key, byte b) throws CryptoException {
+    inst.init(key, b);
+  }
+
+  @Override
+  public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException {
+    inst.init(key, b, bytes, i, i1);
+  }
+
+  @Override
+  public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+      throws CryptoException {}
+
+  @Override
+  public byte getAlgorithm() {
+    return algorithm;
+  }
+
+  @Override
+  public byte getMessageDigestAlgorithm() {
+    return MessageDigest.ALG_NULL;
+  }
+
+  @Override
+  public byte getCipherAlgorithm() {
+    return 0;
+  }
+
+  @Override
+  public byte getPaddingAlgorithm() {
+    return Cipher.PAD_NULL;
+  }
+
+  @Override
+  public short getLength() throws CryptoException {
+    return inst.getLength();
+  }
+
+  @Override
+  public void update(byte[] message, short msgStart, short messageLength) throws CryptoException {
+    // HAL accumulates the data and send it at finish operation.
+  }
+
+  @Override
+  public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+      throws CryptoException {
+    try {
+      if (i1 > MAX_NO_DIGEST_MSG_LEN) {
+        CryptoException.throwIt(CryptoException.ILLEGAL_USE);
+      }
+      // add zeros to the left
+      if (i1 < MAX_NO_DIGEST_MSG_LEN) {
+        Util.arrayFillNonAtomic(
+            KMAndroidSEProvider.getInstance().tmpArray,
+            (short) 0,
+            (short) MAX_NO_DIGEST_MSG_LEN,
+            (byte) 0);
+      }
+      Util.arrayCopyNonAtomic(
+          bytes,
+          i,
+          KMAndroidSEProvider.getInstance().tmpArray,
+          (short) (MAX_NO_DIGEST_MSG_LEN - i1),
+          i1);
+      return inst.signPreComputedHash(
+          KMAndroidSEProvider.getInstance().tmpArray,
+          (short) 0,
+          (short) MAX_NO_DIGEST_MSG_LEN,
+          bytes1,
+          i2);
+    } finally {
+      KMAndroidSEProvider.getInstance().clean();
+    }
+  }
+
+  @Override
+  public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+      throws CryptoException {
+    return inst.sign(bytes, i, i1, bytes1, i2);
+  }
+
+  @Override
+  public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+      throws CryptoException {
+    // Verification is handled inside HAL
+    return false;
+  }
+
+  @Override
+  public boolean verifyPreComputedHash(
+      byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException {
+    // Verification is handled inside HAL
+    return false;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java
new file mode 100644
index 0000000..69cb069
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.seprovider;
+
+/**
+ * KMError includes all the error codes from android keymaster hal specifications. The values are
+ * positive unlike negative values in keymaster hal.
+ */
+public class KMError {
+
+  public static final short OK = 0;
+  public static final short UNSUPPORTED_PURPOSE = 2;
+  public static final short UNSUPPORTED_ALGORITHM = 4;
+  public static final short INVALID_INPUT_LENGTH = 21;
+  public static final short VERIFICATION_FAILED = 30;
+  public static final short TOO_MANY_OPERATIONS = 31;
+  public static final short INVALID_ARGUMENT = 38;
+  public static final short UNKNOWN_ERROR = 1000;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java
new file mode 100644
index 0000000..79983a2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.seprovider;
+
+import javacard.framework.JCSystem;
+
+/**
+ * KMException is shared instance of exception used for all exceptions in the applet. It is used to
+ * throw EMError errors.
+ */
+public class KMException extends RuntimeException {
+
+  private static short[] reason;
+  private static KMException exception;
+
+  private KMException() {}
+
+  public static short reason() {
+    return reason[0];
+  }
+
+  public static void throwIt(short e) {
+    if (reason == null) {
+      reason = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_DESELECT);
+    }
+    if (exception == null) {
+      exception = new KMException();
+    }
+    reason[0] = e;
+    throw exception;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java
new file mode 100644
index 0000000..3d25143
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.security.HMACKey;
+import org.globalplatform.upgrade.Element;
+
+public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey, KMRkpMacKey {
+
+  public HMACKey hmacKey;
+
+  public KMHmacKey(HMACKey key) {
+    hmacKey = key;
+  }
+
+  public static void onSave(Element element, KMHmacKey kmKey) {
+    element.write(kmKey.hmacKey);
+  }
+
+  public static KMHmacKey onRestore(HMACKey hmacKey) {
+    if (hmacKey == null) {
+      return null;
+    }
+    return new KMHmacKey(hmacKey);
+  }
+
+  public static short getBackupPrimitiveByteCount() {
+    return (short) 0;
+  }
+
+  public static short getBackupObjectCount() {
+    return (short) 1;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java
new file mode 100644
index 0000000..26edaa2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMKeyObject.java
@@ -0,0 +1,6 @@
+package com.android.javacard.seprovider;
+
+public class KMKeyObject {
+  public byte algorithm;
+  public Object keyObjectInst;
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java
new file mode 100644
index 0000000..27afb3d
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+/**
+ * KMMasterKey is a marker interface and the SE Provider has to implement this interface. Internally
+ * Masterkey is stored as a Javacard AES key object, which will provide additional security. The
+ * master key is maintained by the SEProvider.
+ */
+public interface KMMasterKey {}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java
new file mode 100644
index 0000000..12e691e
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.seprovider;
+
+/**
+ * KMOperation represents a persistent operation started by keymaster hal's beginOperation function.
+ * This operation is persistent i.e. it will be stored in non volatile memory of se card. It will be
+ * returned back to KMSEProvider for the reuse when the operation is finished.
+ */
+public interface KMOperation {
+
+  // Used for cipher operations
+  short update(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart);
+
+  // Used for signature operations
+  short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength);
+
+  // Used for finishing cipher operations or ecdh keyAgreement.
+  short finish(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart);
+
+  // Used for finishing signing operations.
+  short sign(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] signBuf,
+      short signStart);
+
+  // Used for finishing verifying operations.
+  boolean verify(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] signBuf,
+      short signStart,
+      short signLength);
+
+  // Used for aborting the ongoing operations.
+  void abort();
+
+  // Used for AES GCM cipher operation.
+  void updateAAD(byte[] dataBuf, short dataStart, short dataLength);
+
+  // Used for getting output size before finishing a AES GCM cipher operation. For encryption this
+  // will
+  // include the auth tag which is appended at the end of the encrypted data. For decryption this
+  // will be
+  // size of the decrypted data only.
+  short getAESGCMOutputSize(short dataSize, short macLength);
+
+  KMKeyObject getKeyObject();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java
new file mode 100644
index 0000000..b2a2421
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.KeyAgreement;
+import javacard.security.PrivateKey;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+
+public class KMOperationImpl implements KMOperation {
+
+  private static final byte ALG_TYPE_OFFSET = 0x00;
+  private static final byte PADDING_OFFSET = 0x01;
+  private static final byte PURPOSE_OFFSET = 0x02;
+  private static final byte BLOCK_MODE_OFFSET = 0x03;
+  private static final byte MAC_LENGTH_OFFSET = 0x04;
+  private final byte[] EMPTY = {};
+  // This will hold the length of the buffer stored inside the
+  // Java Card after the GCM update operation.
+  private static final byte AES_GCM_UPDATE_LEN_OFFSET = 0x05;
+  private static final byte PARAMETERS_LENGTH = 6;
+  private short[] parameters;
+  // Either one of Cipher/Signature instance is stored.
+  private Object[] operationInst;
+
+  public KMOperationImpl() {
+    parameters = JCSystem.makeTransientShortArray(PARAMETERS_LENGTH, JCSystem.CLEAR_ON_RESET);
+    operationInst = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET);
+    reset();
+  }
+
+  public short getPurpose() {
+    return parameters[PURPOSE_OFFSET];
+  }
+
+  public void setPurpose(short mode) {
+    parameters[PURPOSE_OFFSET] = mode;
+  }
+
+  public short getMacLength() {
+    return parameters[MAC_LENGTH_OFFSET];
+  }
+
+  public void setMacLength(short macLength) {
+    parameters[MAC_LENGTH_OFFSET] = macLength;
+  }
+
+  public short getPaddingAlgorithm() {
+    return parameters[PADDING_OFFSET];
+  }
+
+  public void setPaddingAlgorithm(short alg) {
+    parameters[PADDING_OFFSET] = alg;
+  }
+
+  public void setBlockMode(short mode) {
+    parameters[BLOCK_MODE_OFFSET] = mode;
+  }
+
+  public short getBlockMode() {
+    return parameters[BLOCK_MODE_OFFSET];
+  }
+
+  public short getAlgorithmType() {
+    return parameters[ALG_TYPE_OFFSET];
+  }
+
+  public void setAlgorithmType(short cipherAlg) {
+    parameters[ALG_TYPE_OFFSET] = cipherAlg;
+  }
+
+  public void setCipher(Cipher cipher) {
+    operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = cipher;
+  }
+
+  public void setSignature(Signature signer) {
+    operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = signer;
+  }
+
+  public void setKeyAgreement(KeyAgreement keyAgreement) {
+    operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = keyAgreement;
+  }
+
+  public boolean isResourceMatches(Object object, byte resourceType) {
+    return operationInst[resourceType] == object;
+  }
+
+  public void setKeyObject(KMKeyObject keyObject) {
+    operationInst[KMPoolManager.RESOURCE_TYPE_KEY] = keyObject;
+  }
+
+  public KMKeyObject getKeyObject() {
+    return (KMKeyObject) operationInst[KMPoolManager.RESOURCE_TYPE_KEY];
+  }
+
+  private void reset() {
+    operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] = null;
+    operationInst[KMPoolManager.RESOURCE_TYPE_KEY] = null;
+    parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE;
+    parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0;
+    parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE;
+    parameters[PURPOSE_OFFSET] = KMType.INVALID_VALUE;
+    parameters[ALG_TYPE_OFFSET] = KMType.INVALID_VALUE;
+    parameters[PADDING_OFFSET] = KMType.INVALID_VALUE;
+  }
+
+  private byte mapPurpose(short purpose) {
+    switch (purpose) {
+      case KMType.ENCRYPT:
+        return Cipher.MODE_ENCRYPT;
+      case KMType.DECRYPT:
+        return Cipher.MODE_DECRYPT;
+      case KMType.SIGN:
+        return Signature.MODE_SIGN;
+      case KMType.VERIFY:
+        return Signature.MODE_VERIFY;
+    }
+    return -1;
+  }
+
+  private void initSymmetricCipher(Key key, byte[] ivBuffer, short ivStart, short ivLength) {
+    Cipher symmCipher = (Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO];
+    byte cipherAlg = symmCipher.getAlgorithm();
+    switch (cipherAlg) {
+      case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD:
+      case Cipher.ALG_AES_CTR:
+        symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength);
+        break;
+      case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD:
+      case Cipher.ALG_DES_ECB_NOPAD:
+        symmCipher.init(key, mapPurpose(getPurpose()));
+        break;
+      case Cipher.ALG_DES_CBC_NOPAD:
+        // Consume only 8 bytes of iv. the random number for iv is of 16 bytes.
+        // While sending back the iv, send only 8 bytes.
+        symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, (short) 8);
+        break;
+      case AEADCipher.ALG_AES_GCM:
+        ((AEADCipher) symmCipher).init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength);
+        break;
+      default: // This should never happen
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+  }
+
+  private void initRsa(Key key, short digest) {
+    if (KMType.SIGN == getPurpose()) {
+      byte mode;
+      if (getPaddingAlgorithm() == KMType.PADDING_NONE
+          || (getPaddingAlgorithm() == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) {
+        mode = Cipher.MODE_DECRYPT;
+      } else {
+        mode = Signature.MODE_SIGN;
+      }
+      ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).init((PrivateKey) key, mode);
+    } else { // RSA Cipher
+      ((Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+          .init((PrivateKey) key, mapPurpose(getPurpose()));
+    }
+  }
+
+  private void initEc(Key key) {
+    if (KMType.AGREE_KEY == getPurpose()) {
+      ((KeyAgreement) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).init((PrivateKey) key);
+    } else {
+      ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+          .init((PrivateKey) key, mapPurpose(getPurpose()));
+    }
+  }
+
+  public void init(Key key, short digest, byte[] buf, short start, short length) {
+    switch (getAlgorithmType()) {
+      case KMType.AES:
+      case KMType.DES:
+        initSymmetricCipher(key, buf, start, length);
+        break;
+      case KMType.HMAC:
+        ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+            .init(key, mapPurpose(getPurpose()));
+        break;
+      case KMType.RSA:
+        initRsa(key, digest);
+        break;
+      case KMType.EC:
+        initEc(key);
+        break;
+      default: // This should never happen
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+        break;
+    }
+  }
+
+  @Override
+  public short update(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+    short len =
+        ((Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+            .update(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart);
+    if (parameters[ALG_TYPE_OFFSET] == KMType.AES && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) {
+      // Every time Block size data is stored as intermediate result.
+      parameters[AES_GCM_UPDATE_LEN_OFFSET] += (short) (inputDataLength - len);
+    }
+    return len;
+  }
+
+  @Override
+  public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength) {
+    ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+        .update(inputDataBuf, inputDataStart, inputDataLength);
+    return 0;
+  }
+
+  private short finishKeyAgreement(
+      byte[] publicKey, short start, short len, byte[] output, short outputStart) {
+    return ((KeyAgreement) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+        .generateSecret(publicKey, start, len, output, outputStart);
+  }
+
+  private short finishCipher(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLen,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+    short len = 0;
+    try {
+      byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray;
+      Cipher cipher = (Cipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO];
+      short cipherAlg = parameters[ALG_TYPE_OFFSET];
+      short blockMode = parameters[BLOCK_MODE_OFFSET];
+      short mode = parameters[PURPOSE_OFFSET];
+      short macLength = parameters[MAC_LENGTH_OFFSET];
+      short padding = parameters[PADDING_OFFSET];
+
+      if (cipherAlg == KMType.AES && blockMode == KMType.GCM) {
+        if (mode == KMType.DECRYPT) {
+          inputDataLen = (short) (inputDataLen - macLength);
+        }
+      } else if ((cipherAlg == KMType.DES || cipherAlg == KMType.AES)
+          && padding == KMType.PKCS7
+          && mode == KMType.ENCRYPT) {
+        byte blkSize = 16;
+        byte paddingBytes;
+        short inputlen = inputDataLen;
+        if (cipherAlg == KMType.DES) {
+          blkSize = 8;
+        }
+        // padding bytes
+        if (inputlen % blkSize == 0) {
+          paddingBytes = blkSize;
+        } else {
+          paddingBytes = (byte) (blkSize - (inputlen % blkSize));
+        }
+        // final len with padding
+        inputlen = (short) (inputlen + paddingBytes);
+        // intermediate buffer to copy input data+padding
+        // fill in the padding
+        Util.arrayFillNonAtomic(tmpArray, (short) 0, inputlen, paddingBytes);
+        // copy the input data
+        Util.arrayCopyNonAtomic(inputDataBuf, inputDataStart, tmpArray, (short) 0, inputDataLen);
+        inputDataBuf = tmpArray;
+        inputDataLen = inputlen;
+        inputDataStart = 0;
+      }
+      len =
+          cipher.doFinal(
+              inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart);
+      if ((cipherAlg == KMType.AES || cipherAlg == KMType.DES)
+          && padding == KMType.PKCS7
+          && mode == KMType.DECRYPT) {
+        byte blkSize = 16;
+        if (cipherAlg == KMType.DES) {
+          blkSize = 8;
+        }
+        if (len > 0) {
+          // verify if padding is corrupted.
+          byte paddingByte = outputDataBuf[(short) (outputDataStart + len - 1)];
+          // padding byte always should be <= block size
+          if ((short) paddingByte > blkSize || (short) paddingByte <= 0) {
+            KMException.throwIt(KMError.INVALID_ARGUMENT);
+          }
+
+          for (short j = 1; j <= paddingByte; ++j) {
+            if (outputDataBuf[(short) (outputDataStart + len - j)] != paddingByte) {
+              KMException.throwIt(KMError.INVALID_ARGUMENT);
+            }
+          }
+          len = (short) (len - (short) paddingByte); // remove the padding bytes
+        }
+      } else if (cipherAlg == KMType.AES && blockMode == KMType.GCM) {
+        if (mode == KMType.ENCRYPT) {
+          len +=
+              ((AEADCipher) cipher)
+                  .retrieveTag(outputDataBuf, (short) (outputDataStart + len), macLength);
+        } else {
+          boolean verified =
+              ((AEADCipher) cipher)
+                  .verifyTag(
+                      inputDataBuf, (short) (inputDataStart + inputDataLen), macLength, macLength);
+          if (!verified) {
+            KMException.throwIt(KMError.VERIFICATION_FAILED);
+          }
+        }
+      }
+    } finally {
+      KMAndroidSEProvider.getInstance().clean();
+    }
+    return len;
+  }
+
+  @Override
+  public short finish(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLen,
+      byte[] outputDataBuf,
+      short outputDataStart) {
+    if (parameters[PURPOSE_OFFSET] == KMType.AGREE_KEY) {
+      return finishKeyAgreement(
+          inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart);
+    } else {
+      return finishCipher(
+          inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart);
+    }
+  }
+
+  @Override
+  public short sign(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] signBuf,
+      short signStart) {
+    return ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+        .sign(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart);
+  }
+
+  @Override
+  public boolean verify(
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] signBuf,
+      short signStart,
+      short signLength) {
+    return ((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+        .verify(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart, signLength);
+  }
+
+  @Override
+  public void abort() {
+    // Few simulators does not reset the Hmac signer instance on init so as
+    // a workaround to reset the hmac signer instance in case of abort/failure of the operation
+    // the corresponding sign / verify function is called.
+    if (operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO] != null) {
+      if ((parameters[PURPOSE_OFFSET] == KMType.SIGN || parameters[PURPOSE_OFFSET] == KMType.VERIFY)
+          && (((Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO]).getAlgorithm()
+              == Signature.ALG_HMAC_SHA_256)) {
+        Signature signer = (Signature) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO];
+        try {
+          if (parameters[PURPOSE_OFFSET] == KMType.SIGN) {
+            signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0);
+          } else {
+            signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0);
+          }
+        } catch (Exception e) {
+          // Ignore.
+        }
+      }
+    }
+    reset();
+  }
+
+  @Override
+  public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) {
+    ((AEADCipher) operationInst[KMPoolManager.RESOURCE_TYPE_CRYPTO])
+        .updateAAD(dataBuf, dataStart, dataLength);
+  }
+
+  @Override
+  public short getAESGCMOutputSize(short dataSize, short macLength) {
+    if (parameters[PURPOSE_OFFSET] == KMType.ENCRYPT) {
+      return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize + macLength);
+    } else {
+      return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength);
+    }
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java
new file mode 100644
index 0000000..fa32c70
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java
@@ -0,0 +1,657 @@
+/*
+ * Copyright(C) 2021 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.framework.JCSystem;
+import javacard.security.AESKey;
+import javacard.security.CryptoException;
+import javacard.security.DESKey;
+import javacard.security.ECPrivateKey;
+import javacard.security.ECPublicKey;
+import javacard.security.HMACKey;
+import javacard.security.KeyAgreement;
+import javacard.security.KeyBuilder;
+import javacard.security.KeyPair;
+import javacard.security.Signature;
+import javacardx.crypto.AEADCipher;
+import javacardx.crypto.Cipher;
+
+/** This class manages all the pool instances. */
+public class KMPoolManager {
+
+  public static final byte MAX_OPERATION_INSTANCES = 4;
+  private static final byte HMAC_MAX_OPERATION_INSTANCES = 8;
+  public static final byte AES_128 = 0x04;
+  public static final byte AES_256 = 0x05;
+  // Resource type constants
+  public static final byte RESOURCE_TYPE_CRYPTO = 0x00;
+  public static final byte RESOURCE_TYPE_KEY = 0x01;
+  // static final variables
+  // --------------------------------------------------------------
+  // P-256 Curve Parameters
+  static byte[] secp256r1_P;
+  static byte[] secp256r1_A;
+
+  static byte[] secp256r1_B;
+  static byte[] secp256r1_S;
+
+  // Uncompressed form
+  static byte[] secp256r1_UCG;
+  static byte[] secp256r1_N;
+  static final short secp256r1_H = 1;
+  // --------------------------------------------------------------
+
+  // Cipher pool
+  private Object[] cipherPool;
+  // Signature pool
+  private Object[] signerPool;
+  // Keyagreement pool
+  private Object[] keyAgreementPool;
+  // KMOperationImpl pool
+  private Object[] operationPool;
+  // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag.
+  private Object[] hmacSignOperationPool;
+
+  private Object[] keysPool;
+  // RKP uses AESGCM and HMAC in generateCSR flow.
+  KMOperation rkpOPeration;
+  Cipher rkpAesGcm;
+  Signature rkpHmac;
+  KMKeyObject rkpHmacKey;
+  KMKeyObject rkpAesKey;
+
+  final byte[] KEY_ALGS = {
+    AES_128, AES_256, KMType.DES, KMType.RSA, KMType.EC, KMType.HMAC,
+  };
+
+  final byte[] CIPHER_ALGS = {
+    Cipher.ALG_AES_BLOCK_128_CBC_NOPAD,
+    Cipher.ALG_AES_BLOCK_128_ECB_NOPAD,
+    Cipher.ALG_DES_CBC_NOPAD,
+    Cipher.ALG_DES_ECB_NOPAD,
+    Cipher.ALG_AES_CTR,
+    Cipher.ALG_RSA_PKCS1,
+    KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1,
+    KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256,
+    Cipher.ALG_RSA_NOPAD,
+    AEADCipher.ALG_AES_GCM
+  };
+
+  final byte[] SIG_ALGS = {
+    Signature.ALG_RSA_SHA_256_PKCS1,
+    Signature.ALG_RSA_SHA_256_PKCS1_PSS,
+    Signature.ALG_ECDSA_SHA_256,
+    Signature.ALG_HMAC_SHA_256,
+    KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD,
+    KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST,
+    KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST
+  };
+
+  final byte[] KEY_AGREE_ALGS = {KeyAgreement.ALG_EC_SVDP_DH_PLAIN};
+
+  private static KMPoolManager poolManager;
+
+  public static KMPoolManager getInstance() {
+    if (poolManager == null) {
+      poolManager = new KMPoolManager();
+    }
+    return poolManager;
+  }
+
+  public static void initStatics() {
+    secp256r1_P =
+      new byte[] {
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF
+      };
+
+    secp256r1_A =
+      new byte[] {
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFC
+      };
+
+    secp256r1_B =
+      new byte[] {
+        (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, (byte) 0xAA, (byte) 0x3A,
+        (byte) 0x93, (byte) 0xE7, (byte) 0xB3, (byte) 0xEB, (byte) 0xBD, (byte) 0x55,
+        (byte) 0x76, (byte) 0x98, (byte) 0x86, (byte) 0xBC, (byte) 0x65, (byte) 0x1D,
+        (byte) 0x06, (byte) 0xB0, (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6,
+        (byte) 0x3B, (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, (byte) 0x27, (byte) 0xD2,
+        (byte) 0x60, (byte) 0x4B
+      };
+
+    secp256r1_S =
+      new byte[] {
+        (byte) 0xC4, (byte) 0x9D, (byte) 0x36, (byte) 0x08, (byte) 0x86, (byte) 0xE7,
+        (byte) 0x04, (byte) 0x93, (byte) 0x6A, (byte) 0x66, (byte) 0x78, (byte) 0xE1,
+        (byte) 0x13, (byte) 0x9D, (byte) 0x26, (byte) 0xB7, (byte) 0x81, (byte) 0x9F,
+        (byte) 0x7E, (byte) 0x90
+      };
+
+    // Uncompressed form
+    secp256r1_UCG =
+      new byte[] {
+        (byte) 0x04, (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, (byte) 0xE1,
+        (byte) 0x2C, (byte) 0x42, (byte) 0x47, (byte) 0xF8, (byte) 0xBC, (byte) 0xE6,
+        (byte) 0xE5, (byte) 0x63, (byte) 0xA4, (byte) 0x40, (byte) 0xF2, (byte) 0x77,
+        (byte) 0x03, (byte) 0x7D, (byte) 0x81, (byte) 0x2D, (byte) 0xEB, (byte) 0x33,
+        (byte) 0xA0, (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, (byte) 0xD8,
+        (byte) 0x98, (byte) 0xC2, (byte) 0x96, (byte) 0x4F, (byte) 0xE3, (byte) 0x42,
+        (byte) 0xE2, (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, (byte) 0x9B, (byte) 0x8E,
+        (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, (byte) 0x7C, (byte) 0x0F, (byte) 0x9E,
+        (byte) 0x16, (byte) 0x2B, (byte) 0xCE, (byte) 0x33, (byte) 0x57, (byte) 0x6B,
+        (byte) 0x31, (byte) 0x5E, (byte) 0xCE, (byte) 0xCB, (byte) 0xB6, (byte) 0x40,
+        (byte) 0x68, (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5
+      };
+
+    secp256r1_N =
+      new byte[] {
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
+        (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
+        (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xBC, (byte) 0xE6,
+        (byte) 0xFA, (byte) 0xAD, (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84,
+        (byte) 0xF3, (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, (byte) 0xFC, (byte) 0x63,
+        (byte) 0x25, (byte) 0x51
+      };
+  }
+
+  private KMPoolManager() {
+    initStatics();
+    cipherPool = new Object[(short) (CIPHER_ALGS.length * MAX_OPERATION_INSTANCES)];
+    // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature.
+    signerPool =
+        new Object[(short) ((SIG_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)];
+    keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * MAX_OPERATION_INSTANCES)];
+
+    keysPool =
+        new Object[(short) ((KEY_ALGS.length * MAX_OPERATION_INSTANCES) + MAX_OPERATION_INSTANCES)];
+    operationPool = new Object[MAX_OPERATION_INSTANCES];
+    hmacSignOperationPool = new Object[MAX_OPERATION_INSTANCES];
+    /* Initialize pools */
+    initializeOperationPool();
+    initializeHmacSignOperationPool();
+    initializeSignerPool();
+    initializeCipherPool();
+    initializeKeyAgreementPool();
+    initializeKeysPool();
+    // Initialize the Crypto and Key objects required for RKP flow.
+    initializeRKpObjects();
+  }
+
+  private void initializeRKpObjects() {
+    rkpOPeration = new KMOperationImpl();
+    rkpAesGcm = Cipher.getInstance(AEADCipher.ALG_AES_GCM, false);
+    rkpHmac = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false);
+    rkpAesKey = createKeyObjectInstance(AES_256);
+    rkpHmacKey = createKeyObjectInstance(KMType.HMAC);
+  }
+
+  private void initializeKeysPool() {
+    for (short index = 0; index < KEY_ALGS.length; index++) {
+      keysPool[index] = createKeyObjectInstance(KEY_ALGS[index]);
+    }
+  }
+
+  private void initializeOperationPool() {
+    for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) {
+      operationPool[index] = new KMOperationImpl();
+    }
+  }
+
+  private void initializeHmacSignOperationPool() {
+    for (short index = 0; index < MAX_OPERATION_INSTANCES; index++) {
+      hmacSignOperationPool[index] = new KMOperationImpl();
+    }
+  }
+
+  // Create a signature instance of each algorithm once.
+  private void initializeSignerPool() {
+    short index;
+    for (index = 0; index < SIG_ALGS.length; index++) {
+      signerPool[index] = getSignatureInstance(SIG_ALGS[index]);
+    }
+
+    // Allocate extra 4 HMAC signer instances required for trusted confirmation
+    for (short len = (short) (index + 4); index < len; index++) {
+      signerPool[index] = getSignatureInstance(Signature.ALG_HMAC_SHA_256);
+    }
+  }
+
+  // Create a cipher instance of each algorithm once.
+  private void initializeCipherPool() {
+    for (short index = 0; index < CIPHER_ALGS.length; index++) {
+      cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]);
+    }
+  }
+
+  private void initializeKeyAgreementPool() {
+    for (short index = 0; index < KEY_AGREE_ALGS.length; index++) {
+      keyAgreementPool[index] = getKeyAgreementInstance(KEY_AGREE_ALGS[index]);
+    }
+  }
+
+  private Object[] getCryptoPoolInstance(short purpose) {
+    switch (purpose) {
+      case KMType.AGREE_KEY:
+        return keyAgreementPool;
+
+      case KMType.ENCRYPT:
+      case KMType.DECRYPT:
+        return cipherPool;
+
+      case KMType.SIGN:
+      case KMType.VERIFY:
+        return signerPool;
+
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+    }
+    return null;
+  }
+
+  private Object createInstance(short purpose, short alg) {
+    switch (purpose) {
+      case KMType.AGREE_KEY:
+        return getKeyAgreementInstance((byte) alg);
+
+      case KMType.ENCRYPT:
+      case KMType.DECRYPT:
+        return getCipherInstance((byte) alg);
+
+      case KMType.SIGN:
+      case KMType.VERIFY:
+        return getSignatureInstance((byte) alg);
+
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+    }
+    return null;
+  }
+
+  private KeyAgreement getKeyAgreementInstance(byte alg) {
+    return KeyAgreement.getInstance(alg, false);
+  }
+
+  private Signature getSignatureInstance(byte alg) {
+    if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg
+        || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) {
+      return new KMRsa2048NoDigestSignature(alg);
+    } else if (KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST == alg) {
+      return new KMEcdsa256NoDigestSignature(alg);
+    } else {
+      return Signature.getInstance(alg, false);
+    }
+  }
+
+  private KMKeyObject createKeyObjectInstance(byte alg) {
+    Object keyObject = null;
+    switch (alg) {
+      case AES_128:
+        keyObject =
+            (AESKey)
+                KeyBuilder.buildKey(
+                    KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false);
+        break;
+      case AES_256:
+        keyObject =
+            (AESKey)
+                KeyBuilder.buildKey(
+                    KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false);
+        break;
+      case KMType.DES:
+        keyObject =
+            (DESKey)
+                KeyBuilder.buildKey(
+                    KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false);
+        break;
+      case KMType.RSA:
+        keyObject = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048);
+        break;
+      case KMType.EC:
+        keyObject = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256);
+        initECKey((KeyPair) keyObject);
+        break;
+      case KMType.HMAC:
+        keyObject =
+            (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false);
+        break;
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+    }
+    KMKeyObject ptr = new KMKeyObject();
+    ptr.algorithm = alg;
+    ptr.keyObjectInst = keyObject;
+    return ptr;
+  }
+
+  private Cipher getCipherInstance(byte alg) {
+    if ((KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 == alg)
+        || (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 == alg)) {
+      return new KMRsaOAEPEncoding(alg);
+    } else {
+      return Cipher.getInstance(alg, false);
+    }
+  }
+
+  /**
+   * Returns the first available resource from operation pool.
+   *
+   * @return instance of the available resource or null if no resource is available.
+   */
+  public KMOperation getResourceFromOperationPool(boolean isTrustedConfOpr) {
+    short index = 0;
+    KMOperationImpl impl;
+    Object[] oprPool;
+    if (isTrustedConfOpr) {
+      oprPool = hmacSignOperationPool;
+    } else {
+      oprPool = operationPool;
+    }
+    while (index < oprPool.length) {
+      impl = (KMOperationImpl) oprPool[index];
+      // Mode is always set. so compare using mode value.
+      if (impl.getPurpose() == KMType.INVALID_VALUE) {
+        return impl;
+      }
+      index++;
+    }
+    return null;
+  }
+
+  private byte getAlgorithm(short purpose, Object object) {
+    switch (purpose) {
+      case KMType.AGREE_KEY:
+        return ((KeyAgreement) object).getAlgorithm();
+
+      case KMType.ENCRYPT:
+      case KMType.DECRYPT:
+        return ((Cipher) object).getAlgorithm();
+
+      case KMType.SIGN:
+      case KMType.VERIFY:
+        return ((Signature) object).getAlgorithm();
+
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+    }
+    return 0;
+  }
+
+  private boolean isResourceBusy(Object obj, byte resourceType) {
+    short index = 0;
+    while (index < MAX_OPERATION_INSTANCES) {
+      if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj, resourceType)
+          || ((KMOperationImpl) hmacSignOperationPool[index])
+              .isResourceMatches(obj, resourceType)) {
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  private void setObject(short purpose, KMOperation operation, Object obj) {
+    switch (purpose) {
+      case KMType.AGREE_KEY:
+        ((KMOperationImpl) operation).setKeyAgreement((KeyAgreement) obj);
+        break;
+      case KMType.ENCRYPT:
+      case KMType.DECRYPT:
+        ((KMOperationImpl) operation).setCipher((Cipher) obj);
+        break;
+      case KMType.SIGN:
+      case KMType.VERIFY:
+        ((KMOperationImpl) operation).setSignature((Signature) obj);
+        break;
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+    }
+  }
+
+  private void reserveOperation(
+      KMOperation operation,
+      short purpose,
+      short strongboxAlgType,
+      short padding,
+      short blockMode,
+      short macLength,
+      Object obj,
+      KMKeyObject keyObject) {
+    ((KMOperationImpl) operation).setPurpose(purpose);
+    ((KMOperationImpl) operation).setAlgorithmType(strongboxAlgType);
+    ((KMOperationImpl) operation).setPaddingAlgorithm(padding);
+    ((KMOperationImpl) operation).setBlockMode(blockMode);
+    ((KMOperationImpl) operation).setMacLength(macLength);
+    ((KMOperationImpl) operation).setKeyObject(keyObject);
+    setObject(purpose, operation, obj);
+  }
+
+  public KMOperation getRKpOperation(
+      short purpose,
+      short alg,
+      short strongboxAlgType,
+      short padding,
+      short blockMode,
+      short macLength) {
+    if (((KMOperationImpl) rkpOPeration).getPurpose() != KMType.INVALID_VALUE) {
+      // Should not come here.
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    Object cryptoObj = null;
+    KMKeyObject keyObject = null;
+
+    switch (alg) {
+      case AEADCipher.ALG_AES_GCM:
+        cryptoObj = rkpAesGcm;
+        keyObject = rkpAesKey;
+        break;
+      case Signature.ALG_HMAC_SHA_256:
+        cryptoObj = rkpHmac;
+        keyObject = rkpHmacKey;
+        break;
+      default:
+        // Should not come here.
+        KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        break;
+    }
+    reserveOperation(
+        rkpOPeration,
+        purpose,
+        strongboxAlgType,
+        padding,
+        blockMode,
+        macLength,
+        cryptoObj,
+        keyObject);
+    return rkpOPeration;
+  }
+
+  public KMOperation getOperationImpl(
+      short purpose,
+      short alg,
+      short strongboxAlgType,
+      short padding,
+      short blockMode,
+      short macLength,
+      short secretLength,
+      boolean isTrustedConfOpr) {
+    KMOperation operation;
+    // Throw exception if no resource from operation pool is available.
+    if (null == (operation = getResourceFromOperationPool(isTrustedConfOpr))) {
+      KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+    }
+    // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose.
+    Object[] pool = getCryptoPoolInstance(purpose);
+    short index = 0;
+    short usageCount = 0;
+    short maxOperations = MAX_OPERATION_INSTANCES;
+    if (Signature.ALG_HMAC_SHA_256 == alg) {
+      maxOperations = HMAC_MAX_OPERATION_INSTANCES;
+    }
+
+    KMKeyObject keyObject = getKeyObjectFromPool(alg, secretLength, maxOperations);
+    while (index < pool.length) {
+      if (usageCount >= maxOperations) {
+        KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+      }
+      if (pool[index] == null) {
+        // Create one of the instance (Cipher / Signer / KeyAgreement] based on purpose.
+        Object cipherObject = createInstance(purpose, alg);
+        JCSystem.beginTransaction();
+        pool[index] = cipherObject;
+        JCSystem.commitTransaction();
+        reserveOperation(
+            operation,
+            purpose,
+            strongboxAlgType,
+            padding,
+            blockMode,
+            macLength,
+            pool[index],
+            keyObject);
+        break;
+      }
+      if (alg == getAlgorithm(purpose, pool[index])) {
+        // Check if the crypto instance is not busy and free to use.
+        if (!isResourceBusy(pool[index], RESOURCE_TYPE_CRYPTO)) {
+          reserveOperation(
+              operation,
+              purpose,
+              strongboxAlgType,
+              padding,
+              blockMode,
+              macLength,
+              pool[index],
+              keyObject);
+          break;
+        }
+        usageCount++;
+      }
+      index++;
+    }
+    return operation;
+  }
+
+  public KMKeyObject getKeyObjectFromPool(short alg, short secretLength, short maxOperations) {
+    KMKeyObject keyObject = null;
+    byte algo = mapAlgorithm(alg, secretLength);
+    short index = 0;
+    short usageCount = 0;
+    while (index < keysPool.length) {
+      if (usageCount >= maxOperations) {
+        KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+      }
+      if (keysPool[index] == null) {
+        keyObject = createKeyObjectInstance(algo);
+        JCSystem.beginTransaction();
+        keysPool[index] = keyObject;
+        JCSystem.commitTransaction();
+        break;
+      }
+      keyObject = (KMKeyObject) keysPool[index];
+      if (algo == keyObject.algorithm) {
+        // Check if the Object instance is not busy and free to use.
+        if (!isResourceBusy(keyObject, RESOURCE_TYPE_KEY)) {
+          break;
+        }
+        usageCount++;
+      }
+      index++;
+    }
+    return keyObject;
+  }
+
+  private byte mapAlgorithm(short alg, short secretLength) {
+    byte algo = 0;
+    switch (alg) {
+      case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD:
+      case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD:
+      case Cipher.ALG_AES_CTR:
+      case AEADCipher.ALG_AES_GCM:
+        if (secretLength == 16) {
+          algo = AES_128;
+        } else if (secretLength == 32) {
+          algo = AES_256;
+        } else {
+          CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+        }
+        break;
+      case Cipher.ALG_DES_CBC_NOPAD:
+      case Cipher.ALG_DES_ECB_NOPAD:
+        algo = KMType.DES;
+        break;
+      case Cipher.ALG_RSA_PKCS1:
+      case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1:
+      case KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256:
+      case Cipher.ALG_RSA_NOPAD:
+      case Signature.ALG_RSA_SHA_256_PKCS1:
+      case Signature.ALG_RSA_SHA_256_PKCS1_PSS:
+      case KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD:
+      case KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST:
+        algo = KMType.RSA;
+        break;
+      case Signature.ALG_ECDSA_SHA_256:
+      case KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST:
+      case KeyAgreement.ALG_EC_SVDP_DH_PLAIN:
+        algo = KMType.EC;
+        break;
+      case Signature.ALG_HMAC_SHA_256:
+        algo = KMType.HMAC;
+        break;
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+    }
+    return algo;
+  }
+
+  public void initECKey(KeyPair ecKeyPair) {
+    ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate();
+    ECPublicKey pubkey = (ECPublicKey) ecKeyPair.getPublic();
+    pubkey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length);
+    pubkey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length);
+    pubkey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length);
+    pubkey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length);
+    pubkey.setK(secp256r1_H);
+    pubkey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length);
+
+    privKey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length);
+    privKey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length);
+    privKey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length);
+    privKey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length);
+    privKey.setK(secp256r1_H);
+    privKey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length);
+  }
+
+  public void powerReset() {
+    short index = 0;
+    while (index < operationPool.length) {
+      ((KMOperationImpl) operationPool[index]).abort();
+      ((KMOperationImpl) hmacSignOperationPool[index]).abort();
+      index++;
+    }
+    // release rkp operation
+    rkpOPeration.abort();
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java
new file mode 100644
index 0000000..8f94c82
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+/**
+ * KMPreSharedKey is a marker interface and the SE Provider has to implement this interface.
+ * Internally Preshared key is stored as a Javacard HMac key object, which will provide additional
+ * security. The pre-shared key is maintained by the SEProvider.
+ */
+public interface KMPreSharedKey {}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java
new file mode 100644
index 0000000..fe6ad84
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java
@@ -0,0 +1,3 @@
+package com.android.javacard.seprovider;
+
+public interface KMRkpMacKey {}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java
new file mode 100644
index 0000000..287a449
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.MessageDigest;
+import javacard.security.Signature;
+import javacardx.crypto.Cipher;
+
+public class KMRsa2048NoDigestSignature extends Signature {
+
+  public static final byte ALG_RSA_SIGN_NOPAD = (byte) 0x65;
+  public static final byte ALG_RSA_PKCS1_NODIGEST = (byte) 0x66;
+  private byte algorithm;
+  private Cipher inst;
+
+  public KMRsa2048NoDigestSignature(byte alg) {
+    algorithm = alg;
+    inst = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
+  }
+
+  @Override
+  public void init(Key key, byte b) throws CryptoException {
+    inst.init(key, b);
+  }
+
+  @Override
+  public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException {
+    inst.init(key, b, bytes, i, i1);
+  }
+
+  @Override
+  public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+      throws CryptoException {}
+
+  @Override
+  public byte getAlgorithm() {
+    return algorithm;
+  }
+
+  @Override
+  public byte getMessageDigestAlgorithm() {
+    return MessageDigest.ALG_NULL;
+  }
+
+  @Override
+  public byte getCipherAlgorithm() {
+    return algorithm;
+  }
+
+  @Override
+  public byte getPaddingAlgorithm() {
+    return Cipher.PAD_NULL;
+  }
+
+  @Override
+  public short getLength() throws CryptoException {
+    return 0;
+  }
+
+  @Override
+  public void update(byte[] bytes, short i, short i1) throws CryptoException {
+    // HAL accumulates the data and send it at finish operation.
+  }
+
+  @Override
+  public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+      throws CryptoException {
+    padData(bytes, i, i1, KMAndroidSEProvider.getInstance().tmpArray, (short) 0);
+    return inst.doFinal(
+        KMAndroidSEProvider.getInstance().tmpArray, (short) 0, (short) 256, bytes1, i2);
+  }
+
+  @Override
+  public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2)
+      throws CryptoException {
+    return 0;
+  }
+
+  @Override
+  public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3)
+      throws CryptoException {
+    // Verification is handled inside HAL
+    return false;
+  }
+
+  @Override
+  public boolean verifyPreComputedHash(
+      byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) throws CryptoException {
+    // Verification is handled inside HAL
+    return false;
+  }
+
+  private void padData(byte[] buf, short start, short len, byte[] outBuf, short outBufStart) {
+    if (!isValidData(buf, start, len)) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    Util.arrayFillNonAtomic(outBuf, (short) outBufStart, (short) 256, (byte) 0x00);
+    if (algorithm == ALG_RSA_SIGN_NOPAD) { // add zero to right
+    } else if (algorithm == ALG_RSA_PKCS1_NODIGEST) { // 0x00||0x01||PS||0x00
+      outBuf[0] = 0x00;
+      outBuf[1] = 0x01;
+      Util.arrayFillNonAtomic(outBuf, (short) 2, (short) (256 - len - 3), (byte) 0xFF);
+      outBuf[(short) (256 - len - 1)] = 0x00;
+    } else {
+      CryptoException.throwIt(CryptoException.ILLEGAL_USE);
+    }
+    Util.arrayCopyNonAtomic(buf, start, outBuf, (short) (256 - len), len);
+  }
+
+  private boolean isValidData(byte[] buf, short start, short len) {
+    if (algorithm == ALG_RSA_SIGN_NOPAD) {
+      if (len > 256) {
+        return false;
+      }
+    } else { // ALG_RSA_PKCS1_NODIGEST
+      if (len > 245) {
+        KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java
new file mode 100644
index 0000000..74322bd
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacard.security.Key;
+import javacard.security.MessageDigest;
+import javacardx.crypto.Cipher;
+
+public class KMRsaOAEPEncoding extends Cipher {
+
+  public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 = (byte) 0x1E;
+  public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 = (byte) 0x1F;
+
+  final short MGF1_BUF_SIZE = 256;
+  static byte[] mgf1Buf;
+  private Cipher cipher;
+  private byte hash;
+  private byte mgf1Hash;
+  private byte algorithm;
+
+  public KMRsaOAEPEncoding(byte alg) {
+    setDigests(alg);
+    cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
+    algorithm = alg;
+    if (null == mgf1Buf) {
+      mgf1Buf =
+          JCSystem.makeTransientByteArray(MGF1_BUF_SIZE, JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT);
+    }
+  }
+
+  private void setDigests(byte alg) {
+    switch (alg) {
+      case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1:
+        hash = MessageDigest.ALG_SHA_256;
+        mgf1Hash = MessageDigest.ALG_SHA;
+        break;
+      case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256:
+        hash = MessageDigest.ALG_SHA_256;
+        mgf1Hash = MessageDigest.ALG_SHA_256;
+        break;
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+    }
+  }
+
+  private short getDigestLength() {
+    switch (hash) {
+      case MessageDigest.ALG_SHA:
+        return MessageDigest.LENGTH_SHA;
+      case MessageDigest.ALG_SHA_224:
+        return MessageDigest.LENGTH_SHA_224;
+      case MessageDigest.ALG_SHA_256:
+        return MessageDigest.LENGTH_SHA_256;
+      case MessageDigest.ALG_SHA_384:
+        return MessageDigest.LENGTH_SHA_384;
+      case MessageDigest.ALG_SHA_512:
+        return MessageDigest.LENGTH_SHA_512;
+      default:
+        CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM);
+    }
+    return 0;
+  }
+
+  @Override
+  public void init(Key theKey, byte theMode) throws CryptoException {
+    cipher.init(theKey, theMode);
+  }
+
+  @Override
+  public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen)
+      throws CryptoException {
+    cipher.init(theKey, theMode, bArray, bOff, bLen);
+  }
+
+  @Override
+  public byte getAlgorithm() {
+    return algorithm;
+  }
+
+  @Override
+  public byte getCipherAlgorithm() {
+    return 0;
+  }
+
+  @Override
+  public byte getPaddingAlgorithm() {
+    return 0;
+  }
+
+  @Override
+  public short doFinal(
+      byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
+      throws CryptoException {
+    short len = cipher.doFinal(inBuff, inOffset, inLength, outBuff, outOffset);
+
+    // https://tools.ietf.org/html/rfc8017#section-7.1
+    // https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf
+    // RSA OAEP Encoding and Decoding Mechanism for a 2048 bit RSA Key.
+    // Msg -> RSA-OAEP-ENCODE -> RSAEncryption -> RSADecryption ->
+    // RSA-OAEP-DECODE -> Msg
+    // RSA-OAEP-ENCODE generates an output length of 255, but RSAEncryption
+    // requires and input of length 256 so we pad 0 to the left of the input
+    // message and make the length equal to 256 and pass to RSAEncryption.
+    // RSADecryption takes input length equal to 256 and generates an
+    // output of length 256. After decryption the first byte of the output
+    // should be 0(left padding we did in encryption).
+    // RSA-OAEP-DECODE takes input of length 255 so remove the left padding of 1
+    // byte.
+    if (len != 256 || outBuff[0] != 0) {
+      CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+    }
+    Util.arrayCopyNonAtomic(
+        outBuff, (short) (outOffset + 1), outBuff, (short) 0, (short) (len - 1));
+    return rsaOAEPDecode(outBuff, (short) 0, (short) (len - 1));
+  }
+
+  @Override
+  public short update(
+      byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset)
+      throws CryptoException {
+    return cipher.update(inBuff, inOffset, inLength, outBuff, outOffset);
+  }
+
+  private void maskGenerationFunction1(
+      byte[] input,
+      short inputOffset,
+      short inputLen,
+      short expectedOutLen,
+      byte[] outBuf,
+      short outOffset) {
+    short counter = 0;
+    MessageDigest.OneShot md = null;
+    try {
+      md = MessageDigest.OneShot.open(mgf1Hash);
+      short digestLen = md.getLength();
+
+      Util.arrayCopyNonAtomic(input, inputOffset, mgf1Buf, (short) 0, inputLen);
+      while (counter < (short) (expectedOutLen / digestLen)) {
+        I2OS(counter, mgf1Buf, (short) inputLen);
+        md.doFinal(
+            mgf1Buf,
+            (short) 0,
+            (short) (4 + inputLen),
+            outBuf,
+            (short) (outOffset + (counter * digestLen)));
+        counter++;
+      }
+
+      if ((short) (counter * digestLen) < expectedOutLen) {
+        I2OS(counter, mgf1Buf, (short) inputLen);
+        md.doFinal(
+            mgf1Buf,
+            (short) 0,
+            (short) (4 + inputLen),
+            outBuf,
+            (short) (outOffset + (counter * digestLen)));
+      }
+
+    } finally {
+      if (md != null) {
+        md.close();
+      }
+      Util.arrayFillNonAtomic(mgf1Buf, (short) 0, (short) MGF1_BUF_SIZE, (byte) 0);
+    }
+  }
+
+  // Integer to Octet String conversion.
+  private void I2OS(short i, byte[] out, short offset) {
+    Util.arrayFillNonAtomic(out, (short) offset, (short) 4, (byte) 0);
+    out[(short) (offset + 3)] = (byte) (i >>> 0);
+    out[(short) (offset + 2)] = (byte) (i >>> 8);
+  }
+
+  private short rsaOAEPDecode(byte[] encodedMsg, short encodedMsgOff, short encodedMsgLen) {
+    MessageDigest.OneShot md = null;
+    byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray;
+
+    try {
+      short hLen = getDigestLength();
+
+      if (encodedMsgLen < (short) (2 * hLen + 1)) {
+        CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+      }
+      // encodedMsg will be in the format of maskedSeed||maskedDB.
+      // maskedSeed length is hLen and maskedDB length is (encodedMsgLen - hLen)
+      // Now retrieve the seedMask by calling MGF(maskedDB, hLen). The length
+      // of the seedMask is hLen.
+      // seedMask = MGF(maskedDB, hLen)
+      maskGenerationFunction1(
+          encodedMsg,
+          (short) (encodedMsgOff + hLen),
+          (short) (encodedMsgLen - hLen),
+          hLen,
+          tmpArray,
+          (short) 0);
+
+      // Get the seed by doing XOR of (maskedSeed ^ seedMask).
+      // seed = (maskedSeed ^ seedMask)
+      for (short i = 0; i < hLen; i++) {
+        // Store the seed in encodeMsg itself.
+        encodedMsg[(short) (encodedMsgOff + i)] ^= tmpArray[i];
+      }
+
+      // Now get the dbMask by calling MGF(seed , (emLen-hLen)).
+      // dbMask = MGF(seed , (emLen-hLen)).
+      maskGenerationFunction1(
+          encodedMsg,
+          (short) encodedMsgOff,
+          hLen,
+          (short) (encodedMsgLen - hLen),
+          tmpArray,
+          (short) 0);
+
+      // Get the DB value. DB = (maskedDB ^ dbMask)
+      // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL)
+      for (short i = 0; i < (short) (encodedMsgLen - hLen); i++) {
+        // Store the DB inside encodeMsg itself.
+        encodedMsg[(short) (encodedMsgOff + i + hLen)] ^= tmpArray[i];
+      }
+
+      // Verify Hash.
+      md = MessageDigest.OneShot.open(hash);
+      Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0);
+      md.doFinal(tmpArray, (short) 0, (short) 0, tmpArray, (short) 0);
+      if (0
+          != Util.arrayCompare(
+              encodedMsg, (short) (encodedMsgOff + hLen), tmpArray, (short) 0, hLen)) {
+        // Verification failed.
+        CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+      }
+
+      // Find the Message block in DB.
+      // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL)
+      // The message will be located at the end of the Data block (DB).
+      // The DB block is first constructed by keeping the message at the end and
+      // to the message 0x01 byte is prepended. The hash of the
+      // encoding parameters is calculated and then copied from the
+      // starting of the block and a variable length of 0's are
+      // appended to the end of the hash till the 0x01 byte.
+      short start = (short) (encodedMsgOff + encodedMsgLen);
+      for (short i = (short) (encodedMsgOff + 2 * hLen);
+          i < (short) (encodedMsgOff + encodedMsgLen);
+          i++) {
+        if ((encodedMsg[i] != 0)) {
+          start = i;
+          break;
+        }
+      }
+      if ((start >= (short) (encodedMsgOff + encodedMsgLen)) || (encodedMsg[start] != 0x01)) {
+        // Bad Padding.
+        CryptoException.throwIt(CryptoException.ILLEGAL_VALUE);
+      }
+      start++; // Message starting pos.
+      if (start < (short) (encodedMsgOff + encodedMsgLen)) {
+        // Copy the message
+        Util.arrayCopyNonAtomic(
+            encodedMsg,
+            start,
+            encodedMsg,
+            encodedMsgOff,
+            (short) (encodedMsgLen - (start - encodedMsgOff)));
+      }
+      return (short) (encodedMsgLen - (start - encodedMsgOff));
+
+    } finally {
+      if (md != null) {
+        md.close();
+      }
+      Util.arrayFillNonAtomic(tmpArray, (short) 0, KMAndroidSEProvider.TMP_ARRAY_SIZE, (byte) 0);
+    }
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java
new file mode 100644
index 0000000..1a564cc
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import org.globalplatform.upgrade.Element;
+
+/**
+ * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to
+ * abstract the cipher, signature and backup and restore related functions. The instance of this
+ * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there
+ * can be only one provider in the applet package.
+ */
+public interface KMSEProvider {
+
+  /**
+   * This function tells if boot signal event is supported or not.
+   *
+   * @return true if supported, false otherwise.
+   */
+  boolean isBootSignalEventSupported();
+
+  /**
+   * This function tells if the device is booted or not.
+   *
+   * @return true if device booted, false otherwise.
+   */
+  boolean isDeviceRebooted();
+
+  /**
+   * This function is supposed to be used to reset the device booted stated after set boot param is
+   * handled
+   *
+   * @param resetBootFlag is false if event has been handled
+   */
+  void clearDeviceBooted(boolean resetBootFlag);
+
+  /**
+   * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it
+   * should throw a CryptoException.
+   *
+   * @param alg will be KMType.AES, KMType.DES or KMType.HMAC.
+   * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC.
+   * @param buf is the buffer in which key has to be returned
+   * @param startOff is the start offset.
+   * @return length of the data in the buf. This should match the keysize (in bytes).
+   */
+  short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff);
+
+  /**
+   * Create a asymmetric key pair. If the algorithms are not supported then it should throw a
+   * CryptoException. For RSA the public key exponent must always be 0x010001. The key size of RSA
+   * key pair must be 2048 bits and key size of EC key pair must be for p256 curve.
+   *
+   * @param alg will be KMType.RSA or KMType.EC.
+   * @param privKeyBuf is the buffer to return the private key exponent in case of RSA or private
+   *     key in case of EC.
+   * @param privKeyStart is the start offset.
+   * @param privKeyMaxLength is the maximum length of this private key buffer.
+   * @param pubModBuf is the buffer to return the modulus in case of RSA or public key in case of
+   *     EC.
+   * @param pubModStart is the start of offset.
+   * @param pubModMaxLength is the maximum length of this public key buffer.
+   * @param lengths is the actual length of the key pair - lengths[0] should be private key and
+   *     lengths[1] should be public key.
+   */
+  void createAsymmetricKey(
+      byte alg,
+      byte[] privKeyBuf,
+      short privKeyStart,
+      short privKeyMaxLength,
+      byte[] pubModBuf,
+      short pubModStart,
+      short pubModMaxLength,
+      short[] lengths);
+
+  /**
+   * Initializes the trusted confirmation operation.
+   *
+   * @param computedHmacKey Instance of the computed Hmac key.
+   * @return instance of KMOperation.
+   */
+  KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey);
+
+  /**
+   * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then
+   * it should throw a CryptoException.
+   *
+   * @param alg will be KMType.AES, KMType.DES or KMType.HMAC.
+   * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for HMAC.
+   * @param buf is the buffer that contains the symmetric key.
+   * @param startOff is the start offset.
+   * @param length of the data in the buf. This should match the keysize (in bytes).
+   * @return true if the symmetric key is supported and valid.
+   */
+  boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length);
+
+  /**
+   * Validate that the imported asymmetric key pair is valid. For RSA the public key exponent must
+   * always be 0x010001. The key size of RSA key pair must be 2048 bits and key size of EC key pair
+   * must be for p256 curve. If the algorithms are not supported then it should throw a
+   * CryptoException.
+   *
+   * @param alg will be KMType.RSA or KMType.EC.
+   * @param privKeyBuf is the buffer that contains the private key exponent in case of RSA or
+   *     private key in case of EC.
+   * @param privKeyStart is the start offset.
+   * @param privKeyLength is the length of this private key buffer.
+   * @param pubModBuf is the buffer that contains the modulus in case of RSA or public key in case
+   *     of EC.
+   * @param pubModStart is the start of offset.
+   * @param pubModLength is the length of this public key buffer.
+   * @return true if the key pair is supported and valid.
+   */
+  boolean importAsymmetricKey(
+      byte alg,
+      byte[] privKeyBuf,
+      short privKeyStart,
+      short privKeyLength,
+      byte[] pubModBuf,
+      short pubModStart,
+      short pubModLength);
+
+  /**
+   * This is a oneshot operation that generates random number of desired length.
+   *
+   * @param num is the buffer in which random number is returned to the applet.
+   * @param offset is start of the buffer.
+   * @param length indicates the size of buffer and desired length of random number in bytes.
+   */
+  void newRandomNumber(byte[] num, short offset, short length);
+
+  /**
+   * This is a oneshot operation that adds the entropy to the entropy pool. This operation
+   * corresponds to addRndEntropy command. This method may ignore the added entropy value if the SE
+   * provider does not support it.
+   *
+   * @param num is the buffer in which entropy value is given.
+   * @param offset is start of the buffer.
+   * @param length length of the buffer.
+   */
+  void addRngEntropy(byte[] num, short offset, short length);
+
+  /**
+   * This is a oneshot operation that generates and returns back a true random number.
+   *
+   * @param num is the buffer in which entropy value is returned.
+   * @param offset is start of the buffer.
+   * @param length length of the buffer.
+   */
+  void getTrueRandomNumber(byte[] num, short offset, short length);
+
+  /**
+   * This is a oneshot operation that performs encryption operation using AES GCM algorithm. It
+   * throws CryptoException if algorithm is not supported or if tag length is not equal to 16 or
+   * nonce length is not equal to 12.
+   *
+   * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt.
+   * @param aesKeyStart is the start in aes key buffer.
+   * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes).
+   * @param data is the buffer that contains data to encrypt.
+   * @param dataStart is the start of the data buffer.
+   * @param dataLen is the length of the data buffer.
+   * @param encData is the buffer of the output encrypted data.
+   * @param encDataStart is the start of the encrypted data buffer.
+   * @param nonce is the buffer of nonce.
+   * @param nonceStart is the start of the nonce buffer.
+   * @param nonceLen is the length of the nonce buffer.
+   * @param authData is the authentication data buffer.
+   * @param authDataStart is the start of the authentication buffer.
+   * @param authDataLen is the length of the authentication buffer.
+   * @param authTag is the buffer to output authentication tag.
+   * @param authTagStart is the start of the buffer.
+   * @param authTagLen is the length of the buffer.
+   * @return length of the encrypted data.
+   */
+  short aesGCMEncrypt(
+      byte[] aesKey,
+      short aesKeyStart,
+      short aesKeyLen,
+      byte[] data,
+      short dataStart,
+      short dataLen,
+      byte[] encData,
+      short encDataStart,
+      byte[] nonce,
+      short nonceStart,
+      short nonceLen,
+      byte[] authData,
+      short authDataStart,
+      short authDataLen,
+      byte[] authTag,
+      short authTagStart,
+      short authTagLen);
+
+  /**
+   * This is a oneshot operation that performs decryption operation using AES GCM algorithm. It
+   * throws CryptoException if algorithm is not supported.
+   *
+   * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt.
+   * @param aesKeyStart is the start in aes key buffer.
+   * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes).
+   * @param encData is the buffer of the input encrypted data.
+   * @param encDataStart is the start of the encrypted data buffer.
+   * @param encDataLen is the length of the data buffer.
+   * @param data is the buffer that contains output decrypted data.
+   * @param dataStart is the start of the data buffer.
+   * @param nonce is the buffer of nonce.
+   * @param nonceStart is the start of the nonce buffer.
+   * @param nonceLen is the length of the nonce buffer.
+   * @param authData is the authentication data buffer.
+   * @param authDataStart is the start of the authentication buffer.
+   * @param authDataLen is the length of the authentication buffer.
+   * @param authTag is the buffer to output authentication tag.
+   * @param authTagStart is the start of the buffer.
+   * @param authTagLen is the length of the buffer.
+   * @return true if the authentication is valid.
+   */
+  boolean aesGCMDecrypt(
+      byte[] aesKey,
+      short aesKeyStart,
+      short aesKeyLen,
+      byte[] encData,
+      short encDataStart,
+      short encDataLen,
+      byte[] data,
+      short dataStart,
+      byte[] nonce,
+      short nonceStart,
+      short nonceLen,
+      byte[] authData,
+      short authDataStart,
+      short authDataLen,
+      byte[] authTag,
+      short authTagStart,
+      short authTagLen);
+
+  /**
+   * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as
+   * defined in android keymaster hal definition.
+   *
+   * @param hmacKey of pre-shared key.
+   * @param label is the label to be used for ckdf.
+   * @param labelStart is the start of label.
+   * @param labelLen is the length of the label.
+   * @param context is the context to be used for ckdf.
+   * @param contextStart is the start of the context
+   * @param contextLength is the length of the context
+   * @param key is the output buffer to return the derived key
+   * @param keyStart is the start of the output buffer.
+   * @return length of the derived key buffer in bytes.
+   */
+  short cmacKDF(
+      KMPreSharedKey hmacKey,
+      byte[] label,
+      short labelStart,
+      short labelLen,
+      byte[] context,
+      short contextStart,
+      short contextLength,
+      byte[] key,
+      short keyStart);
+
+  /**
+   * This is a oneshot operation that signs the data using hmac algorithm.
+   *
+   * @param keyBuf is the buffer with hmac key.
+   * @param keyStart is the start of the buffer.
+   * @param keyLength is the length of the buffer which will be in bytes from 8 to 64.
+   * @param data is the buffer containing data to be signed.
+   * @param dataStart is the start of the data.
+   * @param dataLength is the length of the data.
+   * @param signature is the output signature buffer
+   * @param signatureStart is the start of the signature
+   * @return length of the signature buffer in bytes.
+   */
+  short hmacSign(
+      byte[] keyBuf,
+      short keyStart,
+      short keyLength,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] signature,
+      short signatureStart);
+
+  /**
+   * This is a oneshot operation that signs the data using hmac algorithm.
+   *
+   * @param hmacKey is the KMHmacKey.
+   * @param data is the buffer containing data to be signed.
+   * @param dataStart is the start of the data.
+   * @param dataLength is the length of the data.
+   * @param signature is the output signature buffer
+   * @param signatureStart is the start of the signature
+   * @return length of the signature buffer in bytes.
+   */
+  short hmacSign(
+      Object hmacKey,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] signature,
+      short signatureStart);
+
+  /**
+   * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive
+   * the key, which is used to encrypt the keyblob.
+   *
+   * @param masterkey of masterkey.
+   * @param data is the buffer containing data to be signed.
+   * @param dataStart is the start of the data.
+   * @param dataLength is the length of the data.
+   * @param signature is the output signature buffer
+   * @param signatureStart is the start of the signature
+   * @return length of the signature buffer in bytes.
+   */
+  short hmacKDF(
+      KMMasterKey masterkey,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] signature,
+      short signatureStart);
+
+  /**
+   * This is a oneshot operation that verifies the signature using hmac algorithm.
+   *
+   * @param keyBuf is the buffer with hmac key.
+   * @param keyStart is the start of the buffer.
+   * @param keyLength is the length of the buffer which will be in bytes from 8 to 64.
+   * @param data is the buffer containing data.
+   * @param dataStart is the start of the data.
+   * @param dataLength is the length of the data.
+   * @param signature is the signature buffer.
+   * @param signatureStart is the start of the signature buffer.
+   * @param signatureLen is the length of the signature buffer in bytes.
+   * @return true if the signature matches.
+   */
+  boolean hmacVerify(
+      KMComputedHmacKey hmacKey,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] signature,
+      short signatureStart,
+      short signatureLen);
+
+  /**
+   * This is a oneshot operation that decrypts the data using RSA algorithm with oaep256 padding.
+   * The public exponent is always 0x010001. It throws CryptoException if OAEP encoding validation
+   * fails.
+   *
+   * @param privExp is the private exponent (2048 bit) buffer.
+   * @param privExpStart is the start of the private exponent buffer.
+   * @param privExpLength is the length of the private exponent buffer in bytes.
+   * @param modBuffer is the modulus (2048 bit) buffer.
+   * @param modOff is the start of the modulus buffer.
+   * @param modLength is the length of the modulus buffer in bytes.
+   * @param inputDataBuf is the buffer of the input data.
+   * @param inputDataStart is the start of the input data buffer.
+   * @param inputDataLength is the length of the input data buffer in bytes.
+   * @param outputDataBuf is the output buffer that contains the decrypted data.
+   * @param outputDataStart is the start of the output data buffer.
+   * @return length of the decrypted data.
+   */
+  short rsaDecipherOAEP256(
+      byte[] privExp,
+      short privExpStart,
+      short privExpLength,
+      byte[] modBuffer,
+      short modOff,
+      short modLength,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart);
+
+  /**
+   * This is a oneshot operation that signs the data using EC private key.
+   *
+   * @param ecPrivKey of KMAttestationKey.
+   * @param inputDataBuf is the buffer of the input data.
+   * @param inputDataStart is the start of the input data buffer.
+   * @param inputDataLength is the length of the inpur data buffer in bytes.
+   * @param outputDataBuf is the output buffer that contains the signature.
+   * @param outputDataStart is the start of the output data buffer.
+   * @return length of the decrypted data.
+   */
+  short ecSign256(
+      KMAttestationKey ecPrivKey,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart);
+
+  /**
+   * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2
+   *
+   * @param ikm is the buffer containing input key material.
+   * @param ikmOff is the start of the input key.
+   * @param ikmLen is the length of the input key.
+   * @param salt is the buffer containing the salt.
+   * @param saltOff is the start of the salt buffer.
+   * @param saltLen is the length of the salt buffer.
+   * @param info is the buffer containing the application specific information
+   * @param infoOff is the start of the info buffer.
+   * @param infoLen is the length of the info buffer.
+   * @param out is the output buffer.
+   * @param outOff is the start of the output buffer.
+   * @param outLen is the length of the expected out buffer.
+   * @return Length of the out buffer which is outLen.
+   */
+  short hkdf(
+      byte[] ikm,
+      short ikmOff,
+      short ikmLen,
+      byte[] salt,
+      short saltOff,
+      short saltLen,
+      byte[] info,
+      short infoOff,
+      short infoLen,
+      byte[] out,
+      short outOff,
+      short outLen);
+
+  /**
+   * This function performs ECDH key agreement and generates a secret.
+   *
+   * @param privKey is the buffer containing the private key from first party.
+   * @param privKeyOff is the offset of the private key buffer.
+   * @param privKeyLen is the length of the private key buffer.
+   * @param publicKey is the buffer containing the public key from second party.
+   * @param publicKeyOff is the offset of the public key buffer.
+   * @param publicKeyLen is the length of the public key buffer.
+   * @param secret is the output buffer.
+   * @param secretOff is the offset of the output buffer.
+   * @return The length of the secret.
+   */
+  short ecdhKeyAgreement(
+      byte[] privKey,
+      short privKeyOff,
+      short privKeyLen,
+      byte[] publicKey,
+      short publicKeyOff,
+      short publicKeyLen,
+      byte[] secret,
+      short secretOff);
+
+  /**
+   * This is a oneshort operation that verifies the data using EC public key
+   *
+   * @param pubKey is the public key buffer.
+   * @param pubKeyOffset is the start of the public key buffer.
+   * @param pubKeyLen is the length of the public key.
+   * @param inputDataBuf is the buffer of the input data.
+   * @param inputDataStart is the start of the input data buffer.
+   * @param inputDataLength is the length of the input data buffer in bytes.
+   * @param signatureDataBuf is the buffer the signature input data.
+   * @param signatureDataStart is the start of the signature input data.
+   * @param signatureDataLen is the length of the signature input data.
+   * @return true if verification is successful, otherwise false.
+   */
+  boolean ecVerify256(
+      byte[] pubKey,
+      short pubKeyOffset,
+      short pubKeyLen,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] signatureDataBuf,
+      short signatureDataStart,
+      short signatureDataLen);
+
+  /**
+   * This is a oneshot operation that signs the data using device unique key.
+   *
+   * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data.
+   * @param inputDataBuf is the buffer of the input data.
+   * @param inputDataStart is the start of the input data buffer.
+   * @param inputDataLength is the length of the input data buffer in bytes.
+   * @param outputDataBuf is the output buffer that contains the signature.
+   * @param outputDataStart is the start of the output data buffer.
+   * @return length of the decrypted data.
+   */
+  short ecSign256(
+      KMDeviceUniqueKeyPair ecPrivKey,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart);
+
+  short ecSign256(
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart);
+
+  short rsaSign256Pkcs1(
+      byte[] secret,
+      short secretStart,
+      short secretLength,
+      byte[] modBuf,
+      short modStart,
+      short modLength,
+      byte[] inputDataBuf,
+      short inputDataStart,
+      short inputDataLength,
+      byte[] outputDataBuf,
+      short outputDataStart);
+
+  /**
+   * This creates a persistent operation for signing, verify, encryption and decryption using HMAC,
+   * AES and DES algorithms when keymaster hal's beginOperation function is executed. The
+   * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or
+   * aborted. It throws CryptoException if algorithm is not supported.
+   *
+   * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+   *     KMType.SIGN and KMType.VERIFY for HMAC algorithm
+   * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+   * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+   * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+   * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+   *     0.
+   * @param keyBuf is aes, des or hmac key buffer.
+   * @param keyStart is the start of the key buffer.
+   * @param keyLength is the length of the key buffer.
+   * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+   * @param ivStart is the start of the iv buffer.
+   * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+   *     with ECB mode.
+   * @param macLength is the mac length in case of signing operation for hmac algorithm.
+   * @return KMOperation instance.
+   */
+  KMOperation initSymmetricOperation(
+      byte purpose,
+      byte alg,
+      byte digest,
+      byte padding,
+      byte blockMode,
+      byte[] keyBuf,
+      short keyStart,
+      short keyLength,
+      byte[] ivBuf,
+      short ivStart,
+      short ivLength,
+      short macLength);
+
+  /**
+   * This creates a persistent operation for signing, verify, encryption and decryption using HMAC,
+   * AES and DES algorithms when keymaster hal's beginOperation function is executed. The
+   * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or
+   * aborted. It throws CryptoException if algorithm is not supported.
+   *
+   * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+   *     KMType.SIGN and KMType.VERIFY for HMAC algorithm
+   * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+   * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+   * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+   * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+   *     0.
+   * @param key is a key object.
+   * @param interfaceType defines the type of key in the key object.
+   * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+   * @param ivStart is the start of the iv buffer.
+   * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+   *     with ECB mode.
+   * @param macLength is the mac length in case of signing operation for hmac algorithm.
+   * @param oneShot if true, creates oneshot operation.
+   * @return KMOperation instance.
+   */
+  KMOperation initSymmetricOperation(
+      byte purpose,
+      byte alg,
+      byte digest,
+      byte padding,
+      byte blockMode,
+      Object key,
+      byte interfaceType,
+      byte[] ivBuf,
+      short ivStart,
+      short ivLength,
+      short macLength,
+      boolean oneShot);
+
+  /**
+   * This function creates an Operation instance only for RKP module.
+   *
+   * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be
+   *     KMType.SIGN and KMType.VERIFY for HMAC algorithm
+   * @param alg is KMType.HMAC, KMType.AES or KMType.DES.
+   * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE.
+   * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES).
+   * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is
+   *     0.
+   * @param keyBuf is aes, des or hmac key buffer.
+   * @param keyStart is the start of the key buffer.
+   * @param keyLength is the length of the key buffer.
+   * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode)
+   * @param ivStart is the start of the iv buffer.
+   * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES
+   *     with ECB mode.
+   * @param macLength is the mac length in case of signing operation for hmac algorithm.
+   * @return KMOperation instance.
+   */
+  KMOperation getRkpOperation(
+      byte purpose,
+      byte alg,
+      byte digest,
+      byte padding,
+      byte blockMode,
+      byte[] keyBuf,
+      short keyStart,
+      short keyLength,
+      byte[] ivBuf,
+      short ivStart,
+      short ivLength,
+      short macLength);
+
+  /**
+   * This creates a persistent operation for signing, verify, encryption and decryption using RSA
+   * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public
+   * exponent is always 0x0100101. For EC the curve is always p256. The KMOperation instance can be
+   * reclaimed by the seProvider when KMOperation is finished or aborted. It throws CryptoException
+   * if algorithm is not supported.
+   *
+   * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for RSA. It will be * KMType.SIGN and
+   *     KMType.VERIFY for RSA and EC algorithms.
+   * @param alg is KMType.RSA or KMType.EC algorithms.
+   * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT,
+   *     KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS.
+   * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256.
+   * @param mgfDigest is the MGF digest.
+   * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA.
+   * @param privKeyStart is the start of the private key.
+   * @param privKeyLength is the length of the private key.
+   * @param pubModBuf is the modulus (in case of RSA) or public key (in case of EC).
+   * @param pubModStart is the start of the modulus.
+   * @param pubModLength is the length of the modulus.
+   * @return KMOperation instance that can be executed.
+   */
+  KMOperation initAsymmetricOperation(
+      byte purpose,
+      byte alg,
+      byte padding,
+      byte digest,
+      byte mgfDigest,
+      byte[] privKeyBuf,
+      short privKeyStart,
+      short privKeyLength,
+      byte[] pubModBuf,
+      short pubModStart,
+      short pubModLength);
+
+  /**
+   * This function tells if applet is upgrading or not.
+   *
+   * @return true if upgrading, otherwise false.
+   */
+  boolean isUpgrading();
+
+  /**
+   * This function generates an AES Key of keySizeBits, which is used as an master key. This
+   * generated key is maintained by the SEProvider. This function should be called only once at the
+   * time of installation.
+   *
+   * @param instance of the masterkey.
+   * @param keySizeBits key size in bits.
+   * @return An instance of KMMasterKey.
+   */
+  KMMasterKey createMasterKey(KMMasterKey masterKey, short keySizeBits);
+
+  /**
+   * This function creates an HMACKey and initializes the key with the provided input key data.
+   *
+   * @param keyData buffer containing the key data.
+   * @param offset start of the buffer.
+   * @param length length of the buffer.
+   * @return An instance of the KMComputedHmacKey.
+   */
+  KMComputedHmacKey createComputedHmacKey(
+      KMComputedHmacKey computedHmacKey, byte[] keyData, short offset, short length);
+
+  /** Returns true if factory provisioned attestation key is supported. */
+  boolean isAttestationKeyProvisioned();
+
+  /**
+   * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the
+   * attestation key is provisioned in the factory.
+   */
+  short getAttestationKeyAlgorithm();
+
+  /**
+   * Creates an ECKey instance and sets the public and private keys to it.
+   *
+   * @param testMode to indicate if current execution is for test or production.
+   * @param pubKey buffer containing the public key.
+   * @param pubKeyOff public key buffer start offset.
+   * @param pubKeyLen public key buffer length.
+   * @param privKey buffer containing the private key.
+   * @param privKeyOff private key buffer start offset.
+   * @param privKeyLen private key buffer length.
+   * @return instance of KMDeviceUniqueKey.
+   */
+  KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(
+      KMDeviceUniqueKeyPair key,
+      byte[] pubKey,
+      short pubKeyOff,
+      short pubKeyLen,
+      byte[] privKey,
+      short privKeyOff,
+      short privKeyLen);
+
+  /**
+   * This is a one-shot operation the does digest of the input mesage.
+   *
+   * @param inBuff input buffer to be digested.
+   * @param inOffset start offset of the input buffer.
+   * @param inLength length of the input buffer.
+   * @param outBuff is the output buffer that contains the digested data.
+   * @param outOffset start offset of the digested output buffer.
+   * @return length of the digested data.
+   */
+  short messageDigest256(
+      byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset);
+
+  /**
+   * This function generates a HMAC key from the provided key buffers.
+   *
+   * @param presharedKey instance of the presharedkey.
+   * @param key buffer containing the key data.
+   * @param offset start offset of the buffer.
+   * @param length is the length of the key.
+   * @return instance of KMPresharedKey.
+   */
+  KMPreSharedKey createPreSharedKey(
+      KMPreSharedKey presharedKey, byte[] key, short offset, short length);
+
+  /**
+   * This function saves the key objects while upgrade.
+   *
+   * @param element instance of the Element class where the objects to be stored.
+   * @param interfaceType the type interface of the parent object.
+   * @param object instance of the object to be saved.
+   */
+  void onSave(Element element, byte interfaceType, Object object);
+
+  /**
+   * This function restores the the object from element instance.
+   *
+   * @param element instance of the Element class.
+   * @return restored object.
+   */
+  Object onRestore(Element element);
+
+  /**
+   * This function returns the count of the primitive bytes required to be stored by the
+   * implementation of the interface type.
+   *
+   * @param interfaceType type interface of the parent object.
+   * @return count of the primitive bytes.
+   */
+  short getBackupPrimitiveByteCount(byte interfaceType);
+
+  /**
+   * This function returns the object count required to be stored by the implementation of the
+   * interface type.
+   *
+   * @param interfaceType type interface of the parent object.
+   * @return count of the objects.
+   */
+  short getBackupObjectCount(byte interfaceType);
+
+  /**
+   * This function creates an HMACKey and initializes the key with the provided input key data.
+   *
+   * @param keyData buffer containing the key data.
+   * @param offset start of the buffer.
+   * @param length length of the buffer.
+   * @return An instance of the KMRkpMacKey.
+   */
+  KMRkpMacKey createRkpMacKey(
+      KMRkpMacKey createComputedHmacKey, byte[] keyData, short offset, short length);
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java
new file mode 100644
index 0000000..648d323
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.seprovider;
+
+
+/**
+ * This class declares all types, tag types, and tag keys. It also establishes basic structure of
+ * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also,
+ * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton
+ * prototype objects which just cast the structure over contiguous memory buffer.
+ */
+public abstract class KMType {
+
+  public static final short INVALID_VALUE = (short) 0x8000;
+
+  // Algorithm Enum Tag key and values
+  public static final short ALGORITHM = 0x0002;
+  public static final byte RSA = 0x01;
+  public static final byte DES = 0x21;
+  public static final byte EC = 0x03;
+  public static final byte AES = 0x20;
+  public static final byte HMAC = (byte) 0x80;
+
+  // EcCurve Enum Tag key and values.
+  public static final short ECCURVE = 0x000A;
+  public static final byte P_224 = 0x00;
+  public static final byte P_256 = 0x01;
+  public static final byte P_384 = 0x02;
+  public static final byte P_521 = 0x03;
+
+  // Purpose
+  public static final short PURPOSE = 0x0001;
+  public static final byte ENCRYPT = 0x00;
+  public static final byte DECRYPT = 0x01;
+  public static final byte SIGN = 0x02;
+  public static final byte VERIFY = 0x03;
+  public static final byte DERIVE_KEY = 0x04;
+  public static final byte WRAP_KEY = 0x05;
+  public static final byte AGREE_KEY = 0x06;
+  public static final byte ATTEST_KEY = (byte) 0x07;
+  // Block mode
+  public static final short BLOCK_MODE = 0x0004;
+  public static final byte ECB = 0x01;
+  public static final byte CBC = 0x02;
+  public static final byte CTR = 0x03;
+  public static final byte GCM = 0x20;
+
+  // Digest
+  public static final short DIGEST = 0x0005;
+  public static final byte DIGEST_NONE = 0x00;
+  public static final byte MD5 = 0x01;
+  public static final byte SHA1 = 0x02;
+  public static final byte SHA2_224 = 0x03;
+  public static final byte SHA2_256 = 0x04;
+  public static final byte SHA2_384 = 0x05;
+  public static final byte SHA2_512 = 0x06;
+
+  // Padding mode
+  public static final short PADDING = 0x0006;
+  public static final byte PADDING_NONE = 0x01;
+  public static final byte RSA_OAEP = 0x02;
+  public static final byte RSA_PSS = 0x03;
+  public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04;
+  public static final byte RSA_PKCS1_1_5_SIGN = 0x05;
+  public static final byte PKCS7 = 0x40;
+
+
+}
diff --git a/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java
new file mode 100644
index 0000000..69536ab
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright(C) 2020 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" (short)0IS,
+ * 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 com.android.javacard.seprovider;
+
+import org.globalplatform.upgrade.Element;
+
+public interface KMUpgradable {
+
+  void onSave(Element ele);
+
+  void onRestore(Element ele, short oldVersion, short currentVersion);
+
+  short getBackupPrimitiveByteCount();
+
+  short getBackupObjectCount();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/README.md b/ready_se/google/keymint/KM200/Applet/README.md
new file mode 100644
index 0000000..ace6950
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/README.md
@@ -0,0 +1,15 @@
+# JavaCardKeymaster Applet

+

+This directory contains the implementation of the Keymint 1.0

+interface, in the form of a JavaCard 3.0.5 applet which runs in a secure

+element.  It must be deployed in conjuction with the associated HAL,

+which mediates between Android Keystore and this applet.

+

+# Supported Features!

+

+  - Keymint 1.0 supported functions for required VTS compliance.

+  - SharedSecret 1.0 supported functions for required VTS compliance.

+

+# Not supported features

+  - Factory provisioned attestation key will not be supported in this applet.

+  - Limited usage keys will not be supported in this applet.

diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMArray.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMArray.java
new file mode 100644
index 0000000..aa54d54
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMArray.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMArray represents an array of KMTypes. Array is the sequence of elements of one or more sub
+ * types of KMType. It also acts as a vector of one subtype of KMTypes on the lines of class KMArray
+ * <subType>, where subType is subclass of KMType. Vector is the sequence of elements of one sub
+ * type e.g. KMType.BYTE_BLOB_TYPE. The KMArray instance maps to the CBOR type array. KMArray is a
+ * KMType and it further extends the value field in TLV_HEADER as ARRAY_HEADER struct{short subType;
+ * short length;} followed by sequence of short pointers to KMType instances. The subType can be 0
+ * if this is an array or subType is short KMType value e.g. KMType.BYTE_BLOB_TYPE if this is a
+ * vector of that sub type.
+ */
+public class KMArray extends KMType {
+
+  public static final short ANY_ARRAY_LENGTH = 0x1000;
+  private static final byte ARRAY_HEADER_SIZE = 4;
+  private static KMArray prototype;
+
+  private KMArray() {}
+
+  private static KMArray proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMArray();
+    }
+    KMType.instanceTable[KM_ARRAY_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp() {
+    short ptr = instance(ARRAY_TYPE, (short) ARRAY_HEADER_SIZE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_ARRAY_LENGTH);
+    return ptr;
+  }
+
+  public static short exp(short type) {
+    short ptr = instance(ARRAY_TYPE, (short) ARRAY_HEADER_SIZE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_ARRAY_LENGTH);
+    return ptr;
+  }
+
+  public static short instance(short length) {
+    short ptr = KMType.instance(ARRAY_TYPE, (short) (ARRAY_HEADER_SIZE + (length * 2)));
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length);
+    return ptr;
+  }
+
+  public static short instance(short length, byte type) {
+    short ptr = instance(length);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+    return ptr;
+  }
+
+  public static KMArray cast(short ptr) {
+    if (heap[ptr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public void add(short index, short objPtr) {
+    short len = length();
+    if (index >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    Util.setShort(heap, (short) (getStartOff() + (short) (index * 2)), objPtr);
+  }
+
+  public short get(short index) {
+    short len = length();
+    if (index >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    return Util.getShort(heap, (short) (getStartOff() + (short) (index * 2)));
+  }
+
+  public void swap(short index1, short index2) {
+    short len = length();
+    if (index1 >= len || index2 >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    short indexPtr1 =
+        Util.getShort(
+            heap,
+            (short)
+                (instanceTable[KM_ARRAY_OFFSET]
+                    + TLV_HEADER_SIZE
+                    + ARRAY_HEADER_SIZE
+                    + (short) (index1 * 2)));
+    short indexPtr2 =
+        Util.getShort(
+            heap,
+            (short)
+                (instanceTable[KM_ARRAY_OFFSET]
+                    + TLV_HEADER_SIZE
+                    + ARRAY_HEADER_SIZE
+                    + (short) (index2 * 2)));
+    Util.setShort(
+        heap,
+        (short)
+            (instanceTable[KM_ARRAY_OFFSET]
+                + TLV_HEADER_SIZE
+                + ARRAY_HEADER_SIZE
+                + (short) (index1 * 2)),
+        indexPtr2);
+    Util.setShort(
+        heap,
+        (short)
+            (instanceTable[KM_ARRAY_OFFSET]
+                + TLV_HEADER_SIZE
+                + ARRAY_HEADER_SIZE
+                + (short) (index2 * 2)),
+        indexPtr1);
+  }
+
+  public short containedType() {
+    return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short getStartOff() {
+    return (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE);
+  }
+
+  public short length() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short setLength(short len) {
+    return Util.setShort(
+        heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), len);
+  }
+
+  public byte[] getBuffer() {
+    return heap;
+  }
+
+  public void deleteLastEntry() {
+    short len = length();
+    Util.setShort(
+        heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), (short) (len - 1));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java
new file mode 100644
index 0000000..65ca94f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java
@@ -0,0 +1,434 @@
+package com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+public class KMAsn1Parser {
+
+  public static final byte ASN1_OCTET_STRING = 0x04;
+  public static final byte ASN1_SEQUENCE = 0x30;
+  public static final byte ASN1_SET = 0x31;
+  public static final byte ASN1_INTEGER = 0x02;
+  public static final byte OBJECT_IDENTIFIER = 0x06;
+  public static final byte ASN1_A0_TAG = (byte) 0xA0;
+  public static final byte ASN1_A1_TAG = (byte) 0xA1;
+  public static final byte ASN1_BIT_STRING = 0x03;
+
+  public static final byte ASN1_UTF8_STRING = 0x0C;
+  public static final byte ASN1_TELETEX_STRING = 0x14;
+  public static final byte ASN1_PRINTABLE_STRING = 0x13;
+  public static final byte ASN1_UNIVERSAL_STRING = 0x1C;
+  public static final byte ASN1_BMP_STRING = 0x1E;
+  public static final byte IA5_STRING = 0x16;
+  public static final byte[] EC_CURVE = {
+    0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, 0x01, 0x07
+  };
+  public static final byte[] RSA_ALGORITHM = {
+      0x06,0x09,0x2A,(byte)0x86,0x48,(byte)0x86,
+      (byte)0xF7,0x0D,0x01,0x01,0x01,0x05,0x00
+  };
+  public static final byte[] EC_ALGORITHM = {
+      0x06,0x07,0x2a,(byte)0x86,0x48,(byte)0xce,
+      0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48,
+      (byte)0xce,0x3d,0x03,0x01,0x07
+  };
+  public static final short MAX_EMAIL_ADD_LEN = 255;
+  private static final byte DATA_START_OFFSET = 0;
+  private static final byte DATA_LENGTH_OFFSET = 1;
+  private static final byte DATA_CURSOR_OFFSET = 2;
+  // This array contains the last byte of OID for each oid type.
+  // The first 4 bytes are common as shown above in COMMON_OID
+  private static final byte[] attributeOIds = {
+    0x03, /* commonName COMMON_OID.3 */ 0x04, /* surName COMMON_OID.4*/
+    0x05, /* serialNumber COMMON_OID.5 */ 0x06, /* countryName COMMON_OID.6 */
+    0x07, /* locality COMMON_OID.7 */ 0x08, /* stateOrProviince COMMON_OID.8 */
+    0x0A, /* organizationName COMMON_OID.10 */ 0x0B, /* organizationalUnitName COMMON_OID.11 */
+    0x0C, /* title COMMON_OID.10 */ 0x29, /* name COMMON_OID.41 */
+    0x2A, /* givenName COMMON_OID.42 */ 0x2B, /* initials COMMON_OID.43 */
+    0x2C, /* generationQualifier COMMON_OID.44 */ 0x2E, /* dnQualifer COMMON_OID.46 */
+    0x41, /* pseudonym COMMON_OID.65 */
+  };
+  // https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 124
+  // TODO Specification does not mention about the DN_QUALIFIER_OID max length.
+  // So the max limit is set at 64.
+  // For name the RFC 5280 supports up to 32768, as Javacard doesn't support
+  // that much length, the max limit for name is set to 128.
+  private static final byte[] attributeValueMaxLen = {
+    0x40, /* 1-64 commonName */
+    0x28, /* 1-40 surname */
+    0x40, /* 1-64 serial */
+    0x02, /* 1-2 country */
+    (byte) 0x80, /* 1-128 locality */
+    (byte) 0x80, /* 1-128 state */
+    0x40, /* 1-64 organization */
+    0x40, /* 1-64 organization unit*/
+    0x40, /* 1-64 title */
+    0x29, /* 1-128 name */
+    0x10, /* 1-16 givenName */
+    0x05, /* 1-5 initials */
+    0x03, /* 1-3 gen qualifier */
+    0x40, /* 1-64 dn-qualifier */
+    (byte) 0x80 /* 1-128 pseudonym */
+  };
+  private static KMAsn1Parser inst;
+  // https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 21
+  // 2.5.4
+  public byte[] COMMON_OID = new byte[] {0x06, 0x03, 0x55, 0x04};
+  public byte[] EMAIL_ADDRESS_OID =
+      new byte[] {
+        0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x09, 0x01
+      };
+  private byte[] data;
+  private short[] dataInfo;
+
+  private KMAsn1Parser() {
+    dataInfo = JCSystem.makeTransientShortArray((short) 3, JCSystem.CLEAR_ON_RESET);
+    dataInfo[DATA_START_OFFSET] = 0;
+    dataInfo[DATA_LENGTH_OFFSET] = 0;
+    dataInfo[DATA_CURSOR_OFFSET] = 0;
+  }
+
+  public static KMAsn1Parser instance() {
+    if (inst == null) {
+      inst = new KMAsn1Parser();
+    }
+    return inst;
+  }
+
+  public short decodeRsa(short blob) {
+    init(blob);
+    decodeCommon((short) 0, RSA_ALGORITHM);
+    return decodeRsaPrivateKey((short) 0);
+  }
+
+  public short decodeEc(short blob) {
+    init(blob);
+    decodeCommon((short) 0, EC_ALGORITHM);
+    return decodeEcPrivateKey((short) 1);
+  }
+
+  /*
+     Name ::= CHOICE { -- only one possibility for now --
+         rdnSequence  RDNSequence }
+     RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+     RelativeDistinguishedName ::=
+         SET SIZE (1..MAX) OF AttributeTypeAndValue
+     AttributeTypeAndValue ::= SEQUENCE {
+       type     AttributeType,
+       value    AttributeValue }
+     AttributeType ::= OBJECT IDENTIFIER
+     AttributeValue ::= ANY -- DEFINED BY AttributeType
+  */
+  public void validateDerSubject(short blob) {
+    init(blob);
+    header(ASN1_SEQUENCE);
+    while (dataInfo[DATA_CURSOR_OFFSET]
+        < ((short) (dataInfo[DATA_START_OFFSET] + dataInfo[DATA_LENGTH_OFFSET]))) {
+      header(ASN1_SET);
+      header(ASN1_SEQUENCE);
+      // Parse and validate OBJECT-IDENTIFIER and Value fields
+      // Cursor is incremented in validateAttributeTypeAndValue.
+      validateAttributeTypeAndValue();
+    }
+  }
+
+  public short decodeEcSubjectPublicKeyInfo(short blob) {
+    init(blob);
+    header(ASN1_SEQUENCE);
+    short len = header(ASN1_SEQUENCE);
+    short ecPublicInfo = KMByteBlob.instance(len);
+    getBytes(ecPublicInfo);
+    if (Util.arrayCompare(
+            KMByteBlob.cast(ecPublicInfo).getBuffer(),
+            KMByteBlob.cast(ecPublicInfo).getStartOff(),
+            EC_ALGORITHM,
+            (short) 0,
+            KMByteBlob.cast(ecPublicInfo).length())
+        != 0) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    len = header(ASN1_BIT_STRING);
+    if (len < 1) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    // TODO need to handle if unused bits are not zero
+    byte unusedBits = getByte();
+    if (unusedBits != 0) {
+      KMException.throwIt(KMError.UNIMPLEMENTED);
+    }
+    short pubKey = KMByteBlob.instance((short) (len - 1));
+    getBytes(pubKey);
+    return pubKey;
+  }
+
+  // Seq[Int,Int,Int,Int,<ignore rest>]
+  public short decodeRsaPrivateKey(short version) {
+    short resp = KMArray.instance((short) 3);
+    header(ASN1_OCTET_STRING);
+    header(ASN1_SEQUENCE);
+    short len = header(ASN1_INTEGER);
+    if (len != 1) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    short ver = getByte();
+    if (ver != version) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    len = header(ASN1_INTEGER);
+    short modulus = KMByteBlob.instance(len);
+    getBytes(modulus);
+    updateRsaKeyBuffer(modulus);
+    len = header(ASN1_INTEGER);
+    short pubKey = KMByteBlob.instance(len);
+    getBytes(pubKey);
+    len = header(ASN1_INTEGER);
+    short privKey = KMByteBlob.instance(len);
+    getBytes(privKey);
+    updateRsaKeyBuffer(privKey);
+    KMArray.cast(resp).add((short) 0, modulus);
+    KMArray.cast(resp).add((short) 1, pubKey);
+    KMArray.cast(resp).add((short) 2, privKey);
+    return resp;
+  }
+
+  private void updateRsaKeyBuffer(short blob) {
+    byte[] buffer = KMByteBlob.cast(blob).getBuffer();
+    short startOff = KMByteBlob.cast(blob).getStartOff();
+    short len = KMByteBlob.cast(blob).length();
+    if (0 == buffer[startOff] && len > 256) {
+      KMByteBlob.cast(blob).setStartOff(++startOff);
+      KMByteBlob.cast(blob).setLength(--len);
+    }
+  }
+
+  private short readEcdsa256SigIntegerHeader() {
+    short len = header(ASN1_INTEGER);
+    if (len == 33) {
+      if (0 != getByte()) {
+        KMException.throwIt(KMError.INVALID_DATA);
+      }
+      len--;
+    } else if (len > 33) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return len;
+  }
+
+  // Seq [Int, Int]
+  public short decodeEcdsa256Signature(short blob, byte[] scratchPad, short scratchPadOff) {
+    init(blob);
+    short len = header(ASN1_SEQUENCE);
+    len = readEcdsa256SigIntegerHeader();
+    // concatenate r and s in the buffer (r||s)
+    Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 64, (byte) 0);
+    // read r
+    getBytes(scratchPad, (short) (scratchPadOff + 32 - len), len);
+    len = readEcdsa256SigIntegerHeader();
+    // read s
+    getBytes(scratchPad, (short) (scratchPadOff + 64 - len), len);
+    return (short) 64;
+  }
+
+  // Seq [Int, Blob]
+  public void decodeCommon(short version, byte[] alg) {
+    short len = header(ASN1_SEQUENCE);
+    len = header(ASN1_INTEGER);
+    if (len != 1) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    short ver = getByte();
+    if (ver != version) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    len = header(ASN1_SEQUENCE);
+    short blob = KMByteBlob.instance(len);
+    getBytes(blob);
+    if (Util.arrayCompare(
+            KMByteBlob.cast(blob).getBuffer(),
+            KMByteBlob.cast(blob).getStartOff(),
+            alg,
+            (short) 0,
+            KMByteBlob.cast(blob).length())
+        != 0) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+  }
+
+  // Seq[Int,blob,blob]
+  public short decodeEcPrivateKey(short version) {
+    short resp = KMArray.instance((short) 2);
+    header(ASN1_OCTET_STRING);
+    header(ASN1_SEQUENCE);
+    short len = header(ASN1_INTEGER);
+    if (len != 1) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    short ver = getByte();
+    if (ver != version) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    len = header(ASN1_OCTET_STRING);
+    short privKey = KMByteBlob.instance(len);
+    getBytes(privKey);
+    validateTag0IfPresent();
+    header(ASN1_A1_TAG);
+    len = header(ASN1_BIT_STRING);
+    if (len < 1) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    // TODO need to handle if unused bits are not zero
+    byte unusedBits = getByte();
+    if (unusedBits != 0) {
+      KMException.throwIt(KMError.UNIMPLEMENTED);
+    }
+    short pubKey = KMByteBlob.instance((short) (len - 1));
+    getBytes(pubKey);
+    KMArray.cast(resp).add((short) 0, pubKey);
+    KMArray.cast(resp).add((short) 1, privKey);
+    return resp;
+  }
+
+  private void validateTag0IfPresent() {
+    if (data[dataInfo[DATA_CURSOR_OFFSET]] != ASN1_A0_TAG) {
+      return;
+    }
+    ;
+    short len = header(ASN1_A0_TAG);
+    if (len != EC_CURVE.length) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    if (Util.arrayCompare(data, dataInfo[DATA_CURSOR_OFFSET], EC_CURVE, (short) 0, len) != 0) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    incrementCursor(len);
+  }
+
+  private void validateAttributeTypeAndValue() {
+    // First byte should be OBJECT_IDENTIFIER, otherwise it is not well-formed DER Subject.
+    if (data[dataInfo[DATA_CURSOR_OFFSET]] != OBJECT_IDENTIFIER) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    // Check if the OID matches the email address
+    if ((Util.arrayCompare(
+            data,
+            dataInfo[DATA_CURSOR_OFFSET],
+            EMAIL_ADDRESS_OID,
+            (short) 0,
+            (short) EMAIL_ADDRESS_OID.length)
+        == 0)) {
+      incrementCursor((short) EMAIL_ADDRESS_OID.length);
+      // Validate the length of the attribute value.
+      if (getByte() != IA5_STRING) {
+        KMException.throwIt(KMError.UNKNOWN_ERROR);
+      }
+      short emailLength = getLength();
+      if (emailLength <= 0 && emailLength > MAX_EMAIL_ADD_LEN) {
+        KMException.throwIt(KMError.UNKNOWN_ERROR);
+      }
+      incrementCursor(emailLength);
+      return;
+    }
+    // Check other OIDs.
+    for (short i = 0; i < (short) attributeOIds.length; i++) {
+      if ((Util.arrayCompare(
+                  data,
+                  dataInfo[DATA_CURSOR_OFFSET],
+                  COMMON_OID,
+                  (short) 0,
+                  (short) COMMON_OID.length)
+              == 0)
+          && (attributeOIds[i]
+              == data[(short) (dataInfo[DATA_CURSOR_OFFSET] + COMMON_OID.length)])) {
+        incrementCursor((short) (COMMON_OID.length + 1));
+        // Validate the length of the attribute value.
+        short tag = getByte();
+        if (tag != ASN1_UTF8_STRING
+            && tag != ASN1_TELETEX_STRING
+            && tag != ASN1_PRINTABLE_STRING
+            && tag != ASN1_UNIVERSAL_STRING
+            && tag != ASN1_BMP_STRING) {
+          KMException.throwIt(KMError.UNKNOWN_ERROR);
+        }
+        short attrValueLength = getLength();
+        if (attrValueLength <= 0 && attrValueLength > attributeValueMaxLen[i]) {
+          KMException.throwIt(KMError.UNKNOWN_ERROR);
+        }
+        incrementCursor(attrValueLength);
+        return;
+      }
+    }
+    // If no match is found above then move the cursor to next element.
+    getByte(); // Move Cursor by one byte (OID)
+    incrementCursor(getLength()); // Move cursor to AtrributeTag
+    getByte(); // Move cursor to AttributeValue
+    incrementCursor(getLength()); // Move cursor to next SET element
+  }
+
+  private short header(short tag) {
+    short t = getByte();
+    if (t != tag) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    return getLength();
+  }
+
+  private byte getByte() {
+    byte d = data[dataInfo[DATA_CURSOR_OFFSET]];
+    incrementCursor((short) 1);
+    return d;
+  }
+
+  private short getShort() {
+    short d = Util.getShort(data, dataInfo[DATA_CURSOR_OFFSET]);
+    incrementCursor((short) 2);
+    return d;
+  }
+
+  private void getBytes(short blob) {
+    short len = KMByteBlob.cast(blob).length();
+    Util.arrayCopyNonAtomic(
+        data,
+        dataInfo[DATA_CURSOR_OFFSET],
+        KMByteBlob.cast(blob).getBuffer(),
+        KMByteBlob.cast(blob).getStartOff(),
+        len);
+    incrementCursor(len);
+  }
+
+  private void getBytes(byte[] buffer, short offset, short len) {
+    Util.arrayCopyNonAtomic(data, dataInfo[DATA_CURSOR_OFFSET], buffer, offset, len);
+    incrementCursor(len);
+  }
+
+  private short getLength() {
+    byte len = getByte();
+    if (len >= 0) {
+      return len;
+    }
+    len = (byte) (len & 0x7F);
+    if (len == 1) {
+      return (short) (getByte() & 0xFF);
+    } else if (len == 2) {
+      return getShort();
+    } else {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    return KMType.INVALID_VALUE; // should not come here
+  }
+
+  public void init(short blob) {
+    data = KMByteBlob.cast(blob).getBuffer();
+    dataInfo[DATA_START_OFFSET] = KMByteBlob.cast(blob).getStartOff();
+    dataInfo[DATA_LENGTH_OFFSET] = KMByteBlob.cast(blob).length();
+    dataInfo[DATA_CURSOR_OFFSET] = dataInfo[DATA_START_OFFSET];
+  }
+
+  public void incrementCursor(short n) {
+    dataInfo[DATA_CURSOR_OFFSET] += n;
+    if (dataInfo[DATA_CURSOR_OFFSET]
+        > ((short) (dataInfo[DATA_START_OFFSET] + dataInfo[DATA_LENGTH_OFFSET]))) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBignumTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBignumTag.java
new file mode 100644
index 0000000..5eb7eae
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBignumTag.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMBignumTag represents BIGNUM Tag Type from android keymaster hal specifications. The tag value
+ * of this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte
+ * TAG_TYPE; short length; struct{short BIGNUM_TAG; short tagKey; short blobPtr}}
+ */
+public class KMBignumTag extends KMTag {
+
+  private static KMBignumTag prototype;
+
+  private KMBignumTag() {}
+
+  private static KMBignumTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMBignumTag();
+    }
+    KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short blobPtr = KMByteBlob.exp();
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+    return ptr;
+  }
+
+  public static short instance(short key, short byteBlob) {
+    if (!validateKey(key, byteBlob)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (heap[byteBlob] != BYTE_BLOB_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+    return ptr;
+  }
+
+  public static KMBignumTag cast(short ptr) {
+    if (heap[ptr] != TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BIGNUM_TAG) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  private static boolean validateKey(short key, short byteBlob) {
+    short valueLen = KMByteBlob.cast(byteBlob).length();
+    switch (key) {
+      case CERTIFICATE_SERIAL_NUM:
+        if (valueLen > MAX_CERTIFICATE_SERIAL_SIZE) {
+          return false;
+        }
+        break;
+      default:
+        return false;
+    }
+    return true;
+  }
+
+  public short getKey() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getTagType() {
+    return KMType.BIGNUM_TAG;
+  }
+
+  public short getValue() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+
+  public short length() {
+    short blobPtr =
+        Util.getShort(
+            heap, (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+    return KMByteBlob.cast(blobPtr).length();
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBoolTag.java
new file mode 100644
index 0000000..27730a5
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMBoolTag.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMBoolTag represents BOOL TAG type from the android keymaster hal specifications. If it is
+ * present in the key parameter list then its value is always true. A KMTag always requires a value
+ * because it is a key value pair. The bool tag always has 0x01 as its value. struct{byte TAG_TYPE;
+ * short length; struct{short BOOL_TAG; short tagKey; byte value 1}}
+ */
+public class KMBoolTag extends KMTag {
+
+  // The allowed tag keys of type bool tag.
+  private static final short[] tags = {
+    CALLER_NONCE,
+    INCLUDE_UNIQUE_ID,
+    BOOTLOADER_ONLY,
+    ROLLBACK_RESISTANCE,
+    NO_AUTH_REQUIRED,
+    ALLOW_WHILE_ON_BODY,
+    TRUSTED_USER_PRESENCE_REQUIRED,
+    TRUSTED_CONFIRMATION_REQUIRED,
+    UNLOCKED_DEVICE_REQUIRED,
+    RESET_SINCE_ID_ROTATION,
+    EARLY_BOOT_ONLY,
+    DEVICE_UNIQUE_ATTESTATION
+  };
+  private static KMBoolTag prototype;
+
+  private KMBoolTag() {}
+
+  private static KMBoolTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMBoolTag();
+    }
+    KMType.instanceTable[KM_BOOL_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(TAG_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG);
+    return ptr;
+  }
+
+  public static short instance(short key) {
+    if (!validateKey(key)) {
+      KMException.throwIt(KMError.INVALID_TAG);
+    }
+    short ptr = KMType.instance(TAG_TYPE, (short) 5);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    // Value is always 1.
+    heap[(short) (ptr + TLV_HEADER_SIZE + 4)] = 0x01;
+    return ptr;
+  }
+
+  public static KMBoolTag cast(short ptr) {
+    if (heap[ptr] != TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BOOL_TAG) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  // validate the tag key.
+  private static boolean validateKey(short key) {
+    short index = (short) tags.length;
+    while (--index >= 0) {
+      if (tags[index] == key) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  public static short[] getTags() {
+    return tags;
+  }
+
+  public short getKey() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getTagType() {
+    return KMType.BOOL_TAG;
+  }
+
+  public byte getVal() {
+    return heap[(short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 4)];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteBlob.java
new file mode 100644
index 0000000..98d81fc
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteBlob.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMByteBlob represents contiguous block of bytes. It corresponds to CBOR type of Byte String. It
+ * extends KMType by specifying value field as zero or more sequence of bytes. struct{byte
+ * BYTE_BLOB_TYPE; short length; sequence of bytes}
+ */
+public class KMByteBlob extends KMType {
+
+  private static byte OFFSET_SIZE = 2;
+  private static KMByteBlob prototype;
+
+  protected KMByteBlob() {}
+
+  private static KMByteBlob proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMByteBlob();
+    }
+    KMType.instanceTable[KM_BYTE_BLOB_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    return KMType.exp(BYTE_BLOB_TYPE);
+  }
+
+  // return an empty byte blob instance
+  public static short instance(short length) {
+    short ptr = KMType.instance(BYTE_BLOB_TYPE, (short) (length + 2));
+    Util.setShort(
+        heap, (short) (ptr + TLV_HEADER_SIZE), (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE));
+    Util.setShort(heap, (short) (ptr + 1), length);
+    return ptr;
+  }
+
+  // byte blob from existing buf
+  public static short instance(byte[] buf, short startOff, short length) {
+    short ptr = instance(length);
+    Util.arrayCopyNonAtomic(
+        buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), length);
+    return ptr;
+  }
+
+  // cast the ptr to KMByteBlob
+  public static KMByteBlob cast(short ptr) {
+    if (heap[ptr] != BYTE_BLOB_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  // Add the byte
+  public void add(short index, byte val) {
+    short len = length();
+    if (index >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    heap[(short) (getStartOff() + index)] = val;
+  }
+
+  // Get the byte
+  public byte get(short index) {
+    short len = length();
+    if (index >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    return heap[(short) (getStartOff() + index)];
+  }
+
+  // Get the start of blob
+  public short getStartOff() {
+    return Util.getShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE));
+  }
+
+  public void setStartOff(short offset) {
+    Util.setShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE), offset);
+  }
+
+  // Get the length of the blob
+  public short length() {
+    return Util.getShort(heap, (short) (getBaseOffset() + 1));
+  }
+
+  // Get the buffer pointer in which blob is contained.
+  public byte[] getBuffer() {
+    return heap;
+  }
+
+  public void getValue(byte[] destBuf, short destStart, short destLength) {
+    Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength);
+  }
+
+  public short getValues(byte[] destBuf, short destStart) {
+    short destLength = length();
+    Util.arrayCopyNonAtomic(heap, getStartOff(), destBuf, destStart, destLength);
+    return destLength;
+  }
+
+  public void setValue(byte[] srcBuf, short srcStart, short srcLength) {
+    if (length() < srcLength) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), srcLength);
+    setLength(srcLength);
+  }
+
+  public boolean isValid() {
+    return (length() != 0);
+  }
+
+  protected short getBaseOffset() {
+    return instanceTable[KM_BYTE_BLOB_OFFSET];
+  }
+
+  public void setLength(short len) {
+    Util.setShort(heap, (short) (getBaseOffset() + 1), len);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteTag.java
new file mode 100644
index 0000000..ac49c0e
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMByteTag.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMByteTag represents BYTES Tag Type from android keymaster hal specifications. The tag value of
+ * this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte
+ * TAG_TYPE; short length; struct{short BYTES_TAG; short tagKey; short blobPtr}}
+ */
+public class KMByteTag extends KMTag {
+
+  private static KMByteTag prototype;
+
+  private KMByteTag() {}
+
+  private static KMByteTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMByteTag();
+    }
+    KMType.instanceTable[KM_BYTE_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short blobPtr = KMByteBlob.exp();
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BYTES_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+    return ptr;
+  }
+
+  public static short instance(short key, short byteBlob) {
+    if (!validateKey(key, byteBlob)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (heap[byteBlob] != BYTE_BLOB_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BYTES_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+    return ptr;
+  }
+
+  public static KMByteTag cast(short ptr) {
+    if (heap[ptr] != TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BYTES_TAG) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  private static boolean validateKey(short key, short byteBlob) {
+    short valueLen = KMByteBlob.cast(byteBlob).length();
+    switch (key) {
+      case ATTESTATION_APPLICATION_ID:
+        if (valueLen > MAX_ATTESTATION_APP_ID_SIZE) {
+          return false;
+        }
+        break;
+      case CERTIFICATE_SUBJECT_NAME:
+        {
+          if (valueLen > KMConfigurations.MAX_SUBJECT_DER_LEN) {
+            return false;
+          }
+          KMAsn1Parser asn1Decoder = KMAsn1Parser.instance();
+          asn1Decoder.validateDerSubject(byteBlob);
+        }
+        break;
+      case APPLICATION_ID:
+      case APPLICATION_DATA:
+        if (valueLen > MAX_APP_ID_APP_DATA_SIZE) {
+          return false;
+        }
+        break;
+      case ATTESTATION_CHALLENGE:
+        if (valueLen > MAX_ATTESTATION_CHALLENGE_SIZE) {
+          return false;
+        }
+        break;
+      case ATTESTATION_ID_BRAND:
+      case ATTESTATION_ID_DEVICE:
+      case ATTESTATION_ID_PRODUCT:
+      case ATTESTATION_ID_SERIAL:
+      case ATTESTATION_ID_IMEI:
+      case ATTESTATION_ID_MEID:
+      case ATTESTATION_ID_MANUFACTURER:
+      case ATTESTATION_ID_MODEL:
+        if (valueLen > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) {
+          return false;
+        }
+        break;
+      case ROOT_OF_TRUST:
+      case NONCE:
+        break;
+      default:
+        return false;
+    }
+    return true;
+  }
+
+  public short getKey() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getTagType() {
+    return KMType.BYTES_TAG;
+  }
+
+  public short getValue() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+
+  public short length() {
+    short blobPtr =
+        Util.getShort(
+            heap, (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+    return KMByteBlob.cast(blobPtr).length();
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCose.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCose.java
new file mode 100644
index 0000000..1eb8816
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCose.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+
+/**
+ * This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, CoseSign1,
+ * SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures.
+ */
+public class KMCose {
+
+  // COSE SIGN1
+  public static final byte COSE_SIGN1_ENTRY_COUNT = 4;
+  public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0;
+  public static final byte COSE_SIGN1_PAYLOAD_OFFSET = 2;
+  public static final byte COSE_SIGN1_SIGNATURE_OFFSET = 3;
+  // COSE MAC0
+  public static final byte COSE_MAC0_ENTRY_COUNT = 4;
+  public static final byte COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0;
+  public static final byte COSE_MAC0_PAYLOAD_OFFSET = 2;
+  public static final byte COSE_MAC0_TAG_OFFSET = 3;
+  // COSE ENCRYPT
+  public static final byte COSE_ENCRYPT_ENTRY_COUNT = 4;
+  public static final byte COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3;
+  public static final byte COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3;
+
+  // COSE Labels
+  public static final byte COSE_LABEL_ALGORITHM = 1;
+  public static final byte COSE_LABEL_KEYID = 4;
+  public static final byte COSE_LABEL_IV = 5;
+  public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1
+
+  // COSE Algorithms
+  public static final byte COSE_ALG_AES_GCM_256 = 3; // AES-GCM mode w/ 256-bit key, 128-bit tag.
+  public static final byte COSE_ALG_HMAC_256 = 5; // HMAC w/ SHA-256
+  public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7
+  public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25
+
+  // COSE P256 EC Curve
+  public static final byte COSE_ECCURVE_256 = 1;
+
+  // COSE key types
+  public static final byte COSE_KEY_TYPE_EC2 = 2;
+  public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4;
+
+  // COSE Key Operations
+  public static final byte COSE_KEY_OP_SIGN = 1;
+  public static final byte COSE_KEY_OP_VERIFY = 2;
+  public static final byte COSE_KEY_OP_ENCRYPT = 3;
+  public static final byte COSE_KEY_OP_DECRYPT = 4;
+
+  // AES GCM
+  public static final short AES_GCM_KEY_SIZE_BITS = 256;
+  // Cose key parameters.
+  public static final short COSE_KEY_KEY_TYPE = 1;
+  public static final short COSE_KEY_KEY_ID = 2;
+  public static final short COSE_KEY_ALGORITHM = 3;
+  public static final short COSE_KEY_KEY_OPS = 4;
+  public static final short COSE_KEY_CURVE = -1;
+  public static final short COSE_KEY_PUBKEY_X = -2;
+  public static final short COSE_KEY_PUBKEY_Y = -3;
+  public static final short COSE_KEY_PRIV_KEY = -4;
+  public static final byte[] COSE_TEST_KEY = {
+    (byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90
+  }; // -70000
+  public static final byte COSE_KEY_MAX_SIZE = 4;
+
+  // kdfcontext strings
+  public static final byte[] client = {0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74};
+  public static final byte[] server = {0x73, 0x65, 0x72, 0x76, 0x65, 0x72};
+  // Context strings
+  public static final byte[] MAC_CONTEXT = {0x4d, 0x41, 0x43, 0x30}; // MAC0
+  public static final byte[] SIGNATURE1_CONTEXT = {
+    0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31
+  }; // Signature1
+  public static final byte[] ENCRYPT_CONTEXT = {
+    0x45, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74
+  }; // Encrypt
+  // Certificate payload supported keys
+  public static final byte ISSUER = (byte) 0x01;
+  public static final byte SUBJECT = (byte) 0x02;
+  public static final byte[] SUBJECT_PUBLIC_KEY = {
+    (byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8
+  };
+  public static final byte[] KEY_USAGE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7};
+  // text strings
+  public static final byte[] TEST_ISSUER_NAME = {
+    (byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72
+  }; // "Issuer"
+  public static final byte[] TEST_SUBJECT_NAME = {
+    0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74
+  }; // "Subject"
+  public static final byte[] KEY_USAGE_SIGN = {0x20}; // Key usage sign
+
+  public static final short[] COSE_KEY_LABELS = {
+    KMCose.COSE_KEY_KEY_TYPE,
+    KMCose.COSE_KEY_KEY_ID,
+    KMCose.COSE_KEY_ALGORITHM,
+    KMCose.COSE_KEY_KEY_OPS,
+    KMCose.COSE_KEY_CURVE,
+    KMCose.COSE_KEY_PUBKEY_X,
+    KMCose.COSE_KEY_PUBKEY_Y,
+    KMCose.COSE_KEY_PRIV_KEY
+  };
+  public static final short[] COSE_HEADER_LABELS = {
+    KMCose.COSE_LABEL_ALGORITHM,
+    KMCose.COSE_LABEL_KEYID,
+    KMCose.COSE_LABEL_IV,
+    KMCose.COSE_LABEL_COSE_KEY
+  };
+
+  /**
+   * Constructs the Cose MAC structure.
+   *
+   * @param protectedHeader Bstr pointer which holds the protected header.
+   * @param extAad Bstr pointer which holds the external Aad.
+   * @param payload Bstr pointer which holds the payload of the MAC structure.
+   * @return KMArray instance of MAC structure.
+   */
+  public static short constructCoseMacStructure(
+      short protectedHeader, short extAad, short payload) {
+    // Create MAC Structure and compute HMAC as per https://tools.ietf.org/html/rfc8152#section-6.3
+    //    MAC_structure = [
+    //        context : "MAC" / "MAC0",
+    //        protected : empty_or_serialized_map,
+    //        external_aad : bstr,
+    //        payload : bstr
+    //   ]
+    short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
+    // 1 - Context
+    KMArray.cast(arrPtr)
+        .add(
+            (short) 0,
+            KMTextString.instance(
+                KMCose.MAC_CONTEXT, (short) 0, (short) KMCose.MAC_CONTEXT.length));
+    // 2 - Protected headers.
+    KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+    // 3 - external aad
+    KMArray.cast(arrPtr).add((short) 2, extAad);
+    // 4 - payload.
+    KMArray.cast(arrPtr).add((short) 3, payload);
+    return arrPtr;
+  }
+
+  /**
+   * Constructs the COSE_MAC0 object.
+   *
+   * @param protectedHeader Bstr pointer which holds the protected header.
+   * @param unprotectedHeader Bstr pointer which holds the unprotected header.
+   * @param payload Bstr pointer which holds the payload of the MAC structure.
+   * @param tag Bstr pointer which holds the tag value.
+   * @return KMArray instance of COSE_MAC0 object.
+   */
+  public static short constructCoseMac0(
+      short protectedHeader, short unprotectedHeader, short payload, short tag) {
+    // Construct Cose_MAC0
+    //   COSE_Mac0 = [
+    //      protectedHeader,
+    //      unprotectedHeader,
+    //      payload : bstr / nil,
+    //      tag : bstr,
+    //   ]
+    short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT);
+    // 1 - protected headers
+    KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+    // 2 - unprotected headers
+    KMArray.cast(arrPtr).add((short) 1, unprotectedHeader);
+    // 2 - payload
+    KMArray.cast(arrPtr).add((short) 2, payload);
+    // 3 - tag
+    KMArray.cast(arrPtr).add((short) 3, tag);
+    // Do encode.
+    return arrPtr;
+  }
+
+  /**
+   * Constructs the COSE_Signature structure.
+   *
+   * @param protectedHeader Bstr pointer which holds the protected header.
+   * @param extAad Bstr pointer which holds the aad.
+   * @param payload Bstr pointer which holds the payload.
+   * @return KMArray instance of COSE_Signature object.
+   */
+  public static short constructCoseSignStructure(
+      short protectedHeader, short extAad, short payload) {
+    // Sig_structure = [
+    //       context : "Signature" / "Signature1" / "CounterSignature",
+    //       body_protected : empty_or_serialized_map,
+    //       ? sign_protected : empty_or_serialized_map,
+    //       external_aad : bstr,
+    //       payload : bstr
+    //   ]
+    short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
+    // 1 - Context
+    KMArray.cast(arrPtr)
+        .add(
+            (short) 0,
+            KMTextString.instance(
+                KMCose.SIGNATURE1_CONTEXT, (short) 0, (short) KMCose.SIGNATURE1_CONTEXT.length));
+    // 2 - Protected headers.
+    KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+    // 3 - external aad
+    KMArray.cast(arrPtr).add((short) 2, extAad);
+    // 4 - payload.
+    KMArray.cast(arrPtr).add((short) 3, payload);
+    return arrPtr;
+  }
+
+  /**
+   * Constructs the COSE_Sign1 object.
+   *
+   * @param protectedHeader Bstr pointer which holds the protected header.
+   * @param unProtectedHeader Bstr pointer which holds the unprotected header.
+   * @param payload Bstr pointer which holds the payload.
+   * @param signature Bstr pointer which holds the signature.
+   * @return KMArray instance of COSE_Sign1 object.
+   */
+  public static short constructCoseSign1(
+      short protectedHeader, short unProtectedHeader, short payload, short signature) {
+    //   COSE_Sign = [
+    //      protectedHeader,
+    //      unprotectedHeader,
+    //       payload : bstr / nil,
+    //       signatures : [+ COSE_Signature]
+    //   ]
+    short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT);
+    // 1 - protected headers
+    KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+    // 2 - unprotected headers
+    KMArray.cast(arrPtr).add((short) 1, unProtectedHeader);
+    // 2 - payload
+    KMArray.cast(arrPtr).add((short) 2, payload);
+    // 3 - tag
+    KMArray.cast(arrPtr).add((short) 3, signature);
+    return arrPtr;
+  }
+
+  /**
+   * Constructs array based on the tag values provided.
+   *
+   * @param tag array of tag values to be constructed.
+   * @param includeTestMode flag which indicates if TEST_COSE_KEY should be included or not.
+   * @return instance of KMArray.
+   */
+  private static short handleCosePairTags(
+      short[] tag, short[] keyValues, short valueIndex, boolean includeTestMode) {
+    short index = 0;
+    // var is used to calculate the length of the array.
+    short var = 0;
+    short tagLen = (short) tag.length;
+    // var is used to calculate the length of the array.
+    while (index < tagLen) {
+      if (keyValues[index] != KMType.INVALID_VALUE) {
+        keyValues[(short) (index + valueIndex)] =
+            buildCosePairTag((byte) tag[index], keyValues[index]);
+        var++;
+      }
+      index++;
+    }
+    var += includeTestMode ? 1 : 0;
+    short arrPtr = KMArray.instance(var);
+    index = 0;
+    // var is used to index the array.
+    var = 0;
+    while (index < tagLen) {
+      if (keyValues[(short) (index + valueIndex)] != KMType.INVALID_VALUE) {
+        KMArray.cast(arrPtr).add(var++, keyValues[(short) (index + valueIndex)]);
+      }
+      index++;
+    }
+    return arrPtr;
+  }
+
+  /**
+   * Constructs the COSE_sign1 payload for certificate.
+   *
+   * @param issuer instance of KMCosePairTextStringTag which contains issuer value.
+   * @param subject instance of KMCosePairTextStringTag which contains subject value.
+   * @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey.
+   * @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value.
+   * @return instance of KMArray.
+   */
+  public static short constructCoseCertPayload(
+      short issuer, short subject, short subPublicKey, short keyUsage) {
+    short certPayload = KMArray.instance((short) 4);
+    KMArray.cast(certPayload).add((short) 0, issuer);
+    KMArray.cast(certPayload).add((short) 1, subject);
+    KMArray.cast(certPayload).add((short) 2, subPublicKey);
+    KMArray.cast(certPayload).add((short) 3, keyUsage);
+    certPayload = KMCoseCertPayload.instance(certPayload);
+    KMCoseCertPayload.cast(certPayload).canonicalize();
+    return certPayload;
+  }
+
+  /**
+   * Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, COSE_Mac0 and
+   * COSE_Key.
+   *
+   * @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value.
+   * @param keyId instance of KMByteBlob which contains the key identifier.
+   * @param iv instance of KMByteblob which contains the iv buffer.
+   * @param ephemeralKey instance of KMCoseKey.
+   * @return instance of KMCoseHeaders.
+   */
+  public static short constructHeaders(
+      short[] buff, short alg, short keyId, short iv, short ephemeralKey) {
+    buff[0] = alg;
+    buff[1] = keyId;
+    buff[2] = iv;
+    buff[3] = ephemeralKey;
+    for (short i = 4; i < 8; i++) {
+      buff[i] = KMType.INVALID_VALUE;
+    }
+    short ptr = handleCosePairTags(COSE_HEADER_LABELS, buff, (short) 4, false);
+    ptr = KMCoseHeaders.instance(ptr);
+    KMCoseHeaders.cast(ptr).canonicalize();
+    return ptr;
+  }
+
+  /**
+   * Construct Recipients structure for COSE_Encrypt message.
+   *
+   * @param protectedHeaders instance of KMByteBlob which contains encoded KMCoseHeaders.
+   * @param unprotectedHeaders instance of KMCoseHeaders.
+   * @param cipherText instance of KMSimple
+   * @return instance of KMArray.
+   */
+  public static short constructRecipientsStructure(
+      short protectedHeaders, short unprotectedHeaders, short cipherText) {
+    // recipients : [+COSE_recipient]
+    //  COSE_recipient = [
+    //       Headers,
+    //       ciphertext : bstr / nil,
+    //       ? recipients : [+COSE_recipient]
+    //   ]
+    short arrPtr = KMArray.instance(COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT);
+    // 1 - protected headers
+    KMArray.cast(arrPtr).add((short) 0, protectedHeaders);
+    // 2 - unprotected headers
+    KMArray.cast(arrPtr).add((short) 1, unprotectedHeaders);
+    // 2 - payload
+    KMArray.cast(arrPtr).add((short) 2, cipherText);
+
+    short recipientsArrayPtr = KMArray.instance((short) 1);
+    KMArray.cast(recipientsArrayPtr).add((short) 0, arrPtr);
+    return recipientsArrayPtr;
+  }
+
+  /**
+   * Construct Encrypt structure required for COSE_Encrypt message.
+   *
+   * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders.
+   * @param aad instance of KMByteBlob.
+   * @return instance of KMArray.
+   */
+  public static short constructCoseEncryptStructure(short protectedHeader, short aad) {
+    //  Enc_structure = [
+    //       context : "Encrypt" / "Encrypt0" / "Enc_Recipient" /
+    //           "Mac_Recipient" / "Rec_Recipient",
+    //       protected : empty_or_serialized_map,
+    //       external_aad : bstr
+    //   ]
+    short arrPtr = KMArray.instance(COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT);
+    // 1 - protected headers
+    KMArray.cast(arrPtr)
+        .add(
+            (short) 0,
+            KMTextString.instance(
+                KMCose.ENCRYPT_CONTEXT, (short) 0, (short) KMCose.ENCRYPT_CONTEXT.length));
+    // 2 - unprotected headers
+    KMArray.cast(arrPtr).add((short) 1, protectedHeader);
+    // 2 - payload
+    KMArray.cast(arrPtr).add((short) 2, aad);
+    return arrPtr;
+  }
+
+  /**
+   * Constructs COSE_Encrypt message.
+   *
+   * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders.
+   * @param unProtectedHeader instance of KMCoseHeaders.
+   * @param cipherText instance of KMByteBlob containing the cipher text.
+   * @param recipients instance of KMArray containing the recipients instance
+   * @return instance of KMArray.
+   */
+  public static short constructCoseEncrypt(
+      short protectedHeader, short unProtectedHeader, short cipherText, short recipients) {
+    // COSE_Encrypt = [
+    //      protectedHeader,
+    //      unprotectedHeader,
+    //       ciphertext : bstr / nil,
+    //       recipients : [+COSE_recipient]
+    //   ]
+    short arrPtr = KMArray.instance(KMCose.COSE_ENCRYPT_ENTRY_COUNT);
+    // 1 - protected headers
+    KMArray.cast(arrPtr).add((short) 0, protectedHeader);
+    // 2 - unprotected headers
+    KMArray.cast(arrPtr).add((short) 1, unProtectedHeader);
+    // 2 - payload
+    KMArray.cast(arrPtr).add((short) 2, cipherText);
+    // 3 - tag
+    KMArray.cast(arrPtr).add((short) 3, recipients);
+    return arrPtr;
+  }
+
+  /**
+   * Constructs the instance of KMCosePair*Tag.
+   *
+   * @param key value of the key.
+   * @param valuePtr instance of one of KMType.
+   * @return instance of KMCosePair*Value object.
+   */
+  public static short buildCosePairTag(byte key, short valuePtr) {
+    short type = KMType.getType(valuePtr);
+    short keyPtr;
+    if (key < 0) {
+      keyPtr = KMNInteger.uint_8(key);
+    } else {
+      keyPtr = KMInteger.uint_8(key);
+    }
+    switch (type) {
+      case KMType.INTEGER_TYPE:
+        return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
+      case KMType.NEG_INTEGER_TYPE:
+        return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
+      case KMType.BYTE_BLOB_TYPE:
+        return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
+      case KMType.TEXT_STRING_TYPE:
+        return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
+      case KMType.COSE_KEY_TYPE:
+        return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+        return 0;
+    }
+  }
+
+  /**
+   * Constructs a CoseKey with the provided input paramters.
+   *
+   * @param keyType Instance of the identification of the key type.
+   * @param keyId Instance of key identification value.
+   * @param keyAlg Instance of the algorithm that is used with this key.
+   * @param keyOps Instance of the operation that this key is used for.
+   * @param curve Instance of the EC curve that is used with this key.
+   * @param pubKey Buffer containing the public key.
+   * @param pubKeyOff Start offset of the buffer.
+   * @param pubKeyLen Length of the public key.
+   * @param privKeyPtr Instance of the private key.
+   * @param testMode Represents if key is used in test mode or production mode.
+   * @return Instance of the CoseKey structure.
+   */
+  public static short constructCoseKey(
+      short[] buff,
+      short keyType,
+      short keyId,
+      short keyAlg,
+      short keyOps,
+      short curve,
+      byte[] pubKey,
+      short pubKeyOff,
+      short pubKeyLen,
+      short privKeyPtr,
+      boolean testMode) {
+    if (pubKey[pubKeyOff] == 0x04) { // uncompressed format
+      pubKeyOff += 1;
+      pubKeyLen -= 1;
+    }
+    pubKeyLen = (short) (pubKeyLen / 2);
+    short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen);
+    short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen);
+    short coseKey =
+        constructCoseKey(
+            buff, keyType, keyId, keyAlg, keyOps, curve, xPtr, yPtr, privKeyPtr, testMode);
+    KMCoseKey.cast(coseKey).canonicalize();
+    return coseKey;
+  }
+
+  /**
+   * Constructs the cose key based on input parameters supplied. All the parameters must be
+   * instantiated from either KMInteger or KMNInteger or KMByteblob types.
+   *
+   * @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types.
+   * @param keyId instance of KMByteBlob which holds key identifier value.
+   * @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm.
+   * @param keyOps instance of KMInteger/KMNInteger which holds valid COSE key operations.
+   * @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve.
+   * @param pubX instance of KMByteBlob which holds EC public key's x value.
+   * @param pubY instance of KMByteBlob which holds EC public key's y value.
+   * @param priv instance of KMByteBlob which holds EC private value.
+   * @param includeTestKey flag which identifies whether to construct test key or production key.
+   * @return instance of the KMCoseKey object.
+   */
+  public static short constructCoseKey(
+      short[] buff,
+      short keyType,
+      short keyId,
+      short keyAlg,
+      short keyOps,
+      short curve,
+      short pubX,
+      short pubY,
+      short priv,
+      boolean includeTestKey) {
+    short valueIndex = 8;
+    buff[0] = keyType;
+    buff[1] = keyId;
+    buff[2] = keyAlg;
+    buff[3] = keyOps;
+    buff[4] = curve;
+    buff[5] = pubX;
+    buff[6] = pubY;
+    buff[7] = priv;
+    for (short i = valueIndex; i < 16; i++) {
+      buff[i] = KMType.INVALID_VALUE;
+    }
+    short arrPtr = handleCosePairTags(COSE_KEY_LABELS, buff, valueIndex, includeTestKey);
+    if (includeTestKey) {
+      short testKey =
+          KMCosePairSimpleValueTag.instance(
+              KMNInteger.uint_32(KMCose.COSE_TEST_KEY, (short) 0),
+              KMSimpleValue.instance(KMSimpleValue.NULL));
+      KMArray.cast(arrPtr).add((short) (KMArray.cast(arrPtr).length() - 1), testKey);
+    }
+    arrPtr = KMCoseKey.instance(arrPtr);
+    KMCoseKey.cast(arrPtr).canonicalize();
+    return arrPtr;
+  }
+
+  /**
+   * Constructs key derivation context which is required to compute HKDF.
+   *
+   * @param publicKeyA public key buffer from the first party.
+   * @param publicKeyAOff start position of the public key buffer from first party.
+   * @param publicKeyALen length of the public key buffer from first party.
+   * @param publicKeyB public key buffer from the second party.
+   * @param publicKeyBOff start position of the public key buffer from second party.
+   * @param publicKeyBLen length of the public key buffer from second party.
+   * @param senderIsA true if caller is first party, false if caller is second party.
+   * @return instance of KMArray.
+   */
+  public static short constructKdfContext(
+      byte[] publicKeyA,
+      short publicKeyAOff,
+      short publicKeyALen,
+      byte[] publicKeyB,
+      short publicKeyBOff,
+      short publicKeyBLen,
+      boolean senderIsA) {
+    short index = 0;
+    // Prepare sender info
+    short senderInfo = KMArray.instance((short) 3);
+    KMArray.cast(senderInfo)
+        .add(index++, KMByteBlob.instance(client, (short) 0, (short) client.length));
+    KMArray.cast(senderInfo).add(index++, KMByteBlob.instance((short) 0));
+    KMArray.cast(senderInfo)
+        .add(
+            index,
+            senderIsA
+                ? KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)
+                : KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen));
+
+    // Prepare recipient info
+    index = 0;
+    short recipientInfo = KMArray.instance((short) 3);
+    KMArray.cast(recipientInfo)
+        .add(index++, KMByteBlob.instance(server, (short) 0, (short) server.length));
+    KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance((short) 0));
+    KMArray.cast(recipientInfo)
+        .add(
+            index,
+            senderIsA
+                ? KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)
+                : KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen));
+
+    // supply public info
+    index = 0;
+    short publicInfo = KMArray.instance((short) 2);
+    KMArray.cast(publicInfo).add(index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS));
+    KMArray.cast(publicInfo).add(index, KMByteBlob.instance((short) 0));
+
+    // construct kdf context
+    index = 0;
+    short arrPtr = KMArray.instance((short) 4);
+    KMArray.cast(arrPtr).add(index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256));
+    KMArray.cast(arrPtr).add(index++, senderInfo);
+    KMArray.cast(arrPtr).add(index++, recipientInfo);
+    KMArray.cast(arrPtr).add(index, publicInfo);
+
+    return arrPtr;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java
new file mode 100644
index 0000000..fff9cf8
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseCertPayload represents the COSE_Sign1 payload for each certificate in BCC. The supported
+ * key types are KMInteger, KMNInteger and the supported value types are KMByteBlob and
+ * KMTextString. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short
+ * arrayPtr } where arrayPtr is a pointer to array with any KMCosePairTagType subtype instances.
+ */
+public class KMCoseCertPayload extends KMCoseMap {
+
+  private static KMCoseCertPayload prototype;
+
+  private KMCoseCertPayload() {}
+
+  private static KMCoseCertPayload proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCoseCertPayload();
+    }
+    instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp() {
+    short arrPtr = KMArray.instance((short) 2);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add((short) 0, KMCosePairTextStringTag.exp());
+    arr.add((short) 1, KMCosePairByteBlobTag.exp());
+    return KMCoseCertPayload.instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    short ptr = KMType.instance(COSE_CERT_PAYLOAD_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMCoseCertPayload cast(short ptr) {
+    if (heap[ptr] != COSE_CERT_PAYLOAD_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  @Override
+  public short getVals() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  @Override
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  @Override
+  public void canonicalize() {
+    KMCoseMap.canonicalize(getVals());
+  }
+
+  private short getValueType(short key, short significantKey) {
+    short arr = getVals();
+    short length = length();
+    short keyPtr;
+    short valPtr = 0;
+    short index = 0;
+    short tagType;
+    boolean found = false;
+    while (index < length) {
+      tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+      switch (tagType) {
+        case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+          keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == KMCosePairTagType.getKeyValueShort(keyPtr)
+              && significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) {
+            valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+          keyPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        default:
+          break;
+      }
+      if (found) {
+        break;
+      }
+      index++;
+    }
+    return valPtr;
+  }
+
+  public short getSubjectPublicKey() {
+    return getValueType(
+        Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 2), // LSB
+        Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 0) // MSB (Significant)
+        );
+  }
+
+  public short getSubject() {
+    return getValueType(KMCose.SUBJECT, KMType.INVALID_VALUE);
+  }
+
+  public short getIssuer() {
+    return getValueType(KMCose.ISSUER, KMType.INVALID_VALUE);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java
new file mode 100644
index 0000000..0e722d2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright(C) 2021 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" (short)0IS,
+ * 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseHeaders represents headers section from the Cose standard
+ * https://datatracker.ietf.org/doc/html/rfc8152#section-3. The supported key types are KMInteger,
+ * KMNInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, KMCoseKey. It
+ * corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where
+ * arrayPtr is a pointer to array with any KMTag subtype instances.
+ */
+public class KMCoseHeaders extends KMCoseMap {
+
+  private static KMCoseHeaders prototype;
+
+  private KMCoseHeaders() {}
+
+  private static KMCoseHeaders proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCoseHeaders();
+    }
+    instanceTable[KM_COSE_HEADERS_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp() {
+    short arrPtr = KMArray.instance((short) 4);
+    // CoseKey is internally an Array so evaluate it separately.
+    short coseKeyValueExp = KMCosePairCoseKeyTag.exp();
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add((short) 0, KMCosePairIntegerTag.exp());
+    arr.add((short) 1, KMCosePairNegIntegerTag.exp());
+    arr.add((short) 2, KMCosePairByteBlobTag.exp());
+    arr.add((short) 3, coseKeyValueExp);
+    return KMCoseHeaders.instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    short ptr = KMType.instance(COSE_HEADERS_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMCoseHeaders cast(short ptr) {
+    if (heap[ptr] != COSE_HEADERS_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  @Override
+  public short getVals() {
+    return Util.getShort(heap, (short) (instanceTable[KM_COSE_HEADERS_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  @Override
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  @Override
+  public void canonicalize() {
+    KMCoseMap.canonicalize(getVals());
+  }
+
+  private short getValueType(short key) {
+    short index = 0;
+    short len = length();
+    short arr = getVals();
+    short tagType;
+    short valPtr = 0;
+    short keyPtr;
+    boolean found = false;
+    while (index < len) {
+      tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+      switch (tagType) {
+        case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+          keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+          keyPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        case KMType.COSE_PAIR_INT_TAG_TYPE:
+          keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+          keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        default:
+          break;
+      }
+      if (found) {
+        break;
+      }
+      index++;
+    }
+    return valPtr;
+  }
+
+  public short getKeyIdentifier() {
+    return getValueType(KMCose.COSE_LABEL_KEYID);
+  }
+
+  public short getCoseKey() {
+    return getValueType(KMCose.COSE_LABEL_COSE_KEY);
+  }
+
+  public short getIV() {
+    return getValueType(KMCose.COSE_LABEL_IV);
+  }
+
+  public short getAlgorithm() {
+    return getValueType(KMCose.COSE_LABEL_ALGORITHM);
+  }
+
+  public boolean isDataValid(short[] buff, short alg, short keyIdPtr) {
+    short bufLen = 4;
+    buff[0] = KMCose.COSE_LABEL_ALGORITHM;
+    buff[1] = alg;
+    buff[2] = KMCose.COSE_LABEL_KEYID;
+    buff[3] = keyIdPtr;
+    boolean valid = false;
+    short value;
+    short ptr;
+    short tagIndex = 0;
+    while (tagIndex < bufLen) {
+      value = buff[(short) (tagIndex + 1)];
+      if (value != KMType.INVALID_VALUE) {
+        valid = false;
+        ptr = getValueType(buff[tagIndex]);
+        switch (KMType.getType(ptr)) {
+          case KMType.BYTE_BLOB_TYPE:
+            if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length())
+                && (0
+                    == Util.arrayCompare(
+                        KMByteBlob.cast(value).getBuffer(),
+                        KMByteBlob.cast(value).getStartOff(),
+                        KMByteBlob.cast(ptr).getBuffer(),
+                        KMByteBlob.cast(ptr).getStartOff(),
+                        KMByteBlob.cast(ptr).length()))) {
+              valid = true;
+            }
+            break;
+          case KMType.INTEGER_TYPE:
+            if (value == KMInteger.cast(ptr).getShort()) {
+              valid = true;
+            }
+            break;
+          case KMType.NEG_INTEGER_TYPE:
+            if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) {
+              valid = true;
+            }
+            break;
+          default:
+            break;
+        }
+        if (!valid) {
+          break;
+        }
+      }
+      tagIndex += 2;
+    }
+    return valid;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseKey.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseKey.java
new file mode 100644
index 0000000..d1a9f36
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseKey.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCoseKey represents COSE_Key section from the Cose standard
+ * https://datatracker.ietf.org/doc/html/rfc8152#section-7 The supported key types are KMNInteger,
+ * KMInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, KMSimpleValue. It
+ * corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where
+ * arrayPtr is a pointer to array with any KMTag subtype instances.
+ */
+public class KMCoseKey extends KMCoseMap {
+
+  private static KMCoseKey prototype;
+
+  private KMCoseKey() {}
+
+  private static KMCoseKey proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCoseKey();
+    }
+    instanceTable[KM_COSE_KEY_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp() {
+    short arrPtr = KMArray.instance((short) 4);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add((short) 0, KMCosePairIntegerTag.exp());
+    arr.add((short) 1, KMCosePairNegIntegerTag.exp());
+    arr.add((short) 2, KMCosePairByteBlobTag.exp());
+    arr.add((short) 3, KMCosePairSimpleValueTag.exp());
+    return KMCoseKey.instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    short ptr = KMType.instance(COSE_KEY_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMCoseKey cast(short ptr) {
+    if (heap[ptr] != COSE_KEY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  @Override
+  public short getVals() {
+    return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  @Override
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  private short getValueType(short key, short significantKey) {
+    short arr = getVals();
+    short length = length();
+    short keyPtr;
+    short valPtr = 0;
+    short index = 0;
+    short tagType;
+    boolean found = false;
+    while (index < length) {
+      tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index));
+      switch (tagType) {
+        case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+          keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        case KMType.COSE_PAIR_INT_TAG_TYPE:
+          keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+          keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) {
+            valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+          keyPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getKeyPtr();
+          if (key == KMCosePairTagType.getKeyValueShort(keyPtr)
+              && significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) {
+            valPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getValuePtr();
+            found = true;
+          }
+          break;
+        default:
+          break;
+      }
+      if (found) {
+        break;
+      }
+      index++;
+    }
+    return valPtr;
+  }
+
+  public short getKeyIdentifier() {
+    return getValueType(KMCose.COSE_KEY_KEY_ID, KMType.INVALID_VALUE);
+  }
+
+  public short getEcdsa256PublicKey(byte[] pubKey, short pubKeyOff) {
+    short baseOffset = pubKeyOff;
+    pubKey[pubKeyOff] = (byte) 0x04; // uncompressed.
+    pubKeyOff++;
+    short ptr = getValueType(KMCose.COSE_KEY_PUBKEY_X, KMType.INVALID_VALUE);
+    Util.arrayCopy(
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        pubKey,
+        pubKeyOff,
+        KMByteBlob.cast(ptr).length());
+    pubKeyOff += KMByteBlob.cast(ptr).length();
+    ptr = getValueType(KMCose.COSE_KEY_PUBKEY_Y, KMType.INVALID_VALUE);
+    Util.arrayCopy(
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        pubKey,
+        pubKeyOff,
+        KMByteBlob.cast(ptr).length());
+    pubKeyOff += KMByteBlob.cast(ptr).length();
+    return (short) (pubKeyOff - baseOffset);
+  }
+
+  public short getPrivateKey(byte[] priv, short privOff) {
+    short ptr = getValueType(KMCose.COSE_KEY_PRIV_KEY, KMType.INVALID_VALUE);
+    Util.arrayCopy(
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        priv,
+        privOff,
+        KMByteBlob.cast(ptr).length());
+    return KMByteBlob.cast(ptr).length();
+  }
+
+  public boolean isTestKey() {
+    short ptr =
+        getValueType(
+            Util.getShort(KMCose.COSE_TEST_KEY, (short) 2), // LSB
+            Util.getShort(KMCose.COSE_TEST_KEY, (short) 0) // MSB (Significant)
+            );
+    boolean isTestKey = false;
+    if (ptr != 0) {
+      isTestKey = (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.NULL);
+    }
+    return isTestKey;
+  }
+
+  /**
+   * Verifies the KMCoseKey values against the input values.
+   *
+   * @param keyType value of the key type
+   * @param keyIdPtr instance of KMByteBlob containing the key id.
+   * @param keyAlg value of the algorithm.
+   * @param keyOps value of the key operations.
+   * @param curve value of the curve.
+   * @return true if valid, otherwise false.
+   */
+  public boolean isDataValid(
+      short[] buff, short keyType, short keyIdPtr, short keyAlg, short keyOps, short curve) {
+    short buffLen = 10;
+    buff[0] = KMCose.COSE_KEY_KEY_TYPE;
+    buff[1] = keyType;
+    buff[2] = KMCose.COSE_KEY_KEY_ID;
+    buff[3] = keyIdPtr;
+    buff[4] = KMCose.COSE_KEY_ALGORITHM;
+    buff[5] = keyAlg;
+    buff[6] = KMCose.COSE_KEY_KEY_OPS;
+    buff[7] = keyOps;
+    buff[8] = KMCose.COSE_KEY_CURVE;
+    buff[9] = curve;
+    boolean valid = false;
+    short ptr;
+    short tagIndex = 0;
+    short value;
+    while (tagIndex < buffLen) {
+      value = buff[(short) (tagIndex + 1)];
+      if (value != KMType.INVALID_VALUE) {
+        valid = false;
+        ptr = getValueType(buff[tagIndex], KMType.INVALID_VALUE);
+        switch (KMType.getType(ptr)) {
+          case KMType.BYTE_BLOB_TYPE:
+            if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length())
+                && (0
+                    == Util.arrayCompare(
+                        KMByteBlob.cast(value).getBuffer(),
+                        KMByteBlob.cast(value).getStartOff(),
+                        KMByteBlob.cast(ptr).getBuffer(),
+                        KMByteBlob.cast(ptr).getStartOff(),
+                        KMByteBlob.cast(ptr).length()))) {
+              valid = true;
+            }
+            break;
+          case KMType.INTEGER_TYPE:
+            if (value == KMInteger.cast(ptr).getShort()) {
+              valid = true;
+            }
+            break;
+          case KMType.NEG_INTEGER_TYPE:
+            if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) {
+              valid = true;
+            }
+            break;
+        }
+        if (!valid) {
+          break;
+        }
+      }
+      tagIndex += 2;
+    }
+    return valid;
+  }
+
+  @Override
+  public void canonicalize() {
+    KMCoseMap.canonicalize(getVals());
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseMap.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseMap.java
new file mode 100644
index 0000000..5d373a7
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCoseMap.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright(C) 2021 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" (short)0IS,
+ * 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class represents either a Cose_key or Cose headers as defined in
+ * https://datatracker.ietf.org/doc/html/rfc8152 This is basically a map containing key value pairs.
+ * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class
+ * is confined to support only key and value types which are required for remote key provisioning.
+ * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported.
+ * KMCoseHeaders and KMCoseKey implements this class.
+ */
+public abstract class KMCoseMap extends KMType {
+
+  public static byte[] scratchpad;
+
+  /**
+   * This function creates an instance of either KMCoseHeaders or KMCoseKey based on the type
+   * information provided.
+   *
+   * @param typePtr type information of the underlying KMType.
+   * @param arrPtr instance of KMArray.
+   * @return instance type of either KMCoseHeaders or KMCoseKey.
+   */
+  public static short createInstanceFromType(short typePtr, short arrPtr) {
+    short mapType = KMType.getType(typePtr);
+    switch (mapType) {
+      case KMType.COSE_HEADERS_TYPE:
+        return KMCoseHeaders.instance(arrPtr);
+      case KMType.COSE_KEY_TYPE:
+        return KMCoseKey.instance(arrPtr);
+      case KMType.COSE_CERT_PAYLOAD_TYPE:
+        return KMCoseCertPayload.instance(arrPtr);
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+        return 0;
+    }
+  }
+
+  public static short getVals(short ptr) {
+    short mapType = KMType.getType(ptr);
+    switch (mapType) {
+      case KMType.COSE_HEADERS_TYPE:
+        return KMCoseHeaders.cast(ptr).getVals();
+      case KMType.COSE_KEY_TYPE:
+        return KMCoseKey.cast(ptr).getVals();
+      case KMType.COSE_CERT_PAYLOAD_TYPE:
+        return KMCoseCertPayload.cast(ptr).getVals();
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+        return 0;
+    }
+  }
+
+  private static short getKey(short tagPtr) {
+    short tagType = KMCosePairTagType.getTagValueType(tagPtr);
+    switch (tagType) {
+      case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+        return KMCosePairByteBlobTag.cast(tagPtr).getKeyPtr();
+      case KMType.COSE_PAIR_INT_TAG_TYPE:
+        return KMCosePairIntegerTag.cast(tagPtr).getKeyPtr();
+      case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+        return KMCosePairNegIntegerTag.cast(tagPtr).getKeyPtr();
+      case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+        return KMCosePairSimpleValueTag.cast(tagPtr).getKeyPtr();
+      case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+        return KMCosePairCoseKeyTag.cast(tagPtr).getKeyPtr();
+      case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+        return KMCosePairTextStringTag.cast(tagPtr).getKeyPtr();
+      default:
+        ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return 0;
+  }
+
+  private static void createScratchBuffer() {
+    if (scratchpad == null) {
+      scratchpad = JCSystem.makeTransientByteArray((short) 120, JCSystem.CLEAR_ON_RESET);
+    }
+  }
+
+  protected static void canonicalize(short arr) {
+    canonicalize(arr, KMArray.cast(arr).length());
+  }
+
+  private static void swap(short ptr, short firstIndex, short secondIndex) {
+    if (KMType.getType(ptr) == KMType.ARRAY_TYPE) {
+      KMArray.cast(ptr).swap(firstIndex, secondIndex);
+    } else {
+      KMMap.cast(ptr).swap(firstIndex, secondIndex);
+    }
+  }
+
+  private static boolean compareAndSwap(short ptr, short index) {
+    short firstKey;
+    short secondKey;
+    short firstKeyLen;
+    short secondKeyLen;
+    if (KMType.getType(ptr) == KMType.ARRAY_TYPE) {
+      firstKey = getKey(KMArray.cast(ptr).get(index));
+      secondKey = getKey(KMArray.cast(ptr).get((short) (index + 1)));
+    } else { // Map
+      firstKey = KMMap.cast(ptr).getKey(index);
+      secondKey = KMMap.cast(ptr).getKey((short) (index + 1));
+    }
+    firstKeyLen =
+        KMKeymasterApplet.encoder.encode(
+            firstKey, scratchpad, (short) 0, (short) scratchpad.length);
+    secondKeyLen =
+        KMKeymasterApplet.encoder.encode(
+            secondKey, scratchpad, firstKeyLen, (short) scratchpad.length);
+    if ((firstKeyLen > secondKeyLen)
+        || ((firstKeyLen == secondKeyLen)
+            && (0
+                < Util.arrayCompare(
+                    scratchpad, (short) 0, scratchpad, firstKeyLen, firstKeyLen)))) {
+      swap(ptr, index, (short) (index + 1));
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Canonicalizes using bubble sort.
+   *
+   * @param ptr instance pointer of either array or map.
+   * @param length length of the array or map instance.
+   */
+  public static void canonicalize(short ptr, short length) {
+    short index = 0;
+    short innerIndex = 0;
+    createScratchBuffer();
+    boolean swapped;
+    while (index < length) {
+      swapped = false;
+      innerIndex = 0;
+      while (innerIndex < (short) (length - index - 1)) {
+        swapped |= compareAndSwap(ptr, innerIndex);
+        innerIndex++;
+      }
+      if (!swapped) {
+        break;
+      }
+      index++;
+    }
+  }
+
+  public abstract short getVals();
+
+  public abstract short length();
+
+  public abstract void canonicalize();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java
new file mode 100644
index 0000000..04c3abe
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairByteBlobTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMByteBlob type. struct{byte TAG_TYPE; short length; struct{short BYTE_BLOB_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairByteBlobTag extends KMCosePairTagType {
+
+  public static Object[] keys;
+  private static KMCosePairByteBlobTag prototype;
+
+  private KMCosePairByteBlobTag() {}
+
+  private static KMCosePairByteBlobTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCosePairByteBlobTag();
+    }
+    instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMByteBlob.exp());
+    return ptr;
+  }
+
+  public static short instance(short keyPtr, short valuePtr) {
+    if (!isKeyValueValid(keyPtr)) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+    return ptr;
+  }
+
+  public static KMCosePairByteBlobTag cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    // Validate the value pointer.
+    short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+    if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  private static void createKeys() {
+    if (keys == null) {
+      keys =
+          new Object[] {
+            (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_X},
+            (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_Y},
+            (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PRIV_KEY},
+            (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_IV},
+            (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_KEYID},
+            (Object) new byte[] {(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_KEY_ID},
+            (Object) KMCose.SUBJECT_PUBLIC_KEY,
+            (Object) KMCose.KEY_USAGE
+          };
+    }
+  }
+
+  public static boolean isKeyValueValid(short keyPtr) {
+    createKeys();
+    short type = KMType.getType(keyPtr);
+    short offset = 0;
+    if (type == INTEGER_TYPE) {
+      offset = KMInteger.cast(keyPtr).getStartOff();
+    } else if (type == NEG_INTEGER_TYPE) {
+      offset = KMNInteger.cast(keyPtr).getStartOff();
+    } else {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short index = 0;
+    while (index < (short) keys.length) {
+      if (0
+          == Util.arrayCompare(
+              (byte[]) keys[index],
+              (short) 0,
+              heap,
+              offset,
+              (short) ((byte[]) keys[index]).length)) {
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  public short getValueType() {
+    return BYTE_BLOB_TYPE;
+  }
+
+  @Override
+  public short getKeyPtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  @Override
+  public short getValuePtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java
new file mode 100644
index 0000000..be853de
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java
@@ -0,0 +1,89 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairCoseKeyTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMCOseKey type. struct{byte TAG_TYPE; short length; struct{short COSE_KEY_VALUE_TYPE;
+ * short key; short value}}.
+ */
+public class KMCosePairCoseKeyTag extends KMCosePairTagType {
+
+  public static final byte[] keys = {KMCose.COSE_LABEL_COSE_KEY};
+  private static KMCosePairCoseKeyTag prototype;
+
+  private KMCosePairCoseKeyTag() {}
+
+  private static KMCosePairCoseKeyTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCosePairCoseKeyTag();
+    }
+    instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMCoseKey.exp());
+    return ptr;
+  }
+
+  public static short instance(short keyPtr, short valuePtr) {
+    if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (KMType.getType(valuePtr) != COSE_KEY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+    return ptr;
+  }
+
+  public static KMCosePairCoseKeyTag cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    // Validate the value pointer.
+    short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+    if (KMType.getType(valuePtr) != COSE_KEY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static boolean isKeyValueValid(short keyVal) {
+    short index = 0;
+    while (index < (short) keys.length) {
+      if ((byte) (keyVal & 0xFF) == keys[index]) {
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  public short getValueType() {
+    return COSE_KEY_TYPE;
+  }
+
+  @Override
+  public short getKeyPtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  @Override
+  public short getValuePtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java
new file mode 100644
index 0000000..ea052a6
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMInteger type. struct{byte TAG_TYPE; short length; struct{short INT_VALUE_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairIntegerTag extends KMCosePairTagType {
+
+  private static KMCosePairIntegerTag prototype;
+
+  private KMCosePairIntegerTag() {}
+
+  private static KMCosePairIntegerTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCosePairIntegerTag();
+    }
+    instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMInteger.exp());
+    return ptr;
+  }
+
+  public static short instance(short keyPtr, short valuePtr) {
+    short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+    if (!KMCosePairTagType.isKeyPairValid(
+        heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMInteger.cast(valuePtr).getShort())) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+    return ptr;
+  }
+
+  public static KMCosePairIntegerTag cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    // Validate the value ptr.
+    short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+    if (INTEGER_TYPE != getType(valuePtr)) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public short getValueType() {
+    return INTEGER_TYPE;
+  }
+
+  @Override
+  public short getKeyPtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  @Override
+  public short getValuePtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java
new file mode 100644
index 0000000..7f01202
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairNegIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMNInteger type. struct{byte TAG_TYPE; short length; struct{short NINT_VALUE_TYPE; short
+ * key; short value}}.
+ */
+public class KMCosePairNegIntegerTag extends KMCosePairTagType {
+
+  private static KMCosePairNegIntegerTag prototype;
+
+  private KMCosePairNegIntegerTag() {}
+
+  private static KMCosePairNegIntegerTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCosePairNegIntegerTag();
+    }
+    instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMNInteger.exp());
+    return ptr;
+  }
+
+  public static KMCosePairNegIntegerTag cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    // Validate the value ptr.
+    short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+    if (NEG_INTEGER_TYPE != getType(valuePtr)) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static short instance(short keyPtr, short valuePtr) {
+    short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+    if (!KMCosePairTagType.isKeyPairValid(
+        heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMNInteger.cast(valuePtr).getShort())) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+    return ptr;
+  }
+
+  public short getValueType() {
+    return NEG_INTEGER_TYPE;
+  }
+
+  @Override
+  public short getKeyPtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  @Override
+  public short getValuePtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java
new file mode 100644
index 0000000..a0d7da8
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java
@@ -0,0 +1,76 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairSimpleValueTag represents a key-value type, where key can be KMInteger or KMNInteger
+ * and value is KMSimpleValue type. struct{byte TAG_TYPE; short length; struct{short
+ * SIMPLE_VALUE_TYPE; short key; short value}}.
+ */
+public class KMCosePairSimpleValueTag extends KMCosePairTagType {
+
+  private static KMCosePairSimpleValueTag prototype;
+
+  private KMCosePairSimpleValueTag() {}
+
+  private static KMCosePairSimpleValueTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCosePairSimpleValueTag();
+    }
+    instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMSimpleValue.exp());
+    return ptr;
+  }
+
+  public static short instance(short keyPtr, short valuePtr) {
+    short offset = KMCosePairTagType.getKeyStartOffset(keyPtr);
+    if (!KMCosePairTagType.isKeyPairValid(
+        heap, offset, KMCose.COSE_KEY_MAX_SIZE, KMSimpleValue.cast(valuePtr).getValue())) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+    return ptr;
+  }
+
+  public static KMCosePairSimpleValueTag cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    // Validate the value pointer.
+    short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+    if (KMType.getType(valuePtr) != SIMPLE_VALUE_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public short getValueType() {
+    return SIMPLE_VALUE_TYPE;
+  }
+
+  @Override
+  public short getKeyPtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  @Override
+  public short getValuePtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java
new file mode 100644
index 0000000..f3fc76e
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * This class represents the COSE_Key as defined in
+ * https://datatracker.ietf.org/doc/html/rfc8152#section-7. This is basically a map containing key
+ * value pairs. The label for the key can be (uint / int / tstr) and the value can be of any type.
+ * But this class is confined to support only key and value types which are required for remote key
+ * provisioning. So keys of type (int / uint) and values of type (int / uint / simple / bstr) only
+ * are supported. The structure representing all the sub classes of KMCosePairTagType is as follows:
+ * KM_COSE_PAIR_TAG_TYPE(1byte), Length(2 bytes), COSE_PAIR_*_TAG_TYPE(2 bytes), Key(2 bytes),
+ * Value(2 bytes). Key can be either KMInteger or KMNInteger and Value can be either KMIntger or
+ * KMNinteger or KMSimpleValue or KMByteBlob or KMTextString or KMCoseKey. Each subclass of
+ * KMCosePairTagType is named after their corresponding value type of the Cose pair.
+ */
+public abstract class KMCosePairTagType extends KMType {
+
+  /**
+   * Below table represents the allowed values for a key. The maximum length of the key can be 4
+   * bytes so each key is represented as 4 bytes. The allowed values are placed next to their
+   * corresponding key.
+   */
+  public static Object[] allowedKeyPairs;
+
+  private static void createAllowedKeyPairs() {
+    if (allowedKeyPairs == null) {
+      allowedKeyPairs =
+          new Object[] {
+            // Key type
+            (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_KEY_TYPE},
+            (Object) new byte[] {KMCose.COSE_KEY_TYPE_EC2, KMCose.COSE_KEY_TYPE_SYMMETRIC_KEY},
+            // Key Algorithm
+            (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_ALGORITHM},
+            (Object)
+                new byte[] {
+                  KMCose.COSE_ALG_AES_GCM_256,
+                  KMCose.COSE_ALG_HMAC_256,
+                  KMCose.COSE_ALG_ECDH_ES_HKDF_256,
+                  KMCose.COSE_ALG_ES256
+                },
+            // Key operations
+            (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_KEY_OPS},
+            (Object)
+                new byte[] {
+                  KMCose.COSE_KEY_OP_SIGN,
+                  KMCose.COSE_KEY_OP_VERIFY,
+                  KMCose.COSE_KEY_OP_ENCRYPT,
+                  KMCose.COSE_KEY_OP_DECRYPT
+                },
+            // Key Curve
+            (Object) new byte[] {0, 0, 0, KMCose.COSE_KEY_CURVE},
+            (Object) new byte[] {KMCose.COSE_ECCURVE_256},
+            // Header Label Algorithm
+            (Object) new byte[] {0, 0, 0, KMCose.COSE_LABEL_ALGORITHM},
+            (Object)
+                new byte[] {
+                  KMCose.COSE_ALG_AES_GCM_256,
+                  KMCose.COSE_ALG_HMAC_256,
+                  KMCose.COSE_ALG_ES256,
+                  KMCose.COSE_ALG_ECDH_ES_HKDF_256
+                },
+            // Test Key
+            KMCose.COSE_TEST_KEY,
+            (Object) new byte[] {KMSimpleValue.NULL},
+          };
+    }
+  }
+
+  /**
+   * Validates the key and the values corresponding to key.
+   *
+   * @param key Buffer containing the key.
+   * @param keyOff Offset in the buffer from where key starts.
+   * @param keyLen Length of the key buffer.
+   * @param value Value corresponding to the key.
+   * @return true if key pair is valid, otherwise false.
+   */
+  public static boolean isKeyPairValid(byte[] key, short keyOff, short keyLen, short value) {
+    short index = 0;
+    short valueIdx;
+    byte[] values;
+    boolean valid = false;
+    createAllowedKeyPairs();
+    while (index < allowedKeyPairs.length) {
+      valueIdx = 0;
+      if (isEqual(
+          (byte[]) allowedKeyPairs[index],
+          (short) 0,
+          (short) ((byte[]) allowedKeyPairs[index]).length,
+          key,
+          keyOff,
+          keyLen)) {
+        values = (byte[]) allowedKeyPairs[(short) (index + 1)];
+        while (valueIdx < values.length) {
+          if (values[valueIdx] == (byte) value) {
+            valid = true;
+            break;
+          }
+          valueIdx++;
+        }
+        if (valid) {
+          break;
+        }
+      }
+      index += (short) 2;
+    }
+    return valid;
+  }
+
+  /**
+   * Compares two key buffers.
+   *
+   * @param key1 First buffer containing the key.
+   * @param offset1 Offset of the first buffer.
+   * @param length1 Length of the first buffer.
+   * @param key2 Second buffer containing the key.
+   * @param offset2 Offset of the second buffer.
+   * @param length2 Length of the second buffer.
+   * @return true if both keys are equal, otherwise false.
+   */
+  private static boolean isEqual(
+      byte[] key1, short offset1, short length1, byte[] key2, short offset2, short length2) {
+    if (length1 != length2) {
+      return false;
+    }
+    return (0 == KMInteger.unsignedByteArrayCompare(key1, offset1, key2, offset2, length1));
+  }
+
+  /**
+   * Returns the short value of the key.
+   *
+   * @param keyPtr Pointer to either KMInteger or KMNInteger
+   * @return value of the key as short.
+   */
+  public static short getKeyValueShort(short keyPtr) {
+    short type = KMType.getType(keyPtr);
+    short value = 0;
+    if (type == INTEGER_TYPE) {
+      value = KMInteger.cast(keyPtr).getShort();
+    } else if (type == NEG_INTEGER_TYPE) {
+      value = KMNInteger.cast(keyPtr).getShort();
+    } else {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return value;
+  }
+
+  /**
+   * Returns the significant short value of the key.
+   *
+   * @param keyPtr Pointer to either KMInteger or KMNInteger
+   * @return value of the key as short.
+   */
+  public static short getKeyValueSignificantShort(short keyPtr) {
+    short type = KMType.getType(keyPtr);
+    short value = 0;
+    if (type == INTEGER_TYPE) {
+      value = KMInteger.cast(keyPtr).getSignificantShort();
+    } else if (type == NEG_INTEGER_TYPE) {
+      value = KMNInteger.cast(keyPtr).getSignificantShort();
+    } else {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return value;
+  }
+
+  public static void getKeyValue(short keyPtr, byte[] dest, short offset, short len) {
+    short type = KMType.getType(keyPtr);
+    if (type == INTEGER_TYPE) {
+      KMInteger.cast(keyPtr).getValue(dest, offset, len);
+    } else if (type == NEG_INTEGER_TYPE) {
+      KMNInteger.cast(keyPtr).getValue(dest, offset, len);
+    } else {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+  }
+
+  /**
+   * Returns the key offset from the key pointer.
+   *
+   * @param keyPtr Pointer to either KMInteger or KMNInteger
+   * @return offset from where the key starts.
+   */
+  public static short getKeyStartOffset(short keyPtr) {
+    short type = KMType.getType(keyPtr);
+    short offset = 0;
+    if (type == INTEGER_TYPE) {
+      offset = KMInteger.cast(keyPtr).getStartOff();
+    } else if (type == NEG_INTEGER_TYPE) {
+      offset = KMNInteger.cast(keyPtr).getStartOff();
+    } else {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return offset;
+  }
+
+  /**
+   * Returns the key length.
+   *
+   * @param keyPtr pointer to either KMInteger/KMInteger.
+   * @return length of the key.
+   */
+  public static short getKeyLength(short keyPtr) {
+    short type = KMType.getType(keyPtr);
+    short len = 0;
+    if (type == INTEGER_TYPE) {
+      len = KMInteger.cast(keyPtr).length();
+    } else if (type == NEG_INTEGER_TYPE) {
+      len = KMNInteger.cast(keyPtr).length();
+    } else {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return len;
+  }
+
+  /**
+   * This function returns one of COSE_KEY_TAG_*_VALUE_TYPE tag information.
+   *
+   * @param ptr Pointer to one of the KMCoseKey*Value class.
+   * @return Tag value type.
+   */
+  public static short getTagValueType(short ptr) {
+    return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+  }
+
+  /**
+   * This function returns the key pointer.
+   *
+   * @return key pointer.
+   */
+  public abstract short getKeyPtr();
+
+  /**
+   * This function returns the value pointer.
+   *
+   * @return value pointer.
+   */
+  public abstract short getValuePtr();
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java
new file mode 100644
index 0000000..99506b6
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java
@@ -0,0 +1,91 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMCosePairTextStringTag represents a key-value type, where key can be KMInteger or KMNInteger and
+ * value is KMTextString type. struct{byte TAG_TYPE; short length; struct{short TXT_STR_VALUE_TYPE;
+ * short key; short value}}.
+ */
+public class KMCosePairTextStringTag extends KMCosePairTagType {
+
+  public static final byte[] keys = {
+    KMCose.ISSUER, KMCose.SUBJECT,
+  };
+  private static KMCosePairTextStringTag prototype;
+
+  private KMCosePairTextStringTag() {}
+
+  private static KMCosePairTextStringTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMCosePairTextStringTag();
+    }
+    instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMTextString.exp());
+    return ptr;
+  }
+
+  public static short instance(short keyPtr, short valuePtr) {
+    if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+    return ptr;
+  }
+
+  public static KMCosePairTextStringTag cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != COSE_PAIR_TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    // Validate the value pointer.
+    short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4));
+    if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static boolean isKeyValueValid(short keyVal) {
+    short index = 0;
+    while (index < (short) keys.length) {
+      if ((byte) (keyVal & 0xFF) == keys[index]) {
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  public short getValueType() {
+    return TEXT_STRING_TYPE;
+  }
+
+  @Override
+  public short getKeyPtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  @Override
+  public short getValuePtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java
new file mode 100644
index 0000000..10c7d29
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMDecoder.java
@@ -0,0 +1,774 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+public class KMDecoder {
+
+  // major types
+  private static final short UINT_TYPE = 0x00;
+  private static final short NEG_INT_TYPE = 0x20;
+  private static final short BYTES_TYPE = 0x40;
+  private static final short TSTR_TYPE = 0x60;
+  private static final short ARRAY_TYPE = 0x80;
+  private static final short MAP_TYPE = 0xA0;
+  private static final short SIMPLE_VALUE_TYPE = 0xE0;
+  private static final short SEMANTIC_TAG_TYPE = 0xC0;
+
+  // masks
+  private static final short ADDITIONAL_MASK = 0x1F;
+  private static final short MAJOR_TYPE_MASK = 0xE0;
+
+  // value length
+  private static final short UINT8_LENGTH = 0x18;
+  private static final short UINT16_LENGTH = 0x19;
+  private static final short UINT32_LENGTH = 0x1A;
+  private static final short UINT64_LENGTH = 0x1B;
+
+  private static final byte SCRATCH_BUF_SIZE = 6;
+  private static final byte START_OFFSET = 0;
+  private static final byte LEN_OFFSET = 2;
+  private static final byte TAG_KEY_OFFSET = 4;
+  private Object[] bufferRef;
+  private short[] scratchBuf;
+
+  public KMDecoder() {
+    bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+    scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET);
+    bufferRef[0] = null;
+    scratchBuf[START_OFFSET] = (short) 0;
+    scratchBuf[LEN_OFFSET] = (short) 0;
+    scratchBuf[TAG_KEY_OFFSET] = (short) 0;
+  }
+
+  public short decode(short expression, byte[] buffer, short startOff, short length) {
+    bufferRef[0] = buffer;
+    scratchBuf[START_OFFSET] = startOff;
+    scratchBuf[LEN_OFFSET] = (short) (startOff + length);
+    return decode(expression);
+  }
+
+  public short decodeArray(short exp, byte[] buffer, short startOff, short length) {
+    bufferRef[0] = buffer;
+    scratchBuf[START_OFFSET] = startOff;
+    scratchBuf[LEN_OFFSET] = (short) (startOff + length);
+    short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+    short expLength = KMArray.cast(exp).length();
+    if (payloadLength > expLength) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    short index = 0;
+    short obj;
+    short type;
+    short arrPtr = KMArray.instance(payloadLength);
+    while (index < payloadLength) {
+      type = KMArray.cast(exp).get(index);
+      obj = decode(type);
+      KMArray.cast(arrPtr).add(index, obj);
+      index++;
+    }
+    return arrPtr;
+  }
+
+  private short decode(short exp) {
+    byte type = KMType.getType(exp);
+    switch (type) {
+      case KMType.BYTE_BLOB_TYPE:
+        return decodeByteBlob(exp);
+      case KMType.TEXT_STRING_TYPE:
+        return decodeTstr(exp);
+      case KMType.INTEGER_TYPE:
+        return decodeInteger(exp);
+      case KMType.SIMPLE_VALUE_TYPE:
+        return decodeSimpleValue(exp);
+      case KMType.SEMANTIC_TAG_TYPE:
+        return decodeSemanticTagValue(exp);
+      case KMType.NEG_INTEGER_TYPE:
+        return decodeNegInteger(exp);
+      case KMType.ARRAY_TYPE:
+        return decodeArray(exp);
+      case KMType.MAP_TYPE:
+        return decodeMap(exp);
+      case KMType.ENUM_TYPE:
+        return decodeEnum(exp);
+      case KMType.KEY_PARAM_TYPE:
+        return decodeKeyParam(exp);
+      case KMType.KEY_CHAR_TYPE:
+        return decodeKeyChar(exp);
+      case KMType.VERIFICATION_TOKEN_TYPE:
+        return decodeVerificationToken(exp);
+      case KMType.HMAC_SHARING_PARAM_TYPE:
+        return decodeHmacSharingParam(exp);
+      case KMType.HW_AUTH_TOKEN_TYPE:
+        return decodeHwAuthToken(exp);
+      case KMType.COSE_KEY_TYPE:
+      case KMType.COSE_HEADERS_TYPE:
+      case KMType.COSE_CERT_PAYLOAD_TYPE:
+        return decodeCoseMap(exp);
+      case KMType.COSE_PAIR_TAG_TYPE:
+        short tagValueType = KMCosePairTagType.getTagValueType(exp);
+        return decodeCosePairTag(tagValueType, exp);
+      case KMType.TAG_TYPE:
+        short tagType = KMTag.getTagType(exp);
+        return decodeTag(tagType, exp);
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+        return 0;
+    }
+  }
+
+  private short decodeTag(short tagType, short exp) {
+    switch (tagType) {
+      case KMType.BIGNUM_TAG:
+        return decodeBignumTag(exp);
+      case KMType.BYTES_TAG:
+        return decodeBytesTag(exp);
+      case KMType.BOOL_TAG:
+        return decodeBoolTag(exp);
+      case KMType.UINT_TAG:
+      case KMType.ULONG_TAG:
+      case KMType.DATE_TAG:
+        return decodeIntegerTag(exp);
+      case KMType.ULONG_ARRAY_TAG:
+      case KMType.UINT_ARRAY_TAG:
+        return decodeIntegerArrayTag(exp);
+      case KMType.ENUM_TAG:
+        return decodeEnumTag(exp);
+      case KMType.ENUM_ARRAY_TAG:
+        return decodeEnumArrayTag(exp);
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+        return 0;
+    }
+  }
+
+  private short decodeVerificationToken(short exp) {
+    short vals = decode(KMVerificationToken.cast(exp).getVals());
+    return KMVerificationToken.instance(vals);
+  }
+
+  private short decodeHwAuthToken(short exp) {
+    short vals = decode(KMHardwareAuthToken.cast(exp).getVals());
+    return KMHardwareAuthToken.instance(vals);
+  }
+
+  private short decodeHmacSharingParam(short exp) {
+    short vals = decode(KMHmacSharingParameters.cast(exp).getVals());
+    return KMHmacSharingParameters.instance(vals);
+  }
+
+  private short decodeKeyChar(short exp) {
+    short vals = decode(KMKeyCharacteristics.cast(exp).getVals());
+    return KMKeyCharacteristics.instance(vals);
+  }
+
+  private short decodeCosePairKey(short exp) {
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    short keyPtr = (short) 0;
+    // Cose Key should be always either UINT or Negative int
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) == UINT_TYPE) {
+      keyPtr = decodeInteger(exp);
+    } else if ((buffer[startOff] & MAJOR_TYPE_MASK) == NEG_INT_TYPE) {
+      keyPtr = decodeNegInteger(exp);
+    } else {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    return keyPtr;
+  }
+
+  private short decodeCosePairSimpleValueTag(short exp) {
+    short keyPtr = decodeCosePairKey((KMCosePairSimpleValueTag.cast(exp).getKeyPtr()));
+    short valuePtr = decode(KMCosePairSimpleValueTag.cast(exp).getValuePtr());
+    return KMCosePairSimpleValueTag.instance(keyPtr, valuePtr);
+  }
+
+  private short decodeCosePairIntegerValueTag(short exp) {
+    short keyPtr = decodeCosePairKey((KMCosePairIntegerTag.cast(exp).getKeyPtr()));
+    short valuePtr = decode(KMCosePairIntegerTag.cast(exp).getValuePtr());
+    return KMCosePairIntegerTag.instance(keyPtr, valuePtr);
+  }
+
+  private short decodeCosePairNegIntegerTag(short exp) {
+    short keyPtr = decodeCosePairKey((KMCosePairNegIntegerTag.cast(exp).getKeyPtr()));
+    short valuePtr = decode(KMCosePairNegIntegerTag.cast(exp).getValuePtr());
+    return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr);
+  }
+
+  private short decodeCosePairTxtStringTag(short exp) {
+    short keyPtr = decodeCosePairKey((KMCosePairTextStringTag.cast(exp).getKeyPtr()));
+    short valuePtr = decode(KMCosePairTextStringTag.cast(exp).getValuePtr());
+    return KMCosePairTextStringTag.instance(keyPtr, valuePtr);
+  }
+
+  private short decodeCosePairCoseKeyTag(short exp) {
+    short keyPtr = decodeCosePairKey((KMCosePairCoseKeyTag.cast(exp).getKeyPtr()));
+    short valuePtr = decode(KMCosePairCoseKeyTag.cast(exp).getValuePtr());
+    return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr);
+  }
+
+  private short decodeCosePairByteBlobTag(short exp) {
+    short keyPtr = decodeCosePairKey((KMCosePairByteBlobTag.cast(exp).getKeyPtr()));
+    short valuePtr = decode(KMCosePairByteBlobTag.cast(exp).getValuePtr());
+    return KMCosePairByteBlobTag.instance(keyPtr, valuePtr);
+  }
+
+  private short peekCosePairTagType() {
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    // Cose Key should be always either UINT or Negative int
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE
+        && (buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+
+    short additionalMask = (short) (buffer[startOff] & ADDITIONAL_MASK);
+    short increment = 0;
+    if (additionalMask < UINT8_LENGTH) {
+      increment++;
+    } else if (additionalMask == UINT8_LENGTH) {
+      increment += 2;
+    } else if (additionalMask == UINT16_LENGTH) {
+      increment += 3;
+    } else if (additionalMask == UINT32_LENGTH) {
+      increment += 5;
+    } else {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short majorType = (short) (buffer[(short) (startOff + increment)] & MAJOR_TYPE_MASK);
+    short tagValueType = 0;
+    if (majorType == BYTES_TYPE) {
+      tagValueType = KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE;
+    } else if (majorType == UINT_TYPE) {
+      tagValueType = KMType.COSE_PAIR_INT_TAG_TYPE;
+    } else if (majorType == NEG_INT_TYPE) {
+      tagValueType = KMType.COSE_PAIR_NEG_INT_TAG_TYPE;
+    } else if (majorType == MAP_TYPE) {
+      tagValueType = KMType.COSE_PAIR_COSE_KEY_TAG_TYPE;
+    } else if (majorType == SIMPLE_VALUE_TYPE) {
+      tagValueType = KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE;
+    } else if (majorType == TSTR_TYPE) {
+      tagValueType = KMType.COSE_PAIR_TEXT_STR_TAG_TYPE;
+    } else {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    return tagValueType;
+  }
+
+  private short decodeCosePairTag(short tagValueType, short exp) {
+    switch (tagValueType) {
+      case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+        return decodeCosePairByteBlobTag(exp);
+      case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+        return decodeCosePairNegIntegerTag(exp);
+      case KMType.COSE_PAIR_INT_TAG_TYPE:
+        return decodeCosePairIntegerValueTag(exp);
+      case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+        return decodeCosePairSimpleValueTag(exp);
+      case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+        return decodeCosePairCoseKeyTag(exp);
+      case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+        return decodeCosePairTxtStringTag(exp);
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+        return 0;
+    }
+  }
+
+  private short decodeCoseMap(short exp) {
+    short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+    // get allowed key pairs
+    short allowedKeyPairs = KMCoseMap.getVals(exp);
+    short vals = KMArray.instance(payloadLength);
+    short length = KMArray.cast(allowedKeyPairs).length();
+    short index = 0;
+    boolean tagFound;
+    short tagInd;
+    short cosePairTagType;
+    short tagClass;
+    short allowedType;
+    short obj;
+
+    // For each tag in payload ...
+    while (index < payloadLength) {
+      tagFound = false;
+      tagInd = 0;
+      cosePairTagType = peekCosePairTagType();
+      // Check against the allowed tags ...
+      while (tagInd < length) {
+        tagClass = KMArray.cast(allowedKeyPairs).get(tagInd);
+        allowedType = KMCosePairTagType.getTagValueType(tagClass);
+        if (allowedType == cosePairTagType) {
+          obj = decode(tagClass);
+          KMArray.cast(vals).add(index, obj);
+          tagFound = true;
+          break;
+        }
+        tagInd++;
+      }
+      if (!tagFound) {
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+      } else {
+        index++;
+      }
+    }
+    return KMCoseMap.createInstanceFromType(exp, vals);
+  }
+
+  private short decodeKeyParam(short exp) {
+    short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+    // allowed tags
+    short allowedTags = KMKeyParameters.cast(exp).getVals();
+    short tagRule = KMArray.cast(allowedTags).get((short) 0);
+    boolean ignoreInvalidTags = KMEnum.cast(tagRule).getVal() == KMType.IGNORE_INVALID_TAGS;
+    short vals = KMArray.instance(payloadLength);
+    short length = KMArray.cast(allowedTags).length();
+    short index = 0;
+    boolean tagFound;
+    short tagInd;
+    short tagType;
+    short tagClass;
+    short allowedType;
+    short obj;
+    short arrPos = 0;
+    // For each tag in payload ...
+    while (index < payloadLength) {
+      tagFound = false;
+      tagInd = 1;
+      tagType = peekTagType();
+      // Check against the allowed tags ...
+      while (tagInd < length) {
+        tagClass = KMArray.cast(allowedTags).get(tagInd);
+        allowedType = KMTag.getTagType(tagClass);
+        // If it is part of allowed tags ...
+        if (tagType == allowedType) {
+          // then decodeByteBlob and add that to the array.
+          try {
+            tagFound = true;
+            obj = decode(tagClass);
+            KMArray.cast(vals).add(arrPos++, obj);
+            break;
+          } catch (KMException e) {
+            if (KMException.reason() == KMError.INVALID_TAG) {
+              if (!ignoreInvalidTags) {
+                KMException.throwIt(KMError.INVALID_TAG);
+              }
+            } else {
+              KMException.throwIt(KMException.reason());
+            }
+            break;
+          }
+        }
+        tagInd++;
+      }
+      if (!tagFound) {
+        KMException.throwIt(KMError.INVALID_TAG);
+      } else {
+        index++;
+      }
+    }
+    KMArray.cast(vals).setLength(arrPos);
+    return KMKeyParameters.instance(vals);
+  }
+
+  private short decodeEnumArrayTag(short exp) {
+    readTagKey(KMEnumArrayTag.cast(exp).getTagType());
+    return KMEnumArrayTag.instance(
+        scratchBuf[TAG_KEY_OFFSET], decode(KMEnumArrayTag.cast(exp).getValues()));
+  }
+
+  private short decodeIntegerArrayTag(short exp) {
+    readTagKey(KMIntegerArrayTag.cast(exp).getTagType());
+    // the values are array of integers.
+    return KMIntegerArrayTag.instance(
+        KMIntegerArrayTag.cast(exp).getTagType(),
+        scratchBuf[TAG_KEY_OFFSET],
+        decode(KMIntegerArrayTag.cast(exp).getValues()));
+  }
+
+  private short decodeIntegerTag(short exp) {
+    readTagKey(KMIntegerTag.cast(exp).getTagType());
+    // the value is an integer
+    return KMIntegerTag.instance(
+        KMIntegerTag.cast(exp).getTagType(),
+        scratchBuf[TAG_KEY_OFFSET],
+        decode(KMIntegerTag.cast(exp).getValue()));
+  }
+
+  private short decodeBytesTag(short exp) {
+    readTagKey(KMByteTag.cast(exp).getTagType());
+    // The value must be byte blob
+    return KMByteTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMByteTag.cast(exp).getValue()));
+  }
+
+  private short decodeBignumTag(short exp) {
+    readTagKey(KMBignumTag.cast(exp).getTagType());
+    // The value must be byte blob
+    return KMBignumTag.instance(
+        scratchBuf[TAG_KEY_OFFSET], decode(KMBignumTag.cast(exp).getValue()));
+  }
+
+  private short decodeMap(short exp) {
+    short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE);
+    short mapPtr = KMMap.instance(payloadLength);
+    short index = 0;
+    short type;
+    short keyobj;
+    short valueobj;
+    while (index < payloadLength) {
+      type = KMMap.cast(exp).getKey(index);
+      keyobj = decode(type);
+      type = KMMap.cast(exp).getKeyValue(index);
+      valueobj = decode(type);
+      KMMap.cast(mapPtr).add(index, keyobj, valueobj);
+      index++;
+    }
+    return mapPtr;
+  }
+
+  private short decodeArray(short exp) {
+    short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+    short arrPtr = KMArray.instance(payloadLength);
+    short index = 0;
+    short type;
+    short obj;
+    // check whether array contains one type of objects or multiple types
+    if (KMArray.cast(exp).containedType()
+        == KMType.INVALID_VALUE) { // multiple types specified by expression.
+      if (KMArray.cast(exp).length() != KMArray.ANY_ARRAY_LENGTH) {
+        if (KMArray.cast(exp).length() != payloadLength) {
+          ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+        }
+      }
+      while (index < payloadLength) {
+        type = KMArray.cast(exp).get(index);
+        obj = decode(type);
+        KMArray.cast(arrPtr).add(index, obj);
+        index++;
+      }
+    } else { // Array is a Vector containing objects of one type
+      type = KMArray.cast(exp).containedType();
+      while (index < payloadLength) {
+        obj = decode(type);
+        KMArray.cast(arrPtr).add(index, obj);
+        index++;
+      }
+    }
+    return arrPtr;
+  }
+
+  private short decodeEnumTag(short exp) {
+    readTagKey(KMEnumTag.cast(exp).getTagType());
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    // Enum Tag value will always be integer with max 1 byte length.
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+    byte enumVal = 0;
+    if (len > UINT8_LENGTH) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    if (len < UINT8_LENGTH) {
+      enumVal = (byte) (len & ADDITIONAL_MASK);
+      incrementStartOff((short) 1);
+    } else if (len == UINT8_LENGTH) {
+      incrementStartOff((short) 1);
+      // startOff  is incremented so update the startOff
+      // with latest value before using it.
+      startOff = scratchBuf[START_OFFSET];
+      enumVal = buffer[startOff];
+      incrementStartOff((short) 1);
+    }
+    return KMEnumTag.instance(scratchBuf[TAG_KEY_OFFSET], enumVal);
+  }
+
+  private short decodeBoolTag(short exp) {
+    readTagKey(KMBoolTag.cast(exp).getTagType());
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1.
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != 0x01) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    incrementStartOff((short) 1);
+    return KMBoolTag.instance(scratchBuf[TAG_KEY_OFFSET]);
+  }
+
+  private short decodeEnum(short exp) {
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    // Enum value will always be integer with max 1 byte length.
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+    byte enumVal;
+    if (len > UINT8_LENGTH) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    if (len < UINT8_LENGTH) {
+      enumVal = (byte) (len & ADDITIONAL_MASK);
+      incrementStartOff((short) 1);
+    } else {
+      incrementStartOff((short) 1);
+      // startOff  is incremented so update the startOff
+      // with latest value before using it.
+      startOff = scratchBuf[START_OFFSET];
+      enumVal = buffer[startOff];
+      incrementStartOff((short) 1);
+    }
+    return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal);
+  }
+
+  private short decodeSimpleValue(short exp) {
+    short startOff = scratchBuf[START_OFFSET];
+    byte[] buffer = (byte[]) bufferRef[0];
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != SIMPLE_VALUE_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    byte addInfo = (byte) (buffer[startOff] & ADDITIONAL_MASK);
+    incrementStartOff((short) 1);
+    return KMSimpleValue.instance(addInfo);
+  }
+
+  private short decodeSemanticTagValue(short exp) {
+    // Decode tag.
+    short tag = readMajorTypeWithInteger(exp, SEMANTIC_TAG_TYPE, UINT32_LENGTH);
+    // Decode value pointer.
+    short valuePtr = decode(KMSemanticTag.cast(exp).getValuePtr());
+    return KMSemanticTag.instance(tag, valuePtr);
+  }
+
+  private short readMajorTypeWithInteger(short exp, short majorType, short maxLimit) {
+    short inst;
+    short startOff = scratchBuf[START_OFFSET];
+    byte[] buffer = (byte[]) bufferRef[0];
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != majorType) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+    if (len > maxLimit) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    incrementStartOff((short) 1);
+    // startOff  is incremented so update the startOff
+    // with latest value before using it.
+    startOff = scratchBuf[START_OFFSET];
+    if (len < UINT8_LENGTH) {
+      inst = KMInteger.uint_8((byte) (len & ADDITIONAL_MASK));
+    } else if (len == UINT8_LENGTH) {
+      inst = KMInteger.instance(buffer, startOff, (short) 1);
+      incrementStartOff((short) 1);
+    } else if (len == UINT16_LENGTH) {
+      inst = KMInteger.instance(buffer, startOff, (short) 2);
+      incrementStartOff((short) 2);
+    } else if (len == UINT32_LENGTH) {
+      inst = KMInteger.instance(buffer, startOff, (short) 4);
+      incrementStartOff((short) 4);
+    } else {
+      inst = KMInteger.instance(buffer, startOff, (short) 8);
+      incrementStartOff((short) 8);
+    }
+    return inst;
+  }
+
+  private short decodeInteger(short exp) {
+    return readMajorTypeWithInteger(exp, UINT_TYPE, UINT64_LENGTH);
+  }
+
+  private short decodeNegIntegerValue(byte addInfo, byte[] buf, short startOffset) {
+    short inst;
+    short len = 0;
+    short scratchpad;
+    if (addInfo < UINT8_LENGTH) {
+      addInfo = (byte) (-1 - addInfo);
+      inst = KMNInteger.uint_8(addInfo);
+    } else {
+      switch (addInfo) {
+        case UINT8_LENGTH:
+          len = 1;
+          break;
+        case UINT16_LENGTH:
+          len = 2;
+          break;
+        case UINT32_LENGTH:
+          len = 4;
+          break;
+        case UINT64_LENGTH:
+          len = 8;
+          break;
+        default:
+          ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+      }
+      // Do (-1 - N), as per cbor negative integer decoding rule.
+      // N is the integer value.
+      scratchpad = KMByteBlob.instance((short) (len * 3));
+      byte[] input = KMByteBlob.cast(scratchpad).getBuffer();
+      short offset = KMByteBlob.cast(scratchpad).getStartOff();
+      Util.arrayFillNonAtomic(input, offset, len, (byte) -1);
+      Util.arrayCopyNonAtomic(buf, startOffset, input, (short) (offset + len), len);
+      KMUtils.subtract(
+          input, offset, (short) (offset + len), (short) (offset + 2 * len), (byte) len);
+      inst = KMNInteger.instance(input, (short) (offset + 2 * len), len);
+      incrementStartOff(len);
+    }
+    return inst;
+  }
+
+  private short decodeNegInteger(short exp) {
+    short startOff = scratchBuf[START_OFFSET];
+    byte[] buffer = (byte[]) bufferRef[0];
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short len = (short) (buffer[startOff] & ADDITIONAL_MASK);
+    if (len > UINT64_LENGTH) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    incrementStartOff((short) 1);
+    // startOff  is incremented so update the startOff
+    // with latest value before using it.
+    startOff = scratchBuf[START_OFFSET];
+    return decodeNegIntegerValue((byte) len, buffer, startOff);
+  }
+
+  private short decodeTstr(short exp) {
+    short payloadLength = readMajorTypeWithPayloadLength(TSTR_TYPE);
+    short inst =
+        KMTextString.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength);
+    incrementStartOff(payloadLength);
+    return inst;
+  }
+
+  private short decodeByteBlob(short exp) {
+    short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE);
+    short inst =
+        KMByteBlob.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], payloadLength);
+    incrementStartOff(payloadLength);
+    return inst;
+  }
+
+  private short peekTagType() {
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+
+    if ((short) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    return (short)
+        ((Util.makeShort(buffer[(short) (startOff + 1)], buffer[(short) (startOff + 2)]))
+            & KMType.TAG_TYPE_MASK);
+  }
+
+  private void readTagKey(short expectedTagType) {
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if ((byte) (buffer[startOff] & ADDITIONAL_MASK) != UINT32_LENGTH) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    incrementStartOff((short) 1);
+    short tagType = readShort();
+    scratchBuf[TAG_KEY_OFFSET] = readShort();
+    if (tagType != expectedTagType) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+  }
+
+  // payload length cannot be more then 16 bits.
+  private short readMajorTypeWithPayloadLength(short majorType) {
+    short payloadLength;
+    byte val = readByte();
+    if ((short) (val & MAJOR_TYPE_MASK) != majorType) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short lenType = (short) (val & ADDITIONAL_MASK);
+    if (lenType > UINT16_LENGTH) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    if (lenType < UINT8_LENGTH) {
+      payloadLength = lenType;
+    } else if (lenType == UINT8_LENGTH) {
+      payloadLength = (short) (readByte() & 0xFF);
+    } else {
+      payloadLength = readShort();
+    }
+    return payloadLength;
+  }
+
+  private short readShort() {
+    byte[] buffer = (byte[]) bufferRef[0];
+    short startOff = scratchBuf[START_OFFSET];
+    short val = Util.makeShort(buffer[startOff], buffer[(short) (startOff + 1)]);
+    incrementStartOff((short) 2);
+    return val;
+  }
+
+  private byte readByte() {
+    short startOff = scratchBuf[START_OFFSET];
+    byte val = ((byte[]) bufferRef[0])[startOff];
+    incrementStartOff((short) 1);
+    return val;
+  }
+
+  private void incrementStartOff(short inc) {
+    scratchBuf[START_OFFSET] += inc;
+    if (scratchBuf[START_OFFSET] > scratchBuf[LEN_OFFSET]) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+  }
+
+  public short readKeyblobVersion(byte[] buf, short bufOffset, short bufLen) {
+    bufferRef[0] = buf;
+    scratchBuf[START_OFFSET] = bufOffset;
+    scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen);
+    short arrayLen = readMajorTypeWithPayloadLength(ARRAY_TYPE);
+    if (arrayLen == 0) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short version = KMType.INVALID_VALUE;
+    try {
+      version = decodeInteger(KMInteger.exp());
+    } catch (Exception e) {
+      // Fail to decode Integer. It can happen if it is an old KeyBlob.
+    }
+    return version;
+  }
+
+  public short readCertificateChainHeaderLen(byte[] buf, short bufOffset, short bufLen) {
+    bufferRef[0] = buf;
+    scratchBuf[START_OFFSET] = bufOffset;
+    scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen);
+    readMajorTypeWithPayloadLength(BYTES_TYPE);
+    return (short) (scratchBuf[START_OFFSET] - bufOffset);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java
new file mode 100644
index 0000000..7405e06
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEncoder.java
@@ -0,0 +1,772 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+public class KMEncoder {
+
+  // major types
+  private static final byte UINT_TYPE = 0x00;
+  private static final byte NEG_INT_TYPE = 0x20;
+  private static final byte BYTES_TYPE = 0x40;
+  private static final byte TSTR_TYPE = 0x60;
+  private static final byte ARRAY_TYPE = (byte) 0x80;
+  private static final byte MAP_TYPE = (byte) 0xA0;
+  private static final byte SIMPLE_VALUE_TYPE = (byte) 0xE0;
+  private static final byte SEMANTIC_TAG_TYPE = (byte) 0xC0;
+
+  // masks
+  private static final byte ADDITIONAL_MASK = 0x1F;
+
+  // value length
+  private static final byte UINT8_LENGTH = (byte) 0x18;
+  private static final byte UINT16_LENGTH = (byte) 0x19;
+  private static final byte UINT32_LENGTH = (byte) 0x1A;
+  private static final byte UINT64_LENGTH = (byte) 0x1B;
+  private static final short TINY_PAYLOAD = 0x17;
+  private static final short SHORT_PAYLOAD = 0x100;
+  private static final byte STACK_SIZE = 50;
+  private static final byte SCRATCH_BUF_SIZE = 6;
+  private static final byte START_OFFSET = 0;
+  private static final byte LEN_OFFSET = 2;
+  private static final byte STACK_PTR_OFFSET = 4;
+
+  private Object[] bufferRef;
+  private short[] scratchBuf;
+  private short[] stack;
+
+  public KMEncoder() {
+    bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+    scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET);
+    stack = JCSystem.makeTransientShortArray(STACK_SIZE, JCSystem.CLEAR_ON_RESET);
+    bufferRef[0] = null;
+    scratchBuf[START_OFFSET] = (short) 0;
+    scratchBuf[LEN_OFFSET] = (short) 0;
+    scratchBuf[STACK_PTR_OFFSET] = (short) 0;
+  }
+
+  private void push(short objPtr) {
+    stack[scratchBuf[STACK_PTR_OFFSET]] = objPtr;
+    scratchBuf[STACK_PTR_OFFSET]++;
+  }
+
+  private short pop() {
+    scratchBuf[STACK_PTR_OFFSET]--;
+    return stack[scratchBuf[STACK_PTR_OFFSET]];
+  }
+
+  private void encode(short obj) {
+    push(obj);
+  }
+
+  /**
+   * This functions encodes the given object into the provider buffer space in cbor format.
+   *
+   * @param object Object to be encoded into cbor data.
+   * @param buffer Output where cbor data is copied.
+   * @param startOff is the start offset of the buffer.
+   * @param bufLen length of the buffer
+   * @param encoderOutLimitLen excepted encoded output length.
+   * @return length of the encoded buffer.
+   */
+  public short encode(
+      short object, byte[] buffer, short startOff, short bufLen, short encoderOutLimitLen) {
+    scratchBuf[STACK_PTR_OFFSET] = 0;
+    bufferRef[0] = buffer;
+    scratchBuf[START_OFFSET] = startOff;
+    if ((short) (startOff + encoderOutLimitLen) > bufLen) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    scratchBuf[LEN_OFFSET] = (short) (startOff + encoderOutLimitLen);
+    push(object);
+    encode();
+    return (short) (scratchBuf[START_OFFSET] - startOff);
+  }
+
+  public short encode(short object, byte[] buffer, short startOff, short bufLen) {
+    return encode(object, buffer, startOff, bufLen, (short) (bufLen - startOff));
+  }
+
+  // array{KMError.OK,Array{KMByteBlobs}}
+  public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength) {
+    if (bufferStart > certStart) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    bufferRef[0] = certBuffer;
+    scratchBuf[START_OFFSET] = certStart;
+    scratchBuf[LEN_OFFSET] = (short) (certStart + 1);
+    // Byte Header + cert length
+    scratchBuf[START_OFFSET] -= getEncodedBytesLength(certLength);
+    // Array header - 1 elements i.e. 1 byte
+    scratchBuf[START_OFFSET]--;
+    if (scratchBuf[START_OFFSET] < bufferStart) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    bufferStart = scratchBuf[START_OFFSET];
+    writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 elements
+    writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length
+    return bufferStart;
+  }
+
+  private void encode() {
+    while (scratchBuf[STACK_PTR_OFFSET] > 0) {
+      short exp = pop();
+      byte type = KMType.getType(exp);
+      switch (type) {
+        case KMType.BYTE_BLOB_TYPE:
+          encodeByteBlob(exp);
+          break;
+        case KMType.TEXT_STRING_TYPE:
+          encodeTextString(exp);
+          break;
+        case KMType.INTEGER_TYPE:
+          encodeUnsignedInteger(exp);
+          break;
+        case KMType.SIMPLE_VALUE_TYPE:
+          encodeSimpleValue(exp);
+          break;
+        case KMType.NEG_INTEGER_TYPE:
+          encodeNegInteger(exp);
+          break;
+        case KMType.ARRAY_TYPE:
+          encodeArray(exp);
+          break;
+        case KMType.MAP_TYPE:
+          encodeMap(exp);
+          break;
+        case KMType.ENUM_TYPE:
+          encodeEnum(exp);
+          break;
+        case KMType.KEY_PARAM_TYPE:
+          encodeKeyParam(exp);
+          break;
+        case KMType.SEMANTIC_TAG_TYPE:
+          encodeSemanticTag(exp);
+          break;
+        case KMType.COSE_KEY_TYPE:
+        case KMType.COSE_HEADERS_TYPE:
+        case KMType.COSE_CERT_PAYLOAD_TYPE:
+          encodeCoseMap(exp);
+          break;
+        case KMType.KEY_CHAR_TYPE:
+          encodeKeyChar(exp);
+          break;
+        case KMType.VERIFICATION_TOKEN_TYPE:
+          encodeVeriToken(exp);
+          break;
+        case KMType.HMAC_SHARING_PARAM_TYPE:
+          encodeHmacSharingParam(exp);
+          break;
+        case KMType.HW_AUTH_TOKEN_TYPE:
+          encodeHwAuthToken(exp);
+          break;
+        case KMType.TAG_TYPE:
+          short tagType = KMTag.getTagType(exp);
+          encodeTag(tagType, exp);
+          break;
+        case KMType.COSE_PAIR_TAG_TYPE:
+          short cosePairTagType = KMCosePairTagType.getTagValueType(exp);
+          encodeCosePairTag(cosePairTagType, exp);
+          break;
+        default:
+          ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+      }
+    }
+  }
+
+  private void encodeCosePairIntegerTag(short exp) {
+    KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp);
+    // push key and value ptr in stack to get encoded.
+    encode(cosePairIntTag.getValuePtr());
+    encode(cosePairIntTag.getKeyPtr());
+  }
+
+  private void encodeCosePairByteBlobTag(short exp) {
+    KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp);
+    // push key and value ptr in stack to get encoded.
+    encode(cosePairByteBlobTag.getValuePtr());
+    encode(cosePairByteBlobTag.getKeyPtr());
+  }
+
+  private void encodeCosePairCoseKeyTag(short exp) {
+    KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp);
+    // push key and value ptr in stack to get encoded.
+    encode(cosePairCoseKeyTag.getValuePtr());
+    encode(cosePairCoseKeyTag.getKeyPtr());
+  }
+
+  private void encodeCosePairTextStringTag(short exp) {
+    KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp);
+    // push key and value ptr in stack to get encoded.
+    encode(cosePairTextStringTag.getValuePtr());
+    encode(cosePairTextStringTag.getKeyPtr());
+  }
+
+  private void encodeCosePairSimpleValueTag(short exp) {
+    KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp);
+    // push key and value ptr in stack to get encoded.
+    encode(cosePairSimpleValueTag.getValuePtr());
+    encode(cosePairSimpleValueTag.getKeyPtr());
+  }
+
+  private void encodeCosePairNegIntegerTag(short exp) {
+    KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp);
+    // push key and value ptr in stack to get encoded.
+    encode(cosePairNegIntegerTag.getValuePtr());
+    encode(cosePairNegIntegerTag.getKeyPtr());
+  }
+
+  private void encodeCosePairTag(short tagType, short exp) {
+    switch (tagType) {
+      case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+        encodeCosePairByteBlobTag(exp);
+        return;
+      case KMType.COSE_PAIR_INT_TAG_TYPE:
+        encodeCosePairIntegerTag(exp);
+        return;
+      case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+        encodeCosePairNegIntegerTag(exp);
+        return;
+      case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+        encodeCosePairSimpleValueTag(exp);
+        return;
+      case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+        encodeCosePairTextStringTag(exp);
+        return;
+      case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+        encodeCosePairCoseKeyTag(exp);
+        return;
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+  }
+
+  private void encodeTag(short tagType, short exp) {
+    switch (tagType) {
+      case KMType.BIGNUM_TAG:
+        encodeBignumTag(exp);
+        return;
+      case KMType.BYTES_TAG:
+        encodeBytesTag(exp);
+        return;
+      case KMType.BOOL_TAG:
+        encodeBoolTag(exp);
+        return;
+      case KMType.UINT_TAG:
+      case KMType.ULONG_TAG:
+      case KMType.DATE_TAG:
+        encodeIntegerTag(exp);
+        return;
+      case KMType.ULONG_ARRAY_TAG:
+      case KMType.UINT_ARRAY_TAG:
+        encodeIntegerArrayTag(exp);
+        return;
+      case KMType.ENUM_TAG:
+        encodeEnumTag(exp);
+        return;
+      case KMType.ENUM_ARRAY_TAG:
+        encodeEnumArrayTag(exp);
+        return;
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+  }
+
+  private void encodeCoseMap(short obj) {
+    encodeAsMap(KMCoseMap.getVals(obj));
+  }
+
+  private void encodeKeyParam(short obj) {
+    encodeAsMap(KMKeyParameters.cast(obj).getVals());
+  }
+
+  private void encodeKeyChar(short obj) {
+    encode(KMKeyCharacteristics.cast(obj).getVals());
+  }
+
+  private void encodeVeriToken(short obj) {
+    encode(KMVerificationToken.cast(obj).getVals());
+  }
+
+  private void encodeHwAuthToken(short obj) {
+    encode(KMHardwareAuthToken.cast(obj).getVals());
+  }
+
+  private void encodeHmacSharingParam(short obj) {
+    encode(KMHmacSharingParameters.cast(obj).getVals());
+  }
+
+  private void encodeArray(short obj) {
+    writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length());
+    short len = KMArray.cast(obj).length();
+    short index = (short) (len - 1);
+    short subObj;
+    while (index >= 0) {
+      subObj = KMArray.cast(obj).get(index);
+      if (subObj != KMType.INVALID_VALUE) {
+        encode(subObj);
+      }
+      index--;
+    }
+  }
+
+  public void encodeArrayOnlyLength(short arrLength, byte[] buffer, short offset, short length) {
+    bufferRef[0] = buffer;
+    scratchBuf[START_OFFSET] = offset;
+    scratchBuf[LEN_OFFSET] = (short) (offset + length + 1);
+    writeMajorTypeWithLength(ARRAY_TYPE, length);
+  }
+
+  private void encodeMap(short obj) {
+    writeMajorTypeWithLength(MAP_TYPE, KMMap.cast(obj).length());
+    short len = KMMap.cast(obj).length();
+    short index = (short) (len - 1);
+    while (index >= 0) {
+      encode(KMMap.cast(obj).getKeyValue(index));
+      encode(KMMap.cast(obj).getKey(index));
+      index--;
+    }
+  }
+
+  private void encodeAsMap(short obj) {
+    writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length());
+    short len = KMArray.cast(obj).length();
+    short index = (short) (len - 1);
+    short inst;
+    while (index >= 0) {
+      inst = KMArray.cast(obj).get(index);
+      encode(inst);
+      index--;
+    }
+  }
+
+  private void encodeIntegerArrayTag(short obj) {
+    writeTag(KMIntegerArrayTag.cast(obj).getTagType(), KMIntegerArrayTag.cast(obj).getKey());
+    encode(KMIntegerArrayTag.cast(obj).getValues());
+  }
+
+  private void encodeEnumArrayTag(short obj) {
+    writeTag(KMEnumArrayTag.cast(obj).getTagType(), KMEnumArrayTag.cast(obj).getKey());
+    encode(KMEnumArrayTag.cast(obj).getValues());
+  }
+
+  private void encodeIntegerTag(short obj) {
+    writeTag(KMIntegerTag.cast(obj).getTagType(), KMIntegerTag.cast(obj).getKey());
+    encode(KMIntegerTag.cast(obj).getValue());
+  }
+
+  private void encodeBignumTag(short obj) {
+    writeTag(KMBignumTag.getTagType(obj), KMBignumTag.getKey(obj));
+    encode(KMBignumTag.cast(obj).getValue());
+  }
+
+  private void encodeBytesTag(short obj) {
+    writeTag(KMByteTag.cast(obj).getTagType(), KMByteTag.cast(obj).getKey());
+    encode(KMByteTag.cast(obj).getValue());
+  }
+
+  private void encodeBoolTag(short obj) {
+    writeTag(KMBoolTag.cast(obj).getTagType(), KMBoolTag.cast(obj).getKey());
+    writeByteValue(KMBoolTag.cast(obj).getVal());
+  }
+
+  private void encodeEnumTag(short obj) {
+    writeTag(KMEnumTag.cast(obj).getTagType(), KMEnumTag.cast(obj).getKey());
+    writeByteValue(KMEnumTag.cast(obj).getValue());
+  }
+
+  private void encodeEnum(short obj) {
+    writeByteValue(KMEnum.cast(obj).getVal());
+  }
+
+  private void encodeInteger(byte[] val, short len, short startOff, short majorType) {
+    // find out the most significant byte
+    short msbIndex = findMsb(val, startOff, len);
+    // find the difference between most significant byte and len
+    short diff = (short) (len - msbIndex);
+    if (diff == 0) {
+      writeByte((byte) (majorType | 0));
+    } else if ((diff == 1)
+        && (val[(short) (startOff + msbIndex)] < UINT8_LENGTH)
+        && (val[(short) (startOff + msbIndex)] >= 0)) {
+      writeByte((byte) (majorType | val[(short) (startOff + msbIndex)]));
+    } else if (diff == 1) {
+      writeByte((byte) (majorType | UINT8_LENGTH));
+      writeByte(val[(short) (startOff + msbIndex)]);
+    } else if (diff == 2) {
+      writeByte((byte) (majorType | UINT16_LENGTH));
+      writeBytes(val, (short) (startOff + msbIndex), (short) 2);
+    } else if (diff <= 4) {
+      writeByte((byte) (majorType | UINT32_LENGTH));
+      writeBytes(val, (short) (startOff + len - 4), (short) 4);
+    } else {
+      writeByte((byte) (majorType | UINT64_LENGTH));
+      writeBytes(val, startOff, (short) 8);
+    }
+  }
+
+  // find out the most significant byte
+  public short findMsb(byte[] buf, short offset, short len) {
+    byte index = 0;
+    // find out the most significant byte
+    while (index < len) {
+      if (buf[(short) (offset + index)] > 0) {
+        break;
+      } else if (buf[(short) (offset + index)] < 0) {
+        break;
+      }
+      index++; // index will be equal to len if value is 0.
+    }
+    return index;
+  }
+
+  public void computeOnesCompliment(short msbIndex, byte[] buf, short offset, short len) {
+    // find the difference between most significant byte and len
+    short diff = (short) (len - msbIndex);
+    short correctedOffset = offset;
+    short correctedLen = len;
+    // The offset and length of the buffer for Short and Byte types should be
+    // corrected before computing the 1s compliment. The reason for doing this
+    // is to avoid computation of 1s compliment on the MSB bytes.
+    if (diff == 0) {
+      // Fail
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    } else if (diff == 1) {
+      correctedOffset = (short) (offset + 3);
+      correctedLen = 1;
+    } else if (diff == 2) {
+      correctedOffset = (short) (offset + 2);
+      correctedLen = 2;
+    }
+    // For int and long values the len and offset values are always proper.
+    // int - 4 bytes
+    // long - 8 bytes.
+    KMUtils.computeOnesCompliment(buf, correctedOffset, correctedLen);
+  }
+
+  // Encoding rule for negative Integers is taken from
+  // https://datatracker.ietf.org/doc/html/rfc7049#section-2.1, Major type 1.
+  public short handleNegIntegerEncodingRule(byte[] buf, short offset, short len) {
+    short msbIndex = findMsb(buf, offset, len);
+    // Do -1-N, where N is the negative integer
+    // The value of -1-N is equal to the 1s compliment of N.
+    computeOnesCompliment(msbIndex, buf, offset, len);
+    return msbIndex;
+  }
+
+  // Note: This function modifies the buffer's actual value. So after encoding, restore the original
+  // value by calling removeNegIntegerEncodingRule().
+  public short applyNegIntegerEncodingRule(byte[] buf, short offset, short len) {
+    return handleNegIntegerEncodingRule(buf, offset, len);
+  }
+
+  public void removeNegIntegerEncodingRule(
+      byte[] buf, short offset, short len, short origMsbIndex) {
+    // Do -1-N, where N is the negative integer
+    // The value of -1-N is equal to the 1s compliment of N.
+    computeOnesCompliment(origMsbIndex, buf, offset, len);
+  }
+
+  private void encodeNegInteger(short obj) {
+    byte[] val = KMNInteger.cast(obj).getBuffer();
+    short len = KMNInteger.cast(obj).length();
+    short startOff = KMNInteger.cast(obj).getStartOff();
+    short msbIndex = applyNegIntegerEncodingRule(val, startOff, len);
+    encodeInteger(val, len, startOff, NEG_INT_TYPE);
+    removeNegIntegerEncodingRule(val, startOff, len, msbIndex);
+  }
+
+  private void encodeSemanticTag(short obj) {
+    short tag = KMSemanticTag.cast(obj).getKeyPtr();
+    encode(KMSemanticTag.cast(obj).getValuePtr());
+    encodeInteger(
+        KMInteger.cast(tag).getBuffer(),
+        KMInteger.cast(tag).length(),
+        KMInteger.cast(tag).getStartOff(),
+        SEMANTIC_TAG_TYPE);
+  }
+
+  private void encodeUnsignedInteger(short obj) {
+    byte[] val = KMInteger.cast(obj).getBuffer();
+    short len = KMInteger.cast(obj).length();
+    short startOff = KMInteger.cast(obj).getStartOff();
+    encodeInteger(val, len, startOff, UINT_TYPE);
+  }
+
+  private void encodeSimpleValue(short obj) {
+    byte value = KMSimpleValue.cast(obj).getValue();
+    writeByte((byte) (SIMPLE_VALUE_TYPE | value));
+  }
+
+  private void encodeTextString(short obj) {
+    writeMajorTypeWithLength(TSTR_TYPE, KMTextString.cast(obj).length());
+    writeBytes(
+        KMTextString.cast(obj).getBuffer(),
+        KMTextString.cast(obj).getStartOff(),
+        KMTextString.cast(obj).length());
+  }
+
+  public short encodeByteBlobHeader(short bufLen, byte[] buffer, short startOff, short length) {
+    bufferRef[0] = buffer;
+    scratchBuf[START_OFFSET] = startOff;
+    scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1);
+    writeMajorTypeWithLength(BYTES_TYPE, bufLen);
+    return (short) (scratchBuf[START_OFFSET] - startOff);
+  }
+
+  private void encodeByteBlob(short obj) {
+    writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length());
+    writeBytes(
+        KMByteBlob.cast(obj).getBuffer(),
+        KMByteBlob.cast(obj).getStartOff(),
+        KMByteBlob.cast(obj).length());
+  }
+
+  public short getEncodedLength(short ptr) {
+    short len = 0;
+    short type = KMType.getType(ptr);
+    switch (type) {
+      case KMType.BYTE_BLOB_TYPE:
+        len += getEncodedByteBlobLength(ptr);
+        break;
+      case KMType.TEXT_STRING_TYPE:
+        len += getEncodedTextStringLength(ptr);
+        break;
+      case KMType.INTEGER_TYPE:
+        len += getEncodedIntegerLength(ptr);
+        break;
+      case KMType.NEG_INTEGER_TYPE:
+        len += getEncodedNegIntegerLength(ptr);
+        break;
+      case KMType.ARRAY_TYPE:
+        len += getEncodedArrayLen(ptr);
+        break;
+      case KMType.MAP_TYPE:
+        len += getEncodedMapLen(ptr);
+        break;
+      case KMType.COSE_PAIR_TAG_TYPE:
+        short cosePairTagType = KMCosePairTagType.getTagValueType(ptr);
+        len += getEncodedCosePairTagLen(cosePairTagType, ptr);
+        break;
+      case KMType.COSE_KEY_TYPE:
+      case KMType.COSE_HEADERS_TYPE:
+      case KMType.COSE_CERT_PAYLOAD_TYPE:
+        len += getEncodedArrayLen(KMCoseMap.getVals(ptr));
+        break;
+      default:
+        KMException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return len;
+  }
+
+  private short getEncodedCosePairTagLen(short tagType, short exp) {
+    short length = 0;
+    switch (tagType) {
+      case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE:
+        KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp);
+        length = getEncodedLength(cosePairByteBlobTag.getKeyPtr());
+        length += getEncodedLength(cosePairByteBlobTag.getValuePtr());
+        break;
+      case KMType.COSE_PAIR_INT_TAG_TYPE:
+        KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp);
+        length = getEncodedLength(cosePairIntTag.getValuePtr());
+        length += getEncodedLength(cosePairIntTag.getKeyPtr());
+        break;
+      case KMType.COSE_PAIR_NEG_INT_TAG_TYPE:
+        KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp);
+        length = getEncodedLength(cosePairNegIntegerTag.getValuePtr());
+        length += getEncodedLength(cosePairNegIntegerTag.getKeyPtr());
+        break;
+      case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE:
+        KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp);
+        length = getEncodedLength(cosePairSimpleValueTag.getValuePtr());
+        length += getEncodedLength(cosePairSimpleValueTag.getKeyPtr());
+        break;
+      case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE:
+        KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp);
+        length = getEncodedLength(cosePairTextStringTag.getValuePtr());
+        length += getEncodedLength(cosePairTextStringTag.getKeyPtr());
+        break;
+      case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE:
+        KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp);
+        length = getEncodedLength(cosePairCoseKeyTag.getValuePtr());
+        length += getEncodedLength(cosePairCoseKeyTag.getKeyPtr());
+        break;
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    return length;
+  }
+
+  private short getEncodedMapLen(short obj) {
+    short mapLen = KMMap.cast(obj).length();
+    short len = getEncodedBytesLength(mapLen);
+    short index = 0;
+    while (index < mapLen) {
+      len += getEncodedLength(KMMap.cast(obj).getKey(index));
+      len += getEncodedLength(KMMap.cast(obj).getKeyValue(index));
+      index++;
+    }
+    return len;
+  }
+
+  private short getEncodedArrayLen(short obj) {
+    short arrLen = KMArray.cast(obj).length();
+    short len = getEncodedBytesLength(arrLen);
+    short index = 0;
+    short subObj;
+    while (index < arrLen) {
+      subObj = KMArray.cast(obj).get(index);
+      if (subObj != KMType.INVALID_VALUE) {
+        len += getEncodedLength(subObj);
+      }
+      index++;
+    }
+    return len;
+  }
+
+  public short getEncodedBytesLength(short len) {
+    short ret = 0;
+    if (len < KMEncoder.UINT8_LENGTH && len >= 0) {
+      ret = 1;
+    } else if (len >= KMEncoder.UINT8_LENGTH && len <= (short) 0x00FF) {
+      ret = 2;
+    } else if (len > (short) 0x00FF && len <= (short) 0x7FFF) {
+      ret = 3;
+    } else {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    return ret;
+  }
+
+  private short getEncodedByteBlobLength(short obj) {
+    short len = KMByteBlob.cast(obj).length();
+    len += getEncodedBytesLength(len);
+    return len;
+  }
+
+  private short getEncodedTextStringLength(short obj) {
+    short len = KMTextString.cast(obj).length();
+    len += getEncodedBytesLength(len);
+    return len;
+  }
+
+  private short getEncodedNegIntegerLength(short obj) {
+    byte[] buf = KMNInteger.cast(obj).getBuffer();
+    short len = KMNInteger.cast(obj).length();
+    short offset = KMNInteger.cast(obj).getStartOff();
+    short msbIndex = applyNegIntegerEncodingRule(buf, offset, len);
+    short ret = getEncodedIntegerLength(buf, offset, len);
+    removeNegIntegerEncodingRule(buf, offset, len, msbIndex);
+    return ret;
+  }
+
+  private short getEncodedIntegerLength(byte[] val, short startOff, short len) {
+    short msbIndex = findMsb(val, startOff, len);
+    // find the difference between most significant byte and len
+    short diff = (short) (len - msbIndex);
+    switch (diff) {
+      case 0:
+      case 1: // Byte
+        if ((val[(short) (startOff + msbIndex)] < KMEncoder.UINT8_LENGTH)
+            && (val[(short) (startOff + msbIndex)] >= 0)) {
+          return (short) 1;
+        } else {
+          return (short) 2;
+        }
+      case 2: // Short
+        return (short) 3;
+      case 3:
+      case 4: // UInt32
+        return (short) 5;
+      case 5:
+      case 6:
+      case 7:
+      case 8: // UInt64
+        return (short) 9;
+      default:
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    return 0;
+  }
+
+  private short getEncodedIntegerLength(short obj) {
+    byte[] val = KMInteger.cast(obj).getBuffer();
+    short len = KMInteger.cast(obj).length();
+    short startOff = KMInteger.cast(obj).getStartOff();
+    return getEncodedIntegerLength(val, startOff, len);
+  }
+
+  private void writeByteValue(byte val) {
+    if ((val < UINT8_LENGTH) && (val >= 0)) {
+      writeByte((byte) (UINT_TYPE | val));
+    } else {
+      writeByte((byte) (UINT_TYPE | UINT8_LENGTH));
+      writeByte(val);
+    }
+  }
+
+  private void writeTag(short tagType, short tagKey) {
+    writeByte((byte) (UINT_TYPE | UINT32_LENGTH));
+    writeShort(tagType);
+    writeShort(tagKey);
+  }
+
+  private void writeMajorTypeWithLength(byte majorType, short len) {
+    if (len <= TINY_PAYLOAD) {
+      writeByte((byte) (majorType | (byte) (len & ADDITIONAL_MASK)));
+    } else if (len < SHORT_PAYLOAD) {
+      writeByte((byte) (majorType | UINT8_LENGTH));
+      writeByte((byte) (len & 0xFF));
+    } else {
+      writeByte((byte) (majorType | UINT16_LENGTH));
+      writeShort(len);
+    }
+  }
+
+  private void writeBytes(byte[] buf, short start, short len) {
+    byte[] buffer = (byte[]) bufferRef[0];
+    Util.arrayCopyNonAtomic(buf, start, buffer, scratchBuf[START_OFFSET], len);
+    incrementStartOff(len);
+  }
+
+  private void writeShort(short val) {
+    byte[] buffer = (byte[]) bufferRef[0];
+    buffer[scratchBuf[START_OFFSET]] = (byte) ((val >> 8) & 0xFF);
+    incrementStartOff((short) 1);
+    buffer[scratchBuf[START_OFFSET]] = (byte) ((val & 0xFF));
+    incrementStartOff((short) 1);
+  }
+
+  private void writeByte(byte val) {
+    byte[] buffer = (byte[]) bufferRef[0];
+    buffer[scratchBuf[START_OFFSET]] = val;
+    incrementStartOff((short) 1);
+  }
+
+  private void incrementStartOff(short inc) {
+    scratchBuf[START_OFFSET] += inc;
+    if (scratchBuf[START_OFFSET] >= scratchBuf[LEN_OFFSET]) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java
new file mode 100644
index 0000000..44bf477
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnum.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnum represents an enumeration specified in android keymaster hal specifications. It
+ * corresponds to uint CBOR type and it is a byte value. struct{byte ENUM_TYPE; short length;
+ * struct{short enumType; byte val}}
+ */
+public class KMEnum extends KMType {
+
+  private static KMEnum prototype;
+
+  // The allowed enum types.
+  private static short[] types = {
+    HARDWARE_TYPE,
+    KEY_FORMAT,
+    KEY_DERIVATION_FUNCTION,
+    VERIFIED_BOOT_STATE,
+    DEVICE_LOCKED,
+    USER_AUTH_TYPE,
+    PURPOSE,
+    ECCURVE,
+    RULE
+  };
+
+  private static Object[] enums = null;
+
+  private KMEnum() {}
+
+  private static KMEnum proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMEnum();
+    }
+    KMType.instanceTable[KM_ENUM_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    return KMType.exp(ENUM_TYPE);
+  }
+
+  public static KMEnum cast(short ptr) {
+    if (heap[ptr] != ENUM_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static short instance(short enumType) {
+    if (!validateEnum(enumType, NO_VALUE)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = KMType.instance(ENUM_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
+    return ptr;
+  }
+
+  public static short instance(short enumType, byte val) {
+    if (!validateEnum(enumType, val)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = KMType.instance(ENUM_TYPE, (short) 3);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), enumType);
+    heap[(short) (ptr + TLV_HEADER_SIZE + 2)] = val;
+    return ptr;
+  }
+
+  private static void create() {
+    // The allowed enum values to corresponding enum types in the types array.
+    if (enums == null) {
+      enums =
+          new Object[] {
+            new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX},
+            new byte[] {X509, PKCS8, RAW},
+            new byte[] {
+              DERIVATION_NONE,
+              RFC5869_SHA256,
+              ISO18033_2_KDF1_SHA1,
+              ISO18033_2_KDF1_SHA256,
+              ISO18033_2_KDF2_SHA1,
+              ISO18033_2_KDF2_SHA256
+            },
+            new byte[] {SELF_SIGNED_BOOT, VERIFIED_BOOT, UNVERIFIED_BOOT, FAILED_BOOT},
+            new byte[] {DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE},
+            new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH},
+            new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY},
+            new byte[] {P_224, P_256, P_384, P_521},
+            new byte[] {IGNORE_INVALID_TAGS, FAIL_ON_INVALID_TAGS}
+          };
+    }
+  }
+
+  // isValidTag enumeration keys and values.
+  private static boolean validateEnum(short key, byte value) {
+    create();
+    byte[] vals;
+    short enumInd;
+    // check if key exists
+    short index = (short) types.length;
+    while (--index >= 0) {
+      if (types[index] == key) {
+        // check if value given
+        if (value != NO_VALUE) {
+          // check if the value exist
+          vals = (byte[]) enums[index];
+          enumInd = (short) vals.length;
+          while (--enumInd >= 0) {
+            if (vals[enumInd] == value) {
+              // return true if value exist
+              return true;
+            }
+          }
+          // return false if value does not exist
+          return false;
+        }
+        // return true if key exist and value not given
+        return true;
+      }
+    }
+    // return false if key does not exist
+    return false;
+  }
+
+  public short length() {
+    return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + 1));
+  }
+
+  public byte getVal() {
+    return heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)];
+  }
+
+  public void setVal(byte val) {
+    heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val;
+  }
+
+  public short getEnumType() {
+    return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public void setEnumType(short type) {
+    Util.setShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java
new file mode 100644
index 0000000..ea73c40
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnumArrayTag represents ENUM_REP tag type. It has following structure, struct{byte TAG_TYPE;
+ * short length; struct{short ENUM_ARRAY_TAG; short tagKey; sequence of byte values}}
+ */
+public class KMEnumArrayTag extends KMTag {
+
+  private static KMEnumArrayTag prototype;
+
+  // The allowed tag keys of enum array type.
+  private static short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING, RSA_OAEP_MGF_DIGEST};
+
+  // Tag Values.
+  private static Object[] enums = null;
+
+  private KMEnumArrayTag() {}
+
+  private static KMEnumArrayTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMEnumArrayTag();
+    }
+    KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short blobPtr = KMByteBlob.exp();
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_ARRAY_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr);
+    return ptr;
+  }
+
+  public static short instance(short key) {
+    byte[] vals = getAllowedEnumValues(key);
+    if (vals == null) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short blobPtr = KMByteBlob.exp();
+    return instance(key, blobPtr);
+  }
+
+  public static short instance(short key, short byteBlob) {
+    byte[] allowedVals = getAllowedEnumValues(key);
+    if (allowedVals == null) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    KMByteBlob blob = KMByteBlob.cast(byteBlob);
+    short byteIndex = 0;
+    short enumIndex;
+    boolean validValue;
+    while (byteIndex < blob.length()) {
+      enumIndex = 0;
+      validValue = false;
+      while (enumIndex < allowedVals.length) {
+        if (blob.get(byteIndex) == allowedVals[enumIndex]) {
+          validValue = true;
+          break;
+        }
+        enumIndex++;
+      }
+      if (!validValue) {
+        ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+      }
+      byteIndex++;
+    }
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_ARRAY_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob);
+    return ptr;
+  }
+
+  public static KMEnumArrayTag cast(short ptr) {
+    if (heap[ptr] != TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_ARRAY_TAG) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static void create() {
+    if (enums == null) {
+      // allowed tag values.
+      enums =
+          new Object[] {
+            new byte[] {ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY},
+            new byte[] {ECB, CBC, CTR, GCM},
+            new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512},
+            new byte[] {
+              PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7
+            },
+            new byte[] {DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512},
+          };
+    }
+  }
+
+  private static byte[] getAllowedEnumValues(short key) {
+    create();
+    short index = (short) tags.length;
+    while (--index >= 0) {
+      if (tags[index] == key) {
+        return (byte[]) enums[index];
+      }
+    }
+    return null;
+  }
+
+  public static short getValues(short tagId, short params, byte[] buf, short start) {
+    short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+    if (tag == KMType.INVALID_VALUE) {
+      return KMType.INVALID_VALUE;
+    }
+    tag = KMEnumArrayTag.cast(tag).getValues();
+    return KMByteBlob.cast(tag).getValues(buf, start);
+  }
+
+  public static boolean contains(short tagId, short tagValue, short params) {
+    short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+    if (tag != KMType.INVALID_VALUE) {
+      short index = 0;
+      while (index < KMEnumArrayTag.cast(tag).length()) {
+        if (tagValue == KMEnumArrayTag.cast(tag).get(index)) {
+          return true;
+        }
+        index++;
+      }
+    }
+    return false;
+  }
+
+  public static short length(short tagId, short params) {
+    short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params);
+    if (tag != KMType.INVALID_VALUE) {
+      return KMEnumArrayTag.cast(tag).length();
+    }
+    return KMType.INVALID_VALUE;
+  }
+
+  public short getKey() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getTagType() {
+    return KMType.ENUM_ARRAY_TAG;
+  }
+
+  public short getValues() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+
+  public short length() {
+    short blobPtr =
+        Util.getShort(
+            heap, (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+    return KMByteBlob.cast(blobPtr).length();
+  }
+
+  public short get(short index) {
+    return KMByteBlob.cast(getValues()).get(index);
+  }
+
+  public boolean contains(short tagValue) {
+    short index = 0;
+    while (index < length()) {
+      if (get(index) == (byte) tagValue) {
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  public boolean isValidDigests(byte alg) {
+    short index = 0;
+    short digest;
+    while (index < length()) {
+      digest = get(index);
+      switch (alg) {
+        case KMType.EC:
+        case KMType.RSA:
+          if (digest != KMType.DIGEST_NONE && digest != KMType.SHA2_256 && digest != KMType.SHA1) {
+            return false;
+          }
+          break;
+        case KMType.HMAC:
+          if (digest != KMType.SHA2_256) {
+            return false;
+          }
+          break;
+        case KMType.AES:
+        case KMType.DES:
+          if (digest != KMType.DIGEST_NONE) {
+            return false;
+          }
+          break;
+        default:
+          return false;
+      }
+      index++;
+    }
+    return true;
+  }
+
+  public boolean isValidPaddingModes(byte alg) {
+    short index = 0;
+    short padding;
+    while (index < length()) {
+      padding = get(index);
+      switch (alg) {
+        case KMType.RSA:
+          if (padding != KMType.RSA_OAEP
+              && padding != KMType.PADDING_NONE
+              && padding != KMType.RSA_PKCS1_1_5_SIGN
+              && padding != KMType.RSA_PKCS1_1_5_ENCRYPT
+              && padding != KMType.RSA_PSS) {
+            return false;
+          }
+          break;
+        case KMType.AES:
+        case KMType.DES:
+          if (padding != KMType.PKCS7 && padding != KMType.PADDING_NONE) {
+            return false;
+          }
+          break;
+        case KMType.EC:
+        case KMType.HMAC:
+          if (padding != PADDING_NONE) {
+            return false;
+          }
+          break;
+        default:
+          return false;
+      }
+      index++;
+    }
+    return true;
+  }
+
+  public boolean isValidPurpose(byte alg) {
+    short index = 0;
+    short purpose;
+    while (index < length()) {
+      purpose = get(index);
+      switch (purpose) {
+        case KMType.DECRYPT:
+        case KMType.ENCRYPT:
+          if (alg != KMType.RSA && alg != KMType.AES && alg != KMType.DES) {
+            return false;
+          }
+          break;
+        case KMType.SIGN:
+        case KMType.VERIFY:
+          if (alg != KMType.HMAC && alg != KMType.RSA && alg != KMType.EC) {
+            return false;
+          }
+          break;
+        case KMType.WRAP_KEY:
+          if (alg != KMType.RSA) {
+            return false;
+          }
+          break;
+        default:
+          return false;
+      }
+      index++;
+    }
+    return true;
+  }
+
+  public boolean isValidBlockMode(byte alg) {
+    if (alg == KMType.AES || alg == KMType.DES) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
new file mode 100644
index 0000000..a7bcbe6
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMEnumTag.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMEnumTag represents ENUM Tag type specified in android keymaster hal specifications. struct{byte
+ * TAG_TYPE; short length; struct{short ENUM_TAG; short tagKey; byte value}}
+ */
+public class KMEnumTag extends KMTag {
+
+  private static KMEnumTag prototype;
+
+  // The allowed tag keys of type enum tag.
+  private static short[] tags = {
+    ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE
+  };
+
+  private static Object[] enums = null;
+
+  private KMEnumTag() {}
+
+  private static KMEnumTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMEnumTag();
+    }
+    KMType.instanceTable[KM_ENUM_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    short ptr = instance(TAG_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+    return ptr;
+  }
+
+  public static short instance(short key) {
+    if (!validateEnum(key, NO_VALUE)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = KMType.instance(TAG_TYPE, (short) 4);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    return ptr;
+  }
+
+  public static short instance(short key, byte val) {
+    if (!validateEnum(key, val)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(TAG_TYPE, (short) 5);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), ENUM_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    heap[(short) (ptr + TLV_HEADER_SIZE + 4)] = val;
+    return ptr;
+  }
+
+  public static KMEnumTag cast(short ptr) {
+    if (heap[ptr] != TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != ENUM_TAG) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static void create() {
+    if (enums == null) {
+      // enum tag values.
+      enums =
+          new Object[] {
+            new byte[] {RSA, DES, EC, AES, HMAC},
+            new byte[] {P_224, P_256, P_384, P_521, CURVE_25519},
+            new byte[] {STANDALONE, REQUIRES_FILE_SYSTEM},
+            new byte[] {USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY},
+            new byte[] {GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED},
+            new byte[] {SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX}
+          };
+    }
+  }
+
+  // isValidTag enumeration keys and values.
+  private static boolean validateEnum(short key, byte value) {
+    create();
+    byte[] vals;
+    short enumInd;
+    // check if key exists
+    short index = (short) tags.length;
+    while (--index >= 0) {
+      if (tags[index] == key) {
+        // check if value given
+        if (value != NO_VALUE) {
+          // check if the value exist
+          vals = (byte[]) enums[index];
+          enumInd = (short) vals.length;
+          while (--enumInd >= 0) {
+            if (vals[enumInd] == value) {
+              // return true if value exist
+              return true;
+            }
+          }
+          // return false if value does not exist
+          return false;
+        }
+        // return true if key exist and value not given
+        return true;
+      }
+    }
+    // return false if key does not exist
+    return false;
+  }
+
+  public static short getValue(short tagKey, short keyParameters) {
+    short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagKey, keyParameters);
+    if (tagPtr != KMType.INVALID_VALUE) {
+      return heap[(short) (tagPtr + TLV_HEADER_SIZE + 4)];
+    }
+    return KMType.INVALID_VALUE;
+  }
+
+  public short getKey() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getTagType() {
+    return KMType.ENUM_TAG;
+  }
+
+  public byte getValue() {
+    return heap[(short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMError.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMError.java
new file mode 100644
index 0000000..bbae870
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMError.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+/**
+ * KMError includes all the error codes from android keymaster hal specifications. The values are
+ * positive unlike negative values in keymaster hal.
+ */
+public class KMError {
+
+  public static final short OK = 0;
+  public static final short UNSUPPORTED_PURPOSE = 2;
+  public static final short INCOMPATIBLE_PURPOSE = 3;
+  public static final short UNSUPPORTED_ALGORITHM = 4;
+  public static final short INCOMPATIBLE_ALGORITHM = 5;
+  public static final short UNSUPPORTED_KEY_SIZE = 6;
+  public static final short UNSUPPORTED_BLOCK_MODE = 7;
+  public static final short INCOMPATIBLE_BLOCK_MODE = 8;
+  public static final short UNSUPPORTED_MAC_LENGTH = 9;
+  public static final short UNSUPPORTED_PADDING_MODE = 10;
+  public static final short INCOMPATIBLE_PADDING_MODE = 11;
+  public static final short UNSUPPORTED_DIGEST = 12;
+  public static final short INCOMPATIBLE_DIGEST = 13;
+
+  public static final short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19;
+
+  /** For PKCS8 & PKCS12 */
+  public static final short INVALID_INPUT_LENGTH = 21;
+
+  public static final short KEY_USER_NOT_AUTHENTICATED = 26;
+  public static final short INVALID_OPERATION_HANDLE = 28;
+  public static final short INSUFFICIENT_BUFFER_SPACE = 29;
+  public static final short VERIFICATION_FAILED = 30;
+  public static final short TOO_MANY_OPERATIONS = 31;
+  public static final short INVALID_KEY_BLOB = 33;
+
+  public static final short INVALID_ARGUMENT = 38;
+  public static final short UNSUPPORTED_TAG = 39;
+  public static final short INVALID_TAG = 40;
+  public static final short IMPORT_PARAMETER_MISMATCH = 44;
+  public static final short OPERATION_CANCELLED = 46;
+
+  public static final short MISSING_NONCE = 51;
+  public static final short INVALID_NONCE = 52;
+  public static final short MISSING_MAC_LENGTH = 53;
+  public static final short CALLER_NONCE_PROHIBITED = 55;
+  public static final short KEY_MAX_OPS_EXCEEDED = 56;
+  public static final short INVALID_MAC_LENGTH = 57;
+  public static final short MISSING_MIN_MAC_LENGTH = 58;
+  public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59;
+  public static final short UNSUPPORTED_EC_CURVE = 61;
+  public static final short KEY_REQUIRES_UPGRADE = 62;
+
+  public static final short ATTESTATION_CHALLENGE_MISSING = 63;
+  public static final short ATTESTATION_APPLICATION_ID_MISSING = 65;
+  public static final short CANNOT_ATTEST_IDS = 66;
+  public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67;
+
+  public static final short NO_USER_CONFIRMATION = 71;
+  public static final short DEVICE_LOCKED = 72;
+  public static final short EARLY_BOOT_ENDED = 73;
+  public static final short ATTESTATION_KEYS_NOT_PROVISIONED = 74;
+  public static final short INCOMPATIBLE_MGF_DIGEST = 78;
+  public static final short UNSUPPORTED_MGF_DIGEST = 79;
+  public static final short MISSING_NOT_BEFORE = 80;
+  public static final short MISSING_NOT_AFTER = 81;
+  public static final short MISSING_ISSUER_SUBJECT_NAME = 82;
+  public static final short INVALID_ISSUER_SUBJECT_NAME = 83;
+
+  public static final short UNIMPLEMENTED = 100;
+  public static final short UNKNOWN_ERROR = 1000;
+
+  // Extended errors
+  public static final short SW_CONDITIONS_NOT_SATISFIED = 10001;
+  public static final short UNSUPPORTED_CLA = 10002;
+  public static final short INVALID_P1P2 = 10003;
+  public static final short UNSUPPORTED_INSTRUCTION = 10004;
+  public static final short CMD_NOT_ALLOWED = 10005;
+  public static final short SW_WRONG_LENGTH = 10006;
+  public static final short INVALID_DATA = 10007;
+
+  // Crypto errors
+  public static final short CRYPTO_ILLEGAL_USE = 10008;
+  public static final short CRYPTO_ILLEGAL_VALUE = 10009;
+  public static final short CRYPTO_INVALID_INIT = 10010;
+  public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011;
+  public static final short CRYPTO_UNINITIALIZED_KEY = 10012;
+  // Generic Unknown error.
+  public static final short GENERIC_UNKNOWN_ERROR = 10013;
+
+  // Remote key provisioning error codes.
+  public static final short STATUS_FAILED = 32000;
+  public static final short STATUS_INVALID_MAC = 32001;
+  public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002;
+  public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003;
+  public static final short STATUS_INVALID_EEK = 32004;
+  public static final short INVALID_STATE = 32005;
+
+  public static short translate(short err) {
+    switch (err) {
+      case SW_CONDITIONS_NOT_SATISFIED:
+      case UNSUPPORTED_CLA:
+      case INVALID_P1P2:
+      case INVALID_DATA:
+      case CRYPTO_ILLEGAL_USE:
+      case CRYPTO_ILLEGAL_VALUE:
+      case CRYPTO_INVALID_INIT:
+      case CRYPTO_UNINITIALIZED_KEY:
+      case GENERIC_UNKNOWN_ERROR:
+      case CMD_NOT_ALLOWED:
+      case UNKNOWN_ERROR:
+        return UNKNOWN_ERROR;
+      case CRYPTO_NO_SUCH_ALGORITHM:
+        return UNSUPPORTED_ALGORITHM;
+      case UNSUPPORTED_INSTRUCTION:
+      case SW_WRONG_LENGTH:
+        return UNIMPLEMENTED;
+    }
+    return err;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java
new file mode 100644
index 0000000..e6b1d37
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMHardwareAuthToken represents HardwareAuthToken structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte HW_AUTH_TOKEN_TYPE; short
+ * length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following elements:
+ * {KMInteger Challenge; KMInteger UserId; KMInteger AuthenticatorId; UserAuthType
+ * HwAuthenticatorId; KMInteger TimeStamp; KMByteBlob Mac}
+ */
+public class KMHardwareAuthToken extends KMType {
+
+  public static final byte CHALLENGE = 0x00;
+  public static final byte USER_ID = 0x01;
+  public static final byte AUTHENTICATOR_ID = 0x02;
+  public static final byte HW_AUTHENTICATOR_TYPE = 0x03;
+  public static final byte TIMESTAMP = 0x04;
+  public static final byte MAC = 0x05;
+
+  private static KMHardwareAuthToken prototype;
+
+  private KMHardwareAuthToken() {}
+
+  public static short exp() {
+    short arrPtr = KMArray.instance((short) 6);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add(CHALLENGE, KMInteger.exp());
+    arr.add(USER_ID, KMInteger.exp());
+    arr.add(AUTHENTICATOR_ID, KMInteger.exp());
+    arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE));
+    arr.add(TIMESTAMP, KMInteger.exp());
+    arr.add(MAC, KMByteBlob.exp());
+    return instance(arrPtr);
+  }
+
+  private static KMHardwareAuthToken proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMHardwareAuthToken();
+    }
+    KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short instance() {
+    short arrPtr = KMArray.instance((short) 6);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add(CHALLENGE, KMInteger.uint_16((short) 0));
+    arr.add(USER_ID, KMInteger.uint_16((short) 0));
+    arr.add(AUTHENTICATOR_ID, KMInteger.uint_16((short) 0));
+    arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE, KMType.USER_AUTH_NONE));
+    arr.add(TIMESTAMP, KMInteger.uint_16((short) 0));
+    arr.add(MAC, KMByteBlob.instance((short) 0));
+    return instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    KMArray arr = KMArray.cast(vals);
+    if (arr.length() != 6) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    short ptr = KMType.instance(HW_AUTH_TOKEN_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMHardwareAuthToken cast(short ptr) {
+    if (heap[ptr] != HW_AUTH_TOKEN_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public short getVals() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  public short getChallenge() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(CHALLENGE);
+  }
+
+  public void setChallenge(short vals) {
+    KMInteger.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(CHALLENGE, vals);
+  }
+
+  public short getUserId() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(USER_ID);
+  }
+
+  public void setUserId(short vals) {
+    KMInteger.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(USER_ID, vals);
+  }
+
+  public short getAuthenticatorId() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(AUTHENTICATOR_ID);
+  }
+
+  public void setAuthenticatorId(short vals) {
+    KMInteger.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(AUTHENTICATOR_ID, vals);
+  }
+
+  public short getHwAuthenticatorType() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(HW_AUTHENTICATOR_TYPE);
+  }
+
+  public void setHwAuthenticatorType(short vals) {
+    KMEnum.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals);
+  }
+
+  public short getTimestamp() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(TIMESTAMP);
+  }
+
+  public void setTimestamp(short vals) {
+    KMInteger.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(TIMESTAMP, vals);
+  }
+
+  public short getMac() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(MAC);
+  }
+
+  public void setMac(short vals) {
+    KMByteBlob.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(MAC, vals);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java
new file mode 100644
index 0000000..8642803
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMHmacSharingParameters represents HmacSharingParameters structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte HMAC_SHARING_PARAM_TYPE; short
+ * length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following elements:
+ * {KMByteBlob Seed; KMByteBlob Nonce}
+ */
+public class KMHmacSharingParameters extends KMType {
+
+  public static final byte SEED = 0x00;
+  public static final byte NONCE = 0x01;
+
+  private static KMHmacSharingParameters prototype;
+
+  private KMHmacSharingParameters() {}
+
+  public static short exp() {
+    short arrPtr = KMArray.instance((short) 2);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add(SEED, KMByteBlob.exp());
+    arr.add(NONCE, KMByteBlob.exp());
+    return instance(arrPtr);
+  }
+
+  private static KMHmacSharingParameters proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMHmacSharingParameters();
+    }
+    KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short instance() {
+    short arrPtr = KMArray.instance((short) 2);
+    return instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    short ptr = KMType.instance(HMAC_SHARING_PARAM_TYPE, (short) 2);
+    if (KMArray.cast(vals).length() != 2) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMHmacSharingParameters cast(short ptr) {
+    if (heap[ptr] != HMAC_SHARING_PARAM_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public short getVals() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  public short getNonce() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(NONCE);
+  }
+
+  public void setNonce(short vals) {
+    KMByteBlob.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(NONCE, vals);
+  }
+
+  public short getSeed() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(SEED);
+  }
+
+  public void setSeed(short vals) {
+    KMByteBlob.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(SEED, vals);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMInteger.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMInteger.java
new file mode 100644
index 0000000..b09de0f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMInteger.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * Represents 8 bit, 16 bit, 32 bit and 64 bit unsigned integer. It corresponds to CBOR uint type.
+ * struct{byte INTEGER_TYPE; short length; 4 or 8 bytes of value}
+ */
+public class KMInteger extends KMType {
+
+  public static final byte UINT_32 = 4;
+  public static final byte UINT_64 = 8;
+  private static KMInteger prototype;
+
+  protected KMInteger() {}
+
+  private static KMInteger proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMInteger();
+    }
+    KMType.instanceTable[KM_INTEGER_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // | TYPE(1) | LEN(2) | DATA(4 / 8) |
+  public static short exp() {
+    return KMType.exp(INTEGER_TYPE);
+  }
+
+  // return an empty integer instance
+  public static short instance(short length) {
+    if ((length <= 0) || (length > 8)) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    if (length > 4) {
+      length = UINT_64;
+    } else {
+      length = UINT_32;
+    }
+    return KMType.instance(INTEGER_TYPE, length);
+  }
+
+  public static short instance(byte[] num, short srcOff, short length) {
+    if (length > 8) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    if (length == 1) {
+      return uint_8(num[srcOff]);
+    } else if (length == 2) {
+      return uint_16(Util.getShort(num, srcOff));
+    } else if (length == 4) {
+      return uint_32(num, srcOff);
+    } else {
+      return uint_64(num, srcOff);
+    }
+  }
+
+  public static KMInteger cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != INTEGER_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  // create integer and copy byte value
+  public static short uint_8(byte num) {
+    short ptr = instance(UINT_32);
+    heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num;
+    return ptr;
+  }
+
+  // create integer and copy short value
+  public static short uint_16(short num) {
+    short ptr = instance(UINT_32);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num);
+    return ptr;
+  }
+
+  // create integer and copy integer value
+  public static short uint_32(byte[] num, short offset) {
+    short ptr = instance(UINT_32);
+    Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32);
+    return ptr;
+  }
+
+  // create integer and copy integer value
+  public static short uint_64(byte[] num, short offset) {
+    short ptr = instance(UINT_64);
+    Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64);
+    return ptr;
+  }
+
+  public static short compare(short num1, short num2) {
+    short num1Buf = repository.alloc((short) 8);
+    short num2Buf = repository.alloc((short) 8);
+    Util.arrayFillNonAtomic(repository.getHeap(), num1Buf, (short) 8, (byte) 0);
+    Util.arrayFillNonAtomic(repository.getHeap(), num2Buf, (short) 8, (byte) 0);
+    short len = KMInteger.cast(num1).length();
+    KMInteger.cast(num1).getValue(repository.getHeap(), (short) (num1Buf + (short) (8 - len)), len);
+    len = KMInteger.cast(num2).length();
+    KMInteger.cast(num2).getValue(repository.getHeap(), (short) (num2Buf + (short) (8 - len)), len);
+    return KMInteger.unsignedByteArrayCompare(
+        repository.getHeap(), num1Buf, repository.getHeap(), num2Buf, (short) 8);
+  }
+
+  public static byte unsignedByteArrayCompare(
+      byte[] a1, short offset1, byte[] a2, short offset2, short length) {
+    byte count = (byte) 0;
+    short val1 = (short) 0;
+    short val2 = (short) 0;
+
+    for (; count < length; count++) {
+      val1 = (short) (a1[(short) (count + offset1)] & 0x00FF);
+      val2 = (short) (a2[(short) (count + offset2)] & 0x00FF);
+
+      if (val1 < val2) {
+        return -1;
+      }
+      if (val1 > val2) {
+        return 1;
+      }
+    }
+    return 0;
+  }
+
+  // Get the length of the integer
+  public short length() {
+    return Util.getShort(heap, (short) (getBaseOffset() + 1));
+  }
+
+  // Get the buffer pointer in which blob is contained.
+  public byte[] getBuffer() {
+    return heap;
+  }
+
+  // Get the start of value
+  public short getStartOff() {
+    return (short) (getBaseOffset() + TLV_HEADER_SIZE);
+  }
+
+  public void getValue(byte[] dest, short destOff, short length) {
+    if (length < length()) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    if (length > length()) {
+      length = length();
+      destOff += length;
+    }
+    Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length);
+  }
+
+  public void setValue(byte[] src, short srcOff) {
+    Util.arrayCopyNonAtomic(src, srcOff, heap, getStartOff(), length());
+  }
+
+  public short value(byte[] dest, short destOff) {
+    Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length());
+    return length();
+  }
+
+  public short toLittleEndian(byte[] dest, short destOff) {
+    short index = (short) (length() - 1);
+    while (index >= 0) {
+      dest[destOff++] = heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + index)];
+      index--;
+    }
+    return length();
+  }
+
+  public short getShort() {
+    return Util.getShort(heap, (short) (getStartOff() + 2));
+  }
+
+  public short getSignificantShort() {
+    return Util.getShort(heap, getStartOff());
+  }
+
+  public byte getByte() {
+    return heap[(short) (getStartOff() + 3)];
+  }
+
+  public boolean isZero() {
+    if (getShort() == 0 && getSignificantShort() == 0) {
+      return true;
+    }
+    return false;
+  }
+
+  protected short getBaseOffset() {
+    return instanceTable[KM_INTEGER_OFFSET];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java
new file mode 100644
index 0000000..bf45981
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMIntegerArrayTag represents UINT_REP and ULONG_REP tags specified in keymaster hal specs.
+ * struct{byte TAG_TYPE; short length; struct{short UINT_TAG/ULONG_TAG; short tagKey; short arrPtr},
+ * where arrPtr is the pointer to KMArray of KMInteger instances.
+ */
+public class KMIntegerArrayTag extends KMTag {
+
+  private static final short[] tags = {USER_SECURE_ID};
+  private static KMIntegerArrayTag prototype;
+
+  private KMIntegerArrayTag() {}
+
+  private static KMIntegerArrayTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMIntegerArrayTag();
+    }
+    KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp(short tagType) {
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short arrPtr = KMArray.exp(KMInteger.exp());
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), arrPtr);
+    return ptr;
+  }
+
+  public static short instance(short tagType, short key) {
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (!validateKey(key)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short arrPtr = KMArray.exp();
+    return instance(tagType, key, arrPtr);
+  }
+
+  public static short instance(short tagType, short key, short arrObj) {
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (!validateKey(key)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (heap[arrObj] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), arrObj);
+    return ptr;
+  }
+
+  public static KMIntegerArrayTag cast(short ptr) {
+    if (heap[ptr] != TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  private static boolean validateKey(short key) {
+    short index = (short) tags.length;
+    while (--index >= 0) {
+      if (tags[index] == key) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private static boolean validateTagType(short tagType) {
+    return (tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG);
+  }
+
+  public static boolean contains(short tagId, short tagValue, short params) {
+    short tag = KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params);
+    if (tag != KMType.INVALID_VALUE) {
+      short index = 0;
+      tag = KMIntegerArrayTag.cast(tag).getValues();
+      while (index < KMArray.cast(tag).length()) {
+        if (KMInteger.compare(tagValue, KMArray.cast(tag).get(index)) == 0) {
+          return true;
+        }
+        index++;
+      }
+    }
+    return false;
+  }
+
+  public short getTagType() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short getKey() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getValues() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+
+  public short length() {
+    short ptr = getValues();
+    return KMArray.cast(ptr).length();
+  }
+
+  public void add(short index, short val) {
+    KMArray arr = KMArray.cast(getValues());
+    arr.add(index, val);
+  }
+
+  public short get(short index) {
+    KMArray arr = KMArray.cast(getValues());
+    return arr.get(index);
+  }
+
+  public boolean contains(short tagValue) {
+    short index = 0;
+    while (index < length()) {
+      if (KMInteger.compare(tagValue, get(index)) == 0) {
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java
new file mode 100644
index 0000000..d4c4458
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMIntegerTag represents UINT, ULONG and DATE tags specified in keymaster hal specs. struct{byte
+ * TAG_TYPE; short length; struct{short UINT_TAG/ULONG_TAG/DATE_TAG; short tagKey; 4 or 8 byte
+ * value}}
+ */
+public class KMIntegerTag extends KMTag {
+
+  // Allowed tag keys.
+  private static final short[] tags = {
+    // UINT
+    KEYSIZE,
+    MIN_MAC_LENGTH,
+    MIN_SEC_BETWEEN_OPS,
+    MAX_USES_PER_BOOT,
+    USERID,
+    AUTH_TIMEOUT,
+    OS_VERSION,
+    OS_PATCH_LEVEL,
+    VENDOR_PATCH_LEVEL,
+    BOOT_PATCH_LEVEL,
+    MAC_LENGTH,
+    // ULONG
+    RSA_PUBLIC_EXPONENT,
+    // DATE
+    ACTIVE_DATETIME,
+    ORIGINATION_EXPIRE_DATETIME,
+    USAGE_EXPIRE_DATETIME,
+    CREATION_DATETIME,
+    CERTIFICATE_NOT_BEFORE,
+    CERTIFICATE_NOT_AFTER,
+    USAGE_COUNT_LIMIT,
+    // custom tag
+    AUTH_TIMEOUT_MILLIS,
+  };
+  private static KMIntegerTag prototype;
+
+  private KMIntegerTag() {}
+
+  private static KMIntegerTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMIntegerTag();
+    }
+    KMType.instanceTable[KM_INTEGER_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp(short tagType) {
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short intPtr = KMInteger.exp();
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intPtr);
+    return ptr;
+  }
+
+  public static short instance(short tagType, short key) {
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (!validateKey(key)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short intPtr = KMInteger.exp();
+    return instance(tagType, key, intPtr);
+  }
+
+  public static short instance(short tagType, short key, short intObj) {
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (!validateKey(key)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (heap[intObj] != INTEGER_TYPE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), tagType);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), intObj);
+    return ptr;
+  }
+
+  public static KMIntegerTag cast(short ptr) {
+    if (heap[ptr] != TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short tagType = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (!validateTagType(tagType)) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  private static boolean validateKey(short key) {
+    short index = (short) tags.length;
+    while (--index >= 0) {
+      if (tags[index] == key) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private static boolean validateTagType(short tagType) {
+    return (tagType == DATE_TAG) || (tagType == UINT_TAG) || (tagType == ULONG_TAG);
+  }
+
+  public static short getShortValue(short tagType, short tagKey, short keyParameters) {
+    short ptr;
+    if (tagType == UINT_TAG) {
+      ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters);
+      if (ptr != KMType.INVALID_VALUE) {
+        ptr = KMIntegerTag.cast(ptr).getValue();
+        if (KMInteger.cast(ptr).getSignificantShort() == 0) {
+          return KMInteger.cast(ptr).getShort();
+        }
+      }
+    }
+    return KMType.INVALID_VALUE;
+  }
+
+  public static short getValue(
+      byte[] buf, short offset, short tagType, short tagKey, short keyParameters) {
+    short ptr;
+    if ((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)) {
+      ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters);
+      if (ptr != KMType.INVALID_VALUE) {
+        ptr = KMIntegerTag.cast(ptr).getValue();
+        return KMInteger.cast(ptr).value(buf, offset);
+      }
+    }
+    return KMType.INVALID_VALUE;
+  }
+
+  public short getTagType() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short getKey() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getValue() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+
+  public short length() {
+    KMInteger obj = KMInteger.cast(getValue());
+    return obj.length();
+  }
+
+  public boolean isValidKeySize(byte alg) {
+    short val = KMIntegerTag.cast(KMType.instanceTable[KM_INTEGER_TAG_OFFSET]).getValue();
+    if (KMInteger.cast(val).getSignificantShort() != 0) {
+      return false;
+    }
+    val = KMInteger.cast(val).getShort();
+    switch (alg) {
+      case KMType.RSA:
+        if (val == 2048) {
+          return true;
+        }
+        break;
+      case KMType.AES:
+        if (val == 128 || val == 256) {
+          return true;
+        }
+        break;
+      case KMType.DES:
+        if (val == 168) {
+          return true;
+        }
+        break;
+      case KMType.EC:
+        if (val == 256) {
+          return true;
+        }
+        break;
+      case KMType.HMAC:
+        if (val % 8 == 0 && val >= 64 && val <= 512) {
+          return true;
+        }
+        break;
+      default:
+        break;
+    }
+    return false;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java
new file mode 100644
index 0000000..37b8b7f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMKeyCharacteristics represents KeyCharacteristics structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte KEY_CHAR_TYPE; short length=3;
+ * short arrayPtr} where arrayPtr is a pointer to ordered array with 1 or 3 following elements:
+ * {KMKeyParameters sb; KMKeyParameters tee; KMKeyParameters keystore}
+ */
+public class KMKeyCharacteristics extends KMType {
+
+  public static final byte STRONGBOX_ENFORCED = 0x00;
+  public static final byte TEE_ENFORCED = 0x01;
+  public static final byte KEYSTORE_ENFORCED = 0x02;
+  private static KMKeyCharacteristics prototype;
+
+  private KMKeyCharacteristics() {}
+
+  public static short exp() {
+    short keyParamExp = KMKeyParameters.exp();
+    short arrPtr = KMArray.instance((short) 3);
+
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add(STRONGBOX_ENFORCED, keyParamExp);
+    arr.add(TEE_ENFORCED, keyParamExp);
+    arr.add(KEYSTORE_ENFORCED, keyParamExp);
+    return instance(arrPtr);
+  }
+
+  private static KMKeyCharacteristics proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMKeyCharacteristics();
+    }
+    KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short instance() {
+    short arrPtr = KMArray.instance((short) 3);
+    return instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    short ptr = KMType.instance(KEY_CHAR_TYPE, (short) 3);
+    if (KMArray.cast(vals).length() != 3) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMKeyCharacteristics cast(short ptr) {
+    if (heap[ptr] != KEY_CHAR_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public short getVals() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  public short getKeystoreEnforced() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(KEYSTORE_ENFORCED);
+  }
+
+  public void setKeystoreEnforced(short ptr) {
+    KMKeyParameters.cast(ptr);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(KEYSTORE_ENFORCED, ptr);
+  }
+
+  public short getTeeEnforced() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(TEE_ENFORCED);
+  }
+
+  public void setTeeEnforced(short ptr) {
+    KMKeyParameters.cast(ptr);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(TEE_ENFORCED, ptr);
+  }
+
+  public short getStrongboxEnforced() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(STRONGBOX_ENFORCED);
+  }
+
+  public void setStrongboxEnforced(short ptr) {
+    KMKeyParameters.cast(ptr);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(STRONGBOX_ENFORCED, ptr);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java
new file mode 100644
index 0000000..54ab6ee
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMKeyParameters represents KeyParameters structure from android keymaster hal specifications. It
+ * corresponds to CBOR map type. struct{byte KEY_PARAM_TYPE; short length=2; short arrayPtr} where
+ * arrayPtr is a pointer to array with any KMTag subtype instances.
+ */
+public class KMKeyParameters extends KMType {
+
+  private static final short[] customTags = {
+    KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS,
+  };
+  private static final short[] tagArr = {
+    // Unsupported tags.
+    KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED,
+    KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS
+  };
+  private static final short[] hwEnforcedTagArr = {
+    // HW Enforced
+    KMType.ENUM_ARRAY_TAG, KMType.PURPOSE,
+    KMType.ENUM_TAG, KMType.ALGORITHM,
+    KMType.UINT_TAG, KMType.KEYSIZE,
+    KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT,
+    KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ,
+    KMType.ENUM_ARRAY_TAG, KMType.DIGEST,
+    KMType.ENUM_ARRAY_TAG, KMType.PADDING,
+    KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE,
+    KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST,
+    KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED,
+    KMType.BOOL_TAG, KMType.CALLER_NONCE,
+    KMType.UINT_TAG, KMType.MIN_MAC_LENGTH,
+    KMType.ENUM_TAG, KMType.ECCURVE,
+    KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID,
+    KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE,
+    KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY,
+    KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY,
+    KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT,
+  };
+  private static final short[] swEnforcedTagsArr = {
+    KMType.DATE_TAG, KMType.ACTIVE_DATETIME,
+    KMType.DATE_TAG, KMType.ORIGINATION_EXPIRE_DATETIME,
+    KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME,
+    KMType.UINT_TAG, KMType.USERID,
+    KMType.DATE_TAG, KMType.CREATION_DATETIME,
+    KMType.UINT_TAG, KMType.USAGE_COUNT_LIMIT,
+    KMType.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY,
+    KMType.UINT_TAG, KMType.MAX_BOOT_LEVEL,
+  };
+  private static final short[] teeEnforcedTagsArr = {
+    KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID,
+    KMType.UINT_TAG, KMType.AUTH_TIMEOUT,
+    KMType.ENUM_TAG, KMType.USER_AUTH_TYPE,
+    KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED,
+    KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED,
+  };
+  private static final short[] invalidTagsArr = {
+    KMType.BYTES_TAG, KMType.NONCE,
+    KMType.BYTES_TAG, KMType.ASSOCIATED_DATA,
+    KMType.BYTES_TAG, KMType.UNIQUE_ID,
+    KMType.UINT_TAG, KMType.MAC_LENGTH,
+  };
+  private static KMKeyParameters prototype;
+
+  private KMKeyParameters() {}
+
+  private static KMKeyParameters proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMKeyParameters();
+    }
+    KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp() {
+    short arrPtr = KMArray.instance((short) 11);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.FAIL_ON_INVALID_TAGS));
+    arr.add((short) 1, KMIntegerTag.exp(UINT_TAG));
+    arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG));
+    arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG));
+    arr.add((short) 4, KMIntegerTag.exp(DATE_TAG));
+    arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG));
+    arr.add((short) 6, KMEnumTag.exp());
+    arr.add((short) 7, KMEnumArrayTag.exp());
+    arr.add((short) 8, KMByteTag.exp());
+    arr.add((short) 9, KMBoolTag.exp());
+    arr.add((short) 10, KMBignumTag.exp());
+    return instance(arrPtr);
+  }
+
+  public static short expAny() {
+    short arrPtr = KMArray.instance((short) 11);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add((short) 0, KMEnum.instance(KMType.RULE, KMType.IGNORE_INVALID_TAGS));
+    arr.add((short) 1, KMIntegerTag.exp(UINT_TAG));
+    arr.add((short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG));
+    arr.add((short) 3, KMIntegerTag.exp(ULONG_TAG));
+    arr.add((short) 4, KMIntegerTag.exp(DATE_TAG));
+    arr.add((short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG));
+    arr.add((short) 6, KMEnumTag.exp());
+    arr.add((short) 7, KMEnumArrayTag.exp());
+    arr.add((short) 8, KMByteTag.exp());
+    arr.add((short) 9, KMBoolTag.exp());
+    arr.add((short) 10, KMBignumTag.exp());
+    return instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMKeyParameters cast(short ptr) {
+    if (heap[ptr] != KEY_PARAM_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static short findTag(short tagType, short tagKey, short keyParam) {
+    KMKeyParameters instParam = KMKeyParameters.cast(keyParam);
+    return instParam.findTag(tagType, tagKey);
+  }
+
+  public static boolean hasUnsupportedTags(short keyParamsPtr) {
+    byte index = 0;
+    short tagInd;
+    short tagPtr;
+    short tagKey;
+    short tagType;
+    short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+    short len = KMArray.cast(arrPtr).length();
+    while (index < len) {
+      tagInd = 0;
+      tagPtr = KMArray.cast(arrPtr).get(index);
+      tagKey = KMTag.getKey(tagPtr);
+      tagType = KMTag.getTagType(tagPtr);
+      while (tagInd < (short) tagArr.length) {
+        if ((tagArr[tagInd] == tagType) && (tagArr[(short) (tagInd + 1)] == tagKey)) {
+          return true;
+        }
+        tagInd += 2;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal
+  public static short makeSbEnforced(
+      short keyParamsPtr,
+      byte origin,
+      short osVersionObjPtr,
+      short osPatchObjPtr,
+      short vendorPatchObjPtr,
+      short bootPatchObjPtr,
+      byte[] scratchPad) {
+    byte index = 0;
+    short tagInd;
+    short arrInd = 0;
+    short tagPtr;
+    short tagKey;
+    short tagType;
+    short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+    short len = KMArray.cast(arrPtr).length();
+    while (index < len) {
+      tagInd = 0;
+      tagPtr = KMArray.cast(arrPtr).get(index);
+      tagKey = KMTag.getKey(tagPtr);
+      tagType = KMTag.getTagType(tagPtr);
+      if (!isValidTag(tagType, tagKey)) {
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+      }
+      while (tagInd < (short) hwEnforcedTagArr.length) {
+        if ((hwEnforcedTagArr[tagInd] == tagType)
+            && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) {
+          Util.setShort(scratchPad, arrInd, tagPtr);
+          arrInd += 2;
+          break;
+        }
+        tagInd += 2;
+      }
+      index++;
+    }
+    short originTag = KMEnumTag.instance(KMType.ORIGIN, origin);
+    Util.setShort(scratchPad, arrInd, originTag);
+    arrInd += 2;
+    short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr);
+    Util.setShort(scratchPad, arrInd, osVersionTag);
+    arrInd += 2;
+    short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr);
+    Util.setShort(scratchPad, arrInd, osPatchTag);
+    arrInd += 2;
+    short vendorPatchTag =
+        KMIntegerTag.instance(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, vendorPatchObjPtr);
+    Util.setShort(scratchPad, arrInd, vendorPatchTag);
+    arrInd += 2;
+    short bootPatchTag =
+        KMIntegerTag.instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr);
+    Util.setShort(scratchPad, arrInd, bootPatchTag);
+    arrInd += 2;
+    return createKeyParameters(scratchPad, (short) (arrInd / 2));
+  }
+
+  public static short makeHwEnforced(short sb, short tee) {
+    short len = KMKeyParameters.cast(sb).length();
+    len += KMKeyParameters.cast(tee).length();
+    short hwEnf = KMArray.instance(len);
+    sb = KMKeyParameters.cast(sb).getVals();
+    tee = KMKeyParameters.cast(tee).getVals();
+    len = KMArray.cast(sb).length();
+    short src = 0;
+    short dest = 0;
+    short val = 0;
+    while (src < len) {
+      val = KMArray.cast(sb).get(src);
+      KMArray.cast(hwEnf).add(dest, val);
+      src++;
+      dest++;
+    }
+    src = 0;
+    len = KMArray.cast(tee).length();
+    while (src < len) {
+      val = KMArray.cast(tee).get(src);
+      KMArray.cast(hwEnf).add(dest, val);
+      src++;
+      dest++;
+    }
+    return KMKeyParameters.instance(hwEnf);
+  }
+
+  // ALL_USERS, EXPORTABLE missing from types.hal
+  public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) {
+    byte index = 0;
+    short tagInd;
+    short arrInd = 0;
+    short tagPtr;
+    short tagKey;
+    short tagType;
+    short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+    short len = KMArray.cast(arrPtr).length();
+    while (index < len) {
+      tagInd = 0;
+      tagPtr = KMArray.cast(arrPtr).get(index);
+      tagKey = KMTag.getKey(tagPtr);
+      tagType = KMTag.getTagType(tagPtr);
+      if (!isValidTag(tagType, tagKey)) {
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+      }
+      while (tagInd < (short) swEnforcedTagsArr.length) {
+        if ((swEnforcedTagsArr[tagInd] == tagType)
+            && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) {
+          Util.setShort(scratchPad, arrInd, tagPtr);
+          arrInd += 2;
+          break;
+        }
+        tagInd += 2;
+      }
+      index++;
+    }
+    return createKeyParameters(scratchPad, (short) (arrInd / 2));
+  }
+
+  public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) {
+    byte index = 0;
+    short tagInd;
+    short arrInd = 0;
+    short tagPtr;
+    short tagKey;
+    short tagType;
+    short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals();
+    short len = KMArray.cast(arrPtr).length();
+    while (index < len) {
+      tagInd = 0;
+      tagPtr = KMArray.cast(arrPtr).get(index);
+      tagKey = KMTag.getKey(tagPtr);
+      tagType = KMTag.getTagType(tagPtr);
+      if (!isValidTag(tagType, tagKey)) {
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+      }
+      while (tagInd < (short) teeEnforcedTagsArr.length) {
+        if ((teeEnforcedTagsArr[tagInd] == tagType)
+            && (teeEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) {
+          Util.setShort(scratchPad, arrInd, tagPtr);
+          arrInd += 2;
+          break;
+        }
+        tagInd += 2;
+      }
+      index++;
+    }
+    return createKeyParameters(scratchPad, (short) (arrInd / 2));
+  }
+
+  public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) {
+    short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr);
+    if (appId != KMTag.INVALID_VALUE) {
+      appId = KMByteTag.cast(appId).getValue();
+      if (KMByteBlob.cast(appId).length() == 0) {
+        appId = KMTag.INVALID_VALUE;
+      }
+    }
+    short appData =
+        KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr);
+    if (appData != KMTag.INVALID_VALUE) {
+      appData = KMByteTag.cast(appData).getValue();
+      if (KMByteBlob.cast(appData).length() == 0) {
+        appData = KMTag.INVALID_VALUE;
+      }
+    }
+    return makeHidden(appId, appData, rootOfTrustBlob, scratchPad);
+  }
+
+  public static short makeHidden(
+      short appIdBlob, short appDataBlob, short rootOfTrustBlob, byte[] scratchPad) {
+    // Order in which the hidden array is created should not change.
+    short index = 0;
+    KMByteBlob.cast(rootOfTrustBlob);
+    Util.setShort(scratchPad, index, rootOfTrustBlob);
+    index += 2;
+    if (appIdBlob != KMTag.INVALID_VALUE) {
+      KMByteBlob.cast(appIdBlob);
+      Util.setShort(scratchPad, index, appIdBlob);
+      index += 2;
+    }
+    if (appDataBlob != KMTag.INVALID_VALUE) {
+      KMByteBlob.cast(appDataBlob);
+      Util.setShort(scratchPad, index, appDataBlob);
+      index += 2;
+    }
+    return createKeyParameters(scratchPad, (short) (index / 2));
+  }
+
+  public static boolean isValidTag(short tagType, short tagKey) {
+    short index = 0;
+    if (tagKey == KMType.INVALID_TAG) {
+      return false;
+    }
+    while (index < invalidTagsArr.length) {
+      if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) {
+        return false;
+      }
+      index += 2;
+    }
+    return true;
+  }
+
+  public static short createKeyParameters(byte[] ptrArr, short len) {
+    short arrPtr = KMArray.instance(len);
+    short index = 0;
+    short ptr = 0;
+    while (index < len) {
+      KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr));
+      index++;
+      ptr += 2;
+    }
+    return KMKeyParameters.instance(arrPtr);
+  }
+
+  public static short makeCustomTags(short keyParams, byte[] scratchPad) {
+    short index = 0;
+    short tagPtr;
+    short offset = 0;
+    short len = (short) customTags.length;
+    short tagType;
+    while (index < len) {
+      tagType = customTags[(short) (index + 1)];
+      switch (tagType) {
+        case KMType.AUTH_TIMEOUT_MILLIS:
+          short authTimeOutTag =
+              KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT);
+          if (authTimeOutTag != KMType.INVALID_VALUE) {
+            tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset);
+            Util.setShort(scratchPad, offset, tagPtr);
+            offset += 2;
+          }
+          break;
+        default:
+          break;
+      }
+      index += 2;
+    }
+    return createKeyParameters(scratchPad, (short) (offset / 2));
+  }
+
+  public static short createAuthTimeOutMillisTag(
+      short authTimeOutTag, byte[] scratchPad, short offset) {
+    short authTime = KMIntegerTag.cast(authTimeOutTag).getValue();
+    Util.arrayFillNonAtomic(scratchPad, offset, (short) 40, (byte) 0);
+    Util.arrayCopyNonAtomic(
+        KMInteger.cast(authTime).getBuffer(),
+        KMInteger.cast(authTime).getStartOff(),
+        scratchPad,
+        (short) (offset + 8 - KMInteger.cast(authTime).length()),
+        KMInteger.cast(authTime).length());
+    KMUtils.convertToMilliseconds(scratchPad, offset, (short) (offset + 8), (short) (offset + 16));
+    return KMIntegerTag.instance(
+        KMType.ULONG_TAG,
+        KMType.AUTH_TIMEOUT_MILLIS,
+        KMInteger.uint_64(scratchPad, (short) (offset + 8)));
+  }
+
+  public short getVals() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  public short findTag(short tagType, short tagKey) {
+    KMArray vals = KMArray.cast(getVals());
+    short index = 0;
+    short length = vals.length();
+    short key;
+    short type;
+    short ret = KMType.INVALID_VALUE;
+    short obj;
+    while (index < length) {
+      obj = vals.get(index);
+      key = KMTag.getKey(obj);
+      type = KMTag.getTagType(obj);
+      if ((tagKey == key) && (tagType == type)) {
+        ret = obj;
+        break;
+      }
+      index++;
+    }
+    return ret;
+  }
+
+  public void deleteCustomTags() {
+    short arrPtr = getVals();
+    short index = (short) (customTags.length - 1);
+    short obj;
+    while (index >= 0) {
+      obj = findTag(customTags[(short) (index - 1)], customTags[index]);
+      if (obj != KMType.INVALID_VALUE) {
+        KMArray.cast(arrPtr).deleteLastEntry();
+      }
+      index -= 2;
+    }
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
new file mode 100644
index 0000000..b616085
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java
@@ -0,0 +1,5030 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMAttestationCert;
+import com.android.javacard.seprovider.KMDataStoreConstants;
+import com.android.javacard.seprovider.KMDeviceUniqueKeyPair;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMOperation;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.APDU;
+import javacard.framework.Applet;
+import javacard.framework.AppletEvent;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import javacard.security.CryptoException;
+import javacardx.apdu.ExtendedLength;
+
+/**
+ * KMKeymasterApplet implements the javacard applet. It creates repository and other install time
+ * objects. It also implements the keymaster state machine and handles javacard applet life cycle
+ * events.
+ */
+public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength {
+
+  // Constants.
+  public static final byte[] F4 = {0x01, 0x00, 0x01};
+  public static final byte AES_BLOCK_SIZE = 16;
+  public static final byte DES_BLOCK_SIZE = 8;
+  public static final short MASTER_KEY_SIZE = 128;
+  public static final byte WRAPPING_KEY_SIZE = 32;
+  public static final byte MAX_OPERATIONS_COUNT = 4;
+  public static final byte VERIFIED_BOOT_KEY_SIZE = 32;
+  public static final byte VERIFIED_BOOT_HASH_SIZE = 32;
+  public static final byte BOOT_PATCH_LVL_SIZE = 4;
+  public static final byte TRUSTED_ENVIRONMENT = 1;
+  // "Keymaster HMAC Verification" - used for HMAC key verification.
+  public static final byte[] sharingCheck = {
+    0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, 0x56,
+    0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E
+  };
+  // "KeymasterSharedMac"
+  public static final byte[] ckdfLable = {
+    0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x4D,
+    0x61, 0x63
+  };
+  // "Auth Verification"
+  public static final byte[] authVerification = {
+    0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F,
+    0x6E
+  };
+  // "confirmation token"
+  public static final byte[] confirmationToken = {
+    0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, 0x6B,
+    0x65, 0x6E
+  };
+  public static final short MAX_COSE_BUF_SIZE = (short) 1024;
+  // Maximum allowed buffer size for to encode the key parameters
+  // which is used while creating mac for key paramters.
+  public static final short MAX_KEY_PARAMS_BUF_SIZE = (short) 3072; // 3K
+  // Data Dictionary items
+  public static final byte DATA_ARRAY_SIZE = 40;
+  public static final byte TMP_VARIABLE_ARRAY_SIZE = 5;
+  public static final byte KEY_PARAMETERS = 0;
+  public static final byte KEY_CHARACTERISTICS = 1;
+  public static final byte HIDDEN_PARAMETERS = 2;
+  public static final byte HW_PARAMETERS = 3;
+  public static final byte SW_PARAMETERS = 4;
+  public static final byte AUTH_DATA = 5;
+  public static final byte AUTH_TAG = 6;
+  public static final byte NONCE = 7;
+  public static final byte KEY_BLOB = 8;
+  public static final byte AUTH_DATA_LENGTH = 9;
+  public static final byte SECRET = 10;
+  public static final byte ROT = 11;
+  public static final byte DERIVED_KEY = 12;
+  public static final byte RSA_PUB_EXPONENT = 13;
+  public static final byte APP_ID = 14;
+  public static final byte APP_DATA = 15;
+  public static final byte PUB_KEY = 16;
+  public static final byte IMPORTED_KEY_BLOB = 17;
+  public static final byte ORIGIN = 18;
+  public static final byte NOT_USED = 19;
+  public static final byte MASKING_KEY = 20;
+  public static final byte HMAC_SHARING_PARAMS = 21;
+  public static final byte OP_HANDLE = 22;
+  public static final byte IV = 23;
+  public static final byte INPUT_DATA = 24;
+  public static final byte OUTPUT_DATA = 25;
+  public static final byte HW_TOKEN = 26;
+  public static final byte VERIFICATION_TOKEN = 27;
+  public static final byte SIGNATURE = 28;
+  public static final byte ATTEST_KEY_BLOB = 29;
+  public static final byte ATTEST_KEY_PARAMS = 30;
+  public static final byte ATTEST_KEY_ISSUER = 31;
+  public static final byte CERTIFICATE = 32;
+  public static final byte PLAIN_SECRET = 33;
+  public static final byte TEE_PARAMETERS = 34;
+  public static final byte SB_PARAMETERS = 35;
+  public static final byte CONFIRMATION_TOKEN = 36;
+  public static final byte KEY_BLOB_VERSION_DATA_OFFSET = 37;
+  public static final byte CUSTOM_TAGS = 38;
+  // Keyblob offsets.
+  public static final byte KEY_BLOB_VERSION_OFFSET = 0;
+  public static final byte KEY_BLOB_SECRET = 1;
+  public static final byte KEY_BLOB_NONCE = 2;
+  public static final byte KEY_BLOB_AUTH_TAG = 3;
+  public static final byte KEY_BLOB_PARAMS = 4;
+  public static final byte KEY_BLOB_CUSTOM_TAGS = 5;
+  public static final byte KEY_BLOB_PUB_KEY = 6;
+  // AES GCM constants
+  public static final byte AES_GCM_AUTH_TAG_LENGTH = 16;
+  public static final byte AES_GCM_NONCE_LENGTH = 12;
+  // KEYBLOB_CURRENT_VERSION goes into KeyBlob and will affect all
+  // the KeyBlobs if it is changed. please increment this
+  // version number whenever you change anything related to
+  // KeyBlob (structure, encryption algorithm etc).
+  public static final short KEYBLOB_CURRENT_VERSION = 3;
+  // KeyBlob Verion 1 constant.
+  public static final short KEYBLOB_VERSION_1 = 1;
+  // KeyBlob array size constants.
+  public static final byte SYM_KEY_BLOB_SIZE_V2_V3 = 6;
+  public static final byte ASYM_KEY_BLOB_SIZE_V2_V3 = 7;
+  public static final byte SYM_KEY_BLOB_SIZE_V1 = 5;
+  public static final byte ASYM_KEY_BLOB_SIZE_V1 = 6;
+  public static final byte SYM_KEY_BLOB_SIZE_V0 = 4;
+  public static final byte ASYM_KEY_BLOB_SIZE_V0 = 5;
+  // Key type constants
+  public static final byte SYM_KEY_TYPE = 0;
+  public static final byte ASYM_KEY_TYPE = 1;
+  // SHA-256 Digest length in bits
+  public static final short SHA256_DIGEST_LEN_BITS = 256;
+  // Minimum HMAC length in bits
+  public static final short MIN_HMAC_LENGTH_BITS = 64;
+  // Provision reporting status
+  public static final short NOT_PROVISIONED = 0x0000;
+  public static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001;
+  public static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002;
+  public static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004;
+  public static final short PROVISION_STATUS_ATTEST_IDS = 0x0008;
+  public static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010;
+  public static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020;
+  public static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040;
+  public static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080;
+  public static final short PROVISION_STATUS_SE_LOCKED = 0x0100;
+  public static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200;
+  public static final short PROVISION_STATUS_SECURE_BOOT_MODE = 0x0400;
+  protected static final short KM_HAL_VERSION = (short) 0x5000;
+  // OEM lock / unlock verification constants.
+  protected static final byte[] OEM_LOCK_PROVISION_VERIFICATION_LABEL = { // "OEM Provisioning Lock"
+    0x4f, 0x45, 0x4d, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, 0x67,
+    0x20, 0x4c, 0x6f, 0x63, 0x6b
+  };
+  protected static final byte[] OEM_UNLOCK_PROVISION_VERIFICATION_LABEL = { // "Enable RMA"
+    0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x52, 0x4d, 0x41
+  };
+  // AddRngEntropy
+  protected static final short MAX_SEED_SIZE = 2048;
+  protected static final short MAX_CERT_SIZE = 3000;
+  protected static final short MAX_KEY_CHARS_SIZE = 512;
+  protected static final short MAX_KEYBLOB_SIZE = 1024;
+  private static final short MAX_AUTH_DATA_SIZE = (short) 512;
+  private static final short DERIVE_KEY_INPUT_SIZE = (short) 256;
+  // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys
+  private static final byte[] defaultSubject = {
+    0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, 0x64,
+    0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, 0x65,
+    0x79
+  };
+  private static final byte[] dec319999Ms = {
+    (byte) 0, (byte) 0, (byte) 0xE6, (byte) 0x77, (byte) 0xD2, (byte) 0x1F, (byte) 0xD8, (byte) 0x18
+  };
+  private static final byte[] dec319999 = {
+    0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a,
+  };
+  private static final byte[] jan01970 = {
+    0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a,
+  };
+  private static final byte[] JavacardKeymintDevice = {
+    0x4a, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4b, 0x65, 0x79, 0x6d, 0x69, 0x6e, 0x74, 0x44,
+    0x65, 0x76, 0x69, 0x63, 0x65,
+  };
+  private static final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65};
+  private static final short[] attTags = {
+    KMType.ATTESTATION_ID_BRAND,
+    KMType.ATTESTATION_ID_DEVICE,
+    KMType.ATTESTATION_ID_IMEI,
+    KMType.ATTESTATION_ID_MANUFACTURER,
+    KMType.ATTESTATION_ID_MEID,
+    KMType.ATTESTATION_ID_MODEL,
+    KMType.ATTESTATION_ID_PRODUCT,
+    KMType.ATTESTATION_ID_SERIAL
+  };
+  private static final byte OEM_LOCK = 1;
+  private static final byte OEM_UNLOCK = 0;
+  // Top 32 commands are reserved for provisioning.
+  private static final byte KEYMINT_CMD_APDU_START = 0x20;
+  // RKP
+  public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; // 0x3B
+  public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; // 0x3C
+  public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; // 0x3D
+  public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; // 0x3E
+  // Constant
+  public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; // 0x3F
+  public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; // 0x40
+  public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; // 0x41
+  public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; // 0x42
+  private static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; // 0x21
+  private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; // 0x22
+  private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; // 0x23
+  private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; // 0x24
+  private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; // 0x25
+  private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; // 0x26
+  private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; // 0x27
+  private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; // 0x28
+  private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; // 0x29
+  private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; // 0x2A
+  private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; // 0x2B
+  private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; // 0x2C
+  private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; // 0x2D
+  private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; // 0x2E
+  private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; // 0x2F
+  private static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; // 0x30
+  private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; // 0x31
+  private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; // 0x32
+  private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; // 0x33
+  private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20; // 0x34
+  private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; // 0x35
+  private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; // 0x36
+  private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; // 0x37
+  private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; // 0x38
+  private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; // 0x39
+  private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; // 0x3A
+  // The instructions from 0x43 to 0x4C will be reserved for KeyMint 1.0 for any future use.
+  // KeyMint 2.0 Instructions
+  private static final byte INS_GET_ROT_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 45; // 0x4D
+  private static final byte INS_GET_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 46; // 0x4E
+  private static final byte INS_SEND_ROT_DATA_CMD = KEYMINT_CMD_APDU_START + 47; // 0x4F
+  private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 48; // 0x50
+  private static final byte INS_END_KM_CMD = 0x7F;
+  // Instruction values from 0xCD to 0xFF are completely reserved for Vendors to use and
+  // will never be used by the base line code in future.
+  private static final byte INS_KM_VENDOR_START_CMD = (byte) 0xCD;
+  private static final byte INS_KM_VENDOR_END_CMD = (byte) 0xFF;
+  // ComputeHMAC constants
+  private static final byte HMAC_SHARED_PARAM_MAX_SIZE = 64;
+  protected static RemotelyProvisionedComponentDevice rkp;
+  protected static KMEncoder encoder;
+  protected static KMDecoder decoder;
+  protected static KMRepository repository;
+  protected static KMSEProvider seProvider;
+  protected static KMOperationState[] opTable;
+  protected static KMKeymintDataStore kmDataStore;
+
+  protected static short[] tmpVariables;
+  protected static short[] data;
+  protected static byte[] wrappingKey;
+
+  /** Registers this applet. */
+  protected KMKeymasterApplet(KMSEProvider seImpl) {
+    seProvider = seImpl;
+    boolean isUpgrading = seProvider.isUpgrading();
+    repository = new KMRepository(isUpgrading);
+    encoder = new KMEncoder();
+    decoder = new KMDecoder();
+    kmDataStore = new KMKeymintDataStore(seProvider, repository);
+    data = JCSystem.makeTransientShortArray(DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+    tmpVariables =
+        JCSystem.makeTransientShortArray(TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT);
+    wrappingKey =
+        JCSystem.makeTransientByteArray((short) (WRAPPING_KEY_SIZE + 1), JCSystem.CLEAR_ON_RESET);
+    resetWrappingKey();
+    opTable = new KMOperationState[MAX_OPERATIONS_COUNT];
+    short index = 0;
+    while (index < MAX_OPERATIONS_COUNT) {
+      opTable[index] = new KMOperationState();
+      index++;
+    }
+    KMType.initialize();
+    if (!isUpgrading) {
+      kmDataStore.createMasterKey(MASTER_KEY_SIZE);
+    }
+    // initialize default values
+    initHmacNonceAndSeed();
+    rkp =
+        new RemotelyProvisionedComponentDevice(
+            encoder, decoder, repository, seProvider, kmDataStore);
+  }
+
+  /** Sends a response, may be extended response, as requested by the command. */
+  public static void sendOutgoing(APDU apdu, short resp) {
+    // TODO handle the extended buffer stuff. We can reuse this.
+    short bufferStartOffset = repository.allocAvailableMemory();
+    byte[] buffer = repository.getHeap();
+    // TODO we can change the following to incremental send.
+    short bufferLength =
+        encoder.encode(resp, buffer, bufferStartOffset, repository.getHeapReclaimIndex());
+    if (((short) (bufferLength + bufferStartOffset)) > ((short) repository.getHeap().length)) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    // Send data
+    apdu.setOutgoing();
+    apdu.setOutgoingLength(bufferLength);
+    apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength);
+  }
+
+  /** Receives data, which can be extended data, as requested by the command instance. */
+  public static short receiveIncoming(APDU apdu, short reqExp) {
+    byte[] srcBuffer = apdu.getBuffer();
+    short recvLen = apdu.setIncomingAndReceive();
+    short srcOffset = apdu.getOffsetCdata();
+    // TODO add logic to handle the extended length buffer. In this case the memory can be reused
+    //  from extended buffer.
+    short bufferLength = apdu.getIncomingLength();
+    short bufferStartOffset = repository.allocReclaimableMemory(bufferLength);
+    short index = bufferStartOffset;
+    byte[] buffer = repository.getHeap();
+    while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) {
+      Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen);
+      index += recvLen;
+      recvLen = apdu.receiveBytes(srcOffset);
+    }
+    short req = decoder.decode(reqExp, buffer, bufferStartOffset, bufferLength);
+    repository.reclaimMemory(bufferLength);
+    return req;
+  }
+
+  private static short createKeyBlobInstance(byte keyType) {
+    short arrayLen = 0;
+    switch (keyType) {
+      case ASYM_KEY_TYPE:
+        arrayLen = ASYM_KEY_BLOB_SIZE_V2_V3;
+        break;
+      case SYM_KEY_TYPE:
+        arrayLen = SYM_KEY_BLOB_SIZE_V2_V3;
+        break;
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+    }
+    return KMArray.instance(arrayLen);
+  }
+
+  private static void addTags(short params, boolean hwEnforced, KMAttestationCert cert) {
+    short index = 0;
+    short arr = KMKeyParameters.cast(params).getVals();
+    short len = KMArray.cast(arr).length();
+    short tag;
+    while (index < len) {
+      tag = KMArray.cast(arr).get(index);
+      cert.extensionTag(tag, hwEnforced);
+      index++;
+    }
+  }
+
+  private static void setUniqueId(KMAttestationCert cert, short attAppId, byte[] scratchPad) {
+    if (!KMTag.isPresent(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID)) {
+      return;
+    }
+    // temporal count T
+    short time =
+        KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CREATION_DATETIME, data[KEY_PARAMETERS]);
+    if (time == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    time = KMIntegerTag.cast(time).getValue();
+
+    // Reset After Rotation R - it will be part of HW Enforced key
+    // characteristics
+    byte resetAfterRotation = 0;
+    if (KMTag.isPresent(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION)) {
+      resetAfterRotation = 0x01;
+    }
+
+    cert.makeUniqueId(
+        scratchPad,
+        (short) 0,
+        KMInteger.cast(time).getBuffer(),
+        KMInteger.cast(time).getStartOff(),
+        KMInteger.cast(time).length(),
+        KMByteBlob.cast(attAppId).getBuffer(),
+        KMByteBlob.cast(attAppId).getStartOff(),
+        KMByteBlob.cast(attAppId).length(),
+        resetAfterRotation,
+        kmDataStore.getMasterKey());
+  }
+
+  private static void validateRSAKey(byte[] scratchPad) {
+    // Read key size
+    if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+    }
+    if (!KMTag.isValidPublicExponent(data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+  }
+
+  // Generate key handlers
+  private static void generateRSAKey(byte[] scratchPad) {
+    // Validate RSA Key
+    validateRSAKey(scratchPad);
+    // Now generate 2048 bit RSA keypair for the given exponent
+    short[] lengths = tmpVariables;
+    data[PUB_KEY] = KMByteBlob.instance((short) 256);
+    data[SECRET] = KMByteBlob.instance((short) 256);
+    seProvider.createAsymmetricKey(
+        KMType.RSA,
+        KMByteBlob.cast(data[SECRET]).getBuffer(),
+        KMByteBlob.cast(data[SECRET]).getStartOff(),
+        KMByteBlob.cast(data[SECRET]).length(),
+        KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+        KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+        KMByteBlob.cast(data[PUB_KEY]).length(),
+        lengths);
+
+    data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+  }
+
+  private static void validateAESKey() {
+    // Read key size
+    if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+    }
+    // Read Block mode - array of byte values
+    if (KMTag.isPresent(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE)) {
+      short blockModes =
+          KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]);
+      // If it is a GCM mode
+      if (KMEnumArrayTag.cast(blockModes).contains(KMType.GCM)) {
+        // Min mac length must be present
+        KMTag.assertPresence(
+            data[KEY_PARAMETERS],
+            KMType.UINT_TAG,
+            KMType.MIN_MAC_LENGTH,
+            KMError.MISSING_MIN_MAC_LENGTH);
+        short macLength =
+            KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]);
+        macLength = KMIntegerTag.cast(macLength).getValue();
+        // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits
+        // and greater the 96 bits
+        if (KMInteger.cast(macLength).getSignificantShort() != 0
+            || KMInteger.cast(macLength).getShort() > 128
+            || KMInteger.cast(macLength).getShort() < 96
+            || (KMInteger.cast(macLength).getShort() % 8) != 0) {
+          KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH);
+        }
+      }
+    }
+  }
+
+  private static void generateAESKey(byte[] scratchPad) {
+    validateAESKey();
+    short keysize =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    short len = seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0);
+    data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+    data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+  }
+
+  private static void validateECKeys() {
+    // Read key size
+    short ecCurve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]);
+    /* In KeyMint 2.0, If EC_CURVE not provided, generateKey
+     * must return ErrorCode::UNSUPPORTED_KEY_SIZE or ErrorCode::UNSUPPORTED_EC_CURVE.
+     */
+    if (ecCurve != KMType.P_256) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+    }
+    short ecKeySize = KMEnumTag.getValue(KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    if ((ecKeySize != KMType.INVALID_VALUE) && !KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+    }
+  }
+
+  private static void generateECKeys(byte[] scratchPad) {
+    validateECKeys();
+    short[] lengths = tmpVariables;
+    seProvider.createAsymmetricKey(
+        KMType.EC,
+        scratchPad,
+        (short) 0,
+        (short) 128,
+        scratchPad,
+        (short) 128,
+        (short) 128,
+        lengths);
+    data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]);
+    data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]);
+    data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+  }
+
+  private static void validateTDESKey() {
+    if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+    }
+    // Read Minimum Mac length - it must not be present
+    KMTag.assertAbsence(
+        data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.INVALID_TAG);
+  }
+
+  private static void generateTDESKey(byte[] scratchPad) {
+    validateTDESKey();
+    short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0);
+    data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+    data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+  }
+
+  private static void validateHmacKey() {
+    // If params does not contain any digest throw unsupported digest error.
+    KMTag.assertPresence(
+        data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.DIGEST, KMError.UNSUPPORTED_DIGEST);
+
+    // check whether digest sizes are greater then or equal to min mac length.
+    // Only SHA256 digest must be supported.
+    if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+    }
+    // Read Minimum Mac length
+    KMTag.assertPresence(
+        data[KEY_PARAMETERS],
+        KMType.UINT_TAG,
+        KMType.MIN_MAC_LENGTH,
+        KMError.MISSING_MIN_MAC_LENGTH);
+    short minMacLength =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]);
+
+    if (((short) (minMacLength % 8) != 0)
+        || minMacLength < MIN_HMAC_LENGTH_BITS
+        || minMacLength > SHA256_DIGEST_LEN_BITS) {
+      KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH);
+    }
+    // Read Keysize
+    if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+    }
+  }
+
+  private static void generateHmacKey(byte[] scratchPad) {
+    validateHmacKey();
+    short keysize =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    // generate HMAC Key
+    short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0);
+    data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+    data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+  }
+
+  // This function is only called from processUpgradeKey command.
+  // 1. Update the latest values of OSVersion, OSPatch, VendorPatch and BootPatch in the
+  //    KeyBlob's KeyCharacteristics.
+  // 2. Re-create KeyBlob's KeyCharacteristics from HW_PARAMS to make sure we don't miss
+  //    anything which happens in these functions makeSbEnforced and makeTeeEnforced in
+  //    the future. Like validations.
+  // 3. No need to create Keystore Enforced list here as it is not required to be included in
+  //    the KeyBlob's KeyCharacteristics.
+  // 4. No need to create KeyCharacteristics as upgradeKey does not require to return any
+  //    KeyCharacteristics back.
+  private static void upgradeKeyBlobKeyCharacteristics(short hwParams, byte[] scratchPad) {
+    short osVersion = kmDataStore.getOsVersion();
+    short osPatch = kmDataStore.getOsPatch();
+    short vendorPatch = kmDataStore.getVendorPatchLevel();
+    short bootPatch = kmDataStore.getBootPatchLevel();
+    data[SB_PARAMETERS] =
+        KMKeyParameters.makeSbEnforced(
+            hwParams, (byte) data[ORIGIN], osVersion, osPatch, vendorPatch, bootPatch, scratchPad);
+    data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(hwParams, scratchPad);
+    data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+  }
+
+  private static void makeKeyCharacteristics(byte[] scratchPad) {
+    short osVersion = kmDataStore.getOsVersion();
+    short osPatch = kmDataStore.getOsPatch();
+    short vendorPatch = kmDataStore.getVendorPatchLevel();
+    short bootPatch = kmDataStore.getBootPatchLevel();
+    data[SB_PARAMETERS] =
+        KMKeyParameters.makeSbEnforced(
+            data[KEY_PARAMETERS],
+            (byte) data[ORIGIN],
+            osVersion,
+            osPatch,
+            vendorPatch,
+            bootPatch,
+            scratchPad);
+    data[TEE_PARAMETERS] = KMKeyParameters.makeTeeEnforced(data[KEY_PARAMETERS], scratchPad);
+    data[SW_PARAMETERS] = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS], scratchPad);
+    data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+    data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance();
+    KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setStrongboxEnforced(data[SB_PARAMETERS]);
+    KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setKeystoreEnforced(data[SW_PARAMETERS]);
+    KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setTeeEnforced(data[TEE_PARAMETERS]);
+  }
+
+  private static void createEncryptedKeyBlob(byte[] scratchPad) {
+    // make root of trust blob
+    data[ROT] = readROT(scratchPad, KEYBLOB_CURRENT_VERSION);
+    if (data[ROT] == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    // make hidden key params list
+    data[HIDDEN_PARAMETERS] =
+        KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad);
+    data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_16(KEYBLOB_CURRENT_VERSION);
+    // create custom tags
+    data[CUSTOM_TAGS] = KMKeyParameters.makeCustomTags(data[HW_PARAMETERS], scratchPad);
+    // encrypt the secret and cryptographically attach that to authorization data
+    encryptSecret(scratchPad);
+    // create key blob array
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_VERSION_OFFSET, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_CUSTOM_TAGS, data[CUSTOM_TAGS]);
+
+    short tempChar = KMKeyCharacteristics.instance();
+    short emptyParam = KMArray.instance((short) 0);
+    emptyParam = KMKeyParameters.instance(emptyParam);
+    KMKeyCharacteristics.cast(tempChar).setStrongboxEnforced(data[SB_PARAMETERS]);
+    KMKeyCharacteristics.cast(tempChar).setKeystoreEnforced(emptyParam);
+    KMKeyCharacteristics.cast(tempChar).setTeeEnforced(data[TEE_PARAMETERS]);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, tempChar);
+  }
+
+  // Read RoT
+  public static short readROT(byte[] scratchPad, short version) {
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+    short len = kmDataStore.getBootKey(scratchPad, (short) 0);
+    // As per IKeyMintDevice.aidl specification The root of trust
+    // consists of verifyBootKey, boot state and device locked.
+    if (version <= KEYBLOB_VERSION_1) {
+      // To parse old keyblobs verified boot hash is included in
+      // the root of trust.
+      len += kmDataStore.getVerifiedBootHash(scratchPad, (short) len);
+    }
+    short bootState = kmDataStore.getBootState();
+    len = Util.setShort(scratchPad, len, bootState);
+    if (kmDataStore.isDeviceBootLocked()) {
+      scratchPad[len] = (byte) 1;
+    } else {
+      scratchPad[len] = (byte) 0;
+    }
+    len++;
+    return KMByteBlob.instance(scratchPad, (short) 0, len);
+  }
+
+  private static void encryptSecret(byte[] scratchPad) {
+    // make nonce
+    data[NONCE] = KMByteBlob.instance(AES_GCM_NONCE_LENGTH);
+    data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH);
+    seProvider.newRandomNumber(
+        KMByteBlob.cast(data[NONCE]).getBuffer(),
+        KMByteBlob.cast(data[NONCE]).getStartOff(),
+        KMByteBlob.cast(data[NONCE]).length());
+    // derive master key - stored in derivedKey
+    short len = deriveKey(scratchPad);
+    len =
+        seProvider.aesGCMEncrypt(
+            KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(),
+            KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(),
+            KMByteBlob.cast(data[DERIVED_KEY]).length(),
+            KMByteBlob.cast(data[SECRET]).getBuffer(),
+            KMByteBlob.cast(data[SECRET]).getStartOff(),
+            KMByteBlob.cast(data[SECRET]).length(),
+            scratchPad,
+            (short) 0,
+            KMByteBlob.cast(data[NONCE]).getBuffer(),
+            KMByteBlob.cast(data[NONCE]).getStartOff(),
+            KMByteBlob.cast(data[NONCE]).length(),
+            null,
+            (short) 0,
+            (short) 0,
+            KMByteBlob.cast(data[AUTH_TAG]).getBuffer(),
+            KMByteBlob.cast(data[AUTH_TAG]).getStartOff(),
+            KMByteBlob.cast(data[AUTH_TAG]).length());
+
+    if (len > 0 && len != KMByteBlob.cast(data[SECRET]).length()) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len);
+  }
+
+  private static byte getKeyType(short hardwareParams) {
+    short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hardwareParams);
+    if (KMEnumTag.cast(alg).getValue() == KMType.RSA
+        || KMEnumTag.cast(alg).getValue() == KMType.EC) {
+      return ASYM_KEY_TYPE;
+    }
+    return SYM_KEY_TYPE;
+  }
+
+  private static void makeAuthData(short version, byte[] scratchPad) {
+    // For KeyBlob V2: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and
+    // PUB_KEY.
+    // For KeyBlob V1: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, VERSION and PUB_KEY.
+    // For KeyBlob V0: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS and PUB_KEY.
+    // VERSION is included only for KeyBlobs having version >= 1.
+    // PUB_KEY is included for only ASYMMETRIC KeyBlobs.
+    short index = 0;
+    short numParams = 0;
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 10, (byte) 0);
+    byte keyType = getKeyType(data[HW_PARAMETERS]);
+    // Copy the relevant parameters in the scratchPad in the order
+    // 1. HW_PARAMETERS
+    // 2. HIDDEN_PARAMETERS
+    // 3. VERSION ( Only Version >= 1)
+    // 4. PUB_KEY ( Only for Asymmetric Keys)
+    switch (version) {
+      case (short) 0:
+        numParams = 2;
+        Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+        Util.setShort(
+            scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+        // For Asymmetric Keys include the PUB_KEY.
+        if (keyType == ASYM_KEY_TYPE) {
+          numParams = 3;
+          Util.setShort(scratchPad, (short) 4, data[PUB_KEY]);
+        }
+        break;
+      case (short) 1:
+        numParams = 3;
+        Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+        Util.setShort(
+            scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+        Util.setShort(scratchPad, (short) 4, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+        // For Asymmetric Keys include the PUB_KEY.
+        if (keyType == ASYM_KEY_TYPE) {
+          numParams = 4;
+          Util.setShort(scratchPad, (short) 6, data[PUB_KEY]);
+        }
+        break;
+      case (short) 2:
+        numParams = 4;
+        Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+        Util.setShort(
+            scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+        Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals());
+        Util.setShort(scratchPad, (short) 6, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+        // For Asymmetric Keys include the PUB_KEY.
+        if (keyType == ASYM_KEY_TYPE) {
+          numParams = 5;
+          Util.setShort(scratchPad, (short) 8, data[PUB_KEY]);
+        }
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    short prevReclaimIndex = repository.getHeapReclaimIndex();
+    short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE);
+    index = 0;
+    short len = 0;
+    Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0);
+    while (index < numParams) {
+      short tag = Util.getShort(scratchPad, (short) (index * 2));
+      len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32), prevReclaimIndex);
+      Util.arrayCopyNonAtomic(
+          repository.getHeap(),
+          authIndex,
+          repository.getHeap(),
+          (short) (authIndex + len + 32),
+          (short) 32);
+      len =
+          seProvider.messageDigest256(
+              repository.getHeap(),
+              (short) (authIndex + 32),
+              (short) (len + 32),
+              repository.getHeap(),
+              authIndex);
+      if (len != 32) {
+        KMException.throwIt(KMError.UNKNOWN_ERROR);
+      }
+      index++;
+    }
+    short authDataIndex = repository.alloc(len);
+    Util.arrayCopyNonAtomic(
+        repository.getHeap(), authIndex, repository.getHeap(), authDataIndex, len);
+    repository.reclaimMemory(MAX_AUTH_DATA_SIZE);
+    data[AUTH_DATA] = authDataIndex;
+    data[AUTH_DATA_LENGTH] = len;
+  }
+
+  private static short deriveKeyForOldKeyBlobs(byte[] scratchPad) {
+    // KeyDerivation:
+    // 1. Do HMAC Sign, Auth data.
+    // 2. HMAC Sign generates an output of 32 bytes length.
+    // Consume only first 16 bytes as derived key.
+    // Hmac sign.
+    short len =
+        seProvider.hmacKDF(
+            kmDataStore.getMasterKey(),
+            repository.getHeap(),
+            data[AUTH_DATA],
+            data[AUTH_DATA_LENGTH],
+            scratchPad,
+            (short) 0);
+    if (len < 16) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    len = 16;
+    data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short) 0, len);
+    return len;
+  }
+
+  private static short deriveKey(byte[] scratchPad) {
+    // For KeyBlob V3: Auth Data includes HW_PARAMETERS, HIDDEN_PARAMETERS, CUSTOM_TAGS, VERSION and
+    // PUB_KEY.
+    short index = 0;
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 10, (byte) 0);
+    byte keyType = getKeyType(data[HW_PARAMETERS]);
+    // Copy the relevant parameters in the scratchPad in the order
+    // 1. HW_PARAMETERS
+    // 2. HIDDEN_PARAMETERS
+    // 3. CUSTOM_TAGS
+    // 3. VERSION ( Only Version >= 1)
+    // 4. PUB_KEY ( Only for Asymmetric Keys)
+    short numParams = 4;
+    Util.setShort(scratchPad, (short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals());
+    Util.setShort(scratchPad, (short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals());
+    Util.setShort(scratchPad, (short) 4, KMKeyParameters.cast(data[CUSTOM_TAGS]).getVals());
+    Util.setShort(scratchPad, (short) 6, data[KEY_BLOB_VERSION_DATA_OFFSET]);
+    // For Asymmetric Keys include the PUB_KEY.
+    if (keyType == ASYM_KEY_TYPE) {
+      numParams = 5;
+      Util.setShort(scratchPad, (short) 8, data[PUB_KEY]);
+    }
+    short prevReclaimIndex = repository.getHeapReclaimIndex();
+    short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE);
+    Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0);
+    short len = 0;
+    KMOperation operation = null;
+    try {
+      operation =
+          seProvider.initSymmetricOperation(
+              KMType.SIGN,
+              KMType.HMAC,
+              KMType.SHA2_256,
+              KMType.PADDING_NONE,
+              (byte) KMType.INVALID_VALUE,
+              (Object) kmDataStore.getMasterKey(),
+              KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY,
+              (byte[]) null,
+              (short) 0,
+              (short) 0,
+              (short) 0,
+              false);
+
+      byte arrayHeader = (byte) 0x80;
+      arrayHeader |= (byte) numParams;
+      ((byte[]) repository.getHeap())[authIndex] = arrayHeader;
+      operation.update(repository.getHeap(), authIndex, (short) 1);
+
+      while (index < numParams) {
+        short tag = Util.getShort(scratchPad, (short) (index * 2));
+        len = encoder.encode(tag, repository.getHeap(), (short) authIndex, prevReclaimIndex);
+        operation.update(repository.getHeap(), authIndex, len);
+        index++;
+      }
+      repository.reclaimMemory(MAX_AUTH_DATA_SIZE);
+      // KeyDerivation:
+      // 1. Do HMAC Sign, Auth data.
+      // 2. HMAC Sign generates an output of 32 bytes length.
+      // Consume only first 16 bytes as derived key.
+      // Hmac sign.
+      len = operation.sign(scratchPad, (short) 0, (short) 0, scratchPad, (short) 0);
+    } finally {
+      if (operation != null) {
+        operation.abort();
+      }
+    }
+    if (len < 16) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    len = 16;
+    data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short) 0, len);
+    return len;
+  }
+
+  public static void sendResponse(APDU apdu, short err) {
+    short resp = KMArray.instance((short) 1);
+    err = KMError.translate(err);
+    short error = KMInteger.uint_16(err);
+    KMArray.cast(resp).add((short) 0, error);
+    sendOutgoing(apdu, resp);
+  }
+
+  public static void generateRkpKey(byte[] scratchPad, short keyParams) {
+    data[KEY_PARAMETERS] = keyParams;
+    generateECKeys(scratchPad);
+    // create key blob
+    data[ORIGIN] = KMType.GENERATED;
+    makeKeyCharacteristics(scratchPad);
+    createEncryptedKeyBlob(scratchPad);
+    short prevReclaimIndex = repository.getHeapReclaimIndex();
+    short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+    data[KEY_BLOB] =
+        encoder.encode(
+            data[KEY_BLOB], repository.getHeap(), offset, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+    data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]);
+    repository.reclaimMemory(MAX_KEYBLOB_SIZE);
+  }
+
+  public static short getPubKey() {
+    return data[PUB_KEY];
+  }
+
+  public static short getPivateKey() {
+    return data[KEY_BLOB];
+  }
+
+  /**
+   * Encodes the object to the provided apdu buffer.
+   *
+   * @param object Object to be encoded.
+   * @param apduBuf Buffer on which the encoded data is copied.
+   * @param apduOff Start offset of the buffer.
+   * @param maxLen Max value of the expected out length.
+   * @return length of the encoded buffer.
+   */
+  public static short encodeToApduBuffer(
+      short object, byte[] apduBuf, short apduOff, short maxLen) {
+    short prevReclaimIndex = repository.getHeapReclaimIndex();
+    short offset = repository.allocReclaimableMemory(maxLen);
+    short len = encoder.encode(object, repository.getHeap(), offset, prevReclaimIndex, maxLen);
+    Util.arrayCopyNonAtomic(repository.getHeap(), offset, apduBuf, apduOff, len);
+    // release memory
+    repository.reclaimMemory(maxLen);
+    return len;
+  }
+
+  public static short validateCertChain(
+      boolean validateEekRoot,
+      byte expCertAlg,
+      byte expLeafCertAlg,
+      short certChainArr,
+      byte[] scratchPad,
+      Object[] authorizedEekRoots) {
+    short len = KMArray.cast(certChainArr).length();
+    short coseHeadersExp = KMCoseHeaders.exp();
+    // prepare exp for coseky
+    short coseKeyExp = KMCoseKey.exp();
+    short ptr1;
+    short ptr2;
+    short signStructure;
+    short encodedLen;
+    short prevCoseKey = 0;
+    short keySize;
+    short alg = expCertAlg;
+    short index;
+    for (index = 0; index < len; index++) {
+      ptr1 = KMArray.cast(certChainArr).get(index);
+
+      // validate protected Headers
+      ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET);
+      ptr2 =
+          decoder.decode(
+              coseHeadersExp,
+              KMByteBlob.cast(ptr2).getBuffer(),
+              KMByteBlob.cast(ptr2).getStartOff(),
+              KMByteBlob.cast(ptr2).length());
+      if (!KMCoseHeaders.cast(ptr2).isDataValid(rkp.rkpTmpVariables, alg, KMType.INVALID_VALUE)) {
+        KMException.throwIt(KMError.STATUS_FAILED);
+      }
+
+      // parse and get the public key from payload.
+      ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET);
+      ptr2 =
+          decoder.decode(
+              coseKeyExp,
+              KMByteBlob.cast(ptr2).getBuffer(),
+              KMByteBlob.cast(ptr2).getStartOff(),
+              KMByteBlob.cast(ptr2).length());
+      if ((index == (short) (len - 1)) && len > 1) {
+        alg = expLeafCertAlg;
+      }
+      if (!KMCoseKey.cast(ptr2)
+          .isDataValid(
+              rkp.rkpTmpVariables,
+              KMCose.COSE_KEY_TYPE_EC2,
+              KMType.INVALID_VALUE,
+              alg,
+              KMType.INVALID_VALUE,
+              KMCose.COSE_ECCURVE_256)) {
+        KMException.throwIt(KMError.STATUS_FAILED);
+      }
+      if (prevCoseKey == 0) {
+        prevCoseKey = ptr2;
+      }
+      // Get the public key.
+      keySize = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+      if (keySize != 65) {
+        KMException.throwIt(KMError.STATUS_FAILED);
+      }
+      if (validateEekRoot && (index == 0)) {
+        boolean found = false;
+        // In prod mode the first pubkey should match a well-known Google public key.
+        for (short i = 0; i < (short) authorizedEekRoots.length; i++) {
+          if (0
+              == Util.arrayCompare(
+                  scratchPad,
+                  (short) 0,
+                  (byte[]) authorizedEekRoots[i],
+                  (short) 0,
+                  (short) ((byte[]) authorizedEekRoots[i]).length)) {
+            found = true;
+            break;
+          }
+        }
+        if (!found) {
+          KMException.throwIt(KMError.STATUS_FAILED);
+        }
+      }
+      // Validate signature.
+      signStructure =
+          KMCose.constructCoseSignStructure(
+              KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET),
+              KMByteBlob.instance((short) 0),
+              KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET));
+      encodedLen =
+          KMKeymasterApplet.encodeToApduBuffer(
+              signStructure, scratchPad, keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+      short signatureLen =
+          rkp.encodeES256CoseSignSignature(
+              KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET))
+                  .getBuffer(),
+              KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET))
+                  .getStartOff(),
+              KMByteBlob.length(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)),
+              scratchPad,
+              (short) (keySize + encodedLen));
+
+      if (!seProvider.ecVerify256(
+          scratchPad,
+          (short) 0,
+          keySize,
+          scratchPad,
+          keySize,
+          encodedLen,
+          scratchPad,
+          (short) (keySize + encodedLen),
+          signatureLen)) {
+        KMException.throwIt(KMError.STATUS_FAILED);
+      }
+      prevCoseKey = ptr2;
+    }
+    return prevCoseKey;
+  }
+
+  public static short generateBcc(boolean testMode, byte[] scratchPad) {
+    if (!testMode && kmDataStore.isProvisionLocked()) {
+      KMException.throwIt(KMError.STATUS_FAILED);
+    }
+    KMDeviceUniqueKeyPair deviceUniqueKey = kmDataStore.getRkpDeviceUniqueKeyPair(testMode);
+    short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0);
+    short coseKey =
+        KMCose.constructCoseKey(
+            rkp.rkpTmpVariables,
+            KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+            KMType.INVALID_VALUE,
+            KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+            KMInteger.uint_8(KMCose.COSE_KEY_OP_VERIFY),
+            KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+            scratchPad,
+            (short) 0,
+            temp,
+            KMType.INVALID_VALUE,
+            false);
+    temp =
+        KMKeymasterApplet.encodeToApduBuffer(
+            coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    // Construct payload.
+    short payload =
+        KMCose.constructCoseCertPayload(
+            KMCosePairTextStringTag.instance(
+                KMInteger.uint_8(KMCose.ISSUER),
+                KMTextString.instance(
+                    KMCose.TEST_ISSUER_NAME, (short) 0, (short) KMCose.TEST_ISSUER_NAME.length)),
+            KMCosePairTextStringTag.instance(
+                KMInteger.uint_8(KMCose.SUBJECT),
+                KMTextString.instance(
+                    KMCose.TEST_SUBJECT_NAME, (short) 0, (short) KMCose.TEST_SUBJECT_NAME.length)),
+            KMCosePairByteBlobTag.instance(
+                KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0),
+                KMByteBlob.instance(scratchPad, (short) 0, temp)),
+            KMCosePairByteBlobTag.instance(
+                KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0),
+                KMByteBlob.instance(
+                    KMCose.KEY_USAGE_SIGN, (short) 0, (short) KMCose.KEY_USAGE_SIGN.length)));
+    // temp temporarily holds the length of encoded cert payload.
+    temp =
+        KMKeymasterApplet.encodeToApduBuffer(
+            payload, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    payload = KMByteBlob.instance(scratchPad, (short) 0, temp);
+
+    // protected header
+    short protectedHeader =
+        KMCose.constructHeaders(
+            rkp.rkpTmpVariables,
+            KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE);
+    // temp temporarily holds the length of encoded headers.
+    temp =
+        KMKeymasterApplet.encodeToApduBuffer(
+            protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp);
+
+    // unprotected headers.
+    short arr = KMArray.instance((short) 0);
+    short unprotectedHeader = KMCoseHeaders.instance(arr);
+
+    // construct cose sign structure.
+    short coseSignStructure =
+        KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), payload);
+    // temp temporarily holds the length of encoded sign structure.
+    // Encode cose Sign_Structure.
+    temp =
+        KMKeymasterApplet.encodeToApduBuffer(
+            coseSignStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    // do sign
+    short len =
+        seProvider.ecSign256(deviceUniqueKey, scratchPad, (short) 0, temp, scratchPad, temp);
+    len =
+        KMAsn1Parser.instance()
+            .decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, temp, len), scratchPad, temp);
+    coseSignStructure = KMByteBlob.instance(scratchPad, temp, len);
+
+    // construct cose_sign1
+    short coseSign1 =
+        KMCose.constructCoseSign1(protectedHeader, unprotectedHeader, payload, coseSignStructure);
+
+    // [Cose_Key, Cose_Sign1]
+    short bcc = KMArray.instance((short) 2);
+    KMArray.cast(bcc).add((short) 0, coseKey);
+    KMArray.cast(bcc).add((short) 1, coseSign1);
+    return bcc;
+  }
+
+  protected void initHmacNonceAndSeed() {
+    short nonce = repository.alloc((short) 32);
+    seProvider.newRandomNumber(
+        repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE);
+    kmDataStore.initHmacNonce(repository.getHeap(), nonce, KMKeymintDataStore.HMAC_SEED_NONCE_SIZE);
+  }
+
+  private void releaseAllOperations() {
+    short index = 0;
+    while (index < MAX_OPERATIONS_COUNT) {
+      opTable[index].reset();
+      index++;
+    }
+  }
+
+  private KMOperationState reserveOperation(short algorithm, short opHandle) {
+    short index = 0;
+    while (index < MAX_OPERATIONS_COUNT) {
+      if (opTable[index].getAlgorithm() == KMType.INVALID_VALUE) {
+        opTable[index].reset();
+        opTable[index].setAlgorithm(algorithm);
+        opTable[index].setHandle(
+            KMInteger.cast(opHandle).getBuffer(),
+            KMInteger.cast(opHandle).getStartOff(),
+            KMInteger.cast(opHandle).length());
+        return opTable[index];
+      }
+      index++;
+    }
+    return null;
+  }
+
+  private KMOperationState findOperation(short handle) {
+    return findOperation(
+        KMInteger.cast(handle).getBuffer(),
+        KMInteger.cast(handle).getStartOff(),
+        KMInteger.cast(handle).length());
+  }
+
+  private KMOperationState findOperation(byte[] opHandle, short start, short len) {
+    short index = 0;
+    while (index < MAX_OPERATIONS_COUNT) {
+      if (opTable[index].compare(opHandle, start, len) == 0) {
+        if (opTable[index].getAlgorithm() != KMType.INVALID_VALUE) {
+          return opTable[index];
+        }
+      }
+      index++;
+    }
+    return null;
+  }
+
+  private void releaseOperation(KMOperationState op) {
+    op.reset();
+  }
+
+  /**
+   * Selects this applet.
+   *
+   * @return Returns true if the keymaster is in correct state
+   */
+  @Override
+  public boolean select() {
+    repository.onSelect();
+    return true;
+  }
+
+  /** De-selects this applet. */
+  @Override
+  public void deselect() {
+    repository.onDeselect();
+  }
+
+  /** Uninstalls the applet after cleaning the repository. */
+  @Override
+  public void uninstall() {
+    repository.onUninstall();
+  }
+
+  protected short mapISOErrorToKMError(short reason) {
+    switch (reason) {
+      case ISO7816.SW_CLA_NOT_SUPPORTED:
+        return KMError.UNSUPPORTED_CLA;
+      case ISO7816.SW_CONDITIONS_NOT_SATISFIED:
+        return KMError.SW_CONDITIONS_NOT_SATISFIED;
+      case ISO7816.SW_COMMAND_NOT_ALLOWED:
+        return KMError.CMD_NOT_ALLOWED;
+      case ISO7816.SW_DATA_INVALID:
+        return KMError.INVALID_DATA;
+      case ISO7816.SW_INCORRECT_P1P2:
+        return KMError.INVALID_P1P2;
+      case ISO7816.SW_INS_NOT_SUPPORTED:
+        return KMError.UNSUPPORTED_INSTRUCTION;
+      case ISO7816.SW_WRONG_LENGTH:
+        return KMError.SW_WRONG_LENGTH;
+      case ISO7816.SW_UNKNOWN:
+      default:
+        return KMError.UNKNOWN_ERROR;
+    }
+  }
+
+  protected short mapCryptoErrorToKMError(short reason) {
+    switch (reason) {
+      case CryptoException.ILLEGAL_USE:
+        return KMError.CRYPTO_ILLEGAL_USE;
+      case CryptoException.ILLEGAL_VALUE:
+        return KMError.CRYPTO_ILLEGAL_VALUE;
+      case CryptoException.INVALID_INIT:
+        return KMError.CRYPTO_INVALID_INIT;
+      case CryptoException.NO_SUCH_ALGORITHM:
+        return KMError.CRYPTO_NO_SUCH_ALGORITHM;
+      case CryptoException.UNINITIALIZED_KEY:
+        return KMError.CRYPTO_UNINITIALIZED_KEY;
+      default:
+        return KMError.UNKNOWN_ERROR;
+    }
+  }
+
+  /**
+   * Processes an incoming APDU and handles it using command objects.
+   *
+   * @param apdu the incoming APDU
+   */
+  @Override
+  public void process(APDU apdu) {
+    try {
+      resetTransientBuffers();
+      repository.onProcess();
+      // If this is select applet apdu which is selecting this applet then return
+      if (apdu.isISOInterindustryCLA()) {
+        if (selectingApplet()) {
+          return;
+        }
+      }
+      byte[] apduBuffer = apdu.getBuffer();
+      byte apduIns = apduBuffer[ISO7816.OFFSET_INS];
+      if (!isKeyMintReady(apduIns)) {
+        ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED);
+      }
+      switch (apduIns) {
+        case INS_INIT_STRONGBOX_CMD:
+          processInitStrongBoxCmd(apdu);
+          sendResponse(apdu, KMError.OK);
+          return;
+        case INS_GENERATE_KEY_CMD:
+          processGenerateKey(apdu);
+          break;
+        case INS_IMPORT_KEY_CMD:
+          processImportKeyCmd(apdu);
+          break;
+        case INS_BEGIN_IMPORT_WRAPPED_KEY_CMD:
+          processBeginImportWrappedKeyCmd(apdu);
+          break;
+        case INS_FINISH_IMPORT_WRAPPED_KEY_CMD:
+          processFinishImportWrappedKeyCmd(apdu);
+          break;
+        case INS_EXPORT_KEY_CMD:
+          processExportKeyCmd(apdu);
+          break;
+        case INS_UPGRADE_KEY_CMD:
+          processUpgradeKeyCmd(apdu);
+          break;
+        case INS_DELETE_KEY_CMD:
+          processDeleteKeyCmd(apdu);
+          break;
+        case INS_DELETE_ALL_KEYS_CMD:
+          processDeleteAllKeysCmd(apdu);
+          break;
+        case INS_ADD_RNG_ENTROPY_CMD:
+          processAddRngEntropyCmd(apdu);
+          break;
+        case INS_COMPUTE_SHARED_HMAC_CMD:
+          processComputeSharedHmacCmd(apdu);
+          break;
+        case INS_DESTROY_ATT_IDS_CMD:
+          processDestroyAttIdsCmd(apdu);
+          break;
+        case INS_VERIFY_AUTHORIZATION_CMD:
+          processVerifyAuthorizationCmd(apdu);
+          break;
+        case INS_GET_HMAC_SHARING_PARAM_CMD:
+          processGetHmacSharingParamCmd(apdu);
+          break;
+        case INS_GET_KEY_CHARACTERISTICS_CMD:
+          processGetKeyCharacteristicsCmd(apdu);
+          break;
+        case INS_GET_HW_INFO_CMD:
+          processGetHwInfoCmd(apdu);
+          break;
+        case INS_BEGIN_OPERATION_CMD:
+          processBeginOperationCmd(apdu);
+          break;
+        case INS_UPDATE_OPERATION_CMD:
+          processUpdateOperationCmd(apdu);
+          break;
+        case INS_FINISH_OPERATION_CMD:
+          processFinishOperationCmd(apdu);
+          break;
+        case INS_ABORT_OPERATION_CMD:
+          processAbortOperationCmd(apdu);
+          break;
+        case INS_DEVICE_LOCKED_CMD:
+          processDeviceLockedCmd(apdu);
+          break;
+        case INS_EARLY_BOOT_ENDED_CMD:
+          processEarlyBootEndedCmd(apdu);
+          break;
+        case INS_UPDATE_AAD_OPERATION_CMD:
+          processUpdateAadOperationCmd(apdu);
+          break;
+        case INS_GENERATE_RKP_KEY_CMD:
+        case INS_BEGIN_SEND_DATA_CMD:
+        case INS_UPDATE_CHALLENGE_CMD:
+        case INS_UPDATE_EEK_CHAIN_CMD:
+        case INS_UPDATE_KEY_CMD:
+        case INS_FINISH_SEND_DATA_CMD:
+        case INS_GET_RESPONSE_CMD:
+        case INS_GET_RKP_HARDWARE_INFO:
+          rkp.process(apduIns, apdu);
+          break;
+          // KeyMint 2.0
+        case INS_GET_ROT_CHALLENGE_CMD:
+          processGetRootOfTrustChallenge(apdu);
+          break;
+        case INS_GET_ROT_DATA_CMD:
+          sendResponse(apdu, KMError.UNIMPLEMENTED);
+          break;
+        case INS_SEND_ROT_DATA_CMD:
+          processSendRootOfTrust(apdu);
+          break;
+        default:
+          ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+      }
+    } catch (KMException exception) {
+      freeOperations();
+      resetWrappingKey();
+      sendResponse(apdu, KMException.reason());
+    } catch (ISOException exp) {
+      freeOperations();
+      resetWrappingKey();
+      sendResponse(apdu, mapISOErrorToKMError(exp.getReason()));
+    } catch (CryptoException e) {
+      freeOperations();
+      resetWrappingKey();
+      sendResponse(apdu, mapCryptoErrorToKMError(e.getReason()));
+    } catch (Exception e) {
+      freeOperations();
+      resetWrappingKey();
+      sendResponse(apdu, KMError.GENERIC_UNKNOWN_ERROR);
+    } finally {
+      repository.clean();
+    }
+  }
+
+  private void processGetRootOfTrustChallenge(APDU apdu) {
+    byte[] scratchpad = apdu.getBuffer();
+    // Generate 16-byte random challenge nonce, used to prove freshness when exchanging root of
+    // trust data.
+    seProvider.newRandomNumber(scratchpad, (short) 0, (short) 16);
+    kmDataStore.setChallenge(scratchpad, (short) 0, (short) 16);
+    short challenge = KMByteBlob.instance(scratchpad, (short) 0, (short) 16);
+    short arr = KMArray.instance((short) 2);
+    KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(arr).add((short) 1, challenge);
+    sendOutgoing(apdu, arr);
+  }
+
+  private short sendRootOfTrustCmd(APDU apdu) {
+    short arrInst = KMArray.instance((short) 4);
+    short headers = KMCoseHeaders.exp();
+    KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp());
+    KMArray.cast(arrInst).add((short) 1, headers);
+    KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp());
+    KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp());
+    short semanticTag = KMSemanticTag.exp(arrInst);
+    short arr = KMArray.exp(semanticTag);
+    return receiveIncoming(apdu, arr);
+  }
+
+  private void processSendRootOfTrust(APDU apdu) {
+    byte[] scratchPad = apdu.getBuffer();
+    short cmd = KMType.INVALID_VALUE;
+    // As per VTS if the input data is empty or not well-formed
+    // CoseMac return VERIFICATION_FAILED error.
+    try {
+      cmd = sendRootOfTrustCmd(apdu);
+    } catch (Exception e) {
+      KMException.throwIt(KMError.VERIFICATION_FAILED);
+    }
+
+    short semanticTag = KMArray.cast(cmd).get((short) 0);
+    short coseMacPtr = KMSemanticTag.cast(semanticTag).getValuePtr();
+    // Exp for KMCoseHeaders
+    short coseHeadersExp = KMCoseHeaders.exp();
+    // validate protected Headers
+    short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET);
+    ptr =
+        decoder.decode(
+            coseHeadersExp,
+            KMByteBlob.cast(ptr).getBuffer(),
+            KMByteBlob.cast(ptr).getStartOff(),
+            KMByteBlob.cast(ptr).length());
+
+    if (!KMCoseHeaders.cast(ptr)
+        .isDataValid(tmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) {
+      KMException.throwIt(KMError.VERIFICATION_FAILED);
+    }
+
+    // Validate the Mac
+    short len = kmDataStore.getChallenge(scratchPad, (short) 0);
+    short extAad = KMByteBlob.instance(scratchPad, (short) 0, len);
+    // Compute CoseMac Structure and compare the macs.
+    short rotPayload = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET);
+    short macStructure =
+        KMCose.constructCoseMacStructure(
+            KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET),
+            extAad,
+            rotPayload);
+    short encodedLen =
+        KMKeymasterApplet.encodeToApduBuffer(
+            macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+    if (!seProvider.hmacVerify(
+        kmDataStore.getComputedHmacKey(),
+        scratchPad,
+        (short) 0,
+        encodedLen,
+        KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(),
+        KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getStartOff(),
+        KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length())) {
+      KMException.throwIt(KMError.VERIFICATION_FAILED);
+    }
+    // Store the data only once after reboot.
+    // Allow set boot params only when the host device reboots and the applet is in
+    // active state. If host does not support boot signal event, then allow this
+    // instruction any time.
+    kmDataStore.getDeviceBootStatus(scratchPad, (short) 0);
+    if (((scratchPad[0] & KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS) == 0)) {
+      // store the data.
+      storeRootOfTrust(rotPayload, scratchPad);
+      kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS);
+    }
+    // Invalidate the challenge
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0);
+    kmDataStore.setChallenge(scratchPad, (short) 0, (short) 16);
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void storeRootOfTrust(short rotPayload, byte[] scratchPad) {
+    short byteBlobExp = KMByteBlob.exp();
+    short intExp = KMInteger.exp();
+    short boolExp = KMSimpleValue.exp();
+    short arr = KMArray.instance((short) 5);
+    KMArray.cast(arr).add((short) 0, byteBlobExp); // Verfied boot key.
+    KMArray.cast(arr).add((short) 1, boolExp); // deviceLocked.
+    KMArray.cast(arr).add((short) 2, intExp); // Verified Boot State.
+    KMArray.cast(arr).add((short) 3, byteBlobExp); // Verfied boot hash.
+    KMArray.cast(arr).add((short) 4, intExp); // Boot patch level
+    short semanticExp = KMSemanticTag.exp(arr);
+
+    short semanticPtr =
+        decoder.decode(
+            semanticExp,
+            KMByteBlob.cast(rotPayload).getBuffer(),
+            KMByteBlob.cast(rotPayload).getStartOff(),
+            KMByteBlob.cast(rotPayload).length());
+    short rotArr = KMSemanticTag.cast(semanticPtr).getValuePtr();
+    // Store verified boot key
+    short ptr = KMArray.cast(rotArr).get((short) 0);
+    kmDataStore.setBootKey(
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        KMByteBlob.cast(ptr).length());
+    // Store Boot device locked.
+    ptr = KMArray.cast(rotArr).get((short) 1);
+    kmDataStore.setDeviceLocked(
+        (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.TRUE) ? true : false);
+    // Store verified boot state
+    ptr = KMArray.cast(rotArr).get((short) 2);
+    kmDataStore.setBootState(KMInteger.cast(ptr).getShort());
+    // Store Verified boot hash
+    ptr = KMArray.cast(rotArr).get((short) 3);
+    kmDataStore.setVerifiedBootHash(
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        KMByteBlob.cast(ptr).length());
+    // Store boot patch level
+    ptr = KMArray.cast(rotArr).get((short) 4);
+    kmDataStore.setBootPatchLevel(
+        KMInteger.cast(ptr).getBuffer(),
+        KMInteger.cast(ptr).getStartOff(),
+        KMInteger.cast(ptr).length());
+  }
+
+  // After every device boot, the Keymaster becomes ready to execute all the commands only after
+  // 1. boot parameters are set,
+  // 2. system properties are set and
+  // 3. computed the shared secret successfully.
+  private boolean isKeyMintReady(byte apduIns) {
+    if (kmDataStore.isDeviceReady()) {
+      return true;
+    }
+    // Below commands are allowed even if the Keymaster is not ready.
+    switch (apduIns) {
+      case INS_GET_HW_INFO_CMD:
+      case INS_GET_RKP_HARDWARE_INFO:
+      case INS_ADD_RNG_ENTROPY_CMD:
+      case INS_GET_HMAC_SHARING_PARAM_CMD:
+      case INS_COMPUTE_SHARED_HMAC_CMD:
+      case INS_EARLY_BOOT_ENDED_CMD:
+      case INS_INIT_STRONGBOX_CMD:
+      case INS_GET_ROT_CHALLENGE_CMD:
+      case INS_SEND_ROT_DATA_CMD:
+        return true;
+      default:
+        break;
+    }
+    return false;
+  }
+
+  private void generateUniqueOperationHandle(byte[] buf, short offset, short len) {
+    do {
+      seProvider.newRandomNumber(buf, offset, len);
+    } while (null != findOperation(buf, offset, len));
+  }
+
+  private void freeOperations() {
+    if (data[OP_HANDLE] != KMType.INVALID_VALUE) {
+      KMOperationState op = findOperation(data[OP_HANDLE]);
+      if (op != null) {
+        releaseOperation(op);
+      }
+    }
+  }
+
+  private void processEarlyBootEndedCmd(APDU apdu) {
+    kmDataStore.setEarlyBootEndedStatus(true);
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private short deviceLockedCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 2);
+    short ptr = KMVerificationToken.exp();
+    // passwordOnly
+    KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+    // verification token
+    KMArray.cast(cmd).add((short) 1, ptr);
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processDeviceLockedCmd(APDU apdu) {
+    short cmd = deviceLockedCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    short passwordOnly = KMArray.cast(cmd).get((short) 0);
+    short verToken = KMArray.cast(cmd).get((short) 1);
+    passwordOnly = KMInteger.cast(passwordOnly).getByte();
+    validateVerificationToken(verToken, scratchPad);
+    short verTime = KMVerificationToken.cast(verToken).getTimestamp();
+    short lastDeviceLockedTime;
+    try {
+      lastDeviceLockedTime = kmDataStore.getDeviceTimeStamp();
+    } catch (KMException e) {
+      lastDeviceLockedTime = KMInteger.uint_8((byte) 0);
+    }
+    if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) {
+      Util.arrayFillNonAtomic(scratchPad, (short) 0, KMInteger.UINT_64, (byte) 0);
+      KMInteger.cast(verTime).getValue(scratchPad, (short) 0, KMInteger.UINT_64);
+      kmDataStore.setDeviceLock(true);
+      kmDataStore.setDeviceLockPasswordOnly(passwordOnly == 0x01);
+      kmDataStore.setDeviceLockTimestamp(scratchPad, (short) 0, KMInteger.UINT_64);
+    }
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void resetWrappingKey() {
+    if (!isValidWrappingKey()) {
+      return;
+    }
+    Util.arrayFillNonAtomic(wrappingKey, (short) 1, WRAPPING_KEY_SIZE, (byte) 0);
+    wrappingKey[0] = -1;
+  }
+
+  private boolean isValidWrappingKey() {
+    return wrappingKey[0] != -1;
+  }
+
+  private short getWrappingKey() {
+    return KMByteBlob.instance(wrappingKey, (short) 1, WRAPPING_KEY_SIZE);
+  }
+
+  private void setWrappingKey(short key) {
+    if (KMByteBlob.cast(key).length() != WRAPPING_KEY_SIZE) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    wrappingKey[0] = 0;
+    Util.arrayCopyNonAtomic(
+        KMByteBlob.cast(key).getBuffer(),
+        KMByteBlob.cast(key).getStartOff(),
+        wrappingKey,
+        (short) 1,
+        WRAPPING_KEY_SIZE);
+  }
+
+  protected void resetTransientBuffers() {
+    short index = 0;
+    while (index < data.length) {
+      data[index] = KMType.INVALID_VALUE;
+      index++;
+    }
+    index = 0;
+    while (index < tmpVariables.length) {
+      tmpVariables[index] = KMType.INVALID_VALUE;
+      index++;
+    }
+  }
+
+  public void sendOutgoing(
+      APDU apdu, KMAttestationCert cert, short certStart, short keyblob, short keyChars) {
+    // This is the special case where the output is encoded manually without using
+    // the encoder algorithm. Encoder creates a duplicate copy for each KMType Object.
+    // The output of the generateKey, importKey and importWrappedKey commands are huge so
+    // by manually encoding we can avoid duplicate copies.
+    // The output data is directly written to the end of heap in the below order
+    // output = [
+    //     errorCode  : uint // ErrorCode
+    //     keyBlob    : bstr // KeyBlob.
+    //     keyChars
+    //     certifcate
+    // ]
+    // certificate = [
+    //     x509_cert : bstr // X509 certificate
+    // ]
+    // keyChars = {  // Map
+    // }
+    byte[] buffer = repository.getHeap();
+
+    if (cert == null) {
+      // This happens for Symmetric keys.
+      short bufferStart = repository.allocReclaimableMemory((short) 1);
+      buffer[bufferStart] = (byte) 0x80; // Array of 0 length.
+    } else {
+      // Encode the certificate into cbor data at the end of the heap
+      // certData = [
+      //     x509_cert : bstr // X509 certificate
+      // ]
+      short bufferStart =
+          encoder.encodeCert(
+              repository.getHeap(), certStart, cert.getCertStart(), cert.getCertLength());
+      // reclaim the unused memory in the certificate.
+      repository.reclaimMemory((short) (bufferStart - certStart));
+    }
+
+    // Encode KeyCharacteristics at the end of heap just before data[CERTIFICATE]
+    encodeKeyCharacteristics(keyChars);
+    // and encode it to the end of the buffer before KEY_CHARACTERISTICS
+    encodeKeyBlob(keyblob);
+    // Write Array header and ErrorCode before data[KEY_BLOB]
+    short bufferStartOffset = repository.allocReclaimableMemory((short) 2);
+    Util.setShort(buffer, bufferStartOffset, (short) 0x8400);
+
+    short bufferLength = (short) (KMRepository.HEAP_SIZE - bufferStartOffset);
+    // Send data
+    apdu.setOutgoing();
+    apdu.setOutgoingLength(bufferLength);
+    apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength);
+  }
+
+  private void processGetHwInfoCmd(APDU apdu) {
+    // No arguments expected
+    final byte version = 2;
+    // Make the response
+    short respPtr = KMArray.instance((short) 6);
+    KMArray resp = KMArray.cast(respPtr);
+    resp.add((short) 0, KMInteger.uint_16(KMError.OK));
+    resp.add((short) 1, KMInteger.uint_8(version));
+    resp.add((short) 2, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX));
+    resp.add(
+        (short) 3,
+        KMByteBlob.instance(
+            JavacardKeymintDevice, (short) 0, (short) JavacardKeymintDevice.length));
+    resp.add((short) 4, KMByteBlob.instance(Google, (short) 0, (short) Google.length));
+    resp.add((short) 5, KMInteger.uint_8((byte) 1));
+    // send buffer to host
+    sendOutgoing(apdu, respPtr);
+  }
+
+  private short addRngEntropyCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 1);
+    // Rng entropy
+    KMArray.cast(cmd).add((short) 0, KMByteBlob.exp());
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processAddRngEntropyCmd(APDU apdu) {
+    // Receive the incoming request fully from the host.
+    short cmd = addRngEntropyCmd(apdu);
+    // Process
+    KMByteBlob blob = KMByteBlob.cast(KMArray.cast(cmd).get((short) 0));
+    // Maximum 2KiB of seed is allowed.
+    if (blob.length() > MAX_SEED_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length());
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private short getKeyCharacteristicsCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 3);
+    KMArray.cast(cmd).add((short) 0, KMByteBlob.exp());
+    KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+    KMArray.cast(cmd).add((short) 2, KMByteBlob.exp());
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processGetKeyCharacteristicsCmd(APDU apdu) {
+    // Receive the incoming request fully from the host.
+    short cmd = getKeyCharacteristicsCmd(apdu);
+    // Re-purpose the apdu buffer as scratch pad.
+    byte[] scratchPad = apdu.getBuffer();
+    data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0);
+    data[APP_ID] = KMArray.cast(cmd).get((short) 1);
+    data[APP_DATA] = KMArray.cast(cmd).get((short) 2);
+    if (KMByteBlob.cast(data[APP_ID]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE
+        || KMByteBlob.cast(data[APP_DATA]).length() > KMByteTag.MAX_APP_ID_APP_DATA_SIZE) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    if (!KMByteBlob.cast(data[APP_ID]).isValid()) {
+      data[APP_ID] = KMType.INVALID_VALUE;
+    }
+    if (!KMByteBlob.cast(data[APP_DATA]).isValid()) {
+      data[APP_DATA] = KMType.INVALID_VALUE;
+    }
+    // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+    // function itself.
+    if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+      KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+    }
+    // make response.
+    short resp = KMArray.instance((short) 2);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(resp).add((short) 1, data[KEY_CHARACTERISTICS]);
+    sendOutgoing(apdu, resp);
+  }
+
+  private void processGetHmacSharingParamCmd(APDU apdu) {
+    // No Arguments
+    // Create HMAC Sharing Parameters
+    short params = KMHmacSharingParameters.instance();
+    short nonce = kmDataStore.getHmacNonce();
+    short seed = KMByteBlob.instance((short) 0);
+    KMHmacSharingParameters.cast(params).setNonce(nonce);
+    KMHmacSharingParameters.cast(params).setSeed(seed);
+    // prepare the response
+    short resp = KMArray.instance((short) 2);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(resp).add((short) 1, params);
+    sendOutgoing(apdu, resp);
+  }
+
+  private void processDeleteAllKeysCmd(APDU apdu) {
+    // No arguments
+    // Send ok
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private short createKeyBlobExp(short version) {
+    short keyBlob = KMType.INVALID_VALUE;
+    short byteBlobExp = KMByteBlob.exp();
+    short keyChar = KMKeyCharacteristics.exp();
+    short keyParam = KMKeyParameters.exp();
+    switch (version) {
+      case (short) 0:
+        // Old KeyBlob has a maximum of 5 elements.
+        keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V0);
+        KMArray.cast(keyBlob).add((short) 0, byteBlobExp); // Secret
+        KMArray.cast(keyBlob).add((short) 1, byteBlobExp); // Nonce
+        KMArray.cast(keyBlob).add((short) 2, byteBlobExp); // AuthTag
+        KMArray.cast(keyBlob).add((short) 3, keyChar); // KeyChars
+        KMArray.cast(keyBlob).add((short) 4, byteBlobExp); // PubKey
+        break;
+      case (short) 1:
+        keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V1);
+        KMArray.cast(keyBlob).add((short) 0, KMInteger.exp()); // Version
+        KMArray.cast(keyBlob).add((short) 1, byteBlobExp); // Secret
+        KMArray.cast(keyBlob).add((short) 2, byteBlobExp); // Nonce
+        KMArray.cast(keyBlob).add((short) 3, byteBlobExp); // AuthTag
+        KMArray.cast(keyBlob).add((short) 4, keyChar); // KeyChars
+        KMArray.cast(keyBlob).add((short) 5, byteBlobExp); // PubKey
+        break;
+      case (short) 2:
+      case (short) 3:
+        keyBlob = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3);
+        KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_VERSION_OFFSET, KMInteger.exp());
+        KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_SECRET, byteBlobExp);
+        KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, byteBlobExp);
+        KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_NONCE, byteBlobExp);
+        KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PARAMS, keyChar);
+        KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_CUSTOM_TAGS, keyParam);
+        KMArray.cast(keyBlob).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, byteBlobExp);
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    return keyBlob;
+  }
+
+  private void processDeleteKeyCmd(APDU apdu) {
+    // Send ok
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private short computeSharedHmacCmd(APDU apdu) {
+    short params = KMHmacSharingParameters.exp();
+    short paramsVec = KMArray.exp(params);
+    short cmd = KMArray.instance((short) 1);
+    KMArray.cast(cmd).add((short) 0, paramsVec);
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processComputeSharedHmacCmd(APDU apdu) {
+    // Receive the incoming request fully from the host into buffer.
+    short cmd = computeSharedHmacCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    data[HMAC_SHARING_PARAMS] = KMArray.cast(cmd).get((short) 0);
+    // Concatenate HMAC Params
+    // tmpVariables[0]
+    short paramsLen = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); // total number of params
+    // tmpVariables[1]
+    short concateBuffer = repository.alloc((short) (paramsLen * HMAC_SHARED_PARAM_MAX_SIZE));
+    // tmpVariables[2]
+    short paramIndex = 0; // index for params
+    // tmpVariables[3]
+    short bufferIndex = 0; // index for concatenation buffer
+    // To check if nonce created by Strongbox is found. This value becomes 1 if both
+    // seed and nonce created here are found in hmac sharing parameters received.
+    // tmpVariables[7] = 0;
+    short found = 0;
+    // tmpVariables[9]
+    short nonce = kmDataStore.getHmacNonce();
+
+    while (paramIndex < paramsLen) {
+      // read HmacSharingParam
+      // tmpVariables[4]
+      short param = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(paramIndex);
+      // get seed - 32 bytes max
+      // tmpVariables[5]
+      short seed = KMHmacSharingParameters.cast(param).getSeed();
+      // tmpVariables[6]
+      short seedLength = KMByteBlob.cast(seed).length();
+      // if seed is present
+      if (seedLength != 0) {
+        // then copy that to concatenation buffer
+        Util.arrayCopyNonAtomic(
+            KMByteBlob.cast(seed).getBuffer(),
+            KMByteBlob.cast(seed).getStartOff(),
+            repository.getHeap(),
+            (short) (concateBuffer + bufferIndex), // concat index
+            seedLength);
+        bufferIndex += seedLength; // increment the concat index
+      } else if (found == 0) {
+        found = 1; // Applet does not have any seed. Potentially
+      }
+      // if nonce is present get nonce - 32 bytes
+      // tmpVariables[5]
+      short paramNonce = KMHmacSharingParameters.cast(param).getNonce();
+      short nonceLen = KMByteBlob.cast(paramNonce).length();
+      // if nonce is less then 32 - it is an error
+      if (nonceLen < 32) {
+        KMException.throwIt(KMError.INVALID_ARGUMENT);
+      }
+      // copy nonce to concatenation buffer
+      Util.arrayCopyNonAtomic(
+          KMByteBlob.cast(paramNonce).getBuffer(),
+          KMByteBlob.cast(paramNonce).getStartOff(),
+          repository.getHeap(),
+          (short) (concateBuffer + bufferIndex), // index
+          nonceLen);
+
+      // Check if the nonce generated here is present in the hmacSharingParameters array.
+      // Otherwise throw INVALID_ARGUMENT error.
+      if (found == 1) {
+        if (0
+            == Util.arrayCompare(
+                repository.getHeap(),
+                (short) (concateBuffer + bufferIndex),
+                KMByteBlob.cast(nonce).getBuffer(),
+                KMByteBlob.cast(nonce).getStartOff(),
+                nonceLen)) {
+          found = 2; // hmac nonce for this keymaster found.
+        } else {
+          found = 0;
+        }
+      }
+      bufferIndex += nonceLen; // increment by nonce length
+      paramIndex++; // go to next hmac param in the vector
+    }
+    if (found != 2) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    // generate the key and store it in scratch pad - 32 bytes
+    // tmpVariables[6]
+    short keyLen =
+        seProvider.cmacKDF(
+            kmDataStore.getPresharedKey(),
+            ckdfLable,
+            (short) 0,
+            (short) ckdfLable.length,
+            repository.getHeap(),
+            concateBuffer,
+            bufferIndex,
+            scratchPad,
+            (short) 0);
+
+    // persist the computed hmac key.
+    kmDataStore.createComputedHmacKey(scratchPad, (short) 0, keyLen);
+    // Generate sharingKey verification signature and store that in scratch pad.
+    // tmpVariables[5]
+    short signLen =
+        seProvider.hmacSign(
+            scratchPad,
+            (short) 0,
+            keyLen,
+            sharingCheck,
+            (short) 0,
+            (short) sharingCheck.length,
+            scratchPad,
+            keyLen);
+    kmDataStore.setDeviceBootStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_SUCCESS);
+    // verification signature blob - 32 bytes
+    // tmpVariables[1]
+    short signature = KMByteBlob.instance(scratchPad, keyLen, signLen);
+    // prepare the response
+    short resp = KMArray.instance((short) 2);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(resp).add((short) 1, signature);
+    sendOutgoing(apdu, resp);
+  }
+
+  private short upgradeKeyCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 2);
+    short keyParams = KMKeyParameters.exp();
+    KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Key Blob
+    KMArray.cast(cmd).add((short) 1, keyParams); // Key Params
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private boolean isKeyUpgradeRequired(
+      short keyBlob, short appId, short appData, byte[] scratchPad) {
+    // Check if the KeyBlob is compatible. If there is any change in the KeyBlob, the version
+    // Parameter in the KeyBlob should be updated to the next version.
+    short version = readKeyBlobVersion(keyBlob);
+    parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad, version);
+    if (version < KEYBLOB_CURRENT_VERSION) {
+      return true;
+    }
+    short bootPatchLevel = kmDataStore.getBootPatchLevel();
+    // Fill the key-value properties in the scratchpad
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 16, (byte) 0);
+    Util.setShort(scratchPad, (short) 0, KMType.OS_VERSION);
+    Util.setShort(scratchPad, (short) 2, kmDataStore.getOsVersion());
+    Util.setShort(scratchPad, (short) 4, KMType.OS_PATCH_LEVEL);
+    Util.setShort(scratchPad, (short) 6, kmDataStore.getOsPatch());
+    Util.setShort(scratchPad, (short) 8, KMType.VENDOR_PATCH_LEVEL);
+    Util.setShort(scratchPad, (short) 10, kmDataStore.getVendorPatchLevel());
+    Util.setShort(scratchPad, (short) 12, KMType.BOOT_PATCH_LEVEL);
+    Util.setShort(scratchPad, (short) 14, bootPatchLevel);
+    short index = 0;
+    short tag;
+    short systemParam;
+    boolean isKeyUpgradeRequired = false;
+    while (index < 16) {
+      tag = Util.getShort(scratchPad, index);
+      systemParam = Util.getShort(scratchPad, (short) (index + 2));
+      // validate the tag and check if key needs upgrade.
+      short tagValue = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]);
+      tagValue = KMIntegerTag.cast(tagValue).getValue();
+      short zero = KMInteger.uint_8((byte) 0);
+      if (tagValue != KMType.INVALID_VALUE) {
+        // OS version in key characteristics must be less the OS version stored in Javacard or the
+        // stored version must be zero. Then only upgrade is allowed else it is invalid argument.
+        if ((tag == KMType.OS_VERSION
+            && KMInteger.compare(tagValue, systemParam) == 1
+            && KMInteger.compare(systemParam, zero) == 0)) {
+          // Key needs upgrade.
+          isKeyUpgradeRequired = true;
+        } else if ((KMInteger.compare(tagValue, systemParam) == -1)) {
+          // Each os version or patch level associated with the key must be less than it's
+          // corresponding value stored in Javacard, then only upgrade is allowed otherwise it
+          // is invalid argument.
+          isKeyUpgradeRequired = true;
+        } else if (KMInteger.compare(tagValue, systemParam) == 1) {
+          KMException.throwIt(KMError.INVALID_ARGUMENT);
+        }
+      } else {
+        KMException.throwIt(KMError.UNKNOWN_ERROR);
+      }
+      index += 4;
+    }
+    return isKeyUpgradeRequired;
+  }
+
+  private void processUpgradeKeyCmd(APDU apdu) {
+    // Receive the incoming request fully from the host into buffer.
+    short cmd = upgradeKeyCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+
+    short keyBlob = KMArray.cast(cmd).get((short) 0);
+    data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1);
+    short appId = getApplicationId(data[KEY_PARAMETERS]);
+    short appData = getApplicationData(data[KEY_PARAMETERS]);
+
+    data[KEY_BLOB] = KMType.INVALID_VALUE;
+    // Check if the KeyBlob requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+    // function itself, but if there is a difference in the KeyBlob version isKeyUpgradeRequired()
+    // does not parse the KeyBlob.
+    boolean isKeyUpgradeRequired = isKeyUpgradeRequired(keyBlob, appId, appData, scratchPad);
+    if (isKeyUpgradeRequired) {
+      // copy origin
+      data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]);
+      byte keyType = getKeyType(data[HW_PARAMETERS]);
+      switch (keyType) {
+        case ASYM_KEY_TYPE:
+          data[KEY_BLOB] = KMArray.instance(ASYM_KEY_BLOB_SIZE_V2_V3);
+          KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+          break;
+        case SYM_KEY_TYPE:
+          data[KEY_BLOB] = KMArray.instance(SYM_KEY_BLOB_SIZE_V2_V3);
+          break;
+        default:
+          KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+      }
+      // Update the system properties to the latest values and also re-create the KeyBlob's
+      // KeyCharacteristics to make sure all the values are up-to-date with the latest applet
+      // changes.
+      upgradeKeyBlobKeyCharacteristics(data[HW_PARAMETERS], scratchPad);
+      // create new key blob with current os version etc.
+      createEncryptedKeyBlob(scratchPad);
+      short prevReclaimIndex = repository.getHeapReclaimIndex();
+      short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+      data[KEY_BLOB] =
+          encoder.encode(
+              data[KEY_BLOB], repository.getHeap(), offset, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+      data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]);
+      repository.reclaimMemory(MAX_KEYBLOB_SIZE);
+    } else {
+      data[KEY_BLOB] = KMByteBlob.instance((short) 0);
+    }
+    // prepare the response
+    short resp = KMArray.instance((short) 2);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(resp).add((short) 1, data[KEY_BLOB]);
+    sendOutgoing(apdu, resp);
+  }
+
+  private void processExportKeyCmd(APDU apdu) {
+    sendResponse(apdu, KMError.UNIMPLEMENTED);
+  }
+
+  private void processWrappingKeyBlob(short keyBlob, short wrapParams, byte[] scratchPad) {
+    // Read App Id and App Data if any from un wrapping key params
+    data[APP_ID] = getApplicationId(wrapParams);
+    data[APP_DATA] = getApplicationData(wrapParams);
+    data[KEY_PARAMETERS] = wrapParams;
+    data[KEY_BLOB] = keyBlob;
+    // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+    // function itself.
+    if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+      KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+    }
+    validateWrappingKeyBlob();
+  }
+
+  private void validateWrappingKeyBlob() {
+    // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest
+    // SHA2_256.
+    KMTag.assertPresence(
+        data[SB_PARAMETERS],
+        KMType.ENUM_TAG,
+        KMType.ALGORITHM,
+        KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+    if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM);
+    }
+    if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS])) {
+      KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+    }
+    if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS])) {
+      KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+    }
+    if (!KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, data[HW_PARAMETERS])) {
+      KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE));
+    }
+
+    // Check that the digest and padding mode specified in unwrapping  parameters are SHA2_256
+    // and RSA_OAEP respectively.
+    if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+    }
+    if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+    }
+  }
+
+  private short decryptTransportKey(
+      short privExp, short modulus, short transportKey, byte[] scratchPad) {
+    short length =
+        seProvider.rsaDecipherOAEP256(
+            KMByteBlob.cast(privExp).getBuffer(),
+            KMByteBlob.cast(privExp).getStartOff(),
+            KMByteBlob.cast(privExp).length(),
+            KMByteBlob.cast(modulus).getBuffer(),
+            KMByteBlob.cast(modulus).getStartOff(),
+            KMByteBlob.cast(modulus).length(),
+            KMByteBlob.cast(transportKey).getBuffer(),
+            KMByteBlob.cast(transportKey).getStartOff(),
+            KMByteBlob.cast(transportKey).length(),
+            scratchPad,
+            (short) 0);
+    return KMByteBlob.instance(scratchPad, (short) 0, length);
+  }
+
+  private void unmask(short data, short maskingKey) {
+    short dataLength = KMByteBlob.cast(data).length();
+    short maskLength = KMByteBlob.cast(maskingKey).length();
+    // Length of masking key and transport key must be same.
+    if (maskLength != dataLength) {
+      KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+    }
+    short index = 0; // index
+    // Xor every byte of masking and key and store the result in data[SECRET]
+    while (index < maskLength) {
+      short var1 = (short) (((short) KMByteBlob.cast(maskingKey).get(index)) & 0x00FF);
+      short var2 = (short) (((short) KMByteBlob.cast(data).get(index)) & 0x00FF);
+      KMByteBlob.cast(data).add(index, (byte) (var1 ^ var2));
+      index++;
+    }
+  }
+
+  private short beginImportWrappedKeyCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 4);
+    short params = KMKeyParameters.expAny();
+    KMArray.cast(cmd).add((short) 0, KMByteBlob.exp()); // Encrypted Transport Key
+    KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); // Wrapping Key KeyBlob
+    KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Masking Key
+    params = KMKeyParameters.exp();
+    KMArray.cast(cmd).add((short) 3, params); // Wrapping key blob Params
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processBeginImportWrappedKeyCmd(APDU apdu) {
+    // Receive the incoming request fully from the host into buffer.
+    short cmd = beginImportWrappedKeyCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    // Step -1 parse the wrapping key blob
+    // read wrapping key blob
+    short keyBlob = KMArray.cast(cmd).get((short) 1);
+    // read un wrapping key params
+    short wrappingKeyParameters = KMArray.cast(cmd).get((short) 3);
+    processWrappingKeyBlob(keyBlob, wrappingKeyParameters, scratchPad);
+    // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key
+    short transportKey =
+        decryptTransportKey(
+            data[SECRET], data[PUB_KEY], KMArray.cast(cmd).get((short) 0), scratchPad);
+    // Step 3 - XOR the decrypted AES-GCM key with with masking key
+    unmask(transportKey, KMArray.cast(cmd).get((short) 2));
+    if (isValidWrappingKey()) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    setWrappingKey(transportKey);
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private short aesGCMEncrypt(
+      short aesSecret, short input, short nonce, short authData, short authTag, byte[] scratchPad) {
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0);
+    short len =
+        seProvider.aesGCMEncrypt(
+            KMByteBlob.cast(aesSecret).getBuffer(),
+            KMByteBlob.cast(aesSecret).getStartOff(),
+            KMByteBlob.cast(aesSecret).length(),
+            KMByteBlob.cast(input).getBuffer(),
+            KMByteBlob.cast(input).getStartOff(),
+            KMByteBlob.cast(input).length(),
+            scratchPad,
+            (short) 0,
+            KMByteBlob.cast(nonce).getBuffer(),
+            KMByteBlob.cast(nonce).getStartOff(),
+            KMByteBlob.cast(nonce).length(),
+            KMByteBlob.cast(authData).getBuffer(),
+            KMByteBlob.cast(authData).getStartOff(),
+            KMByteBlob.cast(authData).length(),
+            KMByteBlob.cast(authTag).getBuffer(),
+            KMByteBlob.cast(authTag).getStartOff(),
+            KMByteBlob.cast(authTag).length());
+    return KMByteBlob.instance(scratchPad, (short) 0, len);
+  }
+
+  private short aesGCMDecrypt(
+      short aesSecret, short input, short nonce, short authData, short authTag, byte[] scratchPad) {
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0);
+    if (!seProvider.aesGCMDecrypt(
+        KMByteBlob.cast(aesSecret).getBuffer(),
+        KMByteBlob.cast(aesSecret).getStartOff(),
+        KMByteBlob.cast(aesSecret).length(),
+        KMByteBlob.cast(input).getBuffer(),
+        KMByteBlob.cast(input).getStartOff(),
+        KMByteBlob.cast(input).length(),
+        scratchPad,
+        (short) 0,
+        KMByteBlob.cast(nonce).getBuffer(),
+        KMByteBlob.cast(nonce).getStartOff(),
+        KMByteBlob.cast(nonce).length(),
+        KMByteBlob.cast(authData).getBuffer(),
+        KMByteBlob.cast(authData).getStartOff(),
+        KMByteBlob.cast(authData).length(),
+        KMByteBlob.cast(authTag).getBuffer(),
+        KMByteBlob.cast(authTag).getStartOff(),
+        KMByteBlob.cast(authTag).length())) {
+      KMException.throwIt(KMError.VERIFICATION_FAILED);
+    }
+    return KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(input).length());
+  }
+
+  private short finishImportWrappedKeyCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 8);
+    short params = KMKeyParameters.expAny();
+    KMArray.cast(cmd).add((short) 0, params); // Key Params of wrapped key
+    KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format
+    KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob
+    KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); // Auth Tag
+    KMArray.cast(cmd).add((short) 4, KMByteBlob.exp()); // IV - Nonce
+    KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA
+    KMArray.cast(cmd).add((short) 6, KMInteger.exp()); // Password Sid
+    KMArray.cast(cmd).add((short) 7, KMInteger.exp()); // Biometric Sid
+    return receiveIncoming(apdu, cmd);
+  }
+
+  // TODO remove cmd later on
+  private void processFinishImportWrappedKeyCmd(APDU apdu) {
+    short cmd = finishImportWrappedKeyCmd(apdu);
+    short keyParameters = KMArray.cast(cmd).get((short) 0);
+    short keyFmt = KMArray.cast(cmd).get((short) 1);
+    keyFmt = KMEnum.cast(keyFmt).getVal();
+    validateImportKey(keyParameters, keyFmt);
+    byte[] scratchPad = apdu.getBuffer();
+    // Step 4 - AES-GCM decrypt the wrapped key
+    data[INPUT_DATA] = KMArray.cast(cmd).get((short) 2);
+    data[AUTH_TAG] = KMArray.cast(cmd).get((short) 3);
+    data[NONCE] = KMArray.cast(cmd).get((short) 4);
+    data[AUTH_DATA] = KMArray.cast(cmd).get((short) 5);
+
+    if (!isValidWrappingKey()) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    data[IMPORTED_KEY_BLOB] =
+        aesGCMDecrypt(
+            getWrappingKey(),
+            data[INPUT_DATA],
+            data[NONCE],
+            data[AUTH_DATA],
+            data[AUTH_TAG],
+            scratchPad);
+    resetWrappingKey();
+    // Step 5 - Import decrypted key
+    data[ORIGIN] = KMType.SECURELY_IMPORTED;
+    data[KEY_PARAMETERS] = keyParameters;
+    // create key blob array
+    importKey(apdu, keyFmt, scratchPad);
+  }
+
+  private KMAttestationCert makeCommonCert(byte[] scratchPad) {
+    short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]);
+    boolean rsaCert = KMEnumTag.cast(alg).getValue() == KMType.RSA;
+    KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider);
+
+    short subject =
+        KMKeyParameters.findTag(
+            KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]);
+
+    // If no subject name is specified then use the default subject name.
+    if (subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0) {
+      subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length);
+    } else {
+      subject = KMByteTag.cast(subject).getValue();
+    }
+    cert.subjectName(subject);
+    // Validity period must be specified
+    short notBefore =
+        KMKeyParameters.findTag(
+            KMType.DATE_TAG, KMType.CERTIFICATE_NOT_BEFORE, data[KEY_PARAMETERS]);
+    if (notBefore == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.MISSING_NOT_BEFORE);
+    }
+    notBefore = KMIntegerTag.cast(notBefore).getValue();
+    short notAfter =
+        KMKeyParameters.findTag(
+            KMType.DATE_TAG, KMType.CERTIFICATE_NOT_AFTER, data[KEY_PARAMETERS]);
+    if (notAfter == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.MISSING_NOT_AFTER);
+    }
+    notAfter = KMIntegerTag.cast(notAfter).getValue();
+    // VTS sends notBefore == Epoch.
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0);
+    short epoch = KMInteger.instance(scratchPad, (short) 0, (short) 8);
+    short end = KMInteger.instance(dec319999Ms, (short) 0, (short) dec319999Ms.length);
+    if (KMInteger.compare(notBefore, epoch) == 0) {
+      cert.notBefore(
+          KMByteBlob.instance(jan01970, (short) 0, (short) jan01970.length), true, scratchPad);
+    } else {
+      cert.notBefore(notBefore, false, scratchPad);
+    }
+    // VTS sends notAfter == Dec 31st 9999
+    if (KMInteger.compare(notAfter, end) == 0) {
+      cert.notAfter(
+          KMByteBlob.instance(dec319999, (short) 0, (short) dec319999.length), true, scratchPad);
+    } else {
+      cert.notAfter(notAfter, false, scratchPad);
+    }
+    // Serial number
+    short serialNum =
+        KMKeyParameters.findTag(
+            KMType.BIGNUM_TAG, KMType.CERTIFICATE_SERIAL_NUM, data[KEY_PARAMETERS]);
+    if (serialNum != KMType.INVALID_VALUE) {
+      serialNum = KMBignumTag.cast(serialNum).getValue();
+    } else {
+      serialNum = KMByteBlob.instance((short) 1);
+      KMByteBlob.cast(serialNum).add((short) 0, (byte) 1);
+    }
+    cert.serialNumber(serialNum);
+    return cert;
+  }
+
+  private KMAttestationCert makeAttestationCert(
+      short attKeyBlob, short attKeyParam, short attChallenge, short issuer, byte[] scratchPad) {
+    KMAttestationCert cert = makeCommonCert(scratchPad);
+
+    // Read App Id and App Data.
+    short appId = getApplicationId(attKeyParam);
+    short appData = getApplicationData(attKeyParam);
+    // Take backup of the required global variables KEY_BLOB, PUB_KEY, SECRET, KEY_CHAR
+    // and HW_PARAMS before they get overridden by isKeyUpgradeRequired() function.
+    short origBlob = data[KEY_BLOB];
+    short pubKey = data[PUB_KEY];
+    short privKey = data[SECRET];
+    short hwParams = data[HW_PARAMETERS];
+    short keyChars = data[KEY_CHARACTERISTICS];
+    short customTags = data[CUSTOM_TAGS];
+    // Check if key requires upgrade for attestKeyBlob. The KeyBlob is parsed inside
+    // isKeyUpgradeRequired function itself.
+    if (isKeyUpgradeRequired(attKeyBlob, appId, appData, scratchPad)) {
+      KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+    }
+    // Get the private key of the attest key.
+    short attestationKeySecret = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET);
+    // Get the KeyCharacteristics and SB param of the attest key
+    short attestKeyCharacteristics = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PARAMS);
+    short attestKeySbParams =
+        KMKeyCharacteristics.cast(attestKeyCharacteristics).getStrongboxEnforced();
+    // If the attest key's purpose is not "attest key" then error.
+    short attKeyPurpose =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestKeySbParams);
+    if (!KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)) {
+      KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+    }
+    KMAsn1Parser asn1Decoder = KMAsn1Parser.instance();
+    short length = 0;
+    try {
+      asn1Decoder.validateDerSubject(issuer);
+    } catch (KMException e) {
+      KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
+    }
+    if (KMByteBlob.cast(issuer).length() > KMConfigurations.MAX_SUBJECT_DER_LEN) {
+      KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME);
+    }
+    // If issuer is not present then it is an error
+    if (KMByteBlob.cast(issuer).length() <= 0) {
+      KMException.throwIt(KMError.MISSING_ISSUER_SUBJECT_NAME);
+    }
+    short alg = KMEnumTag.getValue(KMType.ALGORITHM, attestKeySbParams);
+    if (alg == KMType.RSA) {
+      short attestationKeyPublic = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY);
+      cert.rsaAttestKey(attestationKeySecret, attestationKeyPublic, KMType.ATTESTATION_CERT);
+    } else if (alg == KMType.EC) {
+      cert.ecAttestKey(attestationKeySecret, KMType.ATTESTATION_CERT);
+    } else {
+      KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+    }
+    cert.attestationChallenge(attChallenge);
+    cert.issuer(issuer);
+
+    // Restore back the global variables.
+    data[PUB_KEY] = pubKey;
+    data[SECRET] = privKey;
+    data[KEY_BLOB] = origBlob;
+    data[HW_PARAMETERS] = hwParams;
+    data[KEY_CHARACTERISTICS] = keyChars;
+    data[CUSTOM_TAGS] = customTags;
+    data[SW_PARAMETERS] =
+        KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced();
+    data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced();
+    data[SB_PARAMETERS] =
+        KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced();
+    cert.publicKey(data[PUB_KEY]);
+
+    // Save attestation application id - must be present.
+    short attAppId =
+        KMKeyParameters.findTag(
+            KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]);
+    if (attAppId == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING);
+    }
+    cert.extensionTag(attAppId, false);
+    // unique id byte blob - uses application id and temporal month count of
+    // creation time.
+    attAppId = KMByteTag.cast(attAppId).getValue();
+    setUniqueId(cert, attAppId, scratchPad);
+    // Add Attestation Ids if present
+    addAttestationIds(cert, scratchPad);
+
+    // Add Tags
+    addTags(data[HW_PARAMETERS], true, cert);
+    addTags(data[SW_PARAMETERS], false, cert);
+    // Add Device Boot locked status
+    cert.deviceLocked(kmDataStore.isDeviceBootLocked());
+    // VB data
+    cert.verifiedBootHash(getVerifiedBootHash(scratchPad));
+    cert.verifiedBootKey(getBootKey(scratchPad));
+    cert.verifiedBootState((byte) kmDataStore.getBootState());
+    return cert;
+  }
+
+  private KMAttestationCert makeSelfSignedCert(
+      short attPrivKey, short attPubKey, short mode, byte[] scratchPad) {
+    KMAttestationCert cert = makeCommonCert(scratchPad);
+    short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+    short subject =
+        KMKeyParameters.findTag(
+            KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]);
+    // If no subject name is specified then use the default subject name.
+    if (subject == KMType.INVALID_VALUE || KMByteTag.cast(subject).length() == 0) {
+      subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length);
+    } else {
+      subject = KMByteTag.cast(subject).getValue();
+    }
+
+    if (alg == KMType.RSA) {
+      cert.rsaAttestKey(attPrivKey, attPubKey, (byte) mode);
+    } else {
+      cert.ecAttestKey(attPrivKey, (byte) mode);
+    }
+    cert.issuer(subject);
+    cert.subjectName(subject);
+    cert.publicKey(attPubKey);
+    return cert;
+  }
+
+  protected short getBootKey(byte[] scratchPad) {
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE, (byte) 0);
+    short len = kmDataStore.getBootKey(scratchPad, (short) 0);
+    if (len != VERIFIED_BOOT_KEY_SIZE) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE);
+  }
+
+  protected short getVerifiedBootHash(byte[] scratchPad) {
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE, (byte) 0);
+    short len = kmDataStore.getVerifiedBootHash(scratchPad, (short) 0);
+    if (len != VERIFIED_BOOT_HASH_SIZE) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE);
+  }
+
+  // --------------------------------
+  // Only add the Attestation ids which are requested in the attestation parameters.
+  // If the requested attestation ids are not provisioned or deleted then
+  // throw CANNOT_ATTEST_IDS error. If there is mismatch in the attestation
+  // id values of both the requested parameters and the provisioned parameters
+  // then throw INVALID_TAG error.
+  private void addAttestationIds(KMAttestationCert cert, byte[] scratchPad) {
+    byte index = 0;
+    short attIdTag;
+    short attIdTagValue;
+    short storedAttIdLen;
+    while (index < (short) attTags.length) {
+      attIdTag = KMKeyParameters.findTag(KMType.BYTES_TAG, attTags[index], data[KEY_PARAMETERS]);
+      if (attIdTag != KMType.INVALID_VALUE) {
+        attIdTagValue = KMByteTag.cast(attIdTag).getValue();
+        storedAttIdLen = kmDataStore.getAttestationId(attTags[index], scratchPad, (short) 0);
+        // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or
+        // Attestation IDs are deleted.
+        if (storedAttIdLen == 0) {
+          KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+        }
+        // Return INVALID_TAG if Attestation IDs does not match.
+        if ((storedAttIdLen != KMByteBlob.cast(attIdTagValue).length())
+            || (0
+                != Util.arrayCompare(
+                    scratchPad,
+                    (short) 0,
+                    KMByteBlob.cast(attIdTagValue).getBuffer(),
+                    KMByteBlob.cast(attIdTagValue).getStartOff(),
+                    storedAttIdLen))) {
+          KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+        }
+        short blob = KMByteBlob.instance(scratchPad, (short) 0, storedAttIdLen);
+        cert.extensionTag(KMByteTag.instance(attTags[index], blob), true);
+      }
+      index++;
+    }
+  }
+
+  private void processDestroyAttIdsCmd(APDU apdu) {
+    kmDataStore.deleteAttestationIds();
+    sendResponse(apdu, KMError.OK);
+  }
+
+  private void processVerifyAuthorizationCmd(APDU apdu) {
+    sendResponse(apdu, KMError.UNIMPLEMENTED);
+  }
+
+  private short abortOperationCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 1);
+    KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processAbortOperationCmd(APDU apdu) {
+    short cmd = abortOperationCmd(apdu);
+    data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+    KMOperationState op = findOperation(data[OP_HANDLE]);
+    if (op == null) {
+      sendResponse(apdu, KMError.INVALID_OPERATION_HANDLE);
+    } else {
+      releaseOperation(op);
+      sendResponse(apdu, KMError.OK);
+    }
+  }
+
+  private short finishOperationCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 6);
+    KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // op handle
+    KMArray.cast(cmd).add((short) 1, KMByteBlob.exp()); // input data
+    KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); // signature
+    short authToken = KMHardwareAuthToken.exp();
+    KMArray.cast(cmd).add((short) 3, authToken); // auth token
+    short verToken = KMVerificationToken.exp();
+    KMArray.cast(cmd).add((short) 4, verToken); // time stamp token
+    KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // confirmation token
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processFinishOperationCmd(APDU apdu) {
+    short cmd = finishOperationCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+    data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+    data[SIGNATURE] = KMArray.cast(cmd).get((short) 2);
+    data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3);
+    data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 4);
+    data[CONFIRMATION_TOKEN] = KMArray.cast(cmd).get((short) 5);
+    // Check Operation Handle
+    KMOperationState op = findOperation(data[OP_HANDLE]);
+    if (op == null) {
+      KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+    }
+    // Authorize the finish operation
+    authorizeUpdateFinishOperation(op, scratchPad);
+    switch (op.getPurpose()) {
+      case KMType.SIGN:
+        finishTrustedConfirmationOperation(op);
+      case KMType.VERIFY:
+        finishSigningVerifyingOperation(op, scratchPad);
+        break;
+      case KMType.ENCRYPT:
+        finishEncryptOperation(op, scratchPad);
+        break;
+      case KMType.DECRYPT:
+        finishDecryptOperation(op, scratchPad);
+        break;
+      case KMType.AGREE_KEY:
+        finishKeyAgreementOperation(op, scratchPad);
+        break;
+    }
+    if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) {
+      data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+    }
+    // Remove the operation handle
+    releaseOperation(op);
+
+    // make response
+    short resp = KMArray.instance((short) 2);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]);
+    sendOutgoing(apdu, resp);
+  }
+
+  private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) {
+    if (op.getAlgorithm() != KMType.AES && op.getAlgorithm() != KMType.DES) {
+      KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+    }
+    finishAesDesOperation(op);
+  }
+
+  private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) {
+    short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+    switch (op.getAlgorithm()) {
+      case KMType.RSA:
+        // Fill the scratch pad with zero
+        Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+        if (op.getPadding() == KMType.PADDING_NONE && len != 256) {
+          KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+        }
+        len =
+            op.getOperation()
+                .finish(
+                    KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+                    KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+                    len,
+                    scratchPad,
+                    (short) 0);
+
+        data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len);
+        break;
+      case KMType.AES:
+      case KMType.DES:
+        finishAesDesOperation(op);
+        break;
+    }
+  }
+
+  private void finishAesDesOperation(KMOperationState op) {
+    short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+    short blockSize = AES_BLOCK_SIZE;
+    if (op.getAlgorithm() == KMType.DES) {
+      blockSize = DES_BLOCK_SIZE;
+    }
+
+    if (op.getPurpose() == KMType.DECRYPT
+        && len > 0
+        && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC)
+        && ((short) (len % blockSize) != 0)) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+
+    if (op.getBlockMode() == KMType.GCM) {
+      if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) {
+        KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+      }
+      if (op.isAesGcmUpdateAllowed()) {
+        op.setAesGcmUpdateComplete();
+      }
+      // Get the output size
+      len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8));
+    }
+    // If padding i.e. pkcs7 then add padding to right
+    // Output data can at most one block size more the input data in case of pkcs7 encryption
+    // In case of gcm we will allocate extra memory of the size equal to blocksize.
+    data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize));
+    try {
+      len =
+          op.getOperation()
+              .finish(
+                  KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+                  KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+                  KMByteBlob.cast(data[INPUT_DATA]).length(),
+                  KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+                  KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff());
+    } catch (CryptoException e) {
+      if (e.getReason() == CryptoException.ILLEGAL_USE) {
+        // As per VTS, zero length input on AES/DES with PADDING_NONE Should return a zero length
+        // output. But JavaCard fails with CryptoException.ILLEGAL_USE if no input data is
+        // provided via update() method. So ignore this exception in case if all below conditions
+        // are satisfied and simply return empty output.
+        // 1. padding mode is PADDING_NONE.
+        // 2. No input  message is processed in update().
+        // 3. Zero length input data is passed in finish operation.
+        if ((op.getPadding() == KMType.PADDING_NONE)
+            && !op.isInputMsgProcessed()
+            && (KMByteBlob.cast(data[INPUT_DATA]).length() == 0)) {
+          len = 0;
+        } else {
+          KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+        }
+      }
+    }
+    KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len);
+  }
+
+  private void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) {
+    try {
+      KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+      short blob = pkcs8.decodeEcSubjectPublicKeyInfo(data[INPUT_DATA]);
+      short len =
+          op.getOperation()
+              .finish(
+                  KMByteBlob.cast(blob).getBuffer(),
+                  KMByteBlob.cast(blob).getStartOff(),
+                  KMByteBlob.cast(blob).length(),
+                  scratchPad,
+                  (short) 0);
+      data[OUTPUT_DATA] = KMByteBlob.instance((short) 32);
+      Util.arrayCopyNonAtomic(
+          scratchPad,
+          (short) 0,
+          KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+          KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(),
+          len);
+    } catch (CryptoException e) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+  }
+
+  private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratchPad) {
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+    switch (op.getAlgorithm()) {
+      case KMType.RSA:
+        // If there is no padding we can treat signing as a RSA decryption operation.
+        try {
+          if (op.getPurpose() == KMType.SIGN) {
+            // len of signature will be 256 bytes - but it can be less then 256 bytes
+            short len =
+                op.getOperation()
+                    .sign(
+                        KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+                        KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+                        KMByteBlob.cast(data[INPUT_DATA]).length(),
+                        scratchPad,
+                        (short) 0);
+            // Maximum output size of signature is 256 bytes. - the signature will always be
+            // positive
+            data[OUTPUT_DATA] = KMByteBlob.instance((short) 256);
+            Util.arrayCopyNonAtomic(
+                scratchPad,
+                (short) 0,
+                KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+                (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len),
+                len);
+          } else {
+            KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+          }
+        } catch (CryptoException e) {
+          KMException.throwIt(KMError.INVALID_ARGUMENT);
+        }
+        break;
+      case KMType.EC:
+        short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+        // If DIGEST NONE then truncate the input data to 32 bytes.
+        if (op.getDigest() == KMType.DIGEST_NONE && len > 32) {
+          len = 32;
+        }
+        if (op.getPurpose() == KMType.SIGN) {
+          // len of signature will be 512 bits i.e. 64 bytes
+          len =
+              op.getOperation()
+                  .sign(
+                      KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+                      KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+                      len,
+                      scratchPad,
+                      (short) 0);
+          data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len);
+        } else {
+          KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+        }
+        break;
+      case KMType.HMAC:
+        // As per Keymaster HAL documentation, the length of the Hmac output can
+        // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no
+        // such provision to control the length of the Hmac output using JavaCard
+        // crypto APIs and the current implementation always returns 32 bytes
+        // length of Hmac output. So to provide support to TAG_MAC_LENGTH
+        // feature, we truncate the output signature to TAG_MAC_LENGTH and return
+        // the truncated signature back to the caller. At the time of verfication
+        // we again compute the signature of the plain text input, truncate it to
+        // TAG_MAC_LENGTH and compare it with the input signature for
+        // verification.
+        op.getOperation()
+            .sign(
+                KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+                KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+                KMByteBlob.cast(data[INPUT_DATA]).length(),
+                scratchPad,
+                (short) 0);
+        if (op.getPurpose() == KMType.SIGN) {
+          // Copy only signature of mac length size.
+          data[OUTPUT_DATA] =
+              KMByteBlob.instance(scratchPad, (short) 0, (short) (op.getMacLength() / 8));
+        } else if (op.getPurpose() == KMType.VERIFY) {
+          if ((KMByteBlob.cast(data[SIGNATURE]).length() < (MIN_HMAC_LENGTH_BITS / 8))
+              || KMByteBlob.cast(data[SIGNATURE]).length() > (SHA256_DIGEST_LEN_BITS / 8)) {
+            KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+          }
+          if ((KMByteBlob.cast(data[SIGNATURE]).length() < (short) (op.getMinMacLength() / 8))) {
+            KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+          }
+
+          if (0
+              != Util.arrayCompare(
+                  scratchPad,
+                  (short) 0,
+                  KMByteBlob.cast(data[SIGNATURE]).getBuffer(),
+                  KMByteBlob.cast(data[SIGNATURE]).getStartOff(),
+                  KMByteBlob.cast(data[SIGNATURE]).length())) {
+            KMException.throwIt(KMError.VERIFICATION_FAILED);
+          }
+          data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+        }
+        break;
+      default: // This is should never happen
+        KMException.throwIt(KMError.OPERATION_CANCELLED);
+        break;
+    }
+  }
+
+  private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) {
+    // If one time user Authentication is required
+    if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) {
+      // Validate Verification Token.
+      validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad);
+      // validate operation handle.
+      short ptr = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getChallenge();
+      if (KMInteger.compare(ptr, op.getHandle()) != 0) {
+        KMException.throwIt(KMError.VERIFICATION_FAILED);
+      }
+      tmpVariables[0] = op.getAuthTime();
+      tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp();
+      if (tmpVariables[2] == KMType.INVALID_VALUE) {
+        KMException.throwIt(KMError.VERIFICATION_FAILED);
+      }
+      if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) {
+        KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+      }
+      op.setAuthTimeoutValidated(true);
+    } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required
+      if (!validateHwToken(data[HW_TOKEN], scratchPad)) {
+        KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+      }
+      tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge();
+      if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) {
+        KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+      }
+      if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) {
+        KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+      }
+    }
+  }
+
+  private void authorizeKeyUsageForCount(byte[] scratchPad) {
+    short scratchPadOff = 0;
+    Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 12, (byte) 0);
+
+    short usageLimitBufLen =
+        KMIntegerTag.getValue(
+            scratchPad,
+            scratchPadOff,
+            KMType.UINT_TAG,
+            KMType.MAX_USES_PER_BOOT,
+            data[HW_PARAMETERS]);
+
+    if (usageLimitBufLen == KMType.INVALID_VALUE) {
+      return;
+    }
+
+    if (usageLimitBufLen > 4) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+
+    if (kmDataStore.isAuthTagPersisted(data[AUTH_TAG])) {
+      // Get current counter, update and increment it.
+      short len =
+          kmDataStore.getRateLimitedKeyCount(
+              data[AUTH_TAG], scratchPad, (short) (scratchPadOff + 4));
+      if (len != 4) {
+        KMException.throwIt(KMError.UNKNOWN_ERROR);
+      }
+      if (0
+          >= KMInteger.unsignedByteArrayCompare(
+              scratchPad, scratchPadOff, scratchPad, (short) (scratchPadOff + 4), (short) 4)) {
+        KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED);
+      }
+      // Increment the counter.
+      Util.arrayFillNonAtomic(scratchPad, scratchPadOff, len, (byte) 0);
+      Util.setShort(scratchPad, (short) (scratchPadOff + 2), (short) 1);
+      KMUtils.add(
+          scratchPad,
+          scratchPadOff,
+          (short) (scratchPadOff + len),
+          (short) (scratchPadOff + len * 2));
+
+      kmDataStore.setRateLimitedKeyCount(
+          data[AUTH_TAG], scratchPad, (short) (scratchPadOff + len * 2), len);
+    } else {
+      // Persist auth tag.
+      if (!kmDataStore.persistAuthTag(data[AUTH_TAG])) {
+        KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+      }
+    }
+  }
+
+  private void authorizeDeviceUnlock(byte[] scratchPad) {
+    // If device is locked and key characteristics requires unlocked device then check whether
+    // HW auth token has correct timestamp.
+    short ptr =
+        KMKeyParameters.findTag(
+            KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]);
+
+    if (ptr != KMType.INVALID_VALUE && kmDataStore.getDeviceLock()) {
+      if (data[HW_TOKEN] == KMType.INVALID_VALUE) {
+        KMException.throwIt(KMError.DEVICE_LOCKED);
+      }
+      ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp();
+      // Check if the current auth time stamp is greater than device locked time stamp
+      short ts = kmDataStore.getDeviceTimeStamp();
+      if (KMInteger.compare(ptr, ts) <= 0) {
+        KMException.throwIt(KMError.DEVICE_LOCKED);
+      }
+      // Now check if the device unlock requires password only authentication and whether
+      // auth token is generated through password authentication or not.
+      if (kmDataStore.getDeviceLockPasswordOnly()) {
+        ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
+        ptr = KMEnum.cast(ptr).getVal();
+        if (((byte) ptr & KMType.PASSWORD) == 0) {
+          KMException.throwIt(KMError.DEVICE_LOCKED);
+        }
+      }
+      // Unlock the device
+      // repository.deviceLockedFlag = false;
+      kmDataStore.setDeviceLock(false);
+      kmDataStore.clearDeviceLockTimeStamp();
+    }
+  }
+
+  private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) {
+    // concatenation length will be 37 + length of verified parameters list - which
+    // is typically empty
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+    // Add "Auth Verification" - 17 bytes.
+    Util.arrayCopyNonAtomic(
+        authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length);
+    short len = (short) authVerification.length;
+    // concatenate challenge - 8 bytes
+    short ptr = KMVerificationToken.cast(verToken).getChallenge();
+    KMInteger.cast(ptr)
+        .value(
+            scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+    len += KMInteger.UINT_64;
+    // concatenate timestamp -8 bytes
+    ptr = KMVerificationToken.cast(verToken).getTimestamp();
+    KMInteger.cast(ptr)
+        .value(
+            scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+    len += KMInteger.UINT_64;
+    // concatenate security level - 4 bytes
+    scratchPad[(short) (len + 3)] = TRUSTED_ENVIRONMENT;
+    len += KMInteger.UINT_32;
+    // hmac the data
+    ptr = KMVerificationToken.cast(verToken).getMac();
+
+    return seProvider.hmacVerify(
+        kmDataStore.getComputedHmacKey(),
+        scratchPad,
+        (short) 0,
+        len,
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        KMByteBlob.cast(ptr).length());
+  }
+
+  private void validateVerificationToken(short verToken, byte[] scratchPad) {
+    short ptr = KMVerificationToken.cast(verToken).getMac();
+    // If mac length is zero then token is empty.
+    if (KMByteBlob.cast(ptr).length() == 0) {
+      KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+    }
+    if (!verifyVerificationTokenMacInBigEndian(verToken, scratchPad)) {
+      // Throw Exception if none of the combination works.
+      KMException.throwIt(KMError.VERIFICATION_FAILED);
+    }
+  }
+
+  private short updateOperationCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 4);
+    // Arguments
+    KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+    KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+    short authToken = KMHardwareAuthToken.exp();
+    KMArray.cast(cmd).add((short) 2, authToken);
+    short verToken = KMVerificationToken.exp();
+    KMArray.cast(cmd).add((short) 3, verToken);
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processUpdateOperationCmd(APDU apdu) {
+    short cmd = updateOperationCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+    data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+    data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2);
+    data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3);
+
+    // Input data must be present even if it is zero length.
+    if (data[INPUT_DATA] == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+
+    // Check Operation Handle and get op state
+    // Check Operation Handle
+    KMOperationState op = findOperation(data[OP_HANDLE]);
+    if (op == null) {
+      KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+    }
+    // authorize the update operation
+    authorizeUpdateFinishOperation(op, scratchPad);
+
+    if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) {
+      // update the data.
+      op.getOperation()
+          .update(
+              KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+              KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+              KMByteBlob.cast(data[INPUT_DATA]).length());
+      // update trusted confirmation operation
+      updateTrustedConfirmationOperation(op);
+
+      data[OUTPUT_DATA] = KMType.INVALID_VALUE;
+    } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) {
+      // Update for encrypt/decrypt using RSA will not be supported because to do this op state
+      //  will have to buffer the data - so reject the update if it is rsa algorithm.
+      if (op.getAlgorithm() == KMType.RSA) {
+        KMException.throwIt(KMError.OPERATION_CANCELLED);
+      }
+      short len = KMByteBlob.cast(data[INPUT_DATA]).length();
+      short blockSize = DES_BLOCK_SIZE;
+      if (op.getAlgorithm() == KMType.AES) {
+        blockSize = AES_BLOCK_SIZE;
+        if (op.getBlockMode() == KMType.GCM) {
+          // if input data present
+          if (len > 0) {
+            // no more future updateAAD allowed if input data present.
+            if (op.isAesGcmUpdateAllowed()) {
+              op.setAesGcmUpdateComplete();
+            }
+          }
+        }
+      }
+      // Allocate output buffer as input data is already block aligned
+      data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize));
+      // Otherwise just update the data.
+      // HAL consumes all the input and maintains a buffered data inside it. So the
+      // applet sends the inputConsumed length as same as the input length.
+      try {
+        len =
+            op.getOperation()
+                .update(
+                    KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+                    KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+                    KMByteBlob.cast(data[INPUT_DATA]).length(),
+                    KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(),
+                    KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff());
+      } catch (CryptoException e) {
+        KMException.throwIt(KMError.INVALID_TAG);
+      }
+      if (KMByteBlob.cast(data[INPUT_DATA]).length() > 0) {
+        // This flag is used to denote that an input data of length > 0 is received and processed
+        // successfully in update command. This flag is later used in the finish operation
+        // to handle a particular use case, where a zero length input data on AES/DES algorithm
+        // with PADDING_NONE should return a zero length output with OK response.
+        op.setProcessedInputMsg(true);
+      }
+      // Adjust the Output data if it is not equal to input data.
+      // This happens in case of JCardSim provider.
+      KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len);
+    }
+
+    if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) {
+      data[OUTPUT_DATA] = KMByteBlob.instance((short) 0);
+    }
+    // Persist if there are any updates.
+    // op.persist();
+    // make response
+    short resp = KMArray.instance((short) 2);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(resp).add((short) 1, data[OUTPUT_DATA]);
+    sendOutgoing(apdu, resp);
+  }
+
+  private short updateAadOperationCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 4);
+    KMArray.cast(cmd).add((short) 0, KMInteger.exp());
+    KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+    short authToken = KMHardwareAuthToken.exp();
+    KMArray.cast(cmd).add((short) 2, authToken);
+    short verToken = KMVerificationToken.exp();
+    KMArray.cast(cmd).add((short) 3, verToken);
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processUpdateAadOperationCmd(APDU apdu) {
+    short cmd = updateAadOperationCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    data[OP_HANDLE] = KMArray.cast(cmd).get((short) 0);
+    data[INPUT_DATA] = KMArray.cast(cmd).get((short) 1);
+    data[HW_TOKEN] = KMArray.cast(cmd).get((short) 2);
+    data[VERIFICATION_TOKEN] = KMArray.cast(cmd).get((short) 3);
+
+    // Input data must be present even if it is zero length.
+    if (data[INPUT_DATA] == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    // Check Operation Handle and get op state
+    // Check Operation Handle
+    KMOperationState op = findOperation(data[OP_HANDLE]);
+    if (op == null) {
+      KMException.throwIt(KMError.INVALID_OPERATION_HANDLE);
+    }
+    if (op.getAlgorithm() != KMType.AES) {
+      KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM);
+    }
+    if (op.getBlockMode() != KMType.GCM) {
+      KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE);
+    }
+    if (!op.isAesGcmUpdateAllowed()) {
+      KMException.throwIt(KMError.INVALID_TAG);
+    }
+    if (op.getPurpose() != KMType.ENCRYPT && op.getPurpose() != KMType.DECRYPT) {
+      KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+    }
+    // authorize the update operation
+    authorizeUpdateFinishOperation(op, scratchPad);
+    try {
+      op.getOperation()
+          .updateAAD(
+              KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+              KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+              KMByteBlob.cast(data[INPUT_DATA]).length());
+    } catch (CryptoException exp) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    // make response
+    short resp = KMArray.instance((short) 1);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    sendOutgoing(apdu, resp);
+  }
+
+  private short beginOperationCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 4);
+    // Arguments
+    short params = KMKeyParameters.expAny();
+    KMArray.cast(cmd).add((short) 0, KMEnum.instance(KMType.PURPOSE));
+    KMArray.cast(cmd).add((short) 1, KMByteBlob.exp());
+    KMArray.cast(cmd).add((short) 2, params);
+    short authToken = KMHardwareAuthToken.exp();
+    KMArray.cast(cmd).add((short) 3, authToken);
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processBeginOperationCmd(APDU apdu) {
+    // Receive the incoming request fully from the host into buffer.
+    short cmd = beginOperationCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    short purpose = KMArray.cast(cmd).get((short) 0);
+    data[KEY_BLOB] = KMArray.cast(cmd).get((short) 1);
+    data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 2);
+    data[HW_TOKEN] = KMArray.cast(cmd).get((short) 3);
+    purpose = KMEnum.cast(purpose).getVal();
+    // Check for app id and app data.
+    data[APP_ID] = getApplicationId(data[KEY_PARAMETERS]);
+    data[APP_DATA] = getApplicationData(data[KEY_PARAMETERS]);
+    // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired
+    // function itself.
+    if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) {
+      KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE);
+    }
+    KMTag.assertPresence(
+        data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.UNSUPPORTED_ALGORITHM);
+    short algorithm = KMEnumTag.getValue(KMType.ALGORITHM, data[SB_PARAMETERS]);
+    // If Blob usage tag is present in key characteristics then it should be standalone.
+    if (KMTag.isPresent(data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ)) {
+      if (KMEnumTag.getValue(KMType.BLOB_USAGE_REQ, data[SB_PARAMETERS]) != KMType.STANDALONE) {
+        KMException.throwIt(KMError.UNSUPPORTED_TAG);
+      }
+    }
+
+    // Generate a random number for operation handle
+    short buf = KMByteBlob.instance(KMOperationState.OPERATION_HANDLE_SIZE);
+    generateUniqueOperationHandle(
+        KMByteBlob.cast(buf).getBuffer(),
+        KMByteBlob.cast(buf).getStartOff(),
+        KMByteBlob.cast(buf).length());
+    /* opHandle is a KMInteger and is encoded as KMInteger when it is returned back. */
+    short opHandle =
+        KMInteger.instance(
+            KMByteBlob.cast(buf).getBuffer(),
+            KMByteBlob.cast(buf).getStartOff(),
+            KMByteBlob.cast(buf).length());
+    KMOperationState op = reserveOperation(algorithm, opHandle);
+    if (op == null) {
+      KMException.throwIt(KMError.TOO_MANY_OPERATIONS);
+    }
+    data[OP_HANDLE] = op.getHandle();
+    op.setPurpose((byte) purpose);
+    op.setKeySize(KMByteBlob.cast(data[SECRET]).length());
+    authorizeAndBeginOperation(op, scratchPad);
+    switch (op.getPurpose()) {
+      case KMType.SIGN:
+        beginTrustedConfirmationOperation(op);
+      case KMType.VERIFY:
+        beginSignVerifyOperation(op);
+        break;
+      case KMType.ENCRYPT:
+      case KMType.DECRYPT:
+        beginCipherOperation(op);
+        break;
+      case KMType.AGREE_KEY:
+        beginKeyAgreementOperation(op);
+        break;
+      default:
+        KMException.throwIt(KMError.UNIMPLEMENTED);
+        break;
+    }
+    short iv = KMType.INVALID_VALUE;
+    // If the data[IV] is required to be returned.
+    // As per VTS, for the decryption operation don't send the iv back.
+    if (data[IV] != KMType.INVALID_VALUE
+        && op.getPurpose() != KMType.DECRYPT
+        && op.getBlockMode() != KMType.ECB) {
+      iv = KMArray.instance((short) 1);
+      if (op.getAlgorithm() == KMType.DES && op.getBlockMode() == KMType.CBC) {
+        // For AES/DES we are generate an random iv of length 16 bytes.
+        // While sending the iv back for DES/CBC mode of opeation only send
+        // 8 bytes back.
+        short ivBlob = KMByteBlob.instance((short) 8);
+        Util.arrayCopy(
+            KMByteBlob.cast(data[IV]).getBuffer(),
+            KMByteBlob.cast(data[IV]).getStartOff(),
+            KMByteBlob.cast(ivBlob).getBuffer(),
+            KMByteBlob.cast(ivBlob).getStartOff(),
+            (short) 8);
+        data[IV] = ivBlob;
+      }
+      KMArray.cast(iv).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV]));
+    } else {
+      iv = KMArray.instance((short) 0);
+    }
+    short macLen = 0;
+    if (op.getMacLength() != KMType.INVALID_VALUE) {
+      macLen = (short) (op.getMacLength() / 8);
+    }
+    short params = KMKeyParameters.instance(iv);
+    short resp = KMArray.instance((short) 5);
+    KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(resp).add((short) 1, params);
+    KMArray.cast(resp).add((short) 2, data[OP_HANDLE]);
+    KMArray.cast(resp).add((short) 3, KMInteger.uint_8(op.getBufferingMode()));
+    KMArray.cast(resp).add((short) 4, KMInteger.uint_16(macLen));
+    sendOutgoing(apdu, resp);
+  }
+
+  private void authorizeAlgorithm(KMOperationState op) {
+    short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]);
+    if (alg == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+    }
+    op.setAlgorithm((byte) alg);
+  }
+
+  private void authorizePurpose(KMOperationState op) {
+    switch (op.getAlgorithm()) {
+      case KMType.AES:
+      case KMType.DES:
+        if (op.getPurpose() == KMType.SIGN
+            || op.getPurpose() == KMType.VERIFY
+            || op.getPurpose() == KMType.AGREE_KEY) {
+          KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+        }
+        break;
+      case KMType.EC:
+        if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) {
+          KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+        }
+        break;
+      case KMType.HMAC:
+        if (op.getPurpose() == KMType.ENCRYPT
+            || op.getPurpose() == KMType.DECRYPT
+            || op.getPurpose() == KMType.AGREE_KEY) {
+          KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+        }
+        break;
+      case KMType.RSA:
+        if (op.getPurpose() == KMType.AGREE_KEY) {
+          KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+        }
+        break;
+      default:
+        break;
+    }
+    if (!KMEnumArrayTag.contains(KMType.PURPOSE, op.getPurpose(), data[HW_PARAMETERS])) {
+      KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+    }
+  }
+
+  private void authorizeDigest(KMOperationState op) {
+    short digests =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[HW_PARAMETERS]);
+    op.setDigest(KMType.DIGEST_NONE);
+    short param =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]);
+    if (param != KMType.INVALID_VALUE) {
+      if (KMEnumArrayTag.cast(param).length() != 1) {
+        KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+      }
+      param = KMEnumArrayTag.cast(param).get((short) 0);
+      if (!KMEnumArrayTag.cast(digests).contains(param)) {
+        KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+      }
+      op.setDigest((byte) param);
+    } else if (KMEnumArrayTag.contains(
+        KMType.PADDING, KMType.RSA_PKCS1_1_5_SIGN, data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+    }
+    short paramPadding =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]);
+    if (paramPadding != KMType.INVALID_VALUE) {
+      if (KMEnumArrayTag.cast(paramPadding).length() != 1) {
+        // TODO vts fails because it expects UNSUPPORTED_PADDING_MODE
+        KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+      }
+      paramPadding = KMEnumArrayTag.cast(paramPadding).get((short) 0);
+    }
+    switch (op.getAlgorithm()) {
+      case KMType.RSA:
+        if ((paramPadding == KMType.RSA_OAEP || paramPadding == KMType.RSA_PSS)
+            && param == KMType.INVALID_VALUE) {
+          KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+        }
+        break;
+      case KMType.EC:
+      case KMType.HMAC:
+        if ((param == KMType.INVALID_VALUE && op.getPurpose() != KMType.AGREE_KEY)
+            || !isDigestSupported(op.getAlgorithm(), op.getDigest())) {
+          KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+        }
+        break;
+      default:
+        break;
+    }
+  }
+
+  private void authorizePadding(KMOperationState op) {
+    short paddings =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[HW_PARAMETERS]);
+    op.setPadding(KMType.PADDING_NONE);
+    short param =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]);
+    if (param != KMType.INVALID_VALUE) {
+      if (KMEnumArrayTag.cast(param).length() != 1) {
+        KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+      }
+      param = KMEnumArrayTag.cast(param).get((short) 0);
+      if (!KMEnumArrayTag.cast(paddings).contains(param)) {
+        KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+      }
+    }
+    switch (op.getAlgorithm()) {
+      case KMType.RSA:
+        if (param == KMType.INVALID_VALUE) {
+          KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+        }
+        if ((op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY)
+            && param != KMType.PADDING_NONE
+            && param != KMType.RSA_PSS
+            && param != KMType.RSA_PKCS1_1_5_SIGN) {
+          KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+        }
+        if ((op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT)
+            && param != KMType.PADDING_NONE
+            && param != KMType.RSA_OAEP
+            && param != KMType.RSA_PKCS1_1_5_ENCRYPT) {
+          KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+        }
+
+        if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) {
+          KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+        }
+        if ((param == KMType.RSA_OAEP || param == KMType.RSA_PSS)
+            && op.getDigest() == KMType.DIGEST_NONE) {
+          KMException.throwIt(KMError.INCOMPATIBLE_DIGEST);
+        }
+        if (op.getPurpose() == KMType.SIGN
+            || op.getPurpose() == KMType.VERIFY
+            || param == KMType.RSA_OAEP) {
+          // Digest is mandatory in these cases.
+          if (!isDigestSupported(op.getAlgorithm(), op.getDigest())) {
+            KMException.throwIt(KMError.UNSUPPORTED_DIGEST);
+          }
+        }
+        if (param == KMType.RSA_OAEP) {
+          short mgfDigest =
+              KMKeyParameters.findTag(
+                  KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, data[KEY_PARAMETERS]);
+          if (mgfDigest != KMType.INVALID_VALUE) {
+            if (KMEnumArrayTag.cast(mgfDigest).length() != 1) {
+              KMException.throwIt(KMError.INVALID_ARGUMENT);
+            }
+            mgfDigest = KMEnumArrayTag.cast(mgfDigest).get((short) 0);
+            if (mgfDigest == KMType.DIGEST_NONE) {
+              KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST);
+            }
+
+          } else {
+            mgfDigest = KMType.SHA1;
+          }
+          short mgfDigestHwParams =
+              KMKeyParameters.findTag(
+                  KMType.ENUM_ARRAY_TAG, KMType.RSA_OAEP_MGF_DIGEST, data[HW_PARAMETERS]);
+          if ((mgfDigestHwParams != KMType.INVALID_VALUE)
+              && (!KMEnumArrayTag.cast(mgfDigestHwParams).contains(mgfDigest))) {
+            KMException.throwIt(KMError.INCOMPATIBLE_MGF_DIGEST);
+          }
+          if (mgfDigest != KMType.SHA1 && mgfDigest != KMType.SHA2_256) {
+            KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST);
+          }
+          op.setMgfDigest((byte) mgfDigest);
+        }
+        op.setPadding((byte) param);
+        break;
+      case KMType.DES:
+      case KMType.AES:
+        if (param == KMType.INVALID_VALUE) {
+          KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE);
+        }
+        op.setPadding((byte) param);
+        break;
+      default:
+        break;
+    }
+  }
+
+  private void authorizeBlockModeAndMacLength(KMOperationState op) {
+    short param =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]);
+    if (param != KMType.INVALID_VALUE) {
+      if (KMEnumArrayTag.cast(param).length() != 1) {
+        KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+      }
+      param = KMEnumArrayTag.cast(param).get((short) 0);
+    }
+    if (KMType.AES == op.getAlgorithm() || KMType.DES == op.getAlgorithm()) {
+      if (!KMEnumArrayTag.contains(KMType.BLOCK_MODE, param, data[HW_PARAMETERS])) {
+        KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE);
+      }
+    }
+    short macLen =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]);
+    switch (op.getAlgorithm()) {
+      case KMType.AES:
+        // Validate the block mode.
+        switch (param) {
+          case KMType.ECB:
+          case KMType.CBC:
+          case KMType.CTR:
+          case KMType.GCM:
+            break;
+          default:
+            KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+        }
+        if (param == KMType.GCM) {
+          if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) {
+            KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+          }
+          if (macLen == KMType.INVALID_VALUE) {
+            KMException.throwIt(KMError.MISSING_MAC_LENGTH);
+          }
+          if (macLen % 8 != 0
+              || macLen > 128
+              || macLen
+                  < KMIntegerTag.getShortValue(
+                      KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) {
+            KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+          }
+          op.setMacLength(macLen);
+        }
+        if (param == KMType.CTR) {
+          if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) {
+            KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE);
+          }
+        }
+        break;
+      case KMType.DES:
+        // Validate the block mode.
+        switch (param) {
+          case KMType.ECB:
+          case KMType.CBC:
+            break;
+          default:
+            KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE);
+        }
+        if (param == KMType.INVALID_VALUE) {
+          KMException.throwIt(KMError.INVALID_ARGUMENT);
+        }
+        break;
+      case KMType.HMAC:
+        short minMacLen =
+            KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS]);
+        op.setMinMacLength(minMacLen);
+        if (macLen == KMType.INVALID_VALUE) {
+          if (op.getPurpose() == KMType.SIGN) {
+            KMException.throwIt(KMError.MISSING_MAC_LENGTH);
+          }
+        } else {
+          // MAC length may not be specified for verify.
+          if (op.getPurpose() == KMType.VERIFY) {
+            KMException.throwIt(KMError.INVALID_ARGUMENT);
+          }
+          if (macLen % 8 != 0 || macLen > SHA256_DIGEST_LEN_BITS || macLen < MIN_HMAC_LENGTH_BITS) {
+            KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH);
+          }
+          if (macLen < minMacLen) {
+            KMException.throwIt(KMError.INVALID_MAC_LENGTH);
+          }
+          op.setMacLength(macLen);
+        }
+        break;
+      default:
+        break;
+    }
+    op.setBlockMode((byte) param);
+  }
+
+  private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) {
+    authorizePurpose(op);
+    authorizeDigest(op);
+    authorizePadding(op);
+    authorizeBlockModeAndMacLength(op);
+    if (!validateHwToken(data[HW_TOKEN], scratchPad)) {
+      data[HW_TOKEN] = KMType.INVALID_VALUE;
+    }
+    authorizeUserSecureIdAuthTimeout(op, scratchPad);
+    authorizeDeviceUnlock(scratchPad);
+    authorizeKeyUsageForCount(scratchPad);
+
+    KMTag.assertAbsence(
+        data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, KMError.INVALID_KEY_BLOB);
+
+    // Validate early boot
+    // VTS expects error code EARLY_BOOT_ONLY during begin operation if early boot ended tag is
+    // present
+    if (kmDataStore.getEarlyBootEndedStatus()) {
+      KMTag.assertAbsence(
+          data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED);
+    }
+
+    // Authorize Caller Nonce - if caller nonce absent in key char and nonce present in
+    // key params then fail if it is not a Decrypt operation
+    data[IV] = KMType.INVALID_VALUE;
+
+    if (!KMTag.isPresent(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.CALLER_NONCE)
+        && KMTag.isPresent(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.NONCE)
+        && op.getPurpose() != KMType.DECRYPT) {
+      KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED);
+    }
+
+    short nonce = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]);
+    // If Nonce is present then check whether the size of nonce is correct.
+    if (nonce != KMType.INVALID_VALUE) {
+      data[IV] = KMByteTag.cast(nonce).getValue();
+      // For CBC mode - iv must be 8 bytes
+      if (op.getBlockMode() == KMType.CBC
+          && op.getAlgorithm() == KMType.DES
+          && KMByteBlob.cast(data[IV]).length() != 8) {
+        KMException.throwIt(KMError.INVALID_NONCE);
+      }
+
+      // For GCM mode - IV must be 12 bytes
+      if (KMByteBlob.cast(data[IV]).length() != 12 && op.getBlockMode() == KMType.GCM) {
+        KMException.throwIt(KMError.INVALID_NONCE);
+      }
+
+      // For AES CBC and CTR modes IV must be 16 bytes
+      if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR)
+          && op.getAlgorithm() == KMType.AES
+          && KMByteBlob.cast(data[IV]).length() != 16) {
+        KMException.throwIt(KMError.INVALID_NONCE);
+      }
+    } else if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) {
+
+      // For symmetric decryption iv is required
+      if (op.getPurpose() == KMType.DECRYPT
+          && (op.getBlockMode() == KMType.CBC
+              || op.getBlockMode() == KMType.GCM
+              || op.getBlockMode() == KMType.CTR)) {
+        KMException.throwIt(KMError.MISSING_NONCE);
+      } else if (op.getBlockMode() == KMType.ECB) {
+        // For ECB we create zero length nonce
+        data[IV] = KMByteBlob.instance((short) 0);
+      } else if (op.getPurpose() == KMType.ENCRYPT) {
+
+        // For encrypt mode if nonce is absent then create random nonce of correct length
+        byte ivLen = 16;
+        if (op.getBlockMode() == KMType.GCM) {
+          ivLen = 12;
+        } else if (op.getAlgorithm() == KMType.DES) {
+          ivLen = 8;
+        }
+        data[IV] = KMByteBlob.instance(ivLen);
+        seProvider.newRandomNumber(
+            KMByteBlob.cast(data[IV]).getBuffer(),
+            KMByteBlob.cast(data[IV]).getStartOff(),
+            KMByteBlob.cast(data[IV]).length());
+      }
+    }
+  }
+
+  private void beginKeyAgreementOperation(KMOperationState op) {
+    if (op.getAlgorithm() != KMType.EC) {
+      KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+    }
+
+    op.setOperation(
+        seProvider.initAsymmetricOperation(
+            (byte) op.getPurpose(),
+            (byte) op.getAlgorithm(),
+            (byte) op.getPadding(),
+            (byte) op.getDigest(),
+            KMType.DIGEST_NONE, /* No MGF1 Digest */
+            KMByteBlob.cast(data[SECRET]).getBuffer(),
+            KMByteBlob.cast(data[SECRET]).getStartOff(),
+            KMByteBlob.cast(data[SECRET]).length(),
+            null,
+            (short) 0,
+            (short) 0));
+  }
+
+  private void beginCipherOperation(KMOperationState op) {
+    switch (op.getAlgorithm()) {
+      case KMType.RSA:
+        try {
+          if (op.getPurpose() == KMType.DECRYPT) {
+            op.setOperation(
+                seProvider.initAsymmetricOperation(
+                    (byte) op.getPurpose(),
+                    (byte) op.getAlgorithm(),
+                    (byte) op.getPadding(),
+                    (byte) op.getDigest(),
+                    (byte) op.getMgfDigest(),
+                    KMByteBlob.cast(data[SECRET]).getBuffer(),
+                    KMByteBlob.cast(data[SECRET]).getStartOff(),
+                    KMByteBlob.cast(data[SECRET]).length(),
+                    KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+                    KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+                    KMByteBlob.cast(data[PUB_KEY]).length()));
+          } else {
+            KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+          }
+        } catch (CryptoException exp) {
+          KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        }
+        break;
+      case KMType.AES:
+      case KMType.DES:
+        if (op.getBlockMode() == KMType.GCM) {
+          op.setAesGcmUpdateStart();
+        }
+        try {
+          op.setOperation(
+              seProvider.initSymmetricOperation(
+                  (byte) op.getPurpose(),
+                  (byte) op.getAlgorithm(),
+                  (byte) op.getDigest(),
+                  (byte) op.getPadding(),
+                  (byte) op.getBlockMode(),
+                  KMByteBlob.cast(data[SECRET]).getBuffer(),
+                  KMByteBlob.cast(data[SECRET]).getStartOff(),
+                  KMByteBlob.cast(data[SECRET]).length(),
+                  KMByteBlob.cast(data[IV]).getBuffer(),
+                  KMByteBlob.cast(data[IV]).getStartOff(),
+                  KMByteBlob.cast(data[IV]).length(),
+                  op.getMacLength()));
+        } catch (CryptoException exception) {
+          if (exception.getReason() == CryptoException.ILLEGAL_VALUE) {
+            KMException.throwIt(KMError.INVALID_ARGUMENT);
+          } else if (exception.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
+            KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+          }
+        }
+    }
+  }
+
+  private void beginTrustedConfirmationOperation(KMOperationState op) {
+    // Check for trusted confirmation - if required then set the signer in op state.
+    if (KMKeyParameters.findTag(
+            KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, data[HW_PARAMETERS])
+        != KMType.INVALID_VALUE) {
+
+      op.setTrustedConfirmationSigner(
+          seProvider.initTrustedConfirmationSymmetricOperation(kmDataStore.getComputedHmacKey()));
+
+      op.getTrustedConfirmationSigner()
+          .update(confirmationToken, (short) 0, (short) confirmationToken.length);
+    }
+  }
+
+  private void beginSignVerifyOperation(KMOperationState op) {
+    switch (op.getAlgorithm()) {
+      case KMType.RSA:
+        try {
+          if (op.getPurpose() == KMType.SIGN) {
+            op.setOperation(
+                seProvider.initAsymmetricOperation(
+                    (byte) op.getPurpose(),
+                    (byte) op.getAlgorithm(),
+                    (byte) op.getPadding(),
+                    (byte) op.getDigest(),
+                    KMType.DIGEST_NONE, /* No MGF Digest */
+                    KMByteBlob.cast(data[SECRET]).getBuffer(),
+                    KMByteBlob.cast(data[SECRET]).getStartOff(),
+                    KMByteBlob.cast(data[SECRET]).length(),
+                    KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+                    KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+                    KMByteBlob.cast(data[PUB_KEY]).length()));
+          } else {
+            KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+          }
+        } catch (CryptoException exp) {
+          KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        }
+        break;
+      case KMType.EC:
+        try {
+          if (op.getPurpose() == KMType.SIGN) {
+            op.setOperation(
+                seProvider.initAsymmetricOperation(
+                    (byte) op.getPurpose(),
+                    (byte) op.getAlgorithm(),
+                    (byte) op.getPadding(),
+                    (byte) op.getDigest(),
+                    KMType.DIGEST_NONE, /* No MGF Digest */
+                    KMByteBlob.cast(data[SECRET]).getBuffer(),
+                    KMByteBlob.cast(data[SECRET]).getStartOff(),
+                    KMByteBlob.cast(data[SECRET]).length(),
+                    null,
+                    (short) 0,
+                    (short) 0));
+          } else {
+            KMException.throwIt(KMError.UNSUPPORTED_PURPOSE);
+          }
+        } catch (CryptoException exp) {
+          // Javacard does not support NO digest based signing.
+          KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        }
+        break;
+      case KMType.HMAC:
+        // As per Keymaster HAL documentation, the length of the Hmac output can
+        // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no
+        // such provision to control the length of the Hmac output using JavaCard
+        // crypto APIs and the current implementation always returns 32 bytes
+        // length of Hmac output. So to provide support to TAG_MAC_LENGTH
+        // feature, we truncate the output signature to TAG_MAC_LENGTH and return
+        // the truncated signature back to the caller. At the time of verfication
+        // we again compute the signature of the plain text input, truncate it to
+        // TAG_MAC_LENGTH and compare it with the input signature for
+        // verification. So this is the reason we are using KMType.SIGN directly
+        // instead of using op.getPurpose().
+        try {
+          op.setOperation(
+              seProvider.initSymmetricOperation(
+                  (byte) KMType.SIGN,
+                  (byte) op.getAlgorithm(),
+                  (byte) op.getDigest(),
+                  (byte) op.getPadding(),
+                  (byte) op.getBlockMode(),
+                  KMByteBlob.cast(data[SECRET]).getBuffer(),
+                  KMByteBlob.cast(data[SECRET]).getStartOff(),
+                  KMByteBlob.cast(data[SECRET]).length(),
+                  null,
+                  (short) 0,
+                  (short) 0,
+                  (short) 0));
+        } catch (CryptoException exp) {
+          KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        }
+        break;
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        break;
+    }
+  }
+
+  private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, short secureUserIdsObj) {
+    short secureUserId = KMHardwareAuthToken.cast(hwAuthToken).getUserId();
+    if (!KMInteger.cast(secureUserId).isZero()) {
+      if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(secureUserId)) {
+        return true;
+      }
+    }
+
+    short authenticatorId = KMHardwareAuthToken.cast(hwAuthToken).getAuthenticatorId();
+    if (!KMInteger.cast(authenticatorId).isZero()) {
+      if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(authenticatorId)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private boolean authTokenMatches(short userSecureIdsPtr, short authType, byte[] scratchPad) {
+    if (data[HW_TOKEN] == KMType.INVALID_VALUE) {
+      return false;
+    }
+    if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) {
+      return false;
+    }
+    // check auth type
+    tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType();
+    tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal();
+    if (((byte) tmpVariables[2] & (byte) authType) == 0) {
+      return false;
+    }
+    return true;
+  }
+
+  private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) {
+    short authTime;
+    short authType;
+    // Authorize User Secure Id and Auth timeout
+    short userSecureIdPtr =
+        KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]);
+    if (userSecureIdPtr != KMType.INVALID_VALUE) {
+      // Authentication required.
+      if (KMType.INVALID_VALUE
+          != KMKeyParameters.findTag(
+              KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, data[HW_PARAMETERS])) {
+        // Key has both USER_SECURE_ID and NO_AUTH_REQUIRED
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+      }
+      // authenticator type must be provided.
+      if (KMType.INVALID_VALUE
+          == (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) {
+        // Authentication required, but no auth type found.
+        KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+      }
+
+      short authTimeoutTagPtr =
+          KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]);
+      if (authTimeoutTagPtr != KMType.INVALID_VALUE) {
+        // authenticate user
+        if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) {
+          KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED);
+        }
+
+        authTimeoutTagPtr =
+            KMKeyParameters.findTag(
+                KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[CUSTOM_TAGS]);
+        if (authTimeoutTagPtr == KMType.INVALID_VALUE) {
+          KMException.throwIt(KMError.INVALID_KEY_BLOB);
+        }
+        authTime = KMIntegerTag.cast(authTimeoutTagPtr).getValue();
+        // set the one time auth
+        op.setOneTimeAuthReqd(true);
+        // set the authentication time stamp in operation state
+        authTime =
+            addIntegers(
+                authTime, KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(), scratchPad);
+        op.setAuthTime(
+            KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff());
+        // auth time validation will happen in update or finish
+        op.setAuthTimeoutValidated(false);
+      } else {
+        // auth per operation required
+        // store user secure id and authType in OperationState.
+        op.setUserSecureId(userSecureIdPtr);
+        op.setAuthType((byte) authType);
+        // set flags
+        op.setOneTimeAuthReqd(false);
+        op.setAuthPerOperationReqd(true);
+      }
+    }
+  }
+
+  private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) {
+    // The challenge, userId and authenticatorId, authenticatorType and timestamp
+    // are in network order (big-endian).
+    short len = 0;
+    // add 0
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+    len = 1;
+    // concatenate challenge - 8 bytes
+    short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge();
+    KMInteger.cast(ptr)
+        .value(
+            scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+    len += KMInteger.UINT_64;
+    // concatenate user id - 8 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getUserId();
+    KMInteger.cast(ptr)
+        .value(
+            scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+    len += KMInteger.UINT_64;
+    // concatenate authenticator id - 8 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId();
+    KMInteger.cast(ptr)
+        .value(
+            scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+    len += KMInteger.UINT_64;
+    // concatenate authenticator type - 4 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
+    scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+    len += KMInteger.UINT_32;
+    // concatenate timestamp -8 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
+    KMInteger.cast(ptr)
+        .value(
+            scratchPad, (short) (len + (short) (KMInteger.UINT_64 - KMInteger.cast(ptr).length())));
+    len += KMInteger.UINT_64;
+
+    ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+
+    return seProvider.hmacVerify(
+        kmDataStore.getComputedHmacKey(),
+        scratchPad,
+        (short) 0,
+        len,
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        KMByteBlob.cast(ptr).length());
+  }
+
+  private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) {
+    // The challenge, userId and authenticatorId values are in little endian order,
+    // but authenticatorType and timestamp are in network order (big-endian).
+    short len = 0;
+    // add 0
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0);
+    len = 1;
+    // concatenate challenge - 8 bytes
+    short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge();
+    KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+    len += KMInteger.UINT_64;
+    // concatenate user id - 8 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getUserId();
+    KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+    len += KMInteger.UINT_64;
+    // concatenate authenticator id - 8 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId();
+    KMInteger.cast(ptr).toLittleEndian(scratchPad, len);
+    len += KMInteger.UINT_64;
+    // concatenate authenticator type - 4 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType();
+    scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal();
+    len += KMInteger.UINT_32;
+    // concatenate timestamp - 8 bytes
+    ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp();
+    KMInteger.cast(ptr)
+        .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length())));
+    len += KMInteger.UINT_64;
+
+    ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+
+    return seProvider.hmacVerify(
+        kmDataStore.getComputedHmacKey(),
+        scratchPad,
+        (short) 0,
+        len,
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        KMByteBlob.cast(ptr).length());
+  }
+
+  private boolean validateHwToken(short hwToken, byte[] scratchPad) {
+    // CBOR Encoding is always big endian
+    short ptr = KMHardwareAuthToken.cast(hwToken).getMac();
+    // If mac length is zero then token is empty.
+    if (KMByteBlob.cast(ptr).length() == 0) {
+      return false;
+    }
+    if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) {
+      return verifyHwTokenMacInLittleEndian(hwToken, scratchPad);
+    } else {
+      return verifyHwTokenMacInBigEndian(hwToken, scratchPad);
+    }
+  }
+
+  private short importKeyCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 6);
+    // Arguments
+    short params = KMKeyParameters.expAny();
+    KMArray.cast(cmd).add((short) 0, params);
+    KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT));
+    KMArray.cast(cmd).add((short) 2, KMByteBlob.exp());
+    KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); // attest key
+    KMArray.cast(cmd).add((short) 4, params); // attest key params
+    KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); // issuer
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processImportKeyCmd(APDU apdu) {
+    // Receive the incoming request fully from the host into buffer.
+    short cmd = importKeyCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+    data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0);
+    short keyFmt = KMArray.cast(cmd).get((short) 1);
+    data[IMPORTED_KEY_BLOB] = KMArray.cast(cmd).get((short) 2);
+    data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 3);
+    data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 4);
+    data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 5);
+    keyFmt = KMEnum.cast(keyFmt).getVal();
+
+    data[CERTIFICATE] = KMArray.instance((short) 0); // by default the cert is empty.
+    data[ORIGIN] = KMType.IMPORTED;
+    importKey(apdu, keyFmt, scratchPad);
+  }
+
+  private void validateImportKey(short params, short keyFmt) {
+    short attKeyPurpose = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, params);
+    // ATTEST_KEY cannot be combined with any other purpose.
+    if (attKeyPurpose != KMType.INVALID_VALUE
+        && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)
+        && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) {
+      KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+    }
+    // Rollback protection not supported
+    KMTag.assertAbsence(
+        params,
+        KMType.BOOL_TAG,
+        KMType.ROLLBACK_RESISTANCE,
+        KMError.ROLLBACK_RESISTANCE_UNAVAILABLE);
+    // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is
+    // provided to IKeyMintDevice::importKey
+    KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, KMError.EARLY_BOOT_ENDED);
+    // Check if the tags are supported.
+    if (KMKeyParameters.hasUnsupportedTags(params)) {
+      KMException.throwIt(KMError.UNSUPPORTED_TAG);
+    }
+    // Algorithm must be present
+    KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT);
+    short alg = KMEnumTag.getValue(KMType.ALGORITHM, params);
+    // key format must be raw if aes, des or hmac and pkcs8 for rsa and ec.
+    if ((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && keyFmt != KMType.RAW) {
+      KMException.throwIt(KMError.UNIMPLEMENTED);
+    }
+    if ((alg == KMType.RSA || alg == KMType.EC) && keyFmt != KMType.PKCS8) {
+      KMException.throwIt(KMError.UNIMPLEMENTED);
+    }
+  }
+
+  private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) {
+    validateImportKey(data[KEY_PARAMETERS], keyFmt);
+    // Check algorithm and dispatch to appropriate handler.
+    short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+    switch (alg) {
+      case KMType.RSA:
+        importRSAKey(scratchPad);
+        break;
+      case KMType.AES:
+        importAESKey(scratchPad);
+        break;
+      case KMType.DES:
+        importTDESKey(scratchPad);
+        break;
+      case KMType.HMAC:
+        importHmacKey(scratchPad);
+        break;
+      case KMType.EC:
+        importECKeys(scratchPad);
+        break;
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        break;
+    }
+    makeKeyCharacteristics(scratchPad);
+    KMAttestationCert cert =
+        generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad);
+    createEncryptedKeyBlob(scratchPad);
+    sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]);
+  }
+
+  private void importECKeys(byte[] scratchPad) {
+    // Decode key material
+    KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+    short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]);
+    data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0);
+    data[SECRET] = KMArray.cast(keyBlob).get((short) 1);
+    // initialize 256 bit p256 key for given private key and public key.
+    short index = 0;
+    // check whether the key size tag is present in key parameters.
+    short SecretLen = (short) (KMByteBlob.length(data[SECRET]) * 8);
+    short keySize =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    if (keySize != KMType.INVALID_VALUE) {
+      // As per NIST.SP.800-186 page 9,  secret for 256 curve should be between
+      // 256-383
+      if (((256 <= SecretLen) && (383 >= SecretLen)) ^ keySize == 256) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+      if (keySize != 256) {
+        KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+      }
+    } else {
+      if ((256 > SecretLen) || (383 < SecretLen)) {
+        KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+      }
+      // add the key size to scratchPad
+      keySize = KMInteger.uint_16((short) 256);
+      keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keySize);
+      Util.setShort(scratchPad, index, keySize);
+      index += 2;
+    }
+    // check the curve if present in key parameters.
+    short curve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]);
+    if (curve != KMType.INVALID_VALUE) {
+      // As per NIST.SP.800-186 page 9,  secret length for 256 curve should be between
+      // 256-383
+      if (((256 <= SecretLen) && (383 >= SecretLen)) ^ curve == KMType.P_256) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+      if (curve != KMType.P_256) {
+        KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE);
+      }
+    } else {
+      if ((256 > SecretLen) || (383 < SecretLen)) {
+        KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+      }
+      // add the curve to scratchPad
+      curve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256);
+      Util.setShort(scratchPad, index, curve);
+      index += 2;
+    }
+
+    // Check whether key can be created
+    seProvider.importAsymmetricKey(
+        KMType.EC,
+        KMByteBlob.cast(data[SECRET]).getBuffer(),
+        KMByteBlob.cast(data[SECRET]).getStartOff(),
+        KMByteBlob.cast(data[SECRET]).length(),
+        KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+        KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+        KMByteBlob.cast(data[PUB_KEY]).length());
+
+    // add scratch pad to key parameters
+    updateKeyParameters(scratchPad, index);
+    data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+  }
+
+  private void importHmacKey(byte[] scratchPad) {
+    // Get Key
+    data[SECRET] = data[IMPORTED_KEY_BLOB];
+    // create HMAC key of up to 512 bit
+    short index = 0; // index in scratchPad for update params
+    // check the keysize tag if present in key parameters.
+    short keysize =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    if (keysize != KMType.INVALID_VALUE) {
+      if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) {
+        KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+      }
+      if (keysize != (short) (KMByteBlob.length(data[SECRET]) * 8)) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+    } else {
+      // add the key size to scratchPad
+      keysize = (short) (KMByteBlob.length(data[SECRET]) * 8);
+      if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) {
+        KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+      }
+      keysize = KMInteger.uint_16(keysize);
+      short keySizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+      Util.setShort(scratchPad, index, keySizeTag);
+      index += 2;
+    }
+    // Check whether key can be created
+    seProvider.importSymmetricKey(
+        KMType.HMAC,
+        keysize,
+        KMByteBlob.cast(data[SECRET]).getBuffer(),
+        KMByteBlob.cast(data[SECRET]).getStartOff(),
+        KMByteBlob.cast(data[SECRET]).length());
+
+    // update the key parameters list
+    updateKeyParameters(scratchPad, index);
+    // validate HMAC Key parameters
+    validateHmacKey();
+    data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+  }
+
+  private void importTDESKey(byte[] scratchPad) {
+    // Decode Key Material
+    data[SECRET] = data[IMPORTED_KEY_BLOB];
+    short index = 0; // index in scratchPad for update params
+    // check the keysize tag if present in key parameters.
+    short keysize =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    if (keysize != KMType.INVALID_VALUE) {
+      if (keysize != 168) {
+        KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+      }
+      if (192 != (short) (8 * KMByteBlob.length(data[SECRET]))) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+    } else {
+      keysize = (short) (KMByteBlob.length(data[SECRET]) * 8);
+      if (keysize != 192) {
+        KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+      }
+      // add the key size to scratchPad
+      keysize = KMInteger.uint_16((short) 168);
+      short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+      Util.setShort(scratchPad, index, keysizeTag);
+      index += 2;
+    }
+    // Read Minimum Mac length - it must not be present
+    KMTag.assertAbsence(
+        data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMError.INVALID_TAG);
+    // Check whether key can be created
+    seProvider.importSymmetricKey(
+        KMType.DES,
+        keysize,
+        KMByteBlob.cast(data[SECRET]).getBuffer(),
+        KMByteBlob.cast(data[SECRET]).getStartOff(),
+        KMByteBlob.cast(data[SECRET]).length());
+    // update the key parameters list
+    updateKeyParameters(scratchPad, index);
+    data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+  }
+
+  private void validateAesKeySize(short keySizeBits) {
+    if (keySizeBits != 128 && keySizeBits != 256) {
+      KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE);
+    }
+  }
+
+  private void importAESKey(byte[] scratchPad) {
+    // Get Key
+    data[SECRET] = data[IMPORTED_KEY_BLOB];
+    // create 128 or 256 bit AES key
+    short index = 0; // index in scratchPad for update params
+    // check the keysize tag if present in key parameters.
+    short keysize =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    if (keysize != KMType.INVALID_VALUE) {
+      if (keysize != (short) (8 * KMByteBlob.length(data[SECRET]))) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+      validateAesKeySize(keysize);
+    } else {
+      // add the key size to scratchPad
+      keysize = (short) (8 * KMByteBlob.cast(data[SECRET]).length());
+      validateAesKeySize(keysize);
+      keysize = KMInteger.uint_16(keysize);
+      short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+      Util.setShort(scratchPad, index, keysizeTag);
+      index += 2;
+    }
+    // Check whether key can be created
+    seProvider.importSymmetricKey(
+        KMType.AES,
+        keysize,
+        KMByteBlob.cast(data[SECRET]).getBuffer(),
+        KMByteBlob.cast(data[SECRET]).getStartOff(),
+        KMByteBlob.cast(data[SECRET]).length());
+
+    // update the key parameters list
+    updateKeyParameters(scratchPad, index);
+    // validate AES Key parameters
+    validateAESKey();
+    data[KEY_BLOB] = createKeyBlobInstance(SYM_KEY_TYPE);
+  }
+
+  private void importRSAKey(byte[] scratchPad) {
+    // Decode key material
+    KMAsn1Parser pkcs8 = KMAsn1Parser.instance();
+    short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]);
+    data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0);
+    short pubKeyExp = KMArray.cast(keyblob).get((short) 1);
+    data[SECRET] = KMArray.cast(keyblob).get((short) 2);
+    if (F4.length != KMByteBlob.cast(pubKeyExp).length()) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    if (Util.arrayCompare(
+            F4,
+            (short) 0,
+            KMByteBlob.cast(pubKeyExp).getBuffer(),
+            KMByteBlob.cast(pubKeyExp).getStartOff(),
+            (short) F4.length)
+        != 0) {
+      KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+    }
+    short index = 0; // index in scratchPad for update parameters.
+    // validate public exponent if present in key params - it must be 0x010001
+    short len =
+        KMIntegerTag.getValue(
+            scratchPad,
+            (short) 10, // using offset 10 as first 10 bytes reserved for update params
+            KMType.ULONG_TAG,
+            KMType.RSA_PUBLIC_EXPONENT,
+            data[KEY_PARAMETERS]);
+    if (len != KMTag.INVALID_VALUE) {
+      if (len != 4
+          || Util.getShort(scratchPad, (short) 10) != 0x01
+          || Util.getShort(scratchPad, (short) 12) != 0x01) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+    } else {
+      // add public exponent to scratchPad
+      Util.setShort(scratchPad, (short) 10, (short) 0x01);
+      Util.setShort(scratchPad, (short) 12, (short) 0x01);
+      pubKeyExp = KMInteger.uint_32(scratchPad, (short) 10);
+      pubKeyExp = KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, pubKeyExp);
+      Util.setShort(scratchPad, index, pubKeyExp);
+      index += 2;
+    }
+
+    // check the keysize tag if present in key parameters.
+    short keysize =
+        KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]);
+    short kSize = (short) (KMByteBlob.length(data[PUB_KEY]) * 8);
+    if (keysize != KMType.INVALID_VALUE) {
+      if (keysize != 2048 || (keysize != kSize)) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+    } else {
+      if (2048 != kSize) {
+        KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH);
+      }
+      // add the key size to scratchPad
+      keysize = KMInteger.uint_16((short) 2048);
+      keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize);
+      Util.setShort(scratchPad, index, keysize);
+      index += 2;
+    }
+
+    // Check whether key can be created
+    seProvider.importAsymmetricKey(
+        KMType.RSA,
+        KMByteBlob.cast(data[SECRET]).getBuffer(),
+        KMByteBlob.cast(data[SECRET]).getStartOff(),
+        KMByteBlob.cast(data[SECRET]).length(),
+        KMByteBlob.cast(data[PUB_KEY]).getBuffer(),
+        KMByteBlob.cast(data[PUB_KEY]).getStartOff(),
+        KMByteBlob.cast(data[PUB_KEY]).length());
+
+    // update the key parameters list
+    updateKeyParameters(scratchPad, index);
+    // validate RSA Key parameters
+    validateRSAKey(scratchPad);
+    data[KEY_BLOB] = createKeyBlobInstance(ASYM_KEY_TYPE);
+    KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]);
+  }
+
+  private void updateKeyParameters(byte[] newParams, short len) {
+    if (len == 0) {
+      return; // nothing to update
+    }
+    // Create Update Param array and copy current params
+    short params = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals();
+    len = (short) (KMArray.cast(params).length() + (short) (len / 2));
+    short updatedParams = KMArray.instance(len); // update params
+
+    len = KMArray.cast(params).length();
+    short index = 0;
+
+    // copy the existing key parameters to updated array
+    while (index < len) {
+      short tag = KMArray.cast(params).get(index);
+      KMArray.cast(updatedParams).add(index, tag);
+      index++;
+    }
+
+    // copy new parameters to updated array
+    len = KMArray.cast(updatedParams).length();
+    short newParamIndex = 0; // index in ptrArr
+    while (index < len) {
+      short tag = Util.getShort(newParams, newParamIndex);
+      KMArray.cast(updatedParams).add(index, tag);
+      index++;
+      newParamIndex += 2;
+    }
+    // replace with updated key parameters.
+    data[KEY_PARAMETERS] = KMKeyParameters.instance(updatedParams);
+  }
+
+  private short initStrongBoxCmd(APDU apdu) {
+    short cmd = KMArray.instance((short) 3);
+    KMArray.cast(cmd).add((short) 0, KMInteger.exp()); // OS version
+    KMArray.cast(cmd).add((short) 1, KMInteger.exp()); // OS patch level
+    KMArray.cast(cmd).add((short) 2, KMInteger.exp()); // Vendor patch level
+    return receiveIncoming(apdu, cmd);
+  }
+
+  // This command is executed to set the boot parameters.
+  // releaseAllOperations has to be called on every boot, so
+  // it is called from inside initStrongBoxCmd. Later in future if
+  // initStrongBoxCmd is removed, then make sure that releaseAllOperations
+  // is moved to a place where it is called on every boot.
+  private void processInitStrongBoxCmd(APDU apdu) {
+    short cmd = initStrongBoxCmd(apdu);
+    byte[] scratchPad = apdu.getBuffer();
+
+    short osVersion = KMArray.cast(cmd).get((short) 0);
+    short osPatchLevel = KMArray.cast(cmd).get((short) 1);
+    short vendorPatchLevel = KMArray.cast(cmd).get((short) 2);
+    setOsVersion(osVersion);
+    setOsPatchLevel(osPatchLevel);
+    setVendorPatchLevel(vendorPatchLevel);
+    kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_SYSTEM_PROPERTIES_SUCCESS);
+  }
+
+  public void reboot() {
+    // flag to maintain early boot ended state
+    kmDataStore.setEarlyBootEndedStatus(false);
+    // Clear all the operation state.
+    releaseAllOperations();
+    // Hmac is cleared, so generate a new Hmac nonce.
+    initHmacNonceAndSeed();
+    // Clear all auth tags.
+    kmDataStore.removeAllAuthTags();
+  }
+
+  protected void initSystemBootParams(
+      short osVersion, short osPatchLevel, short vendorPatchLevel, short bootPatchLevel) {
+    osVersion = KMInteger.uint_16(osVersion);
+    osPatchLevel = KMInteger.uint_16(osPatchLevel);
+    vendorPatchLevel = KMInteger.uint_16((short) vendorPatchLevel);
+    setOsVersion(osVersion);
+    setOsPatchLevel(osPatchLevel);
+    setVendorPatchLevel(vendorPatchLevel);
+  }
+
+  protected void setOsVersion(short version) {
+    kmDataStore.setOsVersion(
+        KMInteger.cast(version).getBuffer(),
+        KMInteger.cast(version).getStartOff(),
+        KMInteger.cast(version).length());
+  }
+
+  protected void setOsPatchLevel(short patch) {
+    kmDataStore.setOsPatch(
+        KMInteger.cast(patch).getBuffer(),
+        KMInteger.cast(patch).getStartOff(),
+        KMInteger.cast(patch).length());
+  }
+
+  protected void setVendorPatchLevel(short patch) {
+    kmDataStore.setVendorPatchLevel(
+        KMInteger.cast(patch).getBuffer(),
+        KMInteger.cast(patch).getStartOff(),
+        KMInteger.cast(patch).length());
+  }
+
+  private short generateKeyCmd(APDU apdu) {
+    short params = KMKeyParameters.expAny();
+    short blob = KMByteBlob.exp();
+    // Array of expected arguments
+    short cmd = KMArray.instance((short) 4);
+    KMArray.cast(cmd).add((short) 0, params); // key params
+    KMArray.cast(cmd).add((short) 1, blob); // attest key
+    KMArray.cast(cmd).add((short) 2, params); // attest key params
+    KMArray.cast(cmd).add((short) 3, blob); // issuer
+    return receiveIncoming(apdu, cmd);
+  }
+
+  private void processGenerateKey(APDU apdu) {
+    // Receive the incoming request fully from the host into buffer.
+    short cmd = generateKeyCmd(apdu);
+    // Re-purpose the apdu buffer as scratch pad.
+    byte[] scratchPad = apdu.getBuffer();
+    data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0);
+    data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 1);
+    data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 2);
+    data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 3);
+    data[CERTIFICATE] = KMType.INVALID_VALUE; // by default the cert is empty.
+    // ROLLBACK_RESISTANCE not supported.
+    KMTag.assertAbsence(
+        data[KEY_PARAMETERS],
+        KMType.BOOL_TAG,
+        KMType.ROLLBACK_RESISTANCE,
+        KMError.ROLLBACK_RESISTANCE_UNAVAILABLE);
+
+    // Algorithm must be present
+    KMTag.assertPresence(
+        data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT);
+
+    // Check if the tags are supported.
+    if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) {
+      KMException.throwIt(KMError.UNSUPPORTED_TAG);
+    }
+    short attKeyPurpose =
+        KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]);
+    // ATTEST_KEY cannot be combined with any other purpose.
+    if (attKeyPurpose != KMType.INVALID_VALUE
+        && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)
+        && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) {
+      KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE);
+    }
+    short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]);
+    // Check algorithm and dispatch to appropriate handler.
+    switch (alg) {
+      case KMType.RSA:
+        generateRSAKey(scratchPad);
+        break;
+      case KMType.AES:
+        generateAESKey(scratchPad);
+        break;
+      case KMType.DES:
+        generateTDESKey(scratchPad);
+        break;
+      case KMType.HMAC:
+        generateHmacKey(scratchPad);
+        break;
+      case KMType.EC:
+        generateECKeys(scratchPad);
+        break;
+      default:
+        KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM);
+        break;
+    }
+    // create key blob and associated attestation.
+    data[ORIGIN] = KMType.GENERATED;
+    makeKeyCharacteristics(scratchPad);
+    // construct the certificate and place the encoded data in data[CERTIFICATE]
+    KMAttestationCert cert =
+        generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad);
+    createEncryptedKeyBlob(scratchPad);
+    sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]);
+  }
+
+  private short getApplicationId(short params) {
+    short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, params);
+    if (appId != KMTag.INVALID_VALUE) {
+      appId = KMByteTag.cast(appId).getValue();
+      if (KMByteBlob.cast(appId).length() == 0) {
+        // Treat empty as INVALID.
+        return KMType.INVALID_VALUE;
+      }
+    }
+    return appId;
+  }
+
+  private short getApplicationData(short params) {
+    short appData = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, params);
+    if (appData != KMTag.INVALID_VALUE) {
+      appData = KMByteTag.cast(appData).getValue();
+      if (KMByteBlob.cast(appData).length() == 0) {
+        // Treat empty as INVALID.
+        return KMType.INVALID_VALUE;
+      }
+    }
+    return appData;
+  }
+
+  private short getAttestationMode(short attKeyBlob, short attChallenge) {
+    short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]);
+    short mode = KMType.NO_CERT;
+    if (KMEnumTag.cast(alg).getValue() != KMType.RSA
+        && KMEnumTag.cast(alg).getValue() != KMType.EC) {
+      return mode;
+    }
+    // If attestation keyblob preset
+    if (attKeyBlob != KMType.INVALID_VALUE && KMByteBlob.cast(attKeyBlob).length() > 0) {
+      // No attestation challenge present then it is an error
+      if (attChallenge == KMType.INVALID_VALUE || KMByteBlob.cast(attChallenge).length() <= 0) {
+        KMException.throwIt(KMError.ATTESTATION_CHALLENGE_MISSING);
+      } else {
+        mode = KMType.ATTESTATION_CERT;
+      }
+    } else { // no attestation key blob
+      // Attestation challenge present then it is an error because no factory provisioned attest key
+      if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) {
+        KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED);
+      } else if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.ATTEST_KEY, data[HW_PARAMETERS])
+          || KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[HW_PARAMETERS])) {
+        // The Purpose value can be read from either data[HW_PARAMETERS] or data[KEY_PARAMETERS]
+        // as the values will be same, and they are cryptographically bound.
+        mode = KMType.SELF_SIGNED_CERT;
+      } else {
+        mode = KMType.FAKE_CERT;
+      }
+    }
+    return mode;
+  }
+
+  private KMAttestationCert generateAttestation(
+      short attKeyBlob, short attKeyParam, byte[] scratchPad) {
+    // 1) If attestation key is present and attestation challenge is absent then it is an error.
+    // 2) If attestation key is absent and attestation challenge is present then it is an error as
+    // factory provisioned attestation key is not supported.
+    // 3) If both are present and issuer is absent or attest key purpose is not ATTEST_KEY then it
+    // is an error.
+    // 4) If the generated/imported keys are RSA or EC then validity period must be specified.
+    // Device Unique Attestation is not supported.
+    // Device unique attestation not supported
+    short heapStart = repository.getHeapIndex();
+    KMTag.assertAbsence(
+        data[KEY_PARAMETERS],
+        KMType.BOOL_TAG,
+        KMType.DEVICE_UNIQUE_ATTESTATION,
+        KMError.CANNOT_ATTEST_IDS);
+    // Read attestation challenge if present
+    short attChallenge =
+        KMKeyParameters.findTag(
+            KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE, data[KEY_PARAMETERS]);
+    if (attChallenge != KMType.INVALID_VALUE) {
+      attChallenge = KMByteTag.cast(attChallenge).getValue();
+    }
+    // No attestation required for symmetric keys
+    short mode = getAttestationMode(attKeyBlob, attChallenge);
+    KMAttestationCert cert = null;
+
+    switch (mode) {
+      case KMType.ATTESTATION_CERT:
+        cert =
+            makeAttestationCert(
+                attKeyBlob, attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER], scratchPad);
+        break;
+      case KMType.SELF_SIGNED_CERT:
+        cert = makeSelfSignedCert(data[SECRET], data[PUB_KEY], mode, scratchPad);
+        break;
+      case KMType.FAKE_CERT:
+        cert = makeSelfSignedCert(KMType.INVALID_VALUE, data[PUB_KEY], mode, scratchPad);
+        break;
+      default:
+        data[CERTIFICATE] = KMType.INVALID_VALUE;
+        return null;
+    }
+    // Certificate Data is converted to cbor and written to the end of the stack.
+    short certData = repository.allocReclaimableMemory(MAX_CERT_SIZE);
+    // Leave first 4 bytes for Array header and ByteBlob header.
+    cert.buffer(repository.getHeap(), (short) (certData + 4), (short) (MAX_CERT_SIZE - 4));
+    // Build the certificate - this will sign the cert
+    cert.build();
+    // Certificate is now built so the data in the heap starting from heapStart to the current
+    // heap index can be reused. So resetting the heap index to heapStart.
+    repository.setHeapIndex(heapStart);
+    data[CERTIFICATE] = certData;
+    return cert;
+  }
+
+  // Encodes KeyCharacteristics at the end of the heap
+  private void encodeKeyCharacteristics(short keyChars) {
+    byte[] buffer = repository.getHeap();
+    short prevReclaimIndex = repository.getHeapReclaimIndex();
+    short ptr = repository.allocReclaimableMemory(MAX_KEY_CHARS_SIZE);
+    short len = encoder.encode(keyChars, buffer, ptr, prevReclaimIndex, MAX_KEY_CHARS_SIZE);
+    // shift the encoded KeyCharacteristics data towards the right till the data[CERTIFICATE]
+    // offset.
+    Util.arrayCopyNonAtomic(buffer, ptr, buffer, (short) (ptr + (MAX_KEY_CHARS_SIZE - len)), len);
+    // Reclaim the unused memory.
+    repository.reclaimMemory((short) (MAX_KEY_CHARS_SIZE - len));
+  }
+
+  // Encodes KeyBlob at the end of the heap
+  private void encodeKeyBlob(short keyBlobPtr) {
+    // allocate reclaimable memory.
+    byte[] buffer = repository.getHeap();
+    short prevReclaimIndex = repository.getHeapReclaimIndex();
+    short top = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE);
+    short keyBlob = encoder.encode(keyBlobPtr, buffer, top, prevReclaimIndex, MAX_KEYBLOB_SIZE);
+    Util.arrayCopyNonAtomic(
+        repository.getHeap(),
+        top,
+        repository.getHeap(),
+        (short) (top + MAX_KEYBLOB_SIZE - keyBlob),
+        keyBlob);
+    short newTop = (short) (top + MAX_KEYBLOB_SIZE - keyBlob);
+    // Encode the KeyBlob array inside a ByteString. Get the length of
+    // the ByteString header.
+    short encodedBytesLength = encoder.getEncodedBytesLength(keyBlob);
+    newTop -= encodedBytesLength;
+    encoder.encodeByteBlobHeader(keyBlob, buffer, newTop, encodedBytesLength);
+    // Reclaim unused memory.
+    repository.reclaimMemory((short) (newTop - top));
+  }
+
+  private short readKeyBlobVersion(short keyBlob) {
+    short version = KMType.INVALID_VALUE;
+    try {
+      version =
+          decoder.readKeyblobVersion(
+              KMByteBlob.cast(keyBlob).getBuffer(),
+              KMByteBlob.cast(keyBlob).getStartOff(),
+              KMByteBlob.cast(keyBlob).length());
+      if (version == KMType.INVALID_VALUE) {
+        // If Version is not present. Then it is either an old KeyBlob or
+        // corrupted KeyBlob.
+        version = 0;
+      } else {
+        version = KMInteger.cast(version).getShort();
+        if (version > KEYBLOB_CURRENT_VERSION || version < 0) {
+          KMException.throwIt(KMError.INVALID_KEY_BLOB);
+        }
+      }
+    } catch (Exception e) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    return version;
+  }
+
+  private void readKeyBlobParams(short version, short parsedKeyBlob) {
+    data[KEY_BLOB] = parsedKeyBlob;
+    // initialize data
+    switch (version) {
+      case (short) 0:
+        data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 0);
+        data[NONCE] = KMArray.cast(parsedKeyBlob).get((short) 1);
+        data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 2);
+        data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 3);
+        data[PUB_KEY] = KMType.INVALID_VALUE;
+        if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V0) {
+          data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 4);
+        }
+        // Set the data[KEY_BLOB_VERSION_DATA_OFFSET] with integer value of 0 so
+        // that it will used at later point of time.
+        data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_8((byte) 0);
+        break;
+      case (short) 1:
+        data[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get((short) 0);
+        data[SECRET] = KMArray.cast(parsedKeyBlob).get((short) 1);
+        data[NONCE] = KMArray.cast(parsedKeyBlob).get((short) 2);
+        data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get((short) 3);
+        data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get((short) 4);
+        data[PUB_KEY] = KMType.INVALID_VALUE;
+        if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V1) {
+          data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 5);
+        }
+        break;
+      case (short) 2:
+      case (short) 3:
+        data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET);
+        data[NONCE] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_NONCE);
+        data[AUTH_TAG] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_AUTH_TAG);
+        data[KEY_CHARACTERISTICS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PARAMS);
+        data[KEY_BLOB_VERSION_DATA_OFFSET] =
+            KMArray.cast(parsedKeyBlob).get(KEY_BLOB_VERSION_OFFSET);
+        data[CUSTOM_TAGS] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_CUSTOM_TAGS);
+        data[PUB_KEY] = KMType.INVALID_VALUE;
+        if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V2_V3) {
+          data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_PUB_KEY);
+        }
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+  }
+
+  private void decodeKeyBlob(short version, short keyBlob) {
+    // Decode KeyBlob and read the KeyBlob params based on the version.
+    short parsedBlob =
+        decoder.decodeArray(
+            createKeyBlobExp(version),
+            KMByteBlob.cast(keyBlob).getBuffer(),
+            KMByteBlob.cast(keyBlob).getStartOff(),
+            KMByteBlob.cast(keyBlob).length());
+    short minArraySize = 0;
+    switch (version) {
+      case 0:
+        minArraySize = SYM_KEY_BLOB_SIZE_V0;
+        break;
+      case 1:
+        minArraySize = SYM_KEY_BLOB_SIZE_V1;
+        break;
+      case 2:
+      case 3:
+        minArraySize = SYM_KEY_BLOB_SIZE_V2_V3;
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    ;
+    // KeyBlob size should not be less than the minimum KeyBlob size.
+    if (KMArray.cast(parsedBlob).length() < minArraySize) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    readKeyBlobParams(version, parsedBlob);
+  }
+
+  private void processDecryptSecret(short version, short appId, short appData, byte[] scratchPad) {
+    data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced();
+    data[SB_PARAMETERS] =
+        KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced();
+    data[SW_PARAMETERS] =
+        KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced();
+    data[HW_PARAMETERS] = KMKeyParameters.makeHwEnforced(data[SB_PARAMETERS], data[TEE_PARAMETERS]);
+
+    data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, data[ROT], scratchPad);
+    // Decrypt Secret and verify auth tag
+    decryptSecret(scratchPad, version);
+    short keyBlobSecretOff = 0;
+    switch (version) {
+      case 0:
+        // V0 KeyBlob
+        // KEY_BLOB = [
+        //     SECRET,
+        //     NONCE,
+        //     AUTH_TAG,
+        //     KEY_CHARACTERISTICS,
+        //     PUBKEY
+        // ]
+        keyBlobSecretOff = (short) 0;
+        break;
+      case 1:
+        // V1 KeyBlob
+        // KEY_BLOB = [
+        //     VERSION,
+        //     SECRET,
+        //     NONCE,
+        //     AUTH_TAG,
+        //     KEY_CHARACTERISTICS,
+        //     PUBKEY
+        // ]
+        keyBlobSecretOff = (short) 1;
+        break;
+      case 2:
+      case 3:
+        // V2 KeyBlob
+        // KEY_BLOB = [
+        //     VERSION,
+        //     SECRET,
+        //     NONCE,
+        //     AUTH_TAG,
+        //     KEY_CHARACTERISTICS,
+        //     CUSTOM_TAGS,
+        //     PUBKEY
+        // ]
+        keyBlobSecretOff = KEY_BLOB_SECRET;
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    ;
+    KMArray.cast(data[KEY_BLOB]).add(keyBlobSecretOff, data[SECRET]);
+  }
+
+  private void parseEncryptedKeyBlob(
+      short keyBlob, short appId, short appData, byte[] scratchPad, short version) {
+    // make root of trust blob
+    data[ROT] = readROT(scratchPad, version);
+    if (data[ROT] == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    try {
+      decodeKeyBlob(version, keyBlob);
+      processDecryptSecret(version, appId, appData, scratchPad);
+    } catch (Exception e) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+  }
+
+  private void decryptSecret(byte[] scratchPad, short version) {
+    // derive master key - stored in derivedKey
+    short len;
+    short authDataOff = 0;
+    short authDataLen = 0;
+    byte[] authDataBuff = null;
+    switch (version) {
+      case 3:
+        len = deriveKey(scratchPad);
+        break;
+
+      case 2:
+      case 1:
+      case 0:
+        makeAuthData(version, scratchPad);
+        len = deriveKeyForOldKeyBlobs(scratchPad);
+        authDataBuff = repository.getHeap();
+        authDataOff = data[AUTH_DATA];
+        authDataLen = data[AUTH_DATA_LENGTH];
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    if (!seProvider.aesGCMDecrypt(
+        KMByteBlob.cast(data[DERIVED_KEY]).getBuffer(),
+        KMByteBlob.cast(data[DERIVED_KEY]).getStartOff(),
+        KMByteBlob.cast(data[DERIVED_KEY]).length(),
+        KMByteBlob.cast(data[SECRET]).getBuffer(),
+        KMByteBlob.cast(data[SECRET]).getStartOff(),
+        KMByteBlob.cast(data[SECRET]).length(),
+        scratchPad,
+        (short) 0,
+        KMByteBlob.cast(data[NONCE]).getBuffer(),
+        KMByteBlob.cast(data[NONCE]).getStartOff(),
+        KMByteBlob.cast(data[NONCE]).length(),
+        authDataBuff,
+        authDataOff,
+        authDataLen,
+        KMByteBlob.cast(data[AUTH_TAG]).getBuffer(),
+        KMByteBlob.cast(data[AUTH_TAG]).getStartOff(),
+        KMByteBlob.cast(data[AUTH_TAG]).length())) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    // Copy the decrypted secret
+    data[SECRET] =
+        KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[SECRET]).length());
+  }
+
+  private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) {
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0);
+    Util.arrayCopyNonAtomic(
+        KMInteger.cast(authTime).getBuffer(),
+        KMInteger.cast(authTime).getStartOff(),
+        scratchPad,
+        (short) (8 - KMInteger.cast(timeStamp).length()),
+        KMInteger.cast(timeStamp).length());
+
+    // Copy timestamp to scratchpad
+    Util.arrayCopyNonAtomic(
+        KMInteger.cast(timeStamp).getBuffer(),
+        KMInteger.cast(timeStamp).getStartOff(),
+        scratchPad,
+        (short) (16 - KMInteger.cast(timeStamp).length()),
+        KMInteger.cast(timeStamp).length());
+
+    // add authTime in millis to timestamp.
+    KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16);
+    return KMInteger.uint_64(scratchPad, (short) 16);
+  }
+
+  public void powerReset() {
+    // TODO handle power reset signal.
+    releaseAllOperations();
+    resetWrappingKey();
+  }
+
+  private void updateTrustedConfirmationOperation(KMOperationState op) {
+    if (op.isTrustedConfirmationRequired()) {
+      op.getTrustedConfirmationSigner()
+          .update(
+              KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+              KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+              KMByteBlob.cast(data[INPUT_DATA]).length());
+    }
+  }
+
+  private void finishTrustedConfirmationOperation(KMOperationState op) {
+    // Perform trusted confirmation if required
+    if (op.isTrustedConfirmationRequired()) {
+      if (0 == KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()) {
+        KMException.throwIt(KMError.NO_USER_CONFIRMATION);
+      }
+
+      boolean verified =
+          op.getTrustedConfirmationSigner()
+              .verify(
+                  KMByteBlob.cast(data[INPUT_DATA]).getBuffer(),
+                  KMByteBlob.cast(data[INPUT_DATA]).getStartOff(),
+                  KMByteBlob.cast(data[INPUT_DATA]).length(),
+                  KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getBuffer(),
+                  KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getStartOff(),
+                  KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length());
+      if (!verified) {
+        KMException.throwIt(KMError.NO_USER_CONFIRMATION);
+      }
+    }
+  }
+
+  private boolean isDigestSupported(short alg, short digest) {
+    switch (alg) {
+      case KMType.RSA:
+      case KMType.EC:
+        if (digest != KMType.DIGEST_NONE && digest != KMType.SHA2_256) {
+          return false;
+        }
+        break;
+      case KMType.HMAC:
+        if (digest != KMType.SHA2_256) {
+          return false;
+        }
+        break;
+      default:
+        break;
+    }
+    return true;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java
new file mode 100644
index 0000000..3de1d0c
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java
@@ -0,0 +1,1050 @@
+package com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMComputedHmacKey;
+import com.android.javacard.seprovider.KMDataStoreConstants;
+import com.android.javacard.seprovider.KMDeviceUniqueKeyPair;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMMasterKey;
+import com.android.javacard.seprovider.KMPreSharedKey;
+import com.android.javacard.seprovider.KMRkpMacKey;
+import com.android.javacard.seprovider.KMSEProvider;
+import com.android.javacard.seprovider.KMUpgradable;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+import org.globalplatform.upgrade.Element;
+
+public class KMKeymintDataStore implements KMUpgradable {
+
+  // Data table configuration
+  public static final short KM_APPLET_PACKAGE_VERSION_1 = 0x0100;
+  public static final short KM_APPLET_PACKAGE_VERSION_2 = 0x0200;
+  public static final short KM_APPLET_PACKAGE_VERSION_3 = 0x0300;
+  public static final byte DATA_INDEX_SIZE = 17;
+  public static final byte DATA_INDEX_ENTRY_SIZE = 4;
+  public static final byte DATA_INDEX_ENTRY_LENGTH = 0;
+  public static final byte DATA_INDEX_ENTRY_OFFSET = 2;
+  public static final short DATA_MEM_SIZE = 300;
+  // Data table offsets
+  public static final byte HMAC_NONCE = 0;
+  public static final byte BOOT_OS_VERSION = 1;
+  public static final byte BOOT_OS_PATCH_LEVEL = 2;
+  public static final byte VENDOR_PATCH_LEVEL = 3;
+  public static final byte DEVICE_LOCKED_TIME = 4;
+  public static final byte DEVICE_LOCKED = 5;
+  public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 6;
+  // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8
+  public static final byte AUTH_TAG_1 = 7;
+  public static final byte DEVICE_STATUS_FLAG = 15;
+  public static final byte EARLY_BOOT_ENDED_FLAG = 16;
+  // Data Item sizes
+  public static final byte HMAC_SEED_NONCE_SIZE = 32;
+  public static final byte COMPUTED_HMAC_KEY_SIZE = 32;
+  public static final byte OS_VERSION_SIZE = 4;
+  public static final byte OS_PATCH_SIZE = 4;
+  public static final byte VENDOR_PATCH_SIZE = 4;
+  public static final byte DEVICE_LOCK_TS_SIZE = 8;
+  public static final byte MAX_BLOB_STORAGE = 8;
+  public static final byte AUTH_TAG_LENGTH = 16;
+  public static final byte AUTH_TAG_COUNTER_SIZE = 4;
+  public static final byte AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1);
+  // Device boot states. Applet starts executing the
+  // core commands once all the states are set. The commands
+  // that are allowed irrespective of these states are:
+  // All the provision commands
+  // INS_GET_HW_INFO_CMD
+  // INS_ADD_RNG_ENTROPY_CMD
+  // INS_COMPUTE_SHARED_HMAC_CMD
+  // INS_GET_HMAC_SHARING_PARAM_CMD
+  public static final byte SET_BOOT_PARAMS_SUCCESS = 0x01;
+  public static final byte SET_SYSTEM_PROPERTIES_SUCCESS = 0x02;
+  public static final byte NEGOTIATED_SHARED_SECRET_SUCCESS = 0x04;
+  // Old Data table offsets
+  private static final byte OLD_PROVISIONED_STATUS_OFFSET = 18;
+  private static final byte SHARED_SECRET_KEY_SIZE = 32;
+  private static final byte DEVICE_STATUS_FLAG_SIZE = 1;
+  private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 2500; // First 2 bytes for length.
+  private static final short BCC_MAX_SIZE = 512;
+  private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0};
+  private static KMKeymintDataStore kmDataStore;
+  // Secure Boot Mode
+  public byte secureBootMode;
+  // Data - originally was in repository
+  private byte[] attIdBrand;
+  private byte[] attIdDevice;
+  private byte[] attIdProduct;
+  private byte[] attIdSerial;
+  private byte[] attIdImei;
+  private byte[] attIdMeId;
+  private byte[] attIdManufacturer;
+  private byte[] attIdModel;
+  // Boot parameters
+  private byte[] verifiedHash;
+  private byte[] bootKey;
+  private byte[] bootPatchLevel;
+  private boolean deviceBootLocked;
+  private short bootState;
+  // Challenge for Root of trust
+  private byte[] challenge;
+  private short dataIndex;
+  private byte[] dataTable;
+  private KMSEProvider seProvider;
+  private KMRepository repository;
+  private byte[] additionalCertChain;
+  private byte[] bcc;
+  private KMMasterKey masterKey;
+  private KMDeviceUniqueKeyPair testDeviceUniqueKeyPair;
+  private KMDeviceUniqueKeyPair deviceUniqueKeyPair;
+  private KMPreSharedKey preSharedKey;
+  private KMComputedHmacKey computedHmacKey;
+  private KMRkpMacKey rkpMacKey;
+  private byte[] oemRootPublicKey;
+  private short provisionStatus;
+
+  public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) {
+    seProvider = provider;
+    repository = repo;
+    boolean isUpgrading = provider.isUpgrading();
+    initDataTable();
+    // Initialize the device locked status
+    if (!isUpgrading) {
+      additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE];
+      bcc = new byte[BCC_MAX_SIZE];
+      oemRootPublicKey = new byte[65];
+    }
+    setDeviceLockPasswordOnly(false);
+    setDeviceLock(false);
+    kmDataStore = this;
+  }
+
+  public static KMKeymintDataStore instance() {
+    return kmDataStore;
+  }
+
+  private void initDataTable() {
+    if (dataTable == null) {
+      dataTable = new byte[DATA_MEM_SIZE];
+      dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE);
+    }
+  }
+
+  private short dataAlloc(short length) {
+    if (((short) (dataIndex + length)) > dataTable.length) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    dataIndex += length;
+    return (short) (dataIndex - length);
+  }
+
+  private void clearDataEntry(short id) {
+    id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+    short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+    if (dataLen != 0) {
+      short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET));
+      JCSystem.beginTransaction();
+      Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0);
+      JCSystem.commitTransaction();
+    }
+  }
+
+  private void writeDataEntry(short id, byte[] buf, short offset, short len) {
+    short dataPtr;
+    id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+    short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+    if (dataLen == 0) {
+      dataPtr = dataAlloc(len);
+      JCSystem.beginTransaction();
+      Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr);
+      Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len);
+      Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len);
+      JCSystem.commitTransaction();
+    } else {
+      if (len != dataLen) {
+        KMException.throwIt(KMError.UNKNOWN_ERROR);
+      }
+      dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET));
+      JCSystem.beginTransaction();
+      Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len);
+      JCSystem.commitTransaction();
+    }
+  }
+
+  private short readDataEntry(short id, byte[] buf, short offset) {
+    id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+    short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+    if (len != 0) {
+      Util.arrayCopyNonAtomic(
+          dataTable,
+          Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)),
+          buf,
+          offset,
+          len);
+    }
+    return len;
+  }
+
+  private short readDataEntry(byte[] dataTable, short id, byte[] buf, short offset) {
+    id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+    short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+    if (len != 0) {
+      Util.arrayCopyNonAtomic(
+          dataTable,
+          Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)),
+          buf,
+          offset,
+          len);
+    }
+    return len;
+  }
+
+  private short dataLength(short id) {
+    id = (short) (id * DATA_INDEX_ENTRY_SIZE);
+    return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH));
+  }
+
+  public short readData(short id) {
+    short len = dataLength(id);
+    if (len != 0) {
+      short blob = KMByteBlob.instance(dataLength(id));
+      readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+      return blob;
+    }
+    return KMType.INVALID_VALUE;
+  }
+
+  public short getHmacNonce() {
+    return readData(HMAC_NONCE);
+  }
+
+  public short getOsVersion() {
+    short blob = readData(BOOT_OS_VERSION);
+    if (blob == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return KMInteger.uint_32(
+        KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+  }
+
+  public short getVendorPatchLevel() {
+    short blob = readData(VENDOR_PATCH_LEVEL);
+    if (blob == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return KMInteger.uint_32(
+        KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+  }
+
+  public short getOsPatch() {
+    short blob = readData(BOOT_OS_PATCH_LEVEL);
+    if (blob == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return KMInteger.uint_32(
+        KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+  }
+
+  private boolean readBoolean(short id) {
+    short blob = readData(id);
+    if (blob == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return (byte) ((repository.getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01;
+  }
+
+  public boolean getDeviceLock() {
+    return readBoolean(DEVICE_LOCKED);
+  }
+
+  public void setDeviceLock(boolean flag) {
+    writeBoolean(DEVICE_LOCKED, flag);
+  }
+
+  public boolean getDeviceLockPasswordOnly() {
+    return readBoolean(DEVICE_LOCKED_PASSWORD_ONLY);
+  }
+
+  public void setDeviceLockPasswordOnly(boolean flag) {
+    writeBoolean(DEVICE_LOCKED_PASSWORD_ONLY, flag);
+  }
+
+  public boolean getEarlyBootEndedStatus() {
+    return readBoolean(EARLY_BOOT_ENDED_FLAG);
+  }
+
+  public void setEarlyBootEndedStatus(boolean flag) {
+    writeBoolean(EARLY_BOOT_ENDED_FLAG, flag);
+  }
+
+  public short getDeviceTimeStamp() {
+    short blob = readData(DEVICE_LOCKED_TIME);
+    if (blob == KMType.INVALID_VALUE) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return KMInteger.uint_64(
+        KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff());
+  }
+
+  public void setOsVersion(byte[] buf, short start, short len) {
+    if (len != OS_VERSION_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    writeDataEntry(BOOT_OS_VERSION, buf, start, len);
+  }
+
+  public void setVendorPatchLevel(byte[] buf, short start, short len) {
+    if (len != VENDOR_PATCH_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len);
+  }
+
+  private void writeBoolean(short id, boolean flag) {
+    short start = repository.alloc((short) 1);
+    if (flag) {
+      (repository.getHeap())[start] = (byte) 0x01;
+    } else {
+      (repository.getHeap())[start] = (byte) 0x00;
+    }
+    writeDataEntry(id, repository.getHeap(), start, (short) 1);
+  }
+
+  public void setDeviceLockTimestamp(byte[] buf, short start, short len) {
+    if (len != DEVICE_LOCK_TS_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len);
+  }
+
+  public void clearDeviceBootStatus() {
+    clearDataEntry(DEVICE_STATUS_FLAG);
+  }
+
+  public void setDeviceBootStatus(byte initStatus) {
+    short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE);
+    byte[] buf = repository.getHeap();
+    getDeviceBootStatus(buf, offset);
+    buf[offset] |= initStatus;
+    writeDataEntry(DEVICE_STATUS_FLAG, buf, offset, DEVICE_STATUS_FLAG_SIZE);
+    repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE);
+  }
+
+  public boolean isDeviceReady() {
+    boolean result = false;
+    short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE);
+    byte[] buf = repository.getHeap();
+    getDeviceBootStatus(buf, offset);
+    byte bootCompleteStatus =
+        (SET_BOOT_PARAMS_SUCCESS
+            | SET_SYSTEM_PROPERTIES_SUCCESS
+            | NEGOTIATED_SHARED_SECRET_SUCCESS);
+    if (bootCompleteStatus == (buf[offset] & bootCompleteStatus)) {
+      result = true;
+    }
+    repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE);
+    return result;
+  }
+
+  public short getDeviceBootStatus(byte[] scratchpad, short offset) {
+    scratchpad[offset] = 0;
+    return readDataEntry(DEVICE_STATUS_FLAG, scratchpad, offset);
+  }
+
+  public void clearDeviceLockTimeStamp() {
+    clearDataEntry(DEVICE_LOCKED_TIME);
+  }
+
+  public void setOsPatch(byte[] buf, short start, short len) {
+    if (len != OS_PATCH_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len);
+  }
+
+  private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) {
+    readDataEntry(tagId, buf, offset);
+    return (0 == buf[offset]);
+  }
+
+  public void initHmacNonce(byte[] nonce, short offset, short len) {
+    if (len != HMAC_SEED_NONCE_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    writeDataEntry(HMAC_NONCE, nonce, offset, len);
+  }
+
+  public void clearHmacNonce() {
+    clearDataEntry(HMAC_NONCE);
+  }
+
+  public boolean persistAuthTag(short authTag) {
+
+    if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+
+    short authTagEntry = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+    short scratchPadOff = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+    byte[] scratchPad = repository.getHeap();
+    writeAuthTagState(repository.getHeap(), authTagEntry, (byte) 1);
+    Util.arrayCopyNonAtomic(
+        KMByteBlob.cast(authTag).getBuffer(),
+        KMByteBlob.cast(authTag).getStartOff(),
+        repository.getHeap(),
+        (short) (authTagEntry + 1),
+        AUTH_TAG_LENGTH);
+    Util.setShort(
+        repository.getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), (short) 1);
+    short index = 0;
+    while (index < MAX_BLOB_STORAGE) {
+      if ((dataLength((short) (index + AUTH_TAG_1)) == 0)
+          || isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) {
+
+        writeDataEntry(
+            (short) (index + AUTH_TAG_1), repository.getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE);
+        return true;
+      }
+      index++;
+    }
+    return false;
+  }
+
+  public void removeAllAuthTags() {
+    short index = 0;
+    while (index < MAX_BLOB_STORAGE) {
+      clearDataEntry((short) (index + AUTH_TAG_1));
+      index++;
+    }
+  }
+
+  public boolean isAuthTagPersisted(short authTag) {
+    return (KMType.INVALID_VALUE != findTag(authTag));
+  }
+
+  private short findTag(short authTag) {
+    if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    short index = 0;
+    short found;
+    short offset = repository.alloc(AUTH_TAG_ENTRY_SIZE);
+    while (index < MAX_BLOB_STORAGE) {
+      if (dataLength((short) (index + AUTH_TAG_1)) != 0) {
+        readDataEntry((short) (index + AUTH_TAG_1), repository.getHeap(), offset);
+        found =
+            Util.arrayCompare(
+                repository.getHeap(),
+                (short) (offset + 1),
+                KMByteBlob.cast(authTag).getBuffer(),
+                KMByteBlob.cast(authTag).getStartOff(),
+                AUTH_TAG_LENGTH);
+        if (found == 0) {
+          return (short) (index + AUTH_TAG_1);
+        }
+      }
+      index++;
+    }
+    return KMType.INVALID_VALUE;
+  }
+
+  public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) {
+    short tag = findTag(authTag);
+    short blob;
+    if (tag != KMType.INVALID_VALUE) {
+      blob = readData(tag);
+      Util.arrayCopyNonAtomic(
+          KMByteBlob.cast(blob).getBuffer(),
+          (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1),
+          out,
+          outOff,
+          AUTH_TAG_COUNTER_SIZE);
+      return AUTH_TAG_COUNTER_SIZE;
+    }
+    return (short) 0;
+  }
+
+  public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) {
+    short tag = findTag(authTag);
+    if (tag != KMType.INVALID_VALUE) {
+      short dataPtr = readData(tag);
+      Util.arrayCopyNonAtomic(
+          buf,
+          off,
+          KMByteBlob.cast(dataPtr).getBuffer(),
+          (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1),
+          len);
+      writeDataEntry(
+          tag,
+          KMByteBlob.cast(dataPtr).getBuffer(),
+          KMByteBlob.cast(dataPtr).getStartOff(),
+          KMByteBlob.cast(dataPtr).length());
+    }
+  }
+
+  public void persistAdditionalCertChain(byte[] buf, short offset, short len) {
+    // Input buffer contains encoded additional certificate chain as shown below.
+    //    AdditionalDKSignatures = {
+    //      + SignerName => DKCertChain
+    //    }
+    //    SignerName = tstr
+    //    DKCertChain = [
+    //      2* Certificate // Root -> Leaf. Root is the vendo r
+    //            // self-signed cert, leaf contains DK_pu b
+    //    ]
+    //    Certificate = COSE_Sign1 of a public key
+    if ((short) (len + 2) > ADDITIONAL_CERT_CHAIN_MAX_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    JCSystem.beginTransaction();
+    Util.setShort(additionalCertChain, (short) 0, (short) len);
+    Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, (short) 2, len);
+    JCSystem.commitTransaction();
+  }
+
+  public short getAdditionalCertChainLength() {
+    return Util.getShort(additionalCertChain, (short) 0);
+  }
+
+  public byte[] getAdditionalCertChain() {
+    return additionalCertChain;
+  }
+
+  public byte[] getBootCertificateChain() {
+    return bcc;
+  }
+
+  public void persistBootCertificateChain(byte[] buf, short offset, short len) {
+    if ((short) (len + 2) > BCC_MAX_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    JCSystem.beginTransaction();
+    Util.setShort(bcc, (short) 0, (short) len);
+    Util.arrayCopyNonAtomic(buf, offset, bcc, (short) 2, len);
+    JCSystem.commitTransaction();
+  }
+
+  private void writeAuthTagState(byte[] buf, short offset, byte state) {
+    buf[offset] = state;
+  }
+
+  public KMMasterKey createMasterKey(short keySizeBits) {
+    if (masterKey == null) {
+      masterKey = seProvider.createMasterKey(masterKey, keySizeBits);
+    }
+    return (KMMasterKey) masterKey;
+  }
+
+  public KMMasterKey getMasterKey() {
+    return masterKey;
+  }
+
+  public void createPresharedKey(byte[] keyData, short offset, short length) {
+    if (length != SHARED_SECRET_KEY_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    if (preSharedKey == null) {
+      preSharedKey = seProvider.createPreSharedKey(preSharedKey, keyData, offset, length);
+    }
+  }
+
+  public KMPreSharedKey getPresharedKey() {
+    if (preSharedKey == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return preSharedKey;
+  }
+
+  public void createComputedHmacKey(byte[] keyData, short offset, short length) {
+    if (length != COMPUTED_HMAC_KEY_SIZE) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    if (computedHmacKey == null) {
+      computedHmacKey = seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length);
+    } else {
+      seProvider.createComputedHmacKey(computedHmacKey, keyData, offset, length);
+    }
+  }
+
+  public KMComputedHmacKey getComputedHmacKey() {
+    if (computedHmacKey == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return computedHmacKey;
+  }
+
+  public KMDeviceUniqueKeyPair createRkpTestDeviceUniqueKeyPair(
+      byte[] pubKey,
+      short pubKeyOff,
+      short pubKeyLen,
+      byte[] privKey,
+      short privKeyOff,
+      short privKeyLen) {
+    if (testDeviceUniqueKeyPair == null) {
+      testDeviceUniqueKeyPair =
+          seProvider.createRkpDeviceUniqueKeyPair(
+              testDeviceUniqueKeyPair,
+              pubKey,
+              pubKeyOff,
+              pubKeyLen,
+              privKey,
+              privKeyOff,
+              privKeyLen);
+    } else {
+      seProvider.createRkpDeviceUniqueKeyPair(
+          testDeviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+    }
+    return testDeviceUniqueKeyPair;
+  }
+
+  public KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(
+      byte[] pubKey,
+      short pubKeyOff,
+      short pubKeyLen,
+      byte[] privKey,
+      short privKeyOff,
+      short privKeyLen) {
+    if (deviceUniqueKeyPair == null) {
+      deviceUniqueKeyPair =
+          seProvider.createRkpDeviceUniqueKeyPair(
+              deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+    } else {
+      seProvider.createRkpDeviceUniqueKeyPair(
+          deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen);
+    }
+    return deviceUniqueKeyPair;
+  }
+
+  public KMDeviceUniqueKeyPair getRkpDeviceUniqueKeyPair(boolean testMode) {
+    return ((KMDeviceUniqueKeyPair) (testMode ? testDeviceUniqueKeyPair : deviceUniqueKeyPair));
+  }
+
+  public void createRkpMacKey(byte[] keydata, short offset, short length) {
+    if (rkpMacKey == null) {
+      rkpMacKey = seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length);
+    } else {
+      seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length);
+    }
+  }
+
+  public KMRkpMacKey getRkpMacKey() {
+    if (rkpMacKey == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return rkpMacKey;
+  }
+
+  public short getAttestationId(short tag, byte[] buffer, short start) {
+    byte[] attestId = null;
+    switch (tag) {
+        // Attestation Id Brand
+      case KMType.ATTESTATION_ID_BRAND:
+        attestId = attIdBrand;
+        break;
+        // Attestation Id Device
+      case KMType.ATTESTATION_ID_DEVICE:
+        attestId = attIdDevice;
+        break;
+        // Attestation Id Product
+      case KMType.ATTESTATION_ID_PRODUCT:
+        attestId = attIdProduct;
+        break;
+        // Attestation Id Serial
+      case KMType.ATTESTATION_ID_SERIAL:
+        attestId = attIdSerial;
+        break;
+        // Attestation Id IMEI
+      case KMType.ATTESTATION_ID_IMEI:
+        attestId = attIdImei;
+        break;
+        // Attestation Id MEID
+      case KMType.ATTESTATION_ID_MEID:
+        attestId = attIdMeId;
+        break;
+        // Attestation Id Manufacturer
+      case KMType.ATTESTATION_ID_MANUFACTURER:
+        attestId = attIdManufacturer;
+        break;
+        // Attestation Id Model
+      case KMType.ATTESTATION_ID_MODEL:
+        attestId = attIdModel;
+        break;
+    }
+    if (attestId == null) {
+      KMException.throwIt(KMError.CANNOT_ATTEST_IDS);
+    }
+    Util.arrayCopyNonAtomic(attestId, (short) 0, buffer, start, (short) attestId.length);
+    return (short) attestId.length;
+  }
+
+  public void setAttestationId(short tag, byte[] buffer, short start, short length) {
+    switch (tag) {
+        // Attestation Id Brand
+      case KMType.ATTESTATION_ID_BRAND:
+        JCSystem.beginTransaction();
+        attIdBrand = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+        // Attestation Id Device
+      case KMType.ATTESTATION_ID_DEVICE:
+        JCSystem.beginTransaction();
+        attIdDevice = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdDevice, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+        // Attestation Id Product
+      case KMType.ATTESTATION_ID_PRODUCT:
+        JCSystem.beginTransaction();
+        attIdProduct = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdProduct, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+        // Attestation Id Serial
+      case KMType.ATTESTATION_ID_SERIAL:
+        JCSystem.beginTransaction();
+        attIdSerial = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdSerial, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+        // Attestation Id IMEI
+      case KMType.ATTESTATION_ID_IMEI:
+        JCSystem.beginTransaction();
+        attIdImei = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdImei, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+        // Attestation Id MEID
+      case KMType.ATTESTATION_ID_MEID:
+        JCSystem.beginTransaction();
+        attIdMeId = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdMeId, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+        // Attestation Id Manufacturer
+      case KMType.ATTESTATION_ID_MANUFACTURER:
+        JCSystem.beginTransaction();
+        attIdManufacturer = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdManufacturer, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+        // Attestation Id Model
+      case KMType.ATTESTATION_ID_MODEL:
+        JCSystem.beginTransaction();
+        attIdModel = new byte[length];
+        Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length);
+        JCSystem.commitTransaction();
+        break;
+    }
+  }
+
+  public void deleteAttestationIds() {
+    attIdBrand = null;
+    attIdDevice = null;
+    attIdProduct = null;
+    attIdSerial = null;
+    attIdImei = null;
+    attIdMeId = null;
+    attIdManufacturer = null;
+    attIdModel = null;
+    // Trigger garbage collection.
+    JCSystem.requestObjectDeletion();
+  }
+
+  public short getVerifiedBootHash(byte[] buffer, short start) {
+    if (verifiedHash == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length);
+    return (short) verifiedHash.length;
+  }
+
+  public short getBootKey(byte[] buffer, short start) {
+    if (bootKey == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length);
+    return (short) bootKey.length;
+  }
+
+  public short getBootState() {
+    return bootState;
+  }
+
+  public void setBootState(short state) {
+    bootState = state;
+  }
+
+  public boolean isDeviceBootLocked() {
+    return deviceBootLocked;
+  }
+
+  public short getBootPatchLevel() {
+    if (bootPatchLevel == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return KMInteger.uint_32(bootPatchLevel, (short) 0);
+  }
+
+  public void setVerifiedBootHash(byte[] buffer, short start, short length) {
+    if (verifiedHash == null) {
+      verifiedHash = new byte[32];
+    }
+    if (length != 32) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    Util.arrayCopy(buffer, start, verifiedHash, (short) 0, (short) 32);
+  }
+
+  public void setBootKey(byte[] buffer, short start, short length) {
+    if (bootKey == null) {
+      bootKey = new byte[32];
+    }
+    if (length != 32) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    Util.arrayCopy(buffer, start, bootKey, (short) 0, (short) 32);
+  }
+
+  public void setDeviceLocked(boolean state) {
+    deviceBootLocked = state;
+  }
+
+  public void setBootPatchLevel(byte[] buffer, short start, short length) {
+    if (bootPatchLevel == null) {
+      bootPatchLevel = new byte[4];
+    }
+    if (length > 4 || length < 0) {
+      KMException.throwIt(KMError.UNKNOWN_ERROR);
+    }
+    Util.arrayCopy(buffer, start, bootPatchLevel, (short) 0, (short) length);
+  }
+
+  public void setChallenge(byte[] buf, short start, short length) {
+    if (challenge == null) {
+      challenge = new byte[16];
+    }
+    if (length != 16) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    Util.arrayCopy(buf, start, challenge, (short) 0, (short) length);
+  }
+
+  public short getChallenge(byte[] buffer, short start) {
+    if (challenge == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    Util.arrayCopyNonAtomic(challenge, (short) 0, buffer, start, (short) challenge.length);
+    return (short) challenge.length;
+  }
+
+  public boolean isProvisionLocked() {
+    if (0 != (provisionStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) {
+      return true;
+    }
+    return false;
+  }
+
+  public short getProvisionStatus() {
+    return provisionStatus;
+  }
+
+  public void setProvisionStatus(short pStatus) {
+    JCSystem.beginTransaction();
+    provisionStatus |= pStatus;
+    JCSystem.commitTransaction();
+  }
+
+  public void unlockProvision() {
+    JCSystem.beginTransaction();
+    provisionStatus &= ~KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED;
+    JCSystem.commitTransaction();
+  }
+
+  public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) {
+    if (inLength != 65) {
+      KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+    }
+    if (oemRootPublicKey == null) {
+      oemRootPublicKey = new byte[65];
+    }
+    Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength);
+  }
+
+  public byte[] getOEMRootPublicKey() {
+    if (oemRootPublicKey == null) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    return oemRootPublicKey;
+  }
+
+  @Override
+  public void onSave(Element element) {
+    // Prmitives
+    element.write(provisionStatus);
+    element.write(secureBootMode);
+    // Objects
+    element.write(attIdBrand);
+    element.write(attIdDevice);
+    element.write(attIdProduct);
+    element.write(attIdSerial);
+    element.write(attIdImei);
+    element.write(attIdMeId);
+    element.write(attIdManufacturer);
+    element.write(attIdModel);
+    element.write(additionalCertChain);
+    element.write(bcc);
+    element.write(oemRootPublicKey);
+
+    // Key Objects
+    seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, masterKey);
+    seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY, preSharedKey);
+    seProvider.onSave(
+        element, KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR, deviceUniqueKeyPair);
+    seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY, rkpMacKey);
+  }
+
+  @Override
+  public void onRestore(Element element, short oldVersion, short currentVersion) {
+    if (oldVersion <= KM_APPLET_PACKAGE_VERSION_1) {
+      // 1.0 to 3.0 Upgrade happens here.
+      handlePreviousVersionUpgrade(element);
+      return;
+    } else if (oldVersion == KM_APPLET_PACKAGE_VERSION_2) {
+      handleUpgrade(element, oldVersion);
+      JCSystem.beginTransaction();
+      // While upgrading Secure Boot Mode flag from 2.0 to 3.0, implementations
+      // have to update the secureBootMode with the correct input.
+      secureBootMode = 0;
+      provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE;
+      // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain.
+      // From package version 3, the additionalCertificateChain is in X.509 format.
+      // So Unreference the old address and allocate new persistent memory.
+      additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE];
+      JCSystem.commitTransaction();
+      // Request for ObjectDeletion for unreferenced address of additionalCertChain.
+      JCSystem.requestObjectDeletion();
+      return;
+    }
+    handleUpgrade(element, oldVersion);
+  }
+
+  private void handlePreviousVersionUpgrade(Element element) {
+    // Read Primitives
+    // restore old data table index
+    short oldDataIndex = element.readShort();
+    element.readBoolean(); // pop deviceBootLocked
+    element.readShort(); // pop bootState
+
+    // Read Objects
+    // restore old data table
+    byte[] oldDataTable = (byte[]) element.readObject();
+
+    attIdBrand = (byte[]) element.readObject();
+    attIdDevice = (byte[]) element.readObject();
+    attIdProduct = (byte[]) element.readObject();
+    attIdSerial = (byte[]) element.readObject();
+    attIdImei = (byte[]) element.readObject();
+    attIdMeId = (byte[]) element.readObject();
+    attIdManufacturer = (byte[]) element.readObject();
+    attIdModel = (byte[]) element.readObject();
+    element.readObject(); // pop verifiedHash
+    element.readObject(); // pop bootKey
+    element.readObject(); // pop bootPatchLevel
+    additionalCertChain = (byte[]) element.readObject();
+    bcc = (byte[]) element.readObject();
+
+    // Read Key Objects
+    masterKey = (KMMasterKey) seProvider.onRestore(element);
+    seProvider.onRestore(element); // pop computedHmacKey
+    preSharedKey = (KMPreSharedKey) seProvider.onRestore(element);
+    deviceUniqueKeyPair = (KMDeviceUniqueKeyPair) seProvider.onRestore(element);
+    rkpMacKey = (KMRkpMacKey) seProvider.onRestore(element);
+    handleProvisionStatusUpgrade(oldDataTable, oldDataIndex);
+  }
+
+  private void handleUpgrade(Element element, short oldVersion) {
+    // Read Primitives
+    provisionStatus = element.readShort();
+    if (oldVersion >= KM_APPLET_PACKAGE_VERSION_3) {
+      secureBootMode = element.readByte();
+    }
+    // Read Objects
+    attIdBrand = (byte[]) element.readObject();
+    attIdDevice = (byte[]) element.readObject();
+    attIdProduct = (byte[]) element.readObject();
+    attIdSerial = (byte[]) element.readObject();
+    attIdImei = (byte[]) element.readObject();
+    attIdMeId = (byte[]) element.readObject();
+    attIdManufacturer = (byte[]) element.readObject();
+    attIdModel = (byte[]) element.readObject();
+    additionalCertChain = (byte[]) element.readObject();
+    bcc = (byte[]) element.readObject();
+    oemRootPublicKey = (byte[]) element.readObject();
+    // Read Key Objects
+    masterKey = (KMMasterKey) seProvider.onRestore(element);
+    preSharedKey = (KMPreSharedKey) seProvider.onRestore(element);
+    deviceUniqueKeyPair = (KMDeviceUniqueKeyPair) seProvider.onRestore(element);
+    rkpMacKey = (KMRkpMacKey) seProvider.onRestore(element);
+  }
+
+  public void getProvisionStatus(byte[] dataTable, byte[] scratchpad, short offset) {
+    Util.setShort(scratchpad, offset, (short) 0);
+    readDataEntry(dataTable, OLD_PROVISIONED_STATUS_OFFSET, scratchpad, offset);
+  }
+
+  void handleProvisionStatusUpgrade(byte[] dataTable, short dataTableIndex) {
+    short dInex = repository.allocReclaimableMemory((short) 2);
+    byte data[] = repository.getHeap();
+    getProvisionStatus(dataTable, data, dInex);
+    short pStatus = (short) (data[dInex] & 0x00ff);
+    if (KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED
+        == (pStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) {
+      pStatus |=
+          KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED
+              | KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE;
+    }
+    JCSystem.beginTransaction();
+    // While upgrading Secure Boot Mode flag from 1.0 to 3.0, implementations
+    // have to update the secureBootMode with the correct input.
+    secureBootMode = 0;
+    provisionStatus = pStatus;
+    // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain.
+    // From package version 3, the additionalCertificateChain is in X.509 format.
+    // So Unreference the old address and allocate new persistent memory.
+    additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE];
+    JCSystem.commitTransaction();
+    repository.reclaimMemory((short) 2);
+    // Request object deletion for unreferenced address for additionalCertChain
+    JCSystem.requestObjectDeletion();
+  }
+
+  @Override
+  public short getBackupPrimitiveByteCount() {
+    // provisionStatus - 2 bytes
+    // secureBootMode - 1 byte
+    return (short)
+        (3
+            + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY)
+            + seProvider.getBackupPrimitiveByteCount(
+                KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY)
+            + seProvider.getBackupPrimitiveByteCount(
+                KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR)
+            + seProvider.getBackupPrimitiveByteCount(
+                KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY));
+  }
+
+  @Override
+  public short getBackupObjectCount() {
+    // AttestationIds - 8
+    // AdditionalCertificateChain - 1
+    // BCC - 1
+    // oemRootPublicKey - 1
+    return (short)
+        (11
+            + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY)
+            + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY)
+            + seProvider.getBackupObjectCount(
+                KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR)
+            + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMMap.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMMap.java
new file mode 100644
index 0000000..4583e02
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMMap.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+public class KMMap extends KMType {
+
+  public static final short ANY_MAP_LENGTH = 0x1000;
+  private static final byte MAP_HEADER_SIZE = 4;
+  private static KMMap prototype;
+
+  private KMMap() {}
+
+  private static KMMap proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMMap();
+    }
+    instanceTable[KM_MAP_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp() {
+    short ptr = instance(MAP_TYPE, (short) MAP_HEADER_SIZE);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_MAP_LENGTH);
+    return ptr;
+  }
+
+  public static short instance(short length) {
+    short ptr = KMType.instance(MAP_TYPE, (short) (MAP_HEADER_SIZE + (length * 4)));
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length);
+    return ptr;
+  }
+
+  public static short instance(short length, byte type) {
+    short ptr = instance(length);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type);
+    return ptr;
+  }
+
+  public static KMMap cast(short ptr) {
+    if (heap[ptr] != MAP_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public void add(short index, short keyPtr, short valPtr) {
+    short len = length();
+    if (index >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    short keyIndex =
+        (short)
+            (instanceTable[KM_MAP_OFFSET]
+                + TLV_HEADER_SIZE
+                + MAP_HEADER_SIZE
+                + (short) (index * 4));
+    Util.setShort(heap, keyIndex, keyPtr);
+    Util.setShort(heap, (short) (keyIndex + 2), valPtr);
+  }
+
+  public short getKey(short index) {
+    short len = length();
+    if (index >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    return Util.getShort(
+        heap,
+        (short)
+            (instanceTable[KM_MAP_OFFSET]
+                + TLV_HEADER_SIZE
+                + MAP_HEADER_SIZE
+                + (short) (index * 4)));
+  }
+
+  public short getKeyValue(short index) {
+    short len = length();
+    if (index >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    return Util.getShort(
+        heap,
+        (short)
+            (instanceTable[KM_MAP_OFFSET]
+                + TLV_HEADER_SIZE
+                + MAP_HEADER_SIZE
+                + (short) (index * 4 + 2)));
+  }
+
+  public void swap(short index1, short index2) {
+    short len = length();
+    if (index1 >= len || index2 >= len) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    // Swap keys
+    short indexPtr1 =
+        Util.getShort(
+            heap,
+            (short)
+                (instanceTable[KM_MAP_OFFSET]
+                    + TLV_HEADER_SIZE
+                    + MAP_HEADER_SIZE
+                    + (short) (index1 * 4)));
+    short indexPtr2 =
+        Util.getShort(
+            heap,
+            (short)
+                (instanceTable[KM_MAP_OFFSET]
+                    + TLV_HEADER_SIZE
+                    + MAP_HEADER_SIZE
+                    + (short) (index2 * 4)));
+    Util.setShort(
+        heap,
+        (short)
+            (instanceTable[KM_MAP_OFFSET]
+                + TLV_HEADER_SIZE
+                + MAP_HEADER_SIZE
+                + (short) (index1 * 4)),
+        indexPtr2);
+    Util.setShort(
+        heap,
+        (short)
+            (instanceTable[KM_MAP_OFFSET]
+                + TLV_HEADER_SIZE
+                + MAP_HEADER_SIZE
+                + (short) (index2 * 4)),
+        indexPtr1);
+
+    // Swap Values
+    indexPtr1 =
+        Util.getShort(
+            heap,
+            (short)
+                (instanceTable[KM_MAP_OFFSET]
+                    + TLV_HEADER_SIZE
+                    + MAP_HEADER_SIZE
+                    + (short) (index1 * 4 + 2)));
+    indexPtr2 =
+        Util.getShort(
+            heap,
+            (short)
+                (instanceTable[KM_MAP_OFFSET]
+                    + TLV_HEADER_SIZE
+                    + MAP_HEADER_SIZE
+                    + (short) (index2 * 4 + 2)));
+    Util.setShort(
+        heap,
+        (short)
+            (instanceTable[KM_MAP_OFFSET]
+                + TLV_HEADER_SIZE
+                + MAP_HEADER_SIZE
+                + (short) (index1 * 4 + 2)),
+        indexPtr2);
+    Util.setShort(
+        heap,
+        (short)
+            (instanceTable[KM_MAP_OFFSET]
+                + TLV_HEADER_SIZE
+                + MAP_HEADER_SIZE
+                + (short) (index2 * 4 + 2)),
+        indexPtr1);
+  }
+
+  public void canonicalize() {
+    KMCoseMap.canonicalize(instanceTable[KM_MAP_OFFSET], length());
+  }
+
+  public short containedType() {
+    return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short getStartOff() {
+    return (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE);
+  }
+
+  public short length() {
+    return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public byte[] getBuffer() {
+    return heap;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMNInteger.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMNInteger.java
new file mode 100644
index 0000000..6246f21
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMNInteger.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+public class KMNInteger extends KMInteger {
+
+  public static final byte SIGNED_MASK = (byte) 0x80;
+  private static KMNInteger prototype;
+
+  private KMNInteger() {}
+
+  private static KMNInteger proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMNInteger();
+    }
+    instanceTable[KM_NEG_INTEGER_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short exp() {
+    return KMType.exp(NEG_INTEGER_TYPE);
+  }
+
+  // return an empty integer instance
+  public static short instance(short length) {
+    if ((length <= 0) || (length > 8)) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    if (length > 4) {
+      length = KMInteger.UINT_64;
+    } else {
+      length = KMInteger.UINT_32;
+    }
+    return KMType.instance(NEG_INTEGER_TYPE, length);
+  }
+
+  public static short instance(byte[] num, short srcOff, short length) {
+    if (length > 8) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    if (length == 1) {
+      return uint_8(num[srcOff]);
+    } else if (length == 2) {
+      return uint_16(Util.getShort(num, srcOff));
+    } else if (length == 4) {
+      return uint_32(num, srcOff);
+    } else {
+      return uint_64(num, srcOff);
+    }
+  }
+
+  public static KMNInteger cast(short ptr) {
+    byte[] heap = repository.getHeap();
+    if (heap[ptr] != NEG_INTEGER_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  // create integer and copy byte value
+  public static short uint_8(byte num) {
+    if (num >= 0) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(KMInteger.UINT_32);
+    heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num;
+    return ptr;
+  }
+
+  // create integer and copy short value
+  public static short uint_16(short num) {
+    if (num >= 0) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(KMInteger.UINT_32);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num);
+    return ptr;
+  }
+
+  // create integer and copy integer value
+  public static short uint_32(byte[] num, short offset) {
+    if (!isSignedInteger(num, offset)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(KMInteger.UINT_32);
+    Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_32);
+    return ptr;
+  }
+
+  // create integer and copy integer value
+  public static short uint_64(byte[] num, short offset) {
+    if (!isSignedInteger(num, offset)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = instance(KMInteger.UINT_64);
+    Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_64);
+    return ptr;
+  }
+
+  public static boolean isSignedInteger(byte[] num, short offset) {
+    byte val = num[offset];
+    return SIGNED_MASK == (val & SIGNED_MASK);
+  }
+
+  @Override
+  protected short getBaseOffset() {
+    return instanceTable[KM_NEG_INTEGER_OFFSET];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java
new file mode 100644
index 0000000..2a53acd
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMOperationState.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMOperation;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * KMOperationState is the container of an active operation started by beginOperation function. This
+ * operation state is persisted by the applet in non volatile memory. However, this state is not
+ * retained if applet is upgraded. There will be four operation state records maintained i.e. only
+ * four active operations are supported at any given time.
+ */
+public class KMOperationState {
+
+  // sizes
+  public static final byte OPERATION_HANDLE_SIZE = 8;
+  public static final byte DATA_SIZE = 11;
+  public static final byte AUTH_TIME_SIZE = 8;
+  // Secure user ids 5 * 8 = 40 bytes ( Considering Maximum 5 SECURE USER IDs)
+  // First two bytes are reserved to store number of secure ids. So total 42 bytes.
+  public static final byte USER_SECURE_IDS_SIZE = 42;
+  // byte type
+  private static final byte ALG = 0;
+  private static final byte PURPOSE = 1;
+  private static final byte PADDING = 2;
+  private static final byte BLOCK_MODE = 3;
+  private static final byte DIGEST = 4;
+  private static final byte FLAGS = 5;
+  private static final byte KEY_SIZE = 6;
+  private static final byte MAC_LENGTH = 7;
+  private static final byte MGF_DIGEST = 8;
+  private static final byte AUTH_TYPE = 9;
+  private static final byte MIN_MAC_LENGTH = 10;
+  private static final byte OPERATION = 0;
+  private static final byte HMAC_SIGNER_OPERATION = 1;
+  // Flag masks
+  private static final byte AUTH_PER_OP_REQD = 1;
+  private static final byte SECURE_USER_ID_REQD = 2;
+  private static final byte AUTH_TIMEOUT_VALIDATED = 4;
+  private static final byte AES_GCM_UPDATE_ALLOWED = 8;
+  private static final byte PROCESSED_INPUT_MSG = 16;
+  // Max user secure ids.
+  private static final byte MAX_SECURE_USER_IDS = 5;
+
+  // Object References
+  private byte[] opHandle;
+  private byte[] authTime;
+  private byte[] userSecureIds;
+  private short[] data;
+  private Object[] operations;
+
+  public KMOperationState() {
+    opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET);
+    authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET);
+    data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET);
+    operations = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET);
+    userSecureIds = JCSystem.makeTransientByteArray(USER_SECURE_IDS_SIZE, JCSystem.CLEAR_ON_RESET);
+    reset();
+  }
+
+  public void reset() {
+    byte index = 0;
+    while (index < DATA_SIZE) {
+      data[index] = KMType.INVALID_VALUE;
+      index++;
+    }
+    Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0);
+    Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0);
+
+    if (null != operations[OPERATION]) {
+      ((KMOperation) operations[OPERATION]).abort();
+    }
+    operations[OPERATION] = null;
+
+    if (null != operations[HMAC_SIGNER_OPERATION]) {
+      ((KMOperation) operations[HMAC_SIGNER_OPERATION]).abort();
+    }
+    operations[HMAC_SIGNER_OPERATION] = null;
+  }
+
+  public short compare(byte[] handle, short start, short len) {
+    return Util.arrayCompare(handle, start, opHandle, (short) 0, (short) opHandle.length);
+  }
+
+  public short getKeySize() {
+    return data[KEY_SIZE];
+  }
+
+  public void setKeySize(short keySize) {
+    data[KEY_SIZE] = keySize;
+  }
+
+  public short getHandle() {
+    return KMInteger.uint_64(opHandle, (short) 0);
+  }
+
+  public void setHandle(byte[] buf, short start, short len) {
+    Util.arrayCopyNonAtomic(buf, start, opHandle, (short) 0, (short) opHandle.length);
+  }
+
+  public short getPurpose() {
+    return data[PURPOSE];
+  }
+
+  public void setPurpose(short purpose) {
+    data[PURPOSE] = purpose;
+  }
+
+  public boolean isInputMsgProcessed() {
+    return (data[FLAGS] & PROCESSED_INPUT_MSG) != 0;
+  }
+
+  public KMOperation getOperation() {
+    return (KMOperation) operations[OPERATION];
+  }
+
+  public void setOperation(KMOperation op) {
+    operations[OPERATION] = op;
+  }
+
+  public boolean isAuthPerOperationReqd() {
+    return (data[FLAGS] & AUTH_PER_OP_REQD) != 0;
+  }
+
+  public void setAuthPerOperationReqd(boolean flag) {
+    if (flag) {
+      data[FLAGS] = (short) (data[FLAGS] | AUTH_PER_OP_REQD);
+    } else {
+      data[FLAGS] = (short) (data[FLAGS] & (~AUTH_PER_OP_REQD));
+    }
+  }
+
+  public boolean isAuthTimeoutValidated() {
+    return (data[FLAGS] & AUTH_TIMEOUT_VALIDATED) != 0;
+  }
+
+  public void setAuthTimeoutValidated(boolean flag) {
+    if (flag) {
+      data[FLAGS] = (byte) (data[FLAGS] | AUTH_TIMEOUT_VALIDATED);
+    } else {
+      data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED));
+    }
+  }
+
+  public boolean isSecureUserIdReqd() {
+    return (data[FLAGS] & SECURE_USER_ID_REQD) != 0;
+  }
+
+  public short getAuthTime() {
+    return KMInteger.uint_64(authTime, (short) 0);
+  }
+
+  public void setAuthTime(byte[] timeBuf, short start) {
+    Util.arrayCopyNonAtomic(timeBuf, start, authTime, (short) 0, AUTH_TIME_SIZE);
+  }
+
+  public void setProcessedInputMsg(boolean flag) {
+    if (flag) {
+      data[FLAGS] = (byte) (data[FLAGS] | PROCESSED_INPUT_MSG);
+    } else {
+      data[FLAGS] = (byte) (data[FLAGS] & (~PROCESSED_INPUT_MSG));
+    }
+  }
+
+  public void setOneTimeAuthReqd(boolean flag) {
+    if (flag) {
+      data[FLAGS] = (short) (data[FLAGS] | SECURE_USER_ID_REQD);
+    } else {
+      data[FLAGS] = (short) (data[FLAGS] & (~SECURE_USER_ID_REQD));
+    }
+  }
+
+  public short getAuthType() {
+    return data[AUTH_TYPE];
+  }
+
+  public void setAuthType(byte authType) {
+    data[AUTH_TYPE] = authType;
+  }
+
+  public short getUserSecureId() {
+    short offset = 0;
+    short length = Util.getShort(userSecureIds, offset);
+    offset += 2;
+    if (length == 0) {
+      return KMType.INVALID_VALUE;
+    }
+    short arrObj = KMArray.instance(length);
+    short index = 0;
+    short obj;
+    while (index < length) {
+      obj = KMInteger.instance(userSecureIds, (short) (offset + index * 8), (short) 8);
+      KMArray.cast(arrObj).add(index, obj);
+      index++;
+    }
+    return KMIntegerArrayTag.instance(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, arrObj);
+  }
+
+  public void setUserSecureId(short integerArrayPtr) {
+    short length = KMIntegerArrayTag.cast(integerArrayPtr).length();
+    if (length > MAX_SECURE_USER_IDS) {
+      KMException.throwIt(KMError.INVALID_KEY_BLOB);
+    }
+    Util.arrayFillNonAtomic(userSecureIds, (short) 0, USER_SECURE_IDS_SIZE, (byte) 0);
+    short index = 0;
+    short obj;
+    short offset = 0;
+    offset = Util.setShort(userSecureIds, offset, length);
+    while (index < length) {
+      obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index);
+      Util.arrayCopyNonAtomic(
+          KMInteger.cast(obj).getBuffer(),
+          KMInteger.cast(obj).getStartOff(),
+          userSecureIds,
+          (short) (8 - KMInteger.cast(obj).length() + offset + 8 * index),
+          KMInteger.cast(obj).length());
+      index++;
+    }
+  }
+
+  public short getAlgorithm() {
+    return data[ALG];
+  }
+
+  public void setAlgorithm(short algorithm) {
+    data[ALG] = algorithm;
+  }
+
+  public short getPadding() {
+    return data[PADDING];
+  }
+
+  public void setPadding(short padding) {
+    data[PADDING] = padding;
+  }
+
+  public short getBlockMode() {
+    return data[BLOCK_MODE];
+  }
+
+  public void setBlockMode(short blockMode) {
+    data[BLOCK_MODE] = blockMode;
+  }
+
+  public short getDigest() {
+    return data[DIGEST];
+  }
+
+  public void setDigest(byte digest) {
+    data[DIGEST] = digest;
+  }
+
+  public short getMgfDigest() {
+    return data[MGF_DIGEST];
+  }
+
+  public void setMgfDigest(byte mgfDigest) {
+    data[MGF_DIGEST] = mgfDigest;
+  }
+
+  public boolean isAesGcmUpdateAllowed() {
+    return (data[FLAGS] & AES_GCM_UPDATE_ALLOWED) != 0;
+  }
+
+  public void setAesGcmUpdateComplete() {
+    data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED));
+  }
+
+  public void setAesGcmUpdateStart() {
+    data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED);
+  }
+
+  public short getMinMacLength() {
+    return data[MIN_MAC_LENGTH];
+  }
+
+  public void setMinMacLength(short length) {
+    data[MIN_MAC_LENGTH] = length;
+  }
+
+  public short getMacLength() {
+    return data[MAC_LENGTH];
+  }
+
+  public void setMacLength(short length) {
+    data[MAC_LENGTH] = length;
+  }
+
+  public byte getBufferingMode() {
+    short alg = getAlgorithm();
+    short purpose = getPurpose();
+    short digest = getDigest();
+    short padding = getPadding();
+    short blockMode = getBlockMode();
+
+    if (alg == KMType.RSA
+        && ((digest == KMType.DIGEST_NONE && purpose == KMType.SIGN)
+            || purpose == KMType.DECRYPT)) {
+      return KMType.BUF_RSA_DECRYPT_OR_NO_DIGEST;
+    }
+
+    if (alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) {
+      return KMType.BUF_EC_NO_DIGEST;
+    }
+
+    switch (alg) {
+      case KMType.AES:
+        if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) {
+          return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN;
+        } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) {
+          return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN;
+        } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) {
+          return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN;
+        }
+        break;
+      case KMType.DES:
+        if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) {
+          return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN;
+        } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) {
+          return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN;
+        }
+    }
+    return KMType.BUF_NONE;
+  }
+
+  public KMOperation getTrustedConfirmationSigner() {
+    return (KMOperation) operations[HMAC_SIGNER_OPERATION];
+  }
+
+  public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) {
+    operations[HMAC_SIGNER_OPERATION] = hmacSignerOp;
+  }
+
+  public boolean isTrustedConfirmationRequired() {
+    return operations[HMAC_SIGNER_OPERATION] != null;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRepository.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRepository.java
new file mode 100644
index 0000000..dec52eb
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMRepository.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * KMRepository class manages volatile memory usage by the applet. Note the repository is only used
+ * by applet and it is not intended to be used by seProvider.
+ */
+public class KMRepository {
+
+  public static final short HEAP_SIZE = 10000;
+  private static short[] reclaimIndex;
+  // Singleton instance
+  private static KMRepository repository;
+  // Class Attributes
+  private byte[] heap;
+  private short[] heapIndex;
+
+  public KMRepository(boolean isUpgrading) {
+    heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET);
+    heapIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+    reclaimIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+    reclaimIndex[0] = HEAP_SIZE;
+    repository = this;
+  }
+
+  public static KMRepository instance() {
+    return repository;
+  }
+
+  public void onUninstall() {
+    // Javacard Runtime environment cleans up the data.
+
+  }
+
+  public void onProcess() {}
+
+  public void clean() {
+    Util.arrayFillNonAtomic(heap, (short) 0, HEAP_SIZE, (byte) 0);
+    heapIndex[0] = 0;
+    reclaimIndex[0] = HEAP_SIZE;
+  }
+
+  public void onDeselect() {}
+
+  public void onSelect() {
+    // If write through caching is implemented then this method will restore the data into cache
+  }
+
+  // This function uses memory from the back of the heap(transient memory). Call
+  // reclaimMemory function immediately after the use.
+  public short allocReclaimableMemory(short length) {
+    if ((((short) (reclaimIndex[0] - length)) <= heapIndex[0]) || (length >= HEAP_SIZE / 2)) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    reclaimIndex[0] -= length;
+    return reclaimIndex[0];
+  }
+
+  // Reclaims the memory back.
+  public void reclaimMemory(short length) {
+    if (reclaimIndex[0] < heapIndex[0]) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    Util.arrayFillNonAtomic(heap, reclaimIndex[0], length, (byte) 0);
+    reclaimIndex[0] += length;
+  }
+
+  public short allocAvailableMemory() {
+    if (heapIndex[0] >= heap.length) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short index = heapIndex[0];
+    heapIndex[0] = reclaimIndex[0];
+    return index;
+  }
+
+  public short alloc(short length) {
+    if ((((short) (heapIndex[0] + length)) > heap.length)
+        || (((short) (heapIndex[0] + length)) > reclaimIndex[0])) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    heapIndex[0] += length;
+    return (short) (heapIndex[0] - length);
+  }
+
+  public byte[] getHeap() {
+    return heap;
+  }
+
+  public short getHeapIndex() {
+    return heapIndex[0];
+  }
+
+  // Use this function to reset the heapIndex to its previous state.
+  // Some of the data might be lost so use it carefully.
+  public void setHeapIndex(short offset) {
+    if (offset > heapIndex[0] || offset < 0) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    Util.arrayFillNonAtomic(heap, offset, (short) (heapIndex[0] - offset), (byte) 0);
+    heapIndex[0] = offset;
+  }
+
+  public short getHeapReclaimIndex() {
+    return reclaimIndex[0];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java
new file mode 100644
index 0000000..6165c31
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSemanticTag.java
@@ -0,0 +1,75 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+public class KMSemanticTag extends KMType {
+
+  public static final short COSE_MAC_SEMANTIC_TAG = (short) 0x0011;
+  public static final short ROT_SEMANTIC_TAG = (short) 0x9C41;
+  private static KMSemanticTag prototype;
+
+  private KMSemanticTag() {}
+
+  private static KMSemanticTag proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMSemanticTag();
+    }
+    instanceTable[KM_SEMANTIC_TAG_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp(short valuePtr) {
+    short ptr = KMType.instance(SEMANTIC_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMInteger.exp());
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr);
+    return ptr;
+  }
+
+  public static KMSemanticTag cast(short ptr) {
+    if (heap[ptr] != SEMANTIC_TAG_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static short instance(short tag, short value) {
+    if (!isSemanticTagSupported(tag)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    // The maximum tag size can be UINT32. Currently, we support
+    // only two tags which are short.
+    short ptr = KMType.instance(SEMANTIC_TAG_TYPE, (short) 6);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), tag);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), value);
+    return ptr;
+  }
+
+  private static boolean isSemanticTagSupported(short tag) {
+    tag = KMInteger.cast(tag).getShort();
+    switch (tag) {
+      case COSE_MAC_SEMANTIC_TAG:
+      case ROT_SEMANTIC_TAG:
+        break;
+      default:
+        return false;
+    }
+    return true;
+  }
+
+  public short length() {
+    return Util.getShort(heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + 1));
+  }
+
+  public short getKeyPtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + TLV_HEADER_SIZE + 2));
+  }
+
+  public short getValuePtr() {
+    return Util.getShort(
+        heap, (short) (instanceTable[KM_SEMANTIC_TAG_OFFSET] + TLV_HEADER_SIZE + 4));
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java
new file mode 100644
index 0000000..14b7bb8
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMSimpleValue.java
@@ -0,0 +1,67 @@
+package com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+public class KMSimpleValue extends KMType {
+
+  public static final byte FALSE = (byte) 20;
+  public static final byte TRUE = (byte) 21;
+  public static final byte NULL = (byte) 22;
+  private static KMSimpleValue prototype;
+
+  private KMSimpleValue() {}
+
+  private static KMSimpleValue proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMSimpleValue();
+    }
+    instanceTable[KM_SIMPLE_VALUE_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    return KMType.exp(SIMPLE_VALUE_TYPE);
+  }
+
+  public static KMSimpleValue cast(short ptr) {
+    if (heap[ptr] != SIMPLE_VALUE_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (!isSimpleValueValid(heap[(short) (ptr + 3)])) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public static short instance(byte value) {
+    if (!isSimpleValueValid(value)) {
+      ISOException.throwIt(ISO7816.SW_DATA_INVALID);
+    }
+    short ptr = KMType.instance(SIMPLE_VALUE_TYPE, (short) 1);
+    heap[(short) (ptr + 3)] = value;
+    return ptr;
+  }
+
+  private static boolean isSimpleValueValid(byte value) {
+    switch (value) {
+      case TRUE:
+      case FALSE:
+      case NULL:
+        break;
+      default:
+        return false;
+    }
+    return true;
+  }
+
+  public short length() {
+    return Util.getShort(heap, (short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 1));
+  }
+
+  public byte getValue() {
+    return heap[(short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 3)];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTag.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTag.java
new file mode 100644
index 0000000..3033a70
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTag.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMException;
+import javacard.framework.Util;
+
+/**
+ * This class represents a tag as defined by keymaster hal specifications. It is composed of key
+ * value pair. The key consists of short tag type e.g. KMType.ENUM and short tag key e.g.
+ * KMType.ALGORITHM. The key is encoded as uint CBOR type with 4 bytes. This is followed by value
+ * which can be any CBOR type based on key. struct{byte tag=KMType.TAG_TYPE, short length, value)
+ * where value is subtype of KMTag i.e. struct{short tagType=one of tag types declared in KMType ,
+ * short tagKey=one of the tag keys declared in KMType, value} where value is one of the sub-types
+ * of KMType.
+ */
+public class KMTag extends KMType {
+
+  public static short getTagType(short ptr) {
+    return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+  }
+
+  public static short getKey(short ptr) {
+    return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2));
+  }
+
+  public static void assertPresence(short params, short tagType, short tagKey, short error) {
+    if (!isPresent(params, tagType, tagKey)) {
+      KMException.throwIt(error);
+    }
+  }
+
+  public static void assertAbsence(short params, short tagType, short tagKey, short error) {
+    if (isPresent(params, tagType, tagKey)) {
+      KMException.throwIt(error);
+    }
+  }
+
+  public static boolean isPresent(short params, short tagType, short tagKey) {
+    short tag = KMKeyParameters.findTag(tagType, tagKey, params);
+    return tag != KMType.INVALID_VALUE;
+  }
+
+  public static boolean isEqual(short params, short tagType, short tagKey, short value) {
+    switch (tagType) {
+      case KMType.ENUM_TAG:
+        return KMEnumTag.getValue(tagKey, params) == value;
+      case KMType.UINT_TAG:
+      case KMType.DATE_TAG:
+      case KMType.ULONG_TAG:
+        return KMIntegerTag.isEqual(params, tagType, tagKey, value);
+      case KMType.ENUM_ARRAY_TAG:
+        return KMEnumArrayTag.contains(tagKey, value, params);
+      case KMType.UINT_ARRAY_TAG:
+      case KMType.ULONG_ARRAY_TAG:
+        return KMIntegerArrayTag.contains(tagKey, value, params);
+    }
+    return false;
+  }
+
+  public static void assertTrue(boolean condition, short error) {
+    if (!condition) {
+      KMException.throwIt(error);
+    }
+  }
+
+  public static boolean isValidPublicExponent(short params) {
+    short pubExp = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, params);
+    if (pubExp == KMType.INVALID_VALUE) {
+      return false;
+    }
+    pubExp = KMIntegerTag.cast(pubExp).getValue();
+    if (!(KMInteger.cast(pubExp).getShort() == 0x01
+        && KMInteger.cast(pubExp).getSignificantShort() == 0x01)) {
+      return false;
+    }
+    return true;
+  }
+
+  public static boolean isValidKeySize(short params) {
+    short keysize = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, params);
+    if (keysize == KMType.INVALID_VALUE) {
+      return false;
+    }
+    short alg = KMEnumTag.getValue(KMType.ALGORITHM, params);
+    return KMIntegerTag.cast(keysize).isValidKeySize((byte) alg);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTextString.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTextString.java
new file mode 100644
index 0000000..80aebd2
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMTextString.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMTextString represents contiguous block of bytes. It corresponds to CBOR type of Text String. It
+ * extends KMByteBlob by specifying value field as zero or more sequence of bytes. struct{ byte
+ * TEXT_STR_TYPE; short length; sequence of bytes}
+ */
+public class KMTextString extends KMByteBlob {
+
+  private static byte OFFSET_SIZE = 2;
+
+  private static KMTextString prototype;
+
+  private KMTextString() {}
+
+  private static KMTextString proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMTextString();
+    }
+    instanceTable[KM_TEXT_STRING_OFFSET] = ptr;
+    return prototype;
+  }
+
+  // pointer to an empty instance used as expression
+  public static short exp() {
+    return KMType.exp(TEXT_STRING_TYPE);
+  }
+
+  // return an empty byte blob instance
+  public static short instance(short length) {
+    short ptr = KMType.instance(TEXT_STRING_TYPE, (short) (length + OFFSET_SIZE));
+    Util.setShort(
+        heap, (short) (ptr + TLV_HEADER_SIZE), (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE));
+    Util.setShort(heap, (short) (ptr + 1), length);
+    return ptr;
+  }
+
+  // byte blob from existing buf
+  public static short instance(byte[] buf, short startOff, short length) {
+    short ptr = instance(length);
+    Util.arrayCopyNonAtomic(
+        buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), length);
+    return ptr;
+  }
+
+  // cast the ptr to KMTextString
+  public static KMTextString cast(short ptr) {
+    if (heap[ptr] != TEXT_STRING_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  protected short getBaseOffset() {
+    return instanceTable[KM_TEXT_STRING_OFFSET];
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java
new file mode 100644
index 0000000..a18cb7f
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMType.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/**
+ * This class declares all types, tag types, and tag keys. It also establishes basic structure of
+ * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also,
+ * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton
+ * prototype objects which just cast the structure over contiguous memory buffer.
+ */
+public abstract class KMType {
+
+  public static final short INVALID_VALUE = (short) 0x8000;
+  // Types
+  public static final byte BYTE_BLOB_TYPE = 0x01;
+  public static final byte INTEGER_TYPE = 0x02;
+  public static final byte ENUM_TYPE = 0x03;
+  public static final byte TAG_TYPE = 0x04;
+  public static final byte ARRAY_TYPE = 0x05;
+  public static final byte KEY_PARAM_TYPE = 0x06;
+  public static final byte KEY_CHAR_TYPE = 0x07;
+  public static final byte HW_AUTH_TOKEN_TYPE = 0x08;
+  public static final byte VERIFICATION_TOKEN_TYPE = 0x09;
+  public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A;
+  public static final byte X509_CERT = 0x0B;
+  public static final byte NEG_INTEGER_TYPE = 0x0C;
+  public static final byte TEXT_STRING_TYPE = 0x0D;
+  public static final byte MAP_TYPE = 0x0E;
+  public static final byte COSE_KEY_TYPE = 0x0F;
+  public static final byte COSE_PAIR_TAG_TYPE = 0x10;
+  public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20;
+  public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30;
+  public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40;
+  public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60;
+  public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70;
+  public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80;
+  public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90;
+  public static final byte COSE_HEADERS_TYPE = (byte) 0xA0;
+  public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0;
+  public static final byte SEMANTIC_TAG_TYPE = (byte) 0xC0;
+  // Tag Types
+  public static final short INVALID_TAG = 0x0000;
+  public static final short ENUM_TAG = 0x1000;
+  public static final short ENUM_ARRAY_TAG = 0x2000;
+  public static final short UINT_TAG = 0x3000;
+  public static final short UINT_ARRAY_TAG = 0x4000;
+  public static final short ULONG_TAG = 0x5000;
+  public static final short DATE_TAG = 0x6000;
+  public static final short BOOL_TAG = 0x7000;
+  public static final short BIGNUM_TAG = (short) 0x8000;
+  public static final short BYTES_TAG = (short) 0x9000;
+  public static final short ULONG_ARRAY_TAG = (short) 0xA000;
+  public static final short TAG_TYPE_MASK = (short) 0xF000;
+
+  // Enum Tag
+  // Internal tags
+  public static final short RULE = 0x7FFF;
+  public static final byte IGNORE_INVALID_TAGS = 0x00;
+  public static final byte FAIL_ON_INVALID_TAGS = 0x01;
+
+  // Algorithm Enum Tag key and values
+  public static final short ALGORITHM = 0x0002;
+  public static final byte RSA = 0x01;
+  public static final byte DES = 0x21;
+  public static final byte EC = 0x03;
+  public static final byte AES = 0x20;
+  public static final byte HMAC = (byte) 0x80;
+
+  // EcCurve Enum Tag key and values.
+  public static final short ECCURVE = 0x000A;
+  public static final byte P_224 = 0x00;
+  public static final byte P_256 = 0x01;
+  public static final byte P_384 = 0x02;
+  public static final byte P_521 = 0x03;
+  public static final byte CURVE_25519 = 0x04;
+
+  // KeyBlobUsageRequirements Enum Tag key and values.
+  public static final short BLOB_USAGE_REQ = 0x012D;
+  public static final byte STANDALONE = 0x00;
+  public static final byte REQUIRES_FILE_SYSTEM = 0x01;
+
+  // HardwareAuthenticatorType Enum Tag key and values.
+  public static final short USER_AUTH_TYPE = 0x01F8;
+  public static final byte USER_AUTH_NONE = 0x00;
+  public static final byte PASSWORD = 0x01;
+  public static final byte FINGERPRINT = 0x02;
+  public static final byte BOTH = 0x03;
+  // have to be power of 2
+  public static final byte ANY = (byte) 0xFF;
+
+  // Origin Enum Tag key and values.
+  public static final short ORIGIN = 0x02BE;
+  public static final byte GENERATED = 0x00;
+  public static final byte DERIVED = 0x01;
+  public static final byte IMPORTED = 0x02;
+  public static final byte UNKNOWN = 0x03;
+  public static final byte SECURELY_IMPORTED = 0x04;
+
+  // Hardware Type tag key and values
+  public static final short HARDWARE_TYPE = 0x0130;
+  public static final byte SOFTWARE = 0x00;
+  public static final byte TRUSTED_ENVIRONMENT = 0x01;
+  public static final byte STRONGBOX = 0x02;
+
+  // No Tag
+  // Derivation Function - No Tag defined
+  public static final short KEY_DERIVATION_FUNCTION = (short) 0xF001;
+  public static final byte DERIVATION_NONE = 0x00;
+  public static final byte RFC5869_SHA256 = 0x01;
+  public static final byte ISO18033_2_KDF1_SHA1 = 0x02;
+  public static final byte ISO18033_2_KDF1_SHA256 = 0x03;
+  public static final byte ISO18033_2_KDF2_SHA1 = 0x04;
+  public static final byte ISO18033_2_KDF2_SHA256 = 0x05;
+
+  // KeyFormat - No Tag defined.
+  public static final short KEY_FORMAT = (short) 0xF002;
+  public static final byte X509 = 0x00;
+  public static final byte PKCS8 = 0x01;
+  public static final byte RAW = 0x03;
+
+  // Verified Boot State
+  public static final short VERIFIED_BOOT_STATE = (short) 0xF003;
+  public static final byte VERIFIED_BOOT = 0x00;
+  public static final byte SELF_SIGNED_BOOT = 0x01;
+  public static final byte UNVERIFIED_BOOT = 0x02;
+  public static final byte FAILED_BOOT = 0x03;
+
+  // Device Locked
+  public static final short DEVICE_LOCKED = (short) 0xF006;
+  public static final byte DEVICE_LOCKED_TRUE = 0x01;
+  public static final byte DEVICE_LOCKED_FALSE = 0x00;
+
+  // Enum Array Tag
+  // Purpose
+  public static final short PURPOSE = 0x0001;
+  public static final byte ENCRYPT = 0x00;
+  public static final byte DECRYPT = 0x01;
+  public static final byte SIGN = 0x02;
+  public static final byte VERIFY = 0x03;
+  public static final byte DERIVE_KEY = 0x04;
+  public static final byte WRAP_KEY = 0x05;
+  public static final byte AGREE_KEY = 0x06;
+  public static final byte ATTEST_KEY = (byte) 0x07;
+  // Block mode
+  public static final short BLOCK_MODE = 0x0004;
+  public static final byte ECB = 0x01;
+  public static final byte CBC = 0x02;
+  public static final byte CTR = 0x03;
+  public static final byte GCM = 0x20;
+
+  // Digest
+  public static final short DIGEST = 0x0005;
+  public static final byte DIGEST_NONE = 0x00;
+  public static final byte MD5 = 0x01;
+  public static final byte SHA1 = 0x02;
+  public static final byte SHA2_224 = 0x03;
+  public static final byte SHA2_256 = 0x04;
+  public static final byte SHA2_384 = 0x05;
+  public static final byte SHA2_512 = 0x06;
+
+  // Padding mode
+  public static final short PADDING = 0x0006;
+  public static final byte PADDING_NONE = 0x01;
+  public static final byte RSA_OAEP = 0x02;
+  public static final byte RSA_PSS = 0x03;
+  public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04;
+  public static final byte RSA_PKCS1_1_5_SIGN = 0x05;
+  public static final byte PKCS7 = 0x40;
+
+  // OAEP MGF Digests - only SHA-1 is supported in Javacard
+  public static final short RSA_OAEP_MGF_DIGEST = 0xCB;
+
+  // Integer Tag - UINT, ULONG and DATE
+  // UINT tags
+  // Keysize
+  public static final short KEYSIZE = 0x0003;
+  // Min Mac Length
+  public static final short MIN_MAC_LENGTH = 0x0008;
+  // Min Seconds between OPS
+  public static final short MIN_SEC_BETWEEN_OPS = 0x0193;
+  // Max Uses per Boot
+  public static final short MAX_USES_PER_BOOT = 0x0194;
+  // UserId
+  public static final short USERID = 0x01F5;
+  // Auth Timeout
+  public static final short AUTH_TIMEOUT = 0x01F9;
+  // Auth Timeout in Milliseconds
+  public static final short AUTH_TIMEOUT_MILLIS = 0x7FFF;
+  // OS Version
+  public static final short OS_VERSION = 0x02C1;
+  // OS Patch Level
+  public static final short OS_PATCH_LEVEL = 0x02C2;
+  // Vendor Patch Level
+  public static final short VENDOR_PATCH_LEVEL = 0x02CE;
+  // Boot Patch Level
+  public static final short BOOT_PATCH_LEVEL = 0x02CF;
+  // Mac Length
+  public static final short MAC_LENGTH = 0x03EB;
+  // Usage Count Limit
+  public static final short USAGE_COUNT_LIMIT = 0x195;
+
+  // ULONG tags
+  // RSA Public Exponent
+  public static final short RSA_PUBLIC_EXPONENT = 0x00C8;
+
+  // DATE tags
+  public static final short ACTIVE_DATETIME = 0x0190;
+  public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191;
+  public static final short USAGE_EXPIRE_DATETIME = 0x0192;
+  public static final short CREATION_DATETIME = 0x02BD;
+  ;
+  public static final short CERTIFICATE_NOT_BEFORE = 0x03F0;
+  public static final short CERTIFICATE_NOT_AFTER = 0x03F1;
+  // Integer Array Tags - ULONG_REP and UINT_REP.
+  // User Secure Id
+  public static final short USER_SECURE_ID = (short) 0x01F6;
+
+  // Boolean Tag
+  // Caller Nonce
+  public static final short CALLER_NONCE = (short) 0x0007;
+  // Include Unique Id
+  public static final short INCLUDE_UNIQUE_ID = (short) 0x00CA;
+  // Bootloader Only
+  public static final short BOOTLOADER_ONLY = (short) 0x012E;
+  // Rollback Resistance
+  public static final short ROLLBACK_RESISTANCE = (short) 0x012F;
+  // No Auth Required
+  public static final short NO_AUTH_REQUIRED = (short) 0x01F7;
+  // Allow While On Body
+  public static final short ALLOW_WHILE_ON_BODY = (short) 0x01FA;
+  // Max Boot Level
+  public static final short MAX_BOOT_LEVEL = (short) 0x03F2;
+  // Trusted User Presence Required
+  public static final short TRUSTED_USER_PRESENCE_REQUIRED = (short) 0x01FB;
+  // Trusted Confirmation Required
+  public static final short TRUSTED_CONFIRMATION_REQUIRED = (short) 0x01FC;
+  // Unlocked Device Required
+  public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD;
+  // Reset Since Id Rotation
+  public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC;
+  // Early boot ended.
+  public static final short EARLY_BOOT_ONLY = (short) 0x0131;
+  // Device unique attestation.
+  public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0;
+
+  // Byte Tag
+  // Application Id
+  public static final short APPLICATION_ID = (short) 0x0259;
+  // Application Data
+  public static final short APPLICATION_DATA = (short) 0x02BC;
+  // Root Of Trust
+  public static final short ROOT_OF_TRUST = (short) 0x02C0;
+  // Unique Id
+  public static final short UNIQUE_ID = (short) 0x02C3;
+  // Attestation Challenge
+  public static final short ATTESTATION_CHALLENGE = (short) 0x02C4;
+  // Attestation Application Id
+  public static final short ATTESTATION_APPLICATION_ID = (short) 0x02C5;
+  // Attestation Id Brand
+  public static final short ATTESTATION_ID_BRAND = (short) 0x02C6;
+  // Attestation Id Device
+  public static final short ATTESTATION_ID_DEVICE = (short) 0x02C7;
+  // Attestation Id Product
+  public static final short ATTESTATION_ID_PRODUCT = (short) 0x02C8;
+  // Attestation Id Serial
+  public static final short ATTESTATION_ID_SERIAL = (short) 0x02C9;
+  // Attestation Id IMEI
+  public static final short ATTESTATION_ID_IMEI = (short) 0x02CA;
+  // Attestation Id MEID
+  public static final short ATTESTATION_ID_MEID = (short) 0x02CB;
+  // Attestation Id Manufacturer
+  public static final short ATTESTATION_ID_MANUFACTURER = (short) 0x02CC;
+  // Attestation Id Model
+  public static final short ATTESTATION_ID_MODEL = (short) 0x02CD;
+  // Associated Data
+  public static final short ASSOCIATED_DATA = (short) 0x03E8;
+  // Nonce
+  public static final short NONCE = (short) 0x03E9;
+  // Confirmation Token
+  public static final short CONFIRMATION_TOKEN = (short) 0x03ED;
+  // Serial Number - this is a big num but in applet we handle it as byte blob
+  public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE;
+  // Subject Name
+  public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF;
+
+  public static final short LENGTH_FROM_PDU = (short) 0xFFFF;
+
+  public static final byte NO_VALUE = (byte) 0xff;
+  // Support Curves for Eek Chain validation.
+  public static final byte RKP_CURVE_P256 = 1;
+  // Type offsets.
+  public static final byte KM_TYPE_BASE_OFFSET = 0;
+  public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET;
+  public static final byte KM_BOOL_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 1;
+  public static final byte KM_BYTE_BLOB_OFFSET = KM_TYPE_BASE_OFFSET + 2;
+  public static final byte KM_BYTE_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 3;
+  public static final byte KM_ENUM_OFFSET = KM_TYPE_BASE_OFFSET + 4;
+  public static final byte KM_ENUM_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 5;
+  public static final byte KM_ENUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 6;
+  public static final byte KM_HARDWARE_AUTH_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 7;
+  public static final byte KM_HMAC_SHARING_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 8;
+  public static final byte KM_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 9;
+  public static final byte KM_INTEGER_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 10;
+  public static final byte KM_INTEGER_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 11;
+  public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12;
+  public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13;
+  public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14;
+  public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15;
+  public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16;
+  public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17;
+  public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18;
+  public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19;
+  public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20;
+  public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21;
+  public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22;
+  public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23;
+  public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24;
+  public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25;
+  public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26;
+  public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27;
+  public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28;
+  public static final byte KM_SEMANTIC_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 29;
+
+  // Attestation types
+  public static final byte NO_CERT = 0;
+  public static final byte ATTESTATION_CERT = 1;
+  public static final byte SELF_SIGNED_CERT = 2;
+  public static final byte FAKE_CERT = 3;
+  // Buffering Mode
+  public static final byte BUF_NONE = 0;
+  public static final byte BUF_RSA_DECRYPT_OR_NO_DIGEST = 1;
+  public static final byte BUF_EC_NO_DIGEST = 2;
+  public static final byte BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN = 3;
+  public static final byte BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN = 4;
+  public static final byte BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN = 5;
+  public static final byte BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN = 6;
+  public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7;
+
+  // MAX ApplicationID or Application Data size
+  public static final byte MAX_APP_ID_APP_DATA_SIZE = 64;
+  // Max attestation challenge size.
+  public static final short MAX_ATTESTATION_CHALLENGE_SIZE = 128;
+  // Max certificate serial size.
+  public static final byte MAX_CERTIFICATE_SERIAL_SIZE = 20;
+  // Attestation Application ID
+  public static final short MAX_ATTESTATION_APP_ID_SIZE = 1024;
+  // Instance table
+  public static final byte INSTANCE_TABLE_SIZE = 30;
+  protected static final byte TLV_HEADER_SIZE = 3;
+  protected static KMRepository repository;
+  protected static byte[] heap;
+  protected static short[] instanceTable;
+
+  public static void initialize() {
+    instanceTable = JCSystem.makeTransientShortArray(INSTANCE_TABLE_SIZE, JCSystem.CLEAR_ON_RESET);
+    KMType.repository = KMRepository.instance();
+    KMType.heap = repository.getHeap();
+  }
+
+  public static byte getType(short ptr) {
+    return heap[ptr];
+  }
+
+  public static short length(short ptr) {
+    return Util.getShort(heap, (short) (ptr + 1));
+  }
+
+  public static short getValue(short ptr) {
+    return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+  }
+
+  protected static short instance(byte type, short length) {
+    if (length < 0) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE));
+    heap[ptr] = type;
+    Util.setShort(heap, (short) (ptr + 1), length);
+    return ptr;
+  }
+
+  protected static short exp(byte type) {
+    short ptr = repository.alloc(TLV_HEADER_SIZE);
+    heap[ptr] = type;
+    Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE);
+    return ptr;
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java
new file mode 100644
index 0000000..590e73a
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright(C) 2020 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 com.android.javacard.keymaster;
+
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.Util;
+
+/**
+ * KMVerificationToken represents VerificationToken structure from android keymaster hal
+ * specifications. It corresponds to CBOR array type. struct{byte type=VERIFICATION_TOKEN_TYPE;
+ * short length=2; short arrayPtr} where arrayPtr is a pointer to ordered array with following
+ * elements: {KMInteger Challenge; KMInteger Timestamp; KMByteBlob PARAMETERS_VERIFIED;
+ * SecurityLevel level; KMByteBlob Mac}.
+ */
+public class KMVerificationToken extends KMType {
+
+  public static final byte CHALLENGE = 0x00;
+  public static final byte TIMESTAMP = 0x01;
+  public static final byte MAC = 0x02;
+
+  private static KMVerificationToken prototype;
+
+  private KMVerificationToken() {}
+
+  public static short exp() {
+    short arrPtr = KMArray.instance((short) 3);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add(CHALLENGE, KMInteger.exp());
+    arr.add(TIMESTAMP, KMInteger.exp());
+    arr.add(MAC, KMByteBlob.exp());
+    return instance(arrPtr);
+  }
+
+  private static KMVerificationToken proto(short ptr) {
+    if (prototype == null) {
+      prototype = new KMVerificationToken();
+    }
+    KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] = ptr;
+    return prototype;
+  }
+
+  public static short instance() {
+    short arrPtr = KMArray.instance((short) 3);
+    KMArray arr = KMArray.cast(arrPtr);
+    arr.add(CHALLENGE, KMInteger.uint_16((short) 0));
+    arr.add(TIMESTAMP, KMInteger.uint_16((short) 0));
+    arr.add(MAC, KMByteBlob.instance((short) 0));
+    return instance(arrPtr);
+  }
+
+  public static short instance(short vals) {
+    KMArray arr = KMArray.cast(vals);
+    if (arr.length() != 3) {
+      ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
+    }
+    short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short) 2);
+    Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals);
+    return ptr;
+  }
+
+  public static KMVerificationToken cast(short ptr) {
+    if (heap[ptr] != VERIFICATION_TOKEN_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE));
+    if (heap[arrPtr] != ARRAY_TYPE) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    return proto(ptr);
+  }
+
+  public short getVals() {
+    return Util.getShort(
+        heap, (short) (KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] + TLV_HEADER_SIZE));
+  }
+
+  public short length() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).length();
+  }
+
+  public short getChallenge() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(CHALLENGE);
+  }
+
+  public void setChallenge(short vals) {
+    KMInteger.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(CHALLENGE, vals);
+  }
+
+  public short getTimestamp() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(TIMESTAMP);
+  }
+
+  public void setTimestamp(short vals) {
+    KMInteger.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(TIMESTAMP, vals);
+  }
+
+  public short getMac() {
+    short arrPtr = getVals();
+    return KMArray.cast(arrPtr).get(MAC);
+  }
+
+  public void setMac(short vals) {
+    KMByteBlob.cast(vals);
+    short arrPtr = getVals();
+    KMArray.cast(arrPtr).add(MAC, vals);
+  }
+}
diff --git a/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java
new file mode 100644
index 0000000..0b94432
--- /dev/null
+++ b/ready_se/google/keymint/KM200/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java
@@ -0,0 +1,1591 @@
+/*
+ * Copyright(C) 2021 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 com.android.javacard.keymaster;
+
+import com.android.javacard.seprovider.KMDeviceUniqueKeyPair;
+import com.android.javacard.seprovider.KMException;
+import com.android.javacard.seprovider.KMOperation;
+import com.android.javacard.seprovider.KMSEProvider;
+import javacard.framework.APDU;
+import javacard.framework.ISO7816;
+import javacard.framework.ISOException;
+import javacard.framework.JCSystem;
+import javacard.framework.Util;
+
+/*
+ * This class handles the remote key provisioning. Generates an RKP key and generates a certificate signing
+ * request(CSR). The generation of CSR is divided amoung multiple functions to the save the memory inside
+ * the Applet. The set of functions to be called sequentially in the order to complete the process of
+ * generating the CSR are processBeginSendData, processUpdateKey, processUpdateEekChain,
+ * processUpdateChallenge, processFinishSendData and getResponse. ProcessUpdateKey is called N times, where
+ * N is the number of keys. Similarly getResponse is called is multiple times till the client receives the
+ * response completely.
+ */
+public class RemotelyProvisionedComponentDevice {
+
+  // Device Info labels
+  public static final byte[] BRAND = {0x62, 0x72, 0x61, 0x6E, 0x64};
+  public static final byte[] MANUFACTURER = {
+    0x6D, 0x61, 0x6E, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, 0x72, 0x65, 0x72
+  };
+  public static final byte[] PRODUCT = {0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74};
+  public static final byte[] MODEL = {0x6D, 0x6F, 0x64, 0x65, 0x6C};
+  public static final byte[] DEVICE = {0x64, 0x65, 0x76, 0x69, 0x63, 0x65};
+  public static final byte[] VB_STATE = {0x76, 0x62, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65};
+  public static final byte[] BOOTLOADER_STATE = {
+    0x62, 0x6F, 0x6F, 0x74, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65
+  };
+  public static final byte[] VB_META_DIGEST = {
+    0X76, 0X62, 0X6D, 0X65, 0X74, 0X61, 0X5F, 0X64, 0X69, 0X67, 0X65, 0X73, 0X74
+  };
+  public static final byte[] OS_VERSION = {
+    0x6F, 0x73, 0x5F, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E
+  };
+  public static final byte[] SYSTEM_PATCH_LEVEL = {
+    0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76,
+    0x65, 0x6C
+  };
+  public static final byte[] BOOT_PATCH_LEVEL = {
+    0x62, 0x6F, 0x6F, 0x74, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C
+  };
+  public static final byte[] VENDOR_PATCH_LEVEL = {
+    0x76, 0x65, 0x6E, 0x64, 0x6F, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76,
+    0x65, 0x6C
+  };
+  public static final byte[] DEVICE_INFO_VERSION = {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E};
+  public static final byte[] SECURITY_LEVEL = {
+    0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C
+  };
+  public static final byte[] FUSED = {0x66, 0x75, 0x73, 0x65, 0x64};
+  // Verified boot state values
+  public static final byte[] VB_STATE_GREEN = {0x67, 0x72, 0x65, 0x65, 0x6E};
+  public static final byte[] VB_STATE_YELLOW = {0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77};
+  public static final byte[] VB_STATE_ORANGE = {0x6F, 0x72, 0x61, 0x6E, 0x67, 0x65};
+  public static final byte[] VB_STATE_RED = {0x72, 0x65, 0x64};
+  // Boot loader state values
+  public static final byte[] UNLOCKED = {0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64};
+  public static final byte[] LOCKED = {0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64};
+  // Device info CDDL schema version
+  public static final byte DI_SCHEMA_VERSION = 2;
+  public static final byte[] DI_SECURITY_LEVEL = {
+    0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, 0x78
+  };
+  public static final byte DATA_INDEX_ENTRY_SIZE = 4;
+  public static final byte DATA_INDEX_ENTRY_OFFSET = 2;
+  private static final byte TRUE = 0x01;
+  private static final byte FALSE = 0x00;
+  // RKP Version
+  private static final short RKP_VERSION = (short) 0x02;
+  // Boot params
+  private static final byte OS_VERSION_ID = 0x00;
+  private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01;
+  private static final byte BOOT_PATCH_LEVEL_ID = 0x02;
+  private static final byte VENDOR_PATCH_LEVEL_ID = 0x03;
+  private static final boolean IS_ACC_SUPPORTED_IN_RKP_SERVER = false;
+  private static final short MAX_SEND_DATA = 512;
+  private static final byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65};
+  private static final byte[] uniqueId = {
+    0x73, 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x62, 0x6f, 0x78, 0x20, 0x6b, 0x65, 0x79, 0x6d, 0x69, 0x6e,
+    0x74
+  }; // "strongbox keymint"
+  // more data or no data
+  private static final byte MORE_DATA = 0x01; // flag to denote more data to retrieve
+  private static final byte NO_DATA = 0x00;
+  // Response processing states
+  private static final byte START_PROCESSING = 0x00;
+  private static final byte PROCESSING_BCC_IN_PROGRESS = 0x02;
+  private static final byte PROCESSING_BCC_COMPLETE = 0x04;
+  private static final byte PROCESSING_ACC_IN_PROGRESS = 0x08; // Additional certificate chain.
+  private static final byte PROCESSING_ACC_COMPLETE = 0x0A;
+  // data table
+  private static final short DATA_SIZE = 512;
+  private static final byte DATA_INDEX_SIZE = 11;
+  // data offsets
+  private static final byte EPHEMERAL_MAC_KEY = 0;
+  private static final byte TOTAL_KEYS_TO_SIGN = 1;
+  private static final byte KEYS_TO_SIGN_COUNT = 2;
+  private static final byte TEST_MODE = 3;
+  private static final byte EEK_KEY = 4;
+  private static final byte EEK_KEY_ID = 5;
+  private static final byte CHALLENGE = 6;
+  private static final byte GENERATE_CSR_PHASE = 7;
+  private static final byte EPHEMERAL_PUB_KEY = 8;
+  private static final byte RESPONSE_PROCESSING_STATE = 9;
+  private static final byte ACC_PROCESSED_LENGTH = 10;
+
+  // data item sizes
+  private static final byte MAC_KEY_SIZE = 32;
+  private static final byte SHORT_SIZE = 2;
+  private static final byte BYTE_SIZE = 1;
+  private static final byte TEST_MODE_SIZE = 1;
+  // generate csr states
+  private static final byte BEGIN = 0x01;
+  private static final byte UPDATE = 0x02;
+  private static final byte FINISH = 0x04;
+  private static final byte GET_RESPONSE = 0x06;
+
+  // RKP mac key size
+  private static final byte RKP_MAC_KEY_SIZE = 32;
+  public static Object[] authorizedEekRoots;
+  public short[] rkpTmpVariables;
+  // variables
+  private byte[] data;
+  private KMEncoder encoder;
+  private KMDecoder decoder;
+  private KMRepository repository;
+  private KMSEProvider seProvider;
+  private KMKeymintDataStore storeDataInst;
+  private Object[] operation;
+  private short[] dataIndex;
+
+  public RemotelyProvisionedComponentDevice(
+      KMEncoder encoder,
+      KMDecoder decoder,
+      KMRepository repository,
+      KMSEProvider seProvider,
+      KMKeymintDataStore storeDInst) {
+    this.encoder = encoder;
+    this.decoder = decoder;
+    this.repository = repository;
+    this.seProvider = seProvider;
+    this.storeDataInst = storeDInst;
+    rkpTmpVariables = JCSystem.makeTransientShortArray((short) 32, JCSystem.CLEAR_ON_RESET);
+    data = JCSystem.makeTransientByteArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET);
+    operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET);
+    dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET);
+    // Initialize RKP mac key
+    if (!seProvider.isUpgrading()) {
+      short offset = repository.allocReclaimableMemory((short) RKP_MAC_KEY_SIZE);
+      byte[] buffer = repository.getHeap();
+      seProvider.getTrueRandomNumber(buffer, offset, RKP_MAC_KEY_SIZE);
+      storeDataInst.createRkpMacKey(buffer, offset, RKP_MAC_KEY_SIZE);
+      repository.reclaimMemory(RKP_MAC_KEY_SIZE);
+    }
+    operation[0] = null;
+    createAuthorizedEEKRoot();
+  }
+
+  private void createAuthorizedEEKRoot() {
+    if (authorizedEekRoots == null) {
+      authorizedEekRoots =
+        new Object[] {
+          new byte[] {
+            (byte) 0x04, (byte) 0xf7, (byte) 0x14, (byte) 0x8a, (byte) 0xdb, (byte) 0x97,
+            (byte) 0xf4, (byte) 0xcc, (byte) 0x53, (byte) 0xef, (byte) 0xd2, (byte) 0x64,
+            (byte) 0x11, (byte) 0xc4, (byte) 0xe3, (byte) 0x75, (byte) 0x1f, (byte) 0x66,
+            (byte) 0x1f, (byte) 0xa4, (byte) 0x71, (byte) 0x0c, (byte) 0x6c, (byte) 0xcf,
+            (byte) 0xfa, (byte) 0x09, (byte) 0x46, (byte) 0x80, (byte) 0x74, (byte) 0x87,
+            (byte) 0x54, (byte) 0xf2, (byte) 0xad, (byte) 0x5e, (byte) 0x7f, (byte) 0x5b,
+            (byte) 0xf6, (byte) 0xec, (byte) 0xe4, (byte) 0xf6, (byte) 0x19, (byte) 0xcc,
+            (byte) 0xff, (byte) 0x13, (byte) 0x37, (byte) 0xfd, (byte) 0x0f, (byte) 0xa1,
+            (byte) 0xc8, (byte) 0x93, (byte) 0xdb, (byte) 0x18, (byte) 0x06, (byte) 0x76,
+            (byte) 0xc4, (byte) 0x5d, (byte) 0xe6, (byte) 0xd7, (byte) 0x6a, (byte) 0x77,
+            (byte) 0x86, (byte) 0xc3, (byte) 0x2d, (byte) 0xaf, (byte) 0x8f
+          },
+        };
+    }
+  }
+
+  private void initializeDataTable() {
+    clearDataTable();
+    releaseOperation();
+    dataIndex[0] = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE);
+  }
+
+  private short dataAlloc(short length) {
+    if ((short) (dataIndex[0] + length) > (short) data.length) {
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+    dataIndex[0] += length;
+    return (short) (dataIndex[0] - length);
+  }
+
+  private void clearDataTable() {
+    Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0x00);
+    dataIndex[0] = 0x00;
+  }
+
+  private void releaseOperation() {
+    if (operation[0] != null) {
+      ((KMOperation) operation[0]).abort();
+      operation[0] = null;
+    }
+  }
+
+  private short createEntry(short index, short length) {
+    index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+    short ptr = dataAlloc(length);
+    Util.setShort(data, index, length);
+    Util.setShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET), ptr);
+    return ptr;
+  }
+
+  private short getEntry(short index) {
+    index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+    return Util.getShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET));
+  }
+
+  private short getEntryLength(short index) {
+    index = (short) (index * DATA_INDEX_ENTRY_SIZE);
+    return Util.getShort(data, index);
+  }
+
+  private void processGetRkpHwInfoCmd(APDU apdu) {
+    // Make the response
+    // Author name - Google.
+    short respPtr = KMArray.instance((short) 5);
+    KMArray resp = KMArray.cast(respPtr);
+    resp.add((short) 0, KMInteger.uint_16(KMError.OK));
+    resp.add((short) 1, KMInteger.uint_16(RKP_VERSION));
+    resp.add((short) 2, KMByteBlob.instance(google, (short) 0, (short) google.length));
+    resp.add((short) 3, KMInteger.uint_8(KMType.RKP_CURVE_P256));
+    resp.add((short) 4, KMByteBlob.instance(uniqueId, (short) 0, (short) uniqueId.length));
+    KMKeymasterApplet.sendOutgoing(apdu, respPtr);
+  }
+
+  /**
+   * This function generates an EC key pair with attest key as purpose and creates an encrypted key
+   * blob. It then generates a COSEMac message which includes the ECDSA public key.
+   */
+  public void processGenerateRkpKey(APDU apdu) {
+    short arr = KMArray.instance((short) 1);
+    KMArray.cast(arr).add((short) 0, KMSimpleValue.exp());
+    arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+    // Re-purpose the apdu buffer as scratch pad.
+    byte[] scratchPad = apdu.getBuffer();
+    // test mode flag.
+    boolean testMode =
+        (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 0)).getValue());
+    KMKeymasterApplet.generateRkpKey(scratchPad, getEcAttestKeyParameters());
+    short pubKey = KMKeymasterApplet.getPubKey();
+    short coseMac0 = constructCoseMacForRkpKey(testMode, scratchPad, pubKey);
+    // Encode the COSE_MAC0 object
+    arr = KMArray.instance((short) 3);
+    KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+    KMArray.cast(arr).add((short) 1, coseMac0);
+    KMArray.cast(arr).add((short) 2, KMKeymasterApplet.getPivateKey());
+    KMKeymasterApplet.sendOutgoing(apdu, arr);
+  }
+
+  public void processBeginSendData(APDU apdu) throws Exception {
+    try {
+      initializeDataTable();
+      short arr = KMArray.instance((short) 3);
+      KMArray.cast(arr).add((short) 0, KMInteger.exp()); // Array length
+      KMArray.cast(arr).add((short) 1, KMInteger.exp()); // Total length of the encoded CoseKeys.
+      KMArray.cast(arr).add((short) 2, KMSimpleValue.exp());
+      arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+      // Re-purpose the apdu buffer as scratch pad.
+      byte[] scratchPad = apdu.getBuffer();
+      // Generate ephemeral mac key.
+      short dataEntryIndex = createEntry(EPHEMERAL_MAC_KEY, MAC_KEY_SIZE);
+      seProvider.newRandomNumber(data, dataEntryIndex, MAC_KEY_SIZE);
+      // Initialize hmac operation.
+      initHmacOperation();
+      // Partially encode CoseMac structure with partial payload.
+      constructPartialPubKeysToSignMac(
+          scratchPad,
+          KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(),
+          KMInteger.cast(KMArray.cast(arr).get((short) 1)).getShort());
+      // Store the total keys in data table.
+      dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE);
+      Util.setShort(
+          data, dataEntryIndex, KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort());
+      // Store the test mode value in data table.
+      dataEntryIndex = createEntry(TEST_MODE, TEST_MODE_SIZE);
+      data[dataEntryIndex] =
+          (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 2)).getValue())
+              ? TRUE
+              : FALSE;
+      // Store the current csr status, which is BEGIN.
+      createEntry(GENERATE_CSR_PHASE, BYTE_SIZE);
+      updateState(BEGIN);
+      // Send response.
+      KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+    } catch (Exception e) {
+      clearDataTable();
+      releaseOperation();
+      throw e;
+    }
+  }
+
+  public void processUpdateKey(APDU apdu) throws Exception {
+    try {
+      // The prior state can be BEGIN or UPDATE
+      validateState((byte) (BEGIN | UPDATE));
+      validateKeysToSignCount();
+      short headers = KMCoseHeaders.exp();
+      short arrInst = KMArray.instance((short) 4);
+      KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp());
+      KMArray.cast(arrInst).add((short) 1, headers);
+      KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp());
+      KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp());
+      short arr = KMArray.exp(arrInst);
+      arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+      arrInst = KMArray.cast(arr).get((short) 0);
+      // Re-purpose the apdu buffer as scratch pad.
+      byte[] scratchPad = apdu.getBuffer();
+
+      // Validate and extract the CoseKey from CoseMac0 message.
+      short coseKey = validateAndExtractPublicKey(arrInst, scratchPad);
+      // Encode CoseKey
+      short length =
+          KMKeymasterApplet.encodeToApduBuffer(
+              coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+      // Do Hmac update with input as encoded CoseKey.
+      ((KMOperation) operation[0]).update(scratchPad, (short) 0, length);
+      // Increment the count each time this function gets executed.
+      // Store the count in data table.
+      short dataEntryIndex = getEntry(KEYS_TO_SIGN_COUNT);
+      if (dataEntryIndex == 0) {
+        dataEntryIndex = createEntry(KEYS_TO_SIGN_COUNT, SHORT_SIZE);
+      }
+      length = Util.getShort(data, dataEntryIndex);
+      Util.setShort(data, dataEntryIndex, ++length);
+      // Update the csr state
+      updateState(UPDATE);
+      // Send response.
+      KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+    } catch (Exception e) {
+      clearDataTable();
+      releaseOperation();
+      throw e;
+    }
+  }
+
+  public void processUpdateEekChain(APDU apdu) throws Exception {
+    try {
+      // The prior state can be BEGIN or UPDATE
+      validateState((byte) (BEGIN | UPDATE));
+      short headers = KMCoseHeaders.exp();
+      short arrInst = KMArray.instance((short) 4);
+      KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp());
+      KMArray.cast(arrInst).add((short) 1, headers);
+      KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp());
+      KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp());
+      short arrSignPtr = KMArray.exp(arrInst);
+      arrInst = KMKeymasterApplet.receiveIncoming(apdu, arrSignPtr);
+      if (KMArray.cast(arrInst).length() == 0) {
+        KMException.throwIt(KMError.STATUS_INVALID_EEK);
+      }
+      // Re-purpose the apdu buffer as scratch pad.
+      byte[] scratchPad = apdu.getBuffer();
+      // Validate eek chain.
+      short eekKey = validateAndExtractEekPub(arrInst, scratchPad);
+      // Store eek public key and eek id in the data table.
+      short eekKeyId = KMCoseKey.cast(eekKey).getKeyIdentifier();
+      short dataEntryIndex = createEntry(EEK_KEY_ID, KMByteBlob.cast(eekKeyId).length());
+      Util.arrayCopyNonAtomic(
+          KMByteBlob.cast(eekKeyId).getBuffer(),
+          KMByteBlob.cast(eekKeyId).getStartOff(),
+          data,
+          dataEntryIndex,
+          KMByteBlob.cast(eekKeyId).length());
+      // Convert the coseKey to a public key.
+      short len = KMCoseKey.cast(eekKey).getEcdsa256PublicKey(scratchPad, (short) 0);
+      dataEntryIndex = createEntry(EEK_KEY, len);
+      Util.arrayCopyNonAtomic(scratchPad, (short) 0, data, dataEntryIndex, len);
+      // Update the state
+      updateState(UPDATE);
+      KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+    } catch (Exception e) {
+      clearDataTable();
+      releaseOperation();
+      throw e;
+    }
+  }
+
+  public void processUpdateChallenge(APDU apdu) throws Exception {
+    try {
+      // The prior state can be BEGIN or UPDATE
+      validateState((byte) (BEGIN | UPDATE));
+      short arr = KMArray.instance((short) 1);
+      KMArray.cast(arr).add((short) 0, KMByteBlob.exp());
+      arr = KMKeymasterApplet.receiveIncoming(apdu, arr);
+      // Store the challenge in the data table.
+      short challenge = KMArray.cast(arr).get((short) 0);
+      short challengeLen = KMByteBlob.cast(challenge).length();
+      if (challengeLen > 64) {
+        KMException.throwIt(KMError.INVALID_INPUT_LENGTH);
+      }
+      short dataEntryIndex = createEntry(CHALLENGE, challengeLen);
+      Util.arrayCopyNonAtomic(
+          KMByteBlob.cast(challenge).getBuffer(),
+          KMByteBlob.cast(challenge).getStartOff(),
+          data,
+          dataEntryIndex,
+          challengeLen);
+      // Update the state
+      updateState(UPDATE);
+      KMKeymasterApplet.sendResponse(apdu, KMError.OK);
+    } catch (Exception e) {
+      clearDataTable();
+      releaseOperation();
+      throw e;
+    }
+  }
+
+  // This function returns pubKeysToSignMac, deviceInfo and partially constructed protected data
+  // wrapped inside byte blob. The partial protected data contains Headers and encrypted signedMac.
+  public void processFinishSendData(APDU apdu) throws Exception {
+    try {
+      // The prior state should be UPDATE.
+      validateState(UPDATE);
+      byte[] scratchPad = apdu.getBuffer();
+      if (data[getEntry(TOTAL_KEYS_TO_SIGN)] != data[getEntry(KEYS_TO_SIGN_COUNT)]) {
+        // Mismatch in the number of keys sent.
+        ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+      }
+      // PubKeysToSignMac
+      short empty = repository.alloc((short) 0);
+      short len =
+          ((KMOperation) operation[0])
+              .sign(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0);
+      // release operation
+      releaseOperation();
+      short pubKeysToSignMac = KMByteBlob.instance(scratchPad, (short) 0, len);
+      // Create DeviceInfo
+      short deviceInfo = createDeviceInfo(scratchPad);
+      // Generate Nonce for AES-GCM
+      seProvider.newRandomNumber(scratchPad, (short) 0, KMKeymasterApplet.AES_GCM_NONCE_LENGTH);
+      short nonce =
+          KMByteBlob.instance(scratchPad, (short) 0, KMKeymasterApplet.AES_GCM_NONCE_LENGTH);
+      // Initializes cipher instance.
+      initAesGcmOperation(scratchPad, nonce);
+      // Encode Enc_Structure as additional data for AES-GCM.
+      processAesGcmUpdateAad(scratchPad);
+      short partialPayloadLen = processSignedMac(scratchPad, pubKeysToSignMac, deviceInfo);
+      short partialCipherText = KMByteBlob.instance(scratchPad, (short) 0, partialPayloadLen);
+      short coseEncryptProtectedHeader = getCoseEncryptProtectedHeader(scratchPad);
+      short coseEncryptUnProtectedHeader = getCoseEncryptUnprotectedHeader(scratchPad, nonce);
+      len =
+          KMKeymasterApplet.encodeToApduBuffer(
+              deviceInfo, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+      short encodedDeviceInfo = KMByteBlob.instance(scratchPad, (short) 0, len);
+      updateState(FINISH);
+      short arr = KMArray.instance((short) 7);
+      KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+      KMArray.cast(arr).add((short) 1, pubKeysToSignMac);
+      KMArray.cast(arr).add((short) 2, encodedDeviceInfo);
+      KMArray.cast(arr).add((short) 3, coseEncryptProtectedHeader);
+      KMArray.cast(arr).add((short) 4, coseEncryptUnProtectedHeader);
+      KMArray.cast(arr).add((short) 5, partialCipherText);
+      KMArray.cast(arr).add((short) 6, KMInteger.uint_8(MORE_DATA));
+      KMKeymasterApplet.sendOutgoing(apdu, arr);
+    } catch (Exception e) {
+      clearDataTable();
+      releaseOperation();
+      throw e;
+    }
+  }
+
+  public void processGetResponse(APDU apdu) throws Exception {
+    try {
+      // The prior state should be FINISH.
+      validateState((byte) (FINISH | GET_RESPONSE));
+      byte[] scratchPad = apdu.getBuffer();
+      short len = 0;
+      short recipientStructure = KMArray.instance((short) 0);
+      byte moreData = MORE_DATA;
+      byte state = getCurrentOutputProcessingState();
+      switch (state) {
+        case START_PROCESSING:
+        case PROCESSING_BCC_IN_PROGRESS:
+          len = processBcc(scratchPad);
+          updateState(GET_RESPONSE);
+          break;
+        case PROCESSING_BCC_COMPLETE:
+        case PROCESSING_ACC_IN_PROGRESS:
+          len = processAdditionalCertificateChain(scratchPad);
+          updateState(GET_RESPONSE);
+          break;
+        case PROCESSING_ACC_COMPLETE:
+          recipientStructure = processRecipientStructure(scratchPad);
+          len = processFinalData(scratchPad);
+          moreData = NO_DATA;
+          releaseOperation();
+          clearDataTable();
+          break;
+        default:
+          KMException.throwIt(KMError.INVALID_STATE);
+      }
+      short data = KMByteBlob.instance(scratchPad, (short) 0, len);
+      short arr = KMArray.instance((short) 4);
+      KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK));
+      KMArray.cast(arr).add((short) 1, data);
+      KMArray.cast(arr).add((short) 2, recipientStructure);
+      // represents there is more output to retrieve
+      KMArray.cast(arr).add((short) 3, KMInteger.uint_8(moreData));
+      KMKeymasterApplet.sendOutgoing(apdu, arr);
+    } catch (Exception e) {
+      clearDataTable();
+      releaseOperation();
+      throw e;
+    }
+  }
+
+  public void process(short ins, APDU apdu) throws Exception {
+    switch (ins) {
+      case KMKeymasterApplet.INS_GET_RKP_HARDWARE_INFO:
+        processGetRkpHwInfoCmd(apdu);
+        break;
+      case KMKeymasterApplet.INS_GENERATE_RKP_KEY_CMD:
+        processGenerateRkpKey(apdu);
+        break;
+      case KMKeymasterApplet.INS_BEGIN_SEND_DATA_CMD:
+        processBeginSendData(apdu);
+        break;
+      case KMKeymasterApplet.INS_UPDATE_KEY_CMD:
+        processUpdateKey(apdu);
+        break;
+      case KMKeymasterApplet.INS_UPDATE_EEK_CHAIN_CMD:
+        processUpdateEekChain(apdu);
+        break;
+      case KMKeymasterApplet.INS_UPDATE_CHALLENGE_CMD:
+        processUpdateChallenge(apdu);
+        break;
+      case KMKeymasterApplet.INS_FINISH_SEND_DATA_CMD:
+        processFinishSendData(apdu);
+        break;
+      case KMKeymasterApplet.INS_GET_RESPONSE_CMD:
+        processGetResponse(apdu);
+        break;
+      default:
+        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
+    }
+  }
+
+  private boolean isAdditionalCertificateChainPresent() {
+    if (!IS_ACC_SUPPORTED_IN_RKP_SERVER || (TRUE == data[getEntry(TEST_MODE)])) {
+      // Don't include AdditionalCertificateChain in ProtectedData if either
+      // 1. RKP server does not support processing of X.509 Additional Certificate Chain.
+      // 2. Requested CSR for test mode.
+      return false;
+    }
+    return (storeDataInst.getAdditionalCertChainLength() == 0 ? false : true);
+  }
+
+  private short processFinalData(byte[] scratchPad) {
+    // Call finish on AES GCM Cipher
+    short empty = repository.alloc((short) 0);
+    short len =
+        ((KMOperation) operation[0])
+            .finish(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0);
+    return len;
+  }
+
+  private byte getCurrentOutputProcessingState() {
+    short index = getEntry(RESPONSE_PROCESSING_STATE);
+    if (index == 0) {
+      return START_PROCESSING;
+    }
+    return data[index];
+  }
+
+  private void updateOutputProcessingState(byte state) {
+    short dataEntryIndex = getEntry(RESPONSE_PROCESSING_STATE);
+    data[dataEntryIndex] = state;
+  }
+
+  /**
+   * Validates the CoseMac message and extracts the CoseKey from it.
+   *
+   * @param coseMacPtr CoseMac instance to be validated.
+   * @param scratchPad Scratch buffer used to store temp results.
+   * @return CoseKey instance.
+   */
+  private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) {
+    boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false;
+    // Exp for KMCoseHeaders
+    short coseHeadersExp = KMCoseHeaders.exp();
+    // Exp for coseky
+    short coseKeyExp = KMCoseKey.exp();
+
+    // validate protected Headers
+    short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET);
+    ptr =
+        decoder.decode(
+            coseHeadersExp,
+            KMByteBlob.cast(ptr).getBuffer(),
+            KMByteBlob.cast(ptr).getStartOff(),
+            KMByteBlob.cast(ptr).length());
+
+    if (!KMCoseHeaders.cast(ptr)
+        .isDataValid(rkpTmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) {
+      KMException.throwIt(KMError.STATUS_FAILED);
+    }
+
+    // Validate payload.
+    ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET);
+    ptr =
+        decoder.decode(
+            coseKeyExp,
+            KMByteBlob.cast(ptr).getBuffer(),
+            KMByteBlob.cast(ptr).getStartOff(),
+            KMByteBlob.cast(ptr).length());
+
+    if (!KMCoseKey.cast(ptr)
+        .isDataValid(
+            rkpTmpVariables,
+            KMCose.COSE_KEY_TYPE_EC2,
+            KMType.INVALID_VALUE,
+            KMCose.COSE_ALG_ES256,
+            KMType.INVALID_VALUE,
+            KMCose.COSE_ECCURVE_256)) {
+      KMException.throwIt(KMError.STATUS_FAILED);
+    }
+
+    boolean isTestKey = KMCoseKey.cast(ptr).isTestKey();
+    if (isTestKey && !testMode) {
+      KMException.throwIt(KMError.STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
+    } else if (!isTestKey && testMode) {
+      KMException.throwIt(KMError.STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
+    }
+
+    // Compute CoseMac Structure and compare the macs.
+    short macStructure =
+        KMCose.constructCoseMacStructure(
+            KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET),
+            KMByteBlob.instance((short) 0),
+            KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET));
+    short encodedLen =
+        KMKeymasterApplet.encodeToApduBuffer(
+            macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+
+    short hmacLen =
+        rkpHmacSign(testMode, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen);
+
+    if (hmacLen
+        != KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length()) {
+      KMException.throwIt(KMError.STATUS_INVALID_MAC);
+    }
+
+    if (0
+        != Util.arrayCompare(
+            scratchPad,
+            encodedLen,
+            KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(),
+            KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET))
+                .getStartOff(),
+            hmacLen)) {
+      KMException.throwIt(KMError.STATUS_INVALID_MAC);
+    }
+    return ptr;
+  }
+
+  /**
+   * This function validates the EEK Chain and extracts the leaf public key, which is used to
+   * generate shared secret using ECDH.
+   *
+   * @param eekArr EEK cert chain array pointer.
+   * @param scratchPad Scratch buffer used to store temp results.
+   * @return CoseKey instance.
+   */
+  private short validateAndExtractEekPub(short eekArr, byte[] scratchPad) {
+    short leafPubKey = 0;
+    try {
+      leafPubKey =
+          KMKeymasterApplet.validateCertChain(
+              (TRUE == data[getEntry(TEST_MODE)]) ? false : true, // validate EEK root
+              KMCose.COSE_ALG_ES256,
+              KMCose.COSE_ALG_ECDH_ES_HKDF_256,
+              eekArr,
+              scratchPad,
+              authorizedEekRoots);
+    } catch (KMException e) {
+      KMException.throwIt(KMError.STATUS_INVALID_EEK);
+    }
+    return leafPubKey;
+  }
+
+  private void validateKeysToSignCount() {
+    short index = getEntry(KEYS_TO_SIGN_COUNT);
+    short keysToSignCount = 0;
+    if (index != 0) {
+      keysToSignCount = Util.getShort(data, index);
+    }
+    if (Util.getShort(data, getEntry(TOTAL_KEYS_TO_SIGN)) <= keysToSignCount) {
+      // Mismatch in the number of keys sent.
+      ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
+    }
+  }
+
+  private void validateState(byte expectedState) {
+    short dataEntryIndex = getEntry(GENERATE_CSR_PHASE);
+    if (0 == (data[dataEntryIndex] & expectedState)) {
+      KMException.throwIt(KMError.INVALID_STATE);
+    }
+  }
+
+  private void updateState(byte state) {
+    short dataEntryIndex = getEntry(GENERATE_CSR_PHASE);
+    if (dataEntryIndex == 0) {
+      KMException.throwIt(KMError.INVALID_STATE);
+    }
+    data[dataEntryIndex] = state;
+  }
+
+  /**
+   * This function constructs a Mac Structure, encode it and signs the encoded buffer with the
+   * ephemeral mac key.
+   */
+  private void constructPartialPubKeysToSignMac(
+      byte[] scratchPad, short arrayLength, short encodedCoseKeysLen) {
+    short ptr;
+    short len;
+    short headerPtr =
+        KMCose.constructHeaders(
+            rkpTmpVariables,
+            KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256),
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE);
+    // Encode the protected header as byte blob.
+    len =
+        KMKeymasterApplet.encodeToApduBuffer(
+            headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len);
+    // create MAC_Structure
+    ptr =
+        KMCose.constructCoseMacStructure(
+            protectedHeader, KMByteBlob.instance((short) 0), KMType.INVALID_VALUE);
+    // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0
+    len =
+        KMKeymasterApplet.encodeToApduBuffer(
+            ptr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    // Construct partial payload - Bstr Header + Array Header
+    // The maximum combined length of bstr header and array header length is 6 bytes.
+    // The lengths will never exceed Max SHORT value.
+    short arrPtr = KMArray.instance(arrayLength);
+    for (short i = 0; i < arrayLength; i++) {
+      KMArray.cast(arrPtr).add(i, KMType.INVALID_VALUE);
+    }
+    arrayLength = encoder.getEncodedLength(arrPtr);
+    short bufIndex = repository.alloc((short) 6);
+    short partialPayloadLen =
+        encoder.encodeByteBlobHeader(
+            (short) (arrayLength + encodedCoseKeysLen), repository.getHeap(), bufIndex, (short) 3);
+
+    partialPayloadLen +=
+        encoder.encode(
+            arrPtr,
+            repository.getHeap(),
+            (short) (bufIndex + partialPayloadLen),
+            repository.getHeapReclaimIndex());
+    Util.arrayCopyNonAtomic(repository.getHeap(), bufIndex, scratchPad, len, partialPayloadLen);
+    ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen));
+  }
+
+  private short createSignedMac(
+      KMDeviceUniqueKeyPair deviceUniqueKeyPair,
+      byte[] scratchPad,
+      short deviceMapPtr,
+      short pubKeysToSign) {
+    // Challenge
+    short dataEntryIndex = getEntry(CHALLENGE);
+    short challengePtr = KMByteBlob.instance(data, dataEntryIndex, getEntryLength(CHALLENGE));
+    // Ephemeral mac key
+    dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY);
+    short ephmeralMacKey =
+        KMByteBlob.instance(data, dataEntryIndex, getEntryLength(EPHEMERAL_MAC_KEY));
+
+    /* Prepare AAD */
+    short aad = KMArray.instance((short) 3);
+    KMArray.cast(aad).add((short) 0, challengePtr);
+    KMArray.cast(aad).add((short) 1, deviceMapPtr);
+    KMArray.cast(aad).add((short) 2, pubKeysToSign);
+    aad =
+        KMKeymasterApplet.encodeToApduBuffer(
+            aad, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    aad = KMByteBlob.instance(scratchPad, (short) 0, aad);
+
+    /* construct protected header */
+    short protectedHeaders =
+        KMCose.constructHeaders(
+            rkpTmpVariables,
+            KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE);
+    protectedHeaders =
+        KMKeymasterApplet.encodeToApduBuffer(
+            protectedHeaders, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders);
+
+    /* construct cose sign structure */
+    short signStructure = KMCose.constructCoseSignStructure(protectedHeaders, aad, ephmeralMacKey);
+    signStructure =
+        KMKeymasterApplet.encodeToApduBuffer(
+            signStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    short len =
+        seProvider.ecSign256(
+            deviceUniqueKeyPair, scratchPad, (short) 0, signStructure, scratchPad, signStructure);
+    len =
+        KMAsn1Parser.instance()
+            .decodeEcdsa256Signature(
+                KMByteBlob.instance(scratchPad, signStructure, len), scratchPad, signStructure);
+    signStructure = KMByteBlob.instance(scratchPad, signStructure, len);
+
+    /* Construct unprotected headers */
+    short unprotectedHeader = KMArray.instance((short) 0);
+    unprotectedHeader = KMCoseHeaders.instance(unprotectedHeader);
+
+    /* construct Cose_Sign1 */
+    return KMCose.constructCoseSign1(
+        protectedHeaders, unprotectedHeader, ephmeralMacKey, signStructure);
+  }
+
+  private KMDeviceUniqueKeyPair createDeviceUniqueKeyPair(boolean testMode, byte[] scratchPad) {
+    KMDeviceUniqueKeyPair deviceUniqueKeyPair;
+    rkpTmpVariables[0] = 0;
+    rkpTmpVariables[1] = 0;
+    if (testMode) {
+      seProvider.createAsymmetricKey(
+          KMType.EC,
+          scratchPad,
+          (short) 0,
+          (short) 128,
+          scratchPad,
+          (short) 128,
+          (short) 128,
+          rkpTmpVariables);
+      deviceUniqueKeyPair =
+          storeDataInst.createRkpTestDeviceUniqueKeyPair(
+              scratchPad,
+              (short) 128,
+              rkpTmpVariables[1],
+              scratchPad,
+              (short) 0,
+              rkpTmpVariables[0]);
+    } else {
+      deviceUniqueKeyPair = storeDataInst.getRkpDeviceUniqueKeyPair(false);
+    }
+    return deviceUniqueKeyPair;
+  }
+
+  /**
+   * DeviceInfo is a CBOR Map structure described by the following CDDL.
+   *
+   * <p>DeviceInfo = { "brand" : tstr, "manufacturer" : tstr, "product" : tstr, "model" : tstr,
+   * "device" : tstr, "vb_state" : "green" / "yellow" / "orange", // Taken from the AVB values
+   * "bootloader_state" : "locked" / "unlocked", // Taken from the AVB values "vbmeta_digest": bstr,
+   * // Taken from the AVB values ? "os_version" : tstr, // Same as android.os.Build.VERSION.release
+   * "system_patch_level" : uint, // YYYYMMDD "boot_patch_level" : uint, //YYYYMMDD
+   * "vendor_patch_level" : uint, // YYYYMMDD "version" : 2, // TheCDDL schema version
+   * "security_level" : "tee" / "strongbox" "fused": 1 / 0, }
+   */
+  private short createDeviceInfo(byte[] scratchpad) {
+    // Device Info Key Value pairs.
+    for (short i = 0; i < 32; i++) {
+      rkpTmpVariables[i] = KMType.INVALID_VALUE;
+    }
+    short dataOffset = 2;
+    rkpTmpVariables[0] = dataOffset;
+    rkpTmpVariables[1] = 0;
+    short metaOffset = 0;
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        BRAND,
+        getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        MANUFACTURER,
+        getAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, scratchpad));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        PRODUCT,
+        getAttestationId(KMType.ATTESTATION_ID_PRODUCT, scratchpad));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        MODEL,
+        getAttestationId(KMType.ATTESTATION_ID_MODEL, scratchpad));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        DEVICE,
+        getAttestationId(KMType.ATTESTATION_ID_DEVICE, scratchpad));
+    updateItem(rkpTmpVariables, metaOffset, VB_STATE, getVbState());
+    updateItem(rkpTmpVariables, metaOffset, BOOTLOADER_STATE, getBootloaderState());
+    updateItem(rkpTmpVariables, metaOffset, VB_META_DIGEST, getVerifiedBootHash(scratchpad));
+    updateItem(rkpTmpVariables, metaOffset, OS_VERSION, getBootParams(OS_VERSION_ID, scratchpad));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        SYSTEM_PATCH_LEVEL,
+        getBootParams(SYSTEM_PATCH_LEVEL_ID, scratchpad));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        BOOT_PATCH_LEVEL,
+        getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        VENDOR_PATCH_LEVEL,
+        getBootParams(VENDOR_PATCH_LEVEL_ID, scratchpad));
+    updateItem(
+        rkpTmpVariables, metaOffset, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION));
+    updateItem(
+        rkpTmpVariables,
+        metaOffset,
+        SECURITY_LEVEL,
+        KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length));
+    updateItem(rkpTmpVariables, metaOffset, FUSED, KMInteger.uint_8(storeDataInst.secureBootMode));
+    // Create device info map.
+    short map = KMMap.instance(rkpTmpVariables[1]);
+    short mapIndex = 0;
+    short index = 2;
+    while (index < (short) 32) {
+      if (rkpTmpVariables[index] != KMType.INVALID_VALUE) {
+        KMMap.cast(map)
+            .add(mapIndex++, rkpTmpVariables[index], rkpTmpVariables[(short) (index + 1)]);
+      }
+      index += 2;
+    }
+    KMMap.cast(map).canonicalize();
+    return map;
+  }
+
+  // Below 6 methods are helper methods to create device info structure.
+  // ----------------------------------------------------------------------------
+
+  /**
+   * Update the item inside the device info structure.
+   *
+   * @param deviceIds Device Info structure to be updated.
+   * @param metaOffset Out parameter meta information. Offset 0 is index and Offset 1 is length.
+   * @param item Key info to be updated.
+   * @param value value to be updated.
+   */
+  private void updateItem(short[] deviceIds, short metaOffset, byte[] item, short value) {
+    if (KMType.INVALID_VALUE != value) {
+      deviceIds[deviceIds[metaOffset]++] =
+          KMTextString.instance(item, (short) 0, (short) item.length);
+      deviceIds[deviceIds[metaOffset]++] = value;
+      deviceIds[(short) (metaOffset + 1)]++;
+    }
+  }
+
+  private short getAttestationId(short attestId, byte[] scratchpad) {
+    short attIdTagLen = storeDataInst.getAttestationId(attestId, scratchpad, (short) 0);
+    if (attIdTagLen == 0) {
+      KMException.throwIt(KMError.INVALID_STATE);
+    }
+    return KMTextString.instance(scratchpad, (short) 0, attIdTagLen);
+  }
+
+  private short getVerifiedBootHash(byte[] scratchPad) {
+    short len = storeDataInst.getVerifiedBootHash(scratchPad, (short) 0);
+    if (len == 0) {
+      KMException.throwIt(KMError.INVALID_STATE);
+    }
+    return KMByteBlob.instance(scratchPad, (short) 0, len);
+  }
+
+  private short getBootloaderState() {
+    short bootloaderState;
+    if (storeDataInst.isDeviceBootLocked()) {
+      bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length);
+    } else {
+      bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length);
+    }
+    return bootloaderState;
+  }
+
+  private short getVbState() {
+    short state = storeDataInst.getBootState();
+    short vbState = KMType.INVALID_VALUE;
+    if (state == KMType.VERIFIED_BOOT) {
+      vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length);
+    } else if (state == KMType.SELF_SIGNED_BOOT) {
+      vbState = KMTextString.instance(VB_STATE_YELLOW, (short) 0, (short) VB_STATE_YELLOW.length);
+    } else if (state == KMType.UNVERIFIED_BOOT) {
+      vbState = KMTextString.instance(VB_STATE_ORANGE, (short) 0, (short) VB_STATE_ORANGE.length);
+    } else if (state == KMType.FAILED_BOOT) {
+      vbState = KMTextString.instance(VB_STATE_RED, (short) 0, (short) VB_STATE_RED.length);
+    }
+    return vbState;
+  }
+
+  private short converIntegerToTextString(short intPtr, byte[] scratchPad) {
+    // Prepare Hex Values
+    short index = 1;
+    scratchPad[0] = 0x30; // Ascii 0
+    while (index < 10) {
+      scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1);
+      index++;
+    }
+    scratchPad[index++] = 0x41; // Ascii 'A'
+    while (index < 16) {
+      scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1);
+      index++;
+    }
+
+    short intLen = KMInteger.cast(intPtr).length();
+    short intOffset = KMInteger.cast(intPtr).getStartOff();
+    byte[] buf = repository.getHeap();
+    short tsPtr = KMTextString.instance((short) (intLen * 2));
+    short tsStartOff = KMTextString.cast(tsPtr).getStartOff();
+    index = 0;
+    byte nibble;
+    while (index < intLen) {
+      nibble = (byte) ((byte) (buf[intOffset] >> 4) & (byte) 0x0F);
+      buf[tsStartOff] = scratchPad[nibble];
+      nibble = (byte) (buf[intOffset] & 0x0F);
+      buf[(short) (tsStartOff + 1)] = scratchPad[nibble];
+      index++;
+      intOffset++;
+      tsStartOff += 2;
+    }
+    return tsPtr;
+  }
+
+  private short getBootParams(byte bootParam, byte[] scratchPad) {
+    short value = KMType.INVALID_VALUE;
+    switch (bootParam) {
+      case OS_VERSION_ID:
+        value = storeDataInst.getOsVersion();
+        break;
+      case SYSTEM_PATCH_LEVEL_ID:
+        value = storeDataInst.getOsPatch();
+        break;
+      case BOOT_PATCH_LEVEL_ID:
+        value = storeDataInst.getBootPatchLevel();
+        break;
+      case VENDOR_PATCH_LEVEL_ID:
+        value = storeDataInst.getVendorPatchLevel();
+        break;
+      default:
+        KMException.throwIt(KMError.INVALID_ARGUMENT);
+    }
+    // Convert Integer to Text String for OS_VERSION.
+    if (bootParam == OS_VERSION_ID) {
+      value = converIntegerToTextString(value, scratchPad);
+    }
+    return value;
+  }
+  // ----------------------------------------------------------------------------
+
+  // ----------------------------------------------------------------------------
+  // ECDH HKDF
+  private short ecdhHkdfDeriveKey(
+      byte[] privKeyA,
+      short privKeyAOff,
+      short privKeyALen,
+      byte[] pubKeyA,
+      short pubKeyAOff,
+      short pubKeyALen,
+      byte[] pubKeyB,
+      short pubKeyBOff,
+      short pubKeyBLen,
+      byte[] scratchPad) {
+    short key =
+        seProvider.ecdhKeyAgreement(
+            privKeyA,
+            privKeyAOff,
+            privKeyALen,
+            pubKeyB,
+            pubKeyBOff,
+            pubKeyBLen,
+            scratchPad,
+            (short) 0);
+    key = KMByteBlob.instance(scratchPad, (short) 0, key);
+
+    // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04.
+    pubKeyAOff += 1;
+    pubKeyALen -= 1;
+    pubKeyBOff += 1;
+    pubKeyBLen -= 1;
+    short kdfContext =
+        KMCose.constructKdfContext(
+            pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, true);
+    kdfContext =
+        KMKeymasterApplet.encodeToApduBuffer(
+            kdfContext, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext);
+
+    Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0);
+    seProvider.hkdf(
+        KMByteBlob.cast(key).getBuffer(),
+        KMByteBlob.cast(key).getStartOff(),
+        KMByteBlob.cast(key).length(),
+        scratchPad,
+        (short) 0,
+        (short) 32,
+        KMByteBlob.cast(kdfContext).getBuffer(),
+        KMByteBlob.cast(kdfContext).getStartOff(),
+        KMByteBlob.cast(kdfContext).length(),
+        scratchPad,
+        (short) 32, // offset
+        (short) 32 // Length of expected output.
+        );
+    Util.arrayCopy(scratchPad, (short) 32, scratchPad, (short) 0, (short) 32);
+    return (short) 32;
+  }
+
+  // ----------------------------------------------------------------------------
+  // This function returns the instance of private key and It stores the public key in the
+  // data table for later usage.
+  private short generateEphemeralEcKey(byte[] scratchPad) {
+    // Generate ephemeral ec key.
+    rkpTmpVariables[0] = 0;
+    rkpTmpVariables[1] = 0;
+    seProvider.createAsymmetricKey(
+        KMType.EC,
+        scratchPad,
+        (short) 0,
+        (short) 128,
+        scratchPad,
+        (short) 128,
+        (short) 128,
+        rkpTmpVariables);
+    // Copy the ephemeral private key from scratch pad
+    short ptr = KMByteBlob.instance(rkpTmpVariables[0]);
+    Util.arrayCopyNonAtomic(
+        scratchPad,
+        (short) 0,
+        KMByteBlob.cast(ptr).getBuffer(),
+        KMByteBlob.cast(ptr).getStartOff(),
+        rkpTmpVariables[0]);
+    // Store  ephemeral public key in data table for later usage.
+    short dataEntryIndex = createEntry(EPHEMERAL_PUB_KEY, rkpTmpVariables[1]);
+    Util.arrayCopyNonAtomic(scratchPad, (short) 128, data, dataEntryIndex, rkpTmpVariables[1]);
+    return ptr;
+  }
+
+  private void initHmacOperation() {
+    short dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY);
+    operation[0] =
+        seProvider.getRkpOperation(
+            KMType.SIGN,
+            KMType.HMAC,
+            KMType.SHA2_256,
+            KMType.PADDING_NONE,
+            (byte) 0,
+            data,
+            dataEntryIndex,
+            getEntryLength(EPHEMERAL_MAC_KEY),
+            null,
+            (short) 0,
+            (short) 0,
+            (short) 0);
+    if (operation[0] == null) {
+      KMException.throwIt(KMError.STATUS_FAILED);
+    }
+  }
+
+  private void initAesGcmOperation(byte[] scratchPad, short nonce) {
+    // Generate Ephemeral mac key
+    short privKey = generateEphemeralEcKey(scratchPad);
+    short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY);
+    // Generate session key
+    short eekIndex = getEntry(EEK_KEY);
+    // Generate session key
+    short sessionKeyLen =
+        ecdhHkdfDeriveKey(
+            KMByteBlob.cast(privKey).getBuffer(), /* Ephemeral Private Key */
+            KMByteBlob.cast(privKey).getStartOff(),
+            KMByteBlob.cast(privKey).length(),
+            data, /* Ephemeral Public key */
+            pubKeyIndex,
+            getEntryLength(EPHEMERAL_PUB_KEY),
+            data, /* EEK Public key */
+            eekIndex,
+            getEntryLength(EEK_KEY),
+            scratchPad /* scratchpad */);
+    // Initialize the Cipher object.
+    operation[0] =
+        seProvider.getRkpOperation(
+            KMType.ENCRYPT,
+            KMType.AES,
+            (byte) 0,
+            KMType.PADDING_NONE,
+            KMType.GCM,
+            scratchPad, /* key */
+            (short) 0,
+            sessionKeyLen,
+            KMByteBlob.cast(nonce).getBuffer(), /* nonce */
+            KMByteBlob.cast(nonce).getStartOff(),
+            KMByteBlob.cast(nonce).length(),
+            (short) (KMKeymasterApplet.AES_GCM_AUTH_TAG_LENGTH * 8));
+    if (operation[0] == null) {
+      KMException.throwIt(KMError.STATUS_FAILED);
+    }
+  }
+
+  private short processRecipientStructure(byte[] scratchPad) {
+    short protectedHeaderRecipient =
+        KMCose.constructHeaders(
+            rkpTmpVariables,
+            KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256),
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE);
+    // Encode the protected header as byte blob.
+    protectedHeaderRecipient =
+        KMKeymasterApplet.encodeToApduBuffer(
+            protectedHeaderRecipient, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    protectedHeaderRecipient = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaderRecipient);
+
+    /* Construct unprotected headers */
+    short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY);
+    // prepare cosekey
+    short coseKey =
+        KMCose.constructCoseKey(
+            rkpTmpVariables,
+            KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+            KMType.INVALID_VALUE,
+            KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+            KMType.INVALID_VALUE,
+            KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+            data,
+            pubKeyIndex,
+            getEntryLength(EPHEMERAL_PUB_KEY),
+            KMType.INVALID_VALUE,
+            false);
+    short keyIdentifierPtr =
+        KMByteBlob.instance(data, getEntry(EEK_KEY_ID), getEntryLength(EEK_KEY_ID));
+    short unprotectedHeaderRecipient =
+        KMCose.constructHeaders(
+            rkpTmpVariables, KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, coseKey);
+
+    // Construct recipients structure.
+    return KMCose.constructRecipientsStructure(
+        protectedHeaderRecipient,
+        unprotectedHeaderRecipient,
+        KMSimpleValue.instance(KMSimpleValue.NULL));
+  }
+
+  private short getAdditionalCertChainProcessedLength() {
+    short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH);
+    if (dataEntryIndex == 0) {
+      dataEntryIndex = createEntry(ACC_PROCESSED_LENGTH, SHORT_SIZE);
+      Util.setShort(data, dataEntryIndex, (short) 0);
+      return (short) 0;
+    }
+    return Util.getShort(data, dataEntryIndex);
+  }
+
+  private void updateAdditionalCertChainProcessedLength(short processedLen) {
+    short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH);
+    Util.setShort(data, dataEntryIndex, processedLen);
+  }
+
+  private short processAdditionalCertificateChain(byte[] scratchPad) {
+    byte[] persistedData = storeDataInst.getAdditionalCertChain();
+    short totalAccLen = Util.getShort(persistedData, (short) 0);
+    if (totalAccLen == 0) {
+      // No Additional certificate chain present.
+      return 0;
+    }
+    short processedLen = getAdditionalCertChainProcessedLength();
+    short lengthToSend = (short) (totalAccLen - processedLen);
+    if (lengthToSend > MAX_SEND_DATA) {
+      lengthToSend = MAX_SEND_DATA;
+    }
+    short cipherTextLen =
+        ((KMOperation) operation[0])
+            .update(persistedData, (short) (2 + processedLen), lengthToSend, scratchPad, (short) 0);
+    processedLen += lengthToSend;
+    updateAdditionalCertChainProcessedLength(processedLen);
+    // Update the output processing state.
+    updateOutputProcessingState(
+        (processedLen == totalAccLen) ? PROCESSING_ACC_COMPLETE : PROCESSING_ACC_IN_PROGRESS);
+    return cipherTextLen;
+  }
+
+  // BCC for STRONGBOX has chain length of 2. So it can be returned in a single go.
+  private short processBcc(byte[] scratchPad) {
+    // Construct BCC
+    boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false;
+    short len;
+    if (testMode) {
+      short bcc = KMKeymasterApplet.generateBcc(true, scratchPad);
+      len =
+          KMKeymasterApplet.encodeToApduBuffer(
+              bcc, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    } else {
+      byte[] bcc = storeDataInst.getBootCertificateChain();
+      len = Util.getShort(bcc, (short) 0);
+      Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len);
+    }
+    short cipherTextLen =
+        ((KMOperation) operation[0]).update(scratchPad, (short) 0, len, scratchPad, len);
+    // move cipher text on scratch pad from starting position.
+    Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen);
+    createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE);
+    // If there is no additional certificate chain present then put the state to
+    // PROCESSING_ACC_COMPLETE.
+    updateOutputProcessingState(
+        isAdditionalCertificateChainPresent() ? PROCESSING_BCC_COMPLETE : PROCESSING_ACC_COMPLETE);
+    return cipherTextLen;
+  }
+
+  // AAD is the CoseEncrypt structure
+  private void processAesGcmUpdateAad(byte[] scratchPad) {
+    short protectedHeader =
+        KMCose.constructHeaders(
+            rkpTmpVariables,
+            KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256),
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE);
+    // Encode the protected header as byte blob.
+    protectedHeader =
+        KMKeymasterApplet.encodeToApduBuffer(
+            protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader);
+    short coseEncryptStr =
+        KMCose.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0));
+    coseEncryptStr =
+        KMKeymasterApplet.encodeToApduBuffer(
+            coseEncryptStr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    ((KMOperation) operation[0]).updateAAD(scratchPad, (short) 0, coseEncryptStr);
+  }
+
+  private short processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short deviceInfo) {
+    // Construct SignedMac
+    KMDeviceUniqueKeyPair deviceUniqueKeyPair =
+        createDeviceUniqueKeyPair((TRUE == data[getEntry(TEST_MODE)]) ? true : false, scratchPad);
+    // Create signedMac
+    short signedMac =
+        createSignedMac(deviceUniqueKeyPair, scratchPad, deviceInfo, pubKeysToSignMac);
+    // Prepare partial data for encryption.
+    short arrLength = (short) (isAdditionalCertificateChainPresent() ? 3 : 2);
+    short arr = KMArray.instance(arrLength);
+    KMArray.cast(arr).add((short) 0, signedMac);
+    KMArray.cast(arr).add((short) 1, KMType.INVALID_VALUE);
+    if (arrLength == 3) {
+      KMArray.cast(arr).add((short) 2, KMType.INVALID_VALUE);
+    }
+    short len =
+        KMKeymasterApplet.encodeToApduBuffer(
+            arr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    short cipherTextLen =
+        ((KMOperation) operation[0]).update(scratchPad, (short) 0, len, scratchPad, len);
+    Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen);
+    return cipherTextLen;
+  }
+
+  private short getCoseEncryptProtectedHeader(byte[] scratchPad) {
+    // CoseEncrypt protected headers.
+    short protectedHeader =
+        KMCose.constructHeaders(
+            rkpTmpVariables,
+            KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256),
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE);
+    // Encode the protected header as byte blob.
+    protectedHeader =
+        KMKeymasterApplet.encodeToApduBuffer(
+            protectedHeader, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    return KMByteBlob.instance(scratchPad, (short) 0, protectedHeader);
+  }
+
+  private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) {
+    /* CoseEncrypt unprotected headers */
+    return KMCose.constructHeaders(
+        rkpTmpVariables, KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE);
+  }
+
+  private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) {
+    // prepare cosekey
+    short coseKey =
+        KMCose.constructCoseKey(
+            rkpTmpVariables,
+            KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2),
+            KMType.INVALID_VALUE,
+            KMNInteger.uint_8(KMCose.COSE_ALG_ES256),
+            KMType.INVALID_VALUE,
+            KMInteger.uint_8(KMCose.COSE_ECCURVE_256),
+            KMByteBlob.cast(pubKey).getBuffer(),
+            KMByteBlob.cast(pubKey).getStartOff(),
+            KMByteBlob.cast(pubKey).length(),
+            KMType.INVALID_VALUE,
+            testMode);
+    // Encode the cose key and make it as payload.
+    short len =
+        KMKeymasterApplet.encodeToApduBuffer(
+            coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    short payload = KMByteBlob.instance(scratchPad, (short) 0, len);
+    // Prepare protected header, which is required to construct the COSE_MAC0
+    short headerPtr =
+        KMCose.constructHeaders(
+            rkpTmpVariables,
+            KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256),
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE,
+            KMType.INVALID_VALUE);
+    // Encode the protected header as byte blob.
+    len =
+        KMKeymasterApplet.encodeToApduBuffer(
+            headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len);
+    // create MAC_Structure
+    short macStructure =
+        KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), payload);
+    // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0
+    len =
+        KMKeymasterApplet.encodeToApduBuffer(
+            macStructure, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    // HMAC Sign.
+    short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, len, scratchPad, len);
+    // Create COSE_MAC0 object
+    short coseMac0 =
+        KMCose.constructCoseMac0(
+            protectedHeader,
+            KMCoseHeaders.instance(KMArray.instance((short) 0)),
+            payload,
+            KMByteBlob.instance(scratchPad, len, hmacLen));
+    len =
+        KMKeymasterApplet.encodeToApduBuffer(
+            coseMac0, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE);
+    return KMByteBlob.instance(scratchPad, (short) 0, len);
+  }
+
+  private short getEcAttestKeyParameters() {
+    short tagIndex = 0;
+    short arrPtr = KMArray.instance((short) 6);
+    // Key size - 256
+    short keySize =
+        KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256));
+    // Digest - SHA256
+    short byteBlob = KMByteBlob.instance((short) 1);
+    KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256);
+    short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob);
+    // Purpose - Attest
+    byteBlob = KMByteBlob.instance((short) 1);
+    KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY);
+    short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob);
+
+    KMArray.cast(arrPtr).add(tagIndex++, purpose);
+    // Algorithm - EC
+    KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC));
+    KMArray.cast(arrPtr).add(tagIndex++, keySize);
+    KMArray.cast(arrPtr).add(tagIndex++, digest);
+    // Curve - P256
+    KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256));
+    // No Authentication is required to use this key.
+    KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED));
+    return KMKeyParameters.instance(arrPtr);
+  }
+
+  private boolean isSignedByte(byte b) {
+    return ((b & 0x0080) != 0);
+  }
+
+  private short writeIntegerHeader(short valueLen, byte[] data, short offset) {
+    // write length
+    data[offset] = (byte) valueLen;
+    // write INTEGER tag
+    offset--;
+    data[offset] = 0x02;
+    return offset;
+  }
+
+  private short writeSequenceHeader(short valueLen, byte[] data, short offset) {
+    // write length
+    data[offset] = (byte) valueLen;
+    // write INTEGER tag
+    offset--;
+    data[offset] = 0x30;
+    return offset;
+  }
+
+  private short writeSignatureData(
+      byte[] input, short inputOff, short inputlen, byte[] output, short offset) {
+    Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen);
+    if (isSignedByte(input[inputOff])) {
+      offset--;
+      output[offset] = (byte) 0;
+    }
+    return offset;
+  }
+
+  public short encodeES256CoseSignSignature(
+      byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff) {
+    // SEQ [ INTEGER(r), INTEGER(s)]
+    // write from bottom to the top
+    if (len != 64) {
+      KMException.throwIt(KMError.INVALID_DATA);
+    }
+    short maxTotalLen = 72;
+    short end = (short) (scratchPadOff + maxTotalLen);
+    // write s.
+    short start = (short) (end - 32);
+    start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start);
+    // write length and header
+    short length = (short) (end - start);
+    start--;
+    start = writeIntegerHeader(length, scratchPad, start);
+    // write r
+    short rEnd = start;
+    start = (short) (start - 32);
+    start = writeSignatureData(input, offset, (short) 32, scratchPad, start);
+    // write length and header
+    length = (short) (rEnd - start);
+    start--;
+    start = writeIntegerHeader(length, scratchPad, start);
+    // write length and sequence header
+    length = (short) (end - start);
+    start--;
+    start = writeSequenceHeader(length, scratchPad, start);
+    length = (short) (end - start);
+    if (start > scratchPadOff) {
+      // re adjust the buffer
+      Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length);
+    }
+    return length;
+  }
+
+  private short rkpHmacSign(
+      boolean testMode,
+      byte[] data,
+      short dataStart,
+      short dataLength,
+      byte[] signature,
+      short signatureStart) {
+    short result;
+    if (testMode) {
+      short macKey = KMByteBlob.instance(MAC_KEY_SIZE);
+      Util.arrayFillNonAtomic(
+          KMByteBlob.cast(macKey).getBuffer(),
+          KMByteBlob.cast(macKey).getStartOff(),
+          MAC_KEY_SIZE,
+          (byte) 0);
+      result =
+          seProvider.hmacSign(
+              KMByteBlob.cast(macKey).getBuffer(),
+              KMByteBlob.cast(macKey).getStartOff(),
+              MAC_KEY_SIZE,
+              data,
+              dataStart,
+              dataLength,
+              signature,
+              signatureStart);
+    } else {
+      result =
+          seProvider.hmacSign(
+              storeDataInst.getRkpMacKey(), data, dataStart, dataLength, signature, signatureStart);
+    }
+    return result;
+  }
+}