aboutsummaryrefslogtreecommitdiff
path: root/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
diff options
context:
space:
mode:
Diffstat (limited to 'ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java')
-rw-r--r--ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java622
1 files changed, 622 insertions, 0 deletions
diff --git a/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
new file mode 100644
index 0000000..018ac02
--- /dev/null
+++ b/ready_se/google/keymint/KM300/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java
@@ -0,0 +1,622 @@
+/*
+ * 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;
+
+/**
+ * This class extends from KMKeymasterApplet which is main entry point to receive apdu commands. All
+ * the provision commands are processed here and later the data is handed over to the KMDataStore
+ * class which stores the data in the flash memory.
+ */
+public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener {
+ // Magic number version stored along with provisioned data. This is used to differentiate
+ // between data before and after the magic number is used.
+ 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 = 0x0400;
+ // This flag is used to know if card reset happened.
+ 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_UDS_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;
+ // The length of the provisioned pre shared key.
+ public static final byte SHARED_SECRET_KEY_SIZE = 32;
+
+ // Version of the database which is used to differentiate between different version of the
+ // database.
+ 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 updateApduStatusFlags(short apduIns) {
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 0;
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 1;
+ switch (apduIns) {
+ case INS_GET_PROVISION_STATUS_CMD:
+ case INS_SE_FACTORY_PROVISIONING_LOCK_CMD:
+ apduStatusFlags[APDU_CASE4_COMMAND_STATUS_INDEX] = 0;
+ break;
+ default:
+ super.updateApduStatusFlags(apduIns);
+ }
+ }
+
+ @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;
+ }
+ updateApduStatusFlags(apduIns);
+ 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_UDS_CERT_CHAIN_CMD:
+ processProvisionRkpUdsCertChain(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_UDS_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) {
+ // 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 dcc = generateDiceCertChain(scratchPad);
+ short len = KMKeymasterApplet.encodeToApduBuffer(dcc, 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 processProvisionRkpUdsCertChain(APDU apdu) {
+ // X509 certificate chain is received as shown below:
+ /**
+ * x509CertChain = bstr .cbor UdsCerts
+ *
+ * <p>UdsCerts = { * SignerName => UdsCertChain }
+ *
+ * <p>; SignerName is a string identifier that indicates both the signing authority as ; well as
+ * the format of the UdsCertChain SignerName = tstr
+ *
+ * <p>UdsCertChain = [ 2* X509Certificate ; Root -> ... -> Leaf. "Root" is the vendor
+ * self-signed ; cert, "Leaf" contains UDS_Public. There may also be ; intermediate certificates
+ * between Root and Leaf. ]
+ *
+ * <p>; 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();
+ apduStatusFlags[APDU_INCOMING_AND_RECEIVE_STATUS_INDEX] = 1;
+ 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.persistUdsCertChain(
+ buffer,
+ (short) (bufferStartOffset + byteHeaderLen),
+ (short) (bufferLength - byteHeaderLen));
+ kmDataStore.setProvisionStatus(PROVISION_STATUS_UDS_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];
+ }
+}