From 000e74945d69462620d01906559aa87bbfab019b Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Wed, 1 Jun 2022 04:12:11 +0000 Subject: [PATCH] added RMA support in Jcard --- .../javacard/keymaster/KMJCardSimApplet.java | 550 ++++++++++++------ .../javacard/seprovider/KMJCardSimulator.java | 51 +- .../javacard/seprovider/KMSEProvider.java | 71 ++- .../javacard/seprovider/KMUpgradable.java | 2 +- 4 files changed, 472 insertions(+), 202 deletions(-) diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 12ba75ba..904a44ae 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -26,21 +26,28 @@ public class KMJCardSimApplet extends KMKeymasterApplet { + 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 + 1; private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_KEYMINT_PROVIDER_APDU_START + 2; - private static final byte INS_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; + private static final byte INS_OEM_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 4; private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 5; private static final byte INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD = INS_KEYMINT_PROVIDER_APDU_START + 6; private static final byte INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7; - private static final byte INS_SET_BOOT_ENDED_CMD = - INS_KEYMINT_PROVIDER_APDU_START + 8; + private static final byte INS_SET_BOOT_ENDED_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 8; + private static final byte INS_SE_FACTORY_PROVISIONING_LOCK_CMD = INS_KEYMINT_PROVIDER_APDU_START + 9; + private static final byte INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD = INS_KEYMINT_PROVIDER_APDU_START + 10; + private static final byte INS_OEM_UNLOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 11; + private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F; public static final byte BOOT_KEY_MAX_SIZE = 32; @@ -48,18 +55,19 @@ public class KMJCardSimApplet extends KMKeymasterApplet { // Provision reporting status private static final byte NOT_PROVISIONED = 0x00; - private static final byte PROVISION_STATUS_ATTESTATION_KEY = 0x01; - private static final byte PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02; - private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; + private static final byte PROVISION_STATUS_ATTESTATION_KEY = 0x01; // unused in keymint + private static final byte PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02; // unused in keymint + private static final byte PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04; // unused in keymint private static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x40; private static final byte PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = (byte) 0x80; + private static final byte PROVISION_STATUS_SE_LOCKED = 0x01; + private static final byte PROVISION_STATUS_OEM_PUBLIC_KEY = 0x02; public static final short SHARED_SECRET_KEY_SIZE = 32; - // Package version. protected short packageVersion; @@ -89,82 +97,71 @@ public void process(APDU apdu) { } short apduIns = validateApdu(apdu); if (apduIns == KMType.INVALID_VALUE) { - return; + return; } - if (((KMJCardSimulator) seProvider).isPowerReset()) { + if (((KMJCardSimulator)seProvider).isPowerReset()) { super.powerReset(); } - if (kmDataStore.isProvisionLocked()) { + if (isCommandAllowed(apduIns)) { switch (apduIns) { + case INS_PROVISION_ATTEST_IDS_CMD: + processProvisionAttestIdsCmd(apdu); + kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTEST_IDS); + sendError(apdu, KMError.OK); + break; + + case INS_PROVISION_PRESHARED_SECRET_CMD: + processProvisionPreSharedSecretCmd(apdu); + kmDataStore.setProvisionStatus(PROVISION_STATUS_PRESHARED_SECRET); + sendError(apdu, KMError.OK); + break; + + case INS_GET_PROVISION_STATUS_CMD: + processGetProvisionStatusCmd(apdu); + break; + case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); break; case INS_SET_BOOT_ENDED_CMD: - //set the flag to mark boot ended - kmDataStore.setBootEndedStatus(true); + processSetBootEndedCmd(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); sendError(apdu, KMError.OK); break; - case INS_GET_PROVISION_STATUS_CMD: - processGetProvisionStatusCmd(apdu); + case INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD: + processProvisionOEMRootPublicKeyCmd(apdu); + kmDataStore.setProvisionStatus(PROVISION_STATUS_OEM_PUBLIC_KEY); + sendError(apdu, KMError.OK); break; + case INS_OEM_LOCK_PROVISIONING_CMD: + processOEMLockProvisionCmd(apdu); + break; + + case INS_OEM_UNLOCK_PROVISIONING_CMD: + processOEMUnlockProvisionCmd(apdu); + break; + default: super.process(apdu); break; } - return; - } - - switch (apduIns) { - case INS_PROVISION_ATTEST_IDS_CMD: - processProvisionAttestIdsCmd(apdu); - kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTEST_IDS); - sendError(apdu, KMError.OK); - break; - - case INS_PROVISION_PRESHARED_SECRET_CMD: - processProvisionPreSharedSecretCmd(apdu); - kmDataStore.setProvisionStatus(PROVISION_STATUS_PRESHARED_SECRET); - sendError(apdu, KMError.OK); - break; - - case INS_GET_PROVISION_STATUS_CMD: - processGetProvisionStatusCmd(apdu); - break; - - case INS_LOCK_PROVISIONING_CMD: - processLockProvisioningCmd(apdu); - break; - - case INS_SET_BOOT_PARAMS_CMD: - processSetBootParamsCmd(apdu); - break; - - case INS_SET_BOOT_ENDED_CMD: - //set the flag to mark boot ended - kmDataStore.setBootEndedStatus(true); - sendError(apdu, KMError.OK); - break; - - case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: - processProvisionRkpDeviceUniqueKeyPair(apdu); - break; - - case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD: - processProvisionRkpAdditionalCertChain(apdu); - break; - - default: - // Allow other commands only if provision is completed. - if (isProvisioningComplete()) { - super.process(apdu); - } else { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - break; + } else { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } } catch (KMException exception) { sendError(apdu, KMException.reason()); @@ -179,36 +176,258 @@ public void process(APDU apdu) { } } + 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_OEM_ROOT_PUBLIC_KEY_CMD: + if(kmDataStore.isProvisionLocked()) { + result = false; + } + break; - private boolean isProvisioningComplete() { + case INS_OEM_UNLOCK_PROVISIONING_CMD: + if(!kmDataStore.isProvisionLocked()) { + result = false; + } + break; + + case INS_SE_FACTORY_PROVISIONING_LOCK_CMD: + if(!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 (!(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_SET_BOOT_PARAMS_CMD: + case INS_SET_BOOT_ENDED_CMD: + 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 dInex = repository.allocReclaimableMemory((short)1); byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); boolean result = false; - if ((0 != (data[dInex] & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) - && (0 != (data[dInex] & PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) - && (0 != (data[dInex] & PROVISION_STATUS_PRESHARED_SECRET))) { - result = true; + if ((0 != (data[dInex] & PROVISION_STATUS_SE_LOCKED))) { + result = true; } repository.reclaimMemory((short)1); return result; } - private void processLockProvisioningCmd(APDU apdu) { - if (isProvisioningComplete()) { - kmDataStore.setProvisionLocked(); - kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED); - sendError(apdu, KMError.OK); + private boolean isSeFactoryProvisioningComplete() { + short dIndex = repository.allocReclaimableMemory((short)1); + byte data[] = repository.getHeap(); + kmDataStore.getProvisionStatus(data, dIndex); + boolean result = false; + if ((0 != (data[dIndex] & INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD)) + && (0 != ((data[dIndex] & INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD)))) { + result = true; + } + repository.reclaimMemory((short)1); + return result; + } + + private void processOEMUnlockProvisionCmd(APDU apdu) { + handleOEMLockUnlockCmd(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu); + kmDataStore.unlockProvision(PROVISION_STATUS_PROVISIONING_LOCKED); + sendError(apdu, KMError.OK); + } + + private void processOEMLockProvisionCmd(APDU apdu) { + handleOEMLockUnlockCmd(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu); + // Enable the lock bit in provision status. + kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED); + sendError(apdu, KMError.OK); + } + + private void handleOEMLockUnlockCmd(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 { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + 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); + sendError(apdu, KMError.OK); + } + + private static void processProvisionRkpAdditionalCertChain(APDU apdu) { + // Prepare the expression to decode + 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 coseSignArr = KMArray.exp(arrInst); + short map = KMMap.instance((short) 1); + KMMap.cast(map).add((short) 0, KMTextString.exp(), coseSignArr); + // receive incoming data and decode it. + 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); + } + // decode + map = decoder.decode(map, buffer, bufferStartOffset, bufferLength); + arrInst = KMMap.cast(map).getKeyValue((short) 0); + // Validate Additional certificate chain. + short leafCoseKey = + validateCertChain(false, KMCose.COSE_ALG_ES256, KMCose.COSE_ALG_ES256, arrInst, + srcBuffer, null); + // Compare the DK_Pub. + short pubKeyLen = KMCoseKey.cast(leafCoseKey).getEcdsa256PublicKey(srcBuffer, (short) 0); + KMDeviceUniqueKeyPair uniqueKey = kmDataStore.getRkpDeviceUniqueKeyPair(false); + if (uniqueKey == null) { + KMException.throwIt(KMError.STATUS_FAILED); + } + short uniqueKeyLen = uniqueKey.getPublicKey(srcBuffer, pubKeyLen); + if ((pubKeyLen != uniqueKeyLen) || + (0 != Util.arrayCompare(srcBuffer, (short) 0, srcBuffer, pubKeyLen, pubKeyLen))) { + KMException.throwIt(KMError.STATUS_FAILED); + } + kmDataStore.persistAdditionalCertChain(buffer, bufferStartOffset, bufferLength); + kmDataStore.setProvisionStatus(PROVISION_STATUS_ADDITIONAL_CERT_CHAIN); + //reclaim memory + repository.reclaimMemory(bufferLength); + sendError(apdu, KMError.OK); } - private void processProvisionAttestIdsCmd(APDU apdu) { + 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); @@ -231,6 +450,9 @@ public void setAttestationIds(short attIdVals) { 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++; @@ -238,7 +460,6 @@ public void setAttestationIds(short attIdVals) { } private void processProvisionPreSharedSecretCmd(APDU apdu) { - short blob = KMByteBlob.exp(); short argsProto = KMArray.instance((short) 1); KMArray.cast(argsProto).add((short) 0, blob); @@ -255,6 +476,30 @@ private void processProvisionPreSharedSecretCmd(APDU apdu) { 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 (((KMJCardSimulator) 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) { @@ -266,9 +511,24 @@ private void processGetProvisionStatusCmd(APDU apdu) { sendOutgoing(apdu, resp); } + private void processSetBootEndedCmd(APDU apdu) { + if (seProvider.isBootSignalEventSupported() + && (!seProvider.isDeviceRebooted())) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + //set the flag to mark boot ended + kmDataStore.setBootEndedStatus(true); + seProvider.clearDeviceBooted(false); + sendError(apdu, KMError.OK); + } + private void processSetBootParamsCmd(APDU apdu) { - short argsProto = KMArray.instance((short) 5); + if (seProvider.isBootSignalEventSupported() + && (!seProvider.isDeviceRebooted())) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + short argsProto = KMArray.instance((short) 5); byte[] scratchPad = apdu.getBuffer(); // Array of 4 expected arguments // Argument 0 Boot Patch level @@ -314,7 +574,6 @@ private void processSetBootParamsCmd(APDU apdu) { enumVal = KMEnum.cast(bootParam).getVal(); kmDataStore.setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); - // Clear the Computed SharedHmac and Hmac nonce from persistent memory. Util.arrayFillNonAtomic(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE, (byte) 0); kmDataStore.createComputedHmacKey(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE); @@ -323,18 +582,50 @@ private void processSetBootParamsCmd(APDU apdu) { sendError(apdu, KMError.OK); } + private boolean isProvisioningComplete() { + short dInex = repository.allocReclaimableMemory((short)1); + byte data[] = repository.getHeap(); + kmDataStore.getProvisionStatus(data, dInex); + boolean result = false; + if (kmDataStore.isProvisionLocked() || ((0 != (data[dInex] & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) + && (0 != (data[dInex] & PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) + && (0 != (data[dInex] & PROVISION_STATUS_PRESHARED_SECRET)) + && (0 != (data[dInex] & PROVISION_STATUS_ATTEST_IDS)))) { + result = true; + } + repository.reclaimMemory((short)1); + return result; + } + + private boolean isOemProvisionComplete() { + short dInex = repository.allocReclaimableMemory((short)1); + byte data[] = repository.getHeap(); + kmDataStore.getProvisionStatus(data, dInex); + boolean result = false; + if ((0 != (data[dInex] & PROVISION_STATUS_OEM_PUBLIC_KEY)) + && (0 != (data[dInex] & PROVISION_STATUS_SE_LOCKED))) { + result = true; + } + repository.reclaimMemory((short)1); + return result; + } + + private void processLockProvisioningCmd(APDU apdu) { + if (isProvisioningComplete()) { + kmDataStore.setProvisionLocked(); + kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED); + sendError(apdu, KMError.OK); + } else { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + } + private short validateApdu(APDU apdu) { // Read the apdu header and buffer. byte[] apduBuffer = apdu.getBuffer(); byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); - // Validate APDU Header. - if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { - sendError(apdu, KMError.UNSUPPORTED_CLA); - return KMType.INVALID_VALUE; - } - // Validate P1P2. if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { sendError(apdu, KMError.INVALID_P1P2); @@ -343,93 +634,4 @@ private short validateApdu(APDU apdu) { return apduBuffer[ISO7816.OFFSET_INS]; } - 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); - sendError(apdu, KMError.OK); - } - - private static void processProvisionRkpAdditionalCertChain(APDU apdu) { - // Prepare the expression to decode - 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 coseSignArr = KMArray.exp(arrInst); - short map = KMMap.instance((short) 1); - KMMap.cast(map).add((short) 0, KMTextString.exp(), coseSignArr); - // receive incoming data and decode it. - 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); - } - // decode - map = decoder.decode(map, buffer, bufferStartOffset, bufferLength); - arrInst = KMMap.cast(map).getKeyValue((short) 0); - // Validate Additional certificate chain. - short leafCoseKey = - validateCertChain(false, KMCose.COSE_ALG_ES256, KMCose.COSE_ALG_ES256, arrInst, - srcBuffer, null); - // Compare the DK_Pub. - short pubKeyLen = KMCoseKey.cast(leafCoseKey).getEcdsa256PublicKey(srcBuffer, (short) 0); - KMDeviceUniqueKeyPair uniqueKey = kmDataStore.getRkpDeviceUniqueKeyPair(false); - if (uniqueKey == null) { - KMException.throwIt(KMError.STATUS_FAILED); - } - short uniqueKeyLen = uniqueKey.getPublicKey(srcBuffer, pubKeyLen); - if ((pubKeyLen != uniqueKeyLen) || - (0 != Util.arrayCompare(srcBuffer, (short) 0, srcBuffer, pubKeyLen, pubKeyLen))) { - KMException.throwIt(KMError.STATUS_FAILED); - } - kmDataStore.persistAdditionalCertChain(buffer, bufferStartOffset, bufferLength); - kmDataStore.setProvisionStatus(PROVISION_STATUS_ADDITIONAL_CERT_CHAIN); - //reclaim memory - repository.reclaimMemory(bufferLength); - sendError(apdu, KMError.OK); - } - - private static short buildErrorStatus(short err) { - short int32Ptr = KMInteger.instance((short) 4); - short powerResetStatus = 0; - if (((KMJCardSimulator) 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. - return int32Ptr; - } - } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java index 937fb349..35c458bb 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java @@ -1407,7 +1407,7 @@ public void onSave(Element element, byte interfaceType, Object object) { } @Override - public Object onResore(Element element) { + public Object onRestore(Element element) { return null; } @@ -1450,17 +1450,44 @@ public com.android.javacard.seprovider.KMPreSharedKey createPreSharedKey( return (KMPreSharedKey) preSharedKey; } - @Override - public com.android.javacard.seprovider.KMAttestationKey createAttestationKey( - com.android.javacard.seprovider.KMAttestationKey attestationKey, byte[] keyData, short offset, - short length) { - if (attestationKey == null) { - // Strongbox supports only P-256 curve for EC key. - KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); - attestationKey = new KMECPrivateKey(ecKeyPair); + +@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: + KMCipher aesGcm = createAesGcmCipher(purpose, macLength, keyBuf, keyStart, keyLength, + ivBuf, ivStart, ivLength); + opr = new KMOperationImpl(aesGcm); + break; + case KMType.HMAC: + Signature signerVerifier = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, + keyLength); + opr = new KMOperationImpl(signerVerifier); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; } - ((KMECPrivateKey) attestationKey).setS(keyData, offset, length); - return (KMAttestationKey) attestationKey; - } + return opr; +} + +@Override +public boolean isBootSignalEventSupported() { + return false; +} +@Override +public boolean isDeviceRebooted() { + return false; +} + +@Override +public void clearDeviceBooted(boolean resetBootFlag) { + // TODO Auto-generated method stub + +} } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index c4445114..d77f2167 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -25,6 +25,28 @@ */ 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. @@ -540,6 +562,39 @@ KMOperation initSymmetricOperation( short ivLength, short macLength); + /** + * 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 @@ -643,20 +698,6 @@ KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(KMDeviceUniqueKeyPair key, short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset); - /** - * This function creates an ECKey and initializes the ECPrivateKey with the provided input key - * data. The initialized Key is maintained by the SEProvider. This function should be called only - * while provisioning the attestation key. - * - * @param keyData buffer containing the ec private key. - * @param offset start of the buffer. - * @param length length of the buffer. - * @return An instance of KMAttestationKey. - */ - KMAttestationKey createAttestationKey(KMAttestationKey attestationKey, byte[] keyData, - short offset, - short length); - /** * This function generates a HMAC key from the provided key buffers. * @@ -684,7 +725,7 @@ KMPreSharedKey createPreSharedKey(KMPreSharedKey presharedKey, byte[] key, short * @param element instance of the Element class. * @return restored object. */ - Object onResore(Element element); + Object onRestore(Element element); /** * This function returns the count of the primitive bytes required to diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java index 9bca1c8c..7a990681 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java @@ -21,7 +21,7 @@ public interface KMUpgradable { void onSave(Element ele); - void onRestore(Element ele); + void onRestore(Element element, short oldVersion, short currentVersion); short getBackupPrimitiveByteCount();