From 4709a1b1aae0c6ebfee6492219dd59223d80ed9b Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Mon, 30 May 2022 05:17:02 +0000 Subject: [PATCH 01/15] Added RMA support --- .../javacard/keymaster/KMAndroidSEApplet.java | 336 +++++++++++++----- .../seprovider/KMAndroidSEProvider.java | 2 +- .../javacard/seprovider/KMSEProvider.java | 2 +- .../javacard/seprovider/KMUpgradable.java | 2 +- .../javacard/keymaster/KMKeymasterApplet.java | 13 + .../keymaster/KMKeymintDataStore.java | 47 ++- ProvisioningTool/include/constants.h | 13 +- ProvisioningTool/sample_json_keymint_cf.txt | 3 +- ProvisioningTool/src/construct_apdus.cpp | 220 +++++++++++- ProvisioningTool/src/provision.cpp | 198 +++++++++-- ProvisioningTool/src/utils.cpp | 6 +- .../test_resources/oem_root_key.der | Bin 0 -> 138 bytes 12 files changed, 703 insertions(+), 139 deletions(-) create mode 100644 ProvisioningTool/test_resources/oem_root_key.der diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index cde7b6de..98f6975b 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -35,7 +35,7 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis // 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. - private static final short KM_PERSISTENT_DATA_STORAGE_VERSION = 0x0100; + private static final short KM_APPLET_PACKAGE_VERSION = 0x0200; private static final byte KM_BEGIN_STATE = 0x00; private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; @@ -46,7 +46,7 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis 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 = @@ -55,6 +55,10 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis 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_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; @@ -62,14 +66,16 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis // 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; @@ -78,9 +84,7 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis KMAndroidSEApplet() { super(new KMAndroidSEProvider()); - if (!UpgradeManager.isUpgrading()) { - packageVersion = KM_PERSISTENT_DATA_STORAGE_VERSION; - } + packageVersion = KM_APPLET_PACKAGE_VERSION; } /** @@ -111,76 +115,61 @@ public void process(APDU apdu) { super.powerReset(); } - if (kmDataStore.isProvisionLocked()) { + if (isCommandAllowed(apduIns)) { switch (apduIns) { - case INS_SET_BOOT_PARAMS_CMD: - processSetBootParamsCmd(apdu); + case INS_PROVISION_ATTEST_IDS_CMD: + processProvisionAttestIdsCmd(apdu); + kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTEST_IDS); + sendError(apdu, KMError.OK); break; - - case INS_SET_BOOT_ENDED_CMD: - //set the flag to mark boot ended - kmDataStore.setBootEndedStatus(true); + + 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_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_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()); @@ -195,6 +184,181 @@ 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; + + 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_SE_LOCKED))) { + result = true; + } + repository.reclaimMemory((short)1); + return result; + } + + 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 { + 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(); @@ -417,9 +581,23 @@ private boolean isProvisioningComplete() { byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); boolean result = false; - if ((0 != (data[dInex] & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) + 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_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); @@ -444,12 +622,12 @@ public void onCleanup() { public void onConsolidate() { } - private boolean isUpgradeAllowed(short version) { + private boolean isUpgradeAllowed(short oldVersion) { boolean upgradeAllowed = false; - short oldMajorVersion = (short) ((version >> 8) & 0x00FF); - short oldMinorVersion = (short) (version & 0x00FF); - short currentMajorVersion = (short) (KM_PERSISTENT_DATA_STORAGE_VERSION >> 8 & 0x00FF); - short currentMinorVersion = (short) (KM_PERSISTENT_DATA_STORAGE_VERSION & 0x00FF); + short oldMajorVersion = (short) ((oldVersion >> 8) & 0x00FF); + short oldMinorVersion = (short) (oldVersion & 0x00FF); + short currentMajorVersion = (short) (KM_APPLET_PACKAGE_VERSION >> 8 & 0x00FF); + short currentMinorVersion = (short) (KM_APPLET_PACKAGE_VERSION & 0x00FF); // Downgrade of the Applet is not allowed. // Upgrade is not allowed to a next version which is not immediate. if ((short) (currentMajorVersion - oldMajorVersion) == 1) { @@ -471,12 +649,12 @@ public void onRestore(Element element) { if (magicNumber != KM_MAGIC_NUMBER) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - short packageVersion = element.readShort(); + short oldPackageVersion = element.readShort(); // Validate version. - if (0 != packageVersion && !isUpgradeAllowed(packageVersion)) { + if (!isUpgradeAllowed(oldPackageVersion)) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - kmDataStore.onRestore(element); + kmDataStore.onRestore(element, oldPackageVersion, KM_APPLET_PACKAGE_VERSION); } @Override @@ -500,12 +678,6 @@ private short validateApdu(APDU apdu) { 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); diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index 5365dcaa..4dde7acf 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -1125,7 +1125,7 @@ public void onSave(Element element, byte interfaceType, Object object) { } @Override - public Object onResore(Element element) { + public Object onRestore(Element element) { if (element == null) { return null; } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 9177109c..6e209daa 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -704,7 +704,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/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java index 9bca1c8c..dee3413a 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java +++ b/Applet/AndroidSEProviderLib/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 ele, short oldVersion, short currentVersion); short getBackupPrimitiveByteCount(); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 622ef678..2b1736a3 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -104,6 +104,19 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe 0x65, 0x6E }; + //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 + }; + + private static final byte OEM_LOCK = 1; + private static final byte OEM_UNLOCK = 0; + 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. diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index 581a18e2..34a477c7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -102,6 +102,7 @@ public class KMKeymintDataStore implements KMUpgradable { private KMPreSharedKey preSharedKey; private KMComputedHmacKey computedHmacKey; private KMRkpMacKey rkpMacKey; + private byte[] oemRootPublicKey; public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) { seProvider = provider; @@ -112,10 +113,10 @@ public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) { if (!isUpgrading) { additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; bcc = new byte[BCC_MAX_SIZE]; + oemRootPublicKey = new byte[65]; setDeviceLock(false); setDeviceLockPasswordOnly(false); } -// initializeCertificateDataBuffer(isUpgrading, factoryAttestSupport); } private void initDataTable(boolean isUpgrading) { @@ -775,6 +776,25 @@ public void getProvisionStatus(byte[] scratchpad, short offset) { readDataEntry(PROVISIONED_STATUS, scratchpad, offset); } + public void unlockProvision(byte unlockOffset) { + short offset = repository.alloc((short) 1); + byte[] buf = repository.getHeap(); + getProvisionStatus(buf, offset); + buf[offset] &= ~unlockOffset; + writeDataEntry(PROVISIONED_STATUS, buf, offset, (short) 1); + } + + public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) { + if (inLength != 65) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength); + } + + public byte[] getOEMRootPublicKey() { + return oemRootPublicKey; + } + @Override public void onSave(Element element) { // Prmitives @@ -796,6 +816,8 @@ public void onSave(Element element) { element.write(bootPatchLevel); 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_COMPUTED_HMAC_KEY, @@ -806,7 +828,7 @@ public void onSave(Element element) { } @Override - public void onRestore(Element element) { + public void onRestore(Element element, short oldVersion, short currentVersion) { // Read Primitives dataIndex = element.readShort(); deviceBootLocked = element.readBoolean(); @@ -826,12 +848,16 @@ public void onRestore(Element element) { bootPatchLevel = (byte[]) element.readObject(); additionalCertChain = (byte[]) element.readObject(); bcc = (byte[]) element.readObject(); + //oemRootPublicKey has to be provisioned + if (oldVersion >= 0x0200) { + oemRootPublicKey = (byte[]) element.readObject(); + } // Read Key Objects - masterKey = (KMMasterKey) seProvider.onResore(element); - computedHmacKey = (KMComputedHmacKey) seProvider.onResore(element); - preSharedKey = (KMPreSharedKey) seProvider.onResore(element); - deviceUniqueKeyPair = (KMDeviceUniqueKeyPair) seProvider.onResore(element); - rkpMacKey = (KMRkpMacKey) seProvider.onResore(element); + masterKey = (KMMasterKey) seProvider.onRestore(element); + computedHmacKey = (KMComputedHmacKey) seProvider.onRestore(element); + preSharedKey = (KMPreSharedKey) seProvider.onRestore(element); + deviceUniqueKeyPair = (KMDeviceUniqueKeyPair) seProvider.onRestore(element); + rkpMacKey = (KMRkpMacKey) seProvider.onRestore(element); } @Override @@ -853,9 +879,10 @@ public short getBackupObjectCount() { // dataTable - 1 // AttestationIds - 8 // bootParameters - 3 - // AdditionalCertificateChain - 1 - // BCC - 1 - return (short) (14 + + // AdditionalCertificateChain - 1 + // BCC - 1 + // oemRootPublicKey - 1 + return (short) (15 + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY) + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/include/constants.h index 89854703..62c2efcc 100644 --- a/ProvisioningTool/include/constants.h +++ b/ProvisioningTool/include/constants.h @@ -58,6 +58,10 @@ typedef UniquePtr BIGNUM_Ptr; // EC Affine point length for Nist P256. constexpr uint32_t kAffinePointLength = 32; +// OEM Lock / Unlock Verification message +constexpr char kOemProvisioningLock[] = "OEM Provisioning Lock"; +constexpr char kEnableRma[] = "Enable RMA"; + // Tags constexpr uint64_t kTagAlgorithm = 268435458u; constexpr uint64_t kTagDigest = 536870917u; @@ -77,6 +81,7 @@ constexpr uint64_t kCurveP256 = 1; constexpr uint64_t kAlgorithmEc = 3; constexpr uint64_t kDigestSha256 = 4; constexpr uint64_t kPurposeAttest = 0x7F; +constexpr uint64_t kPurposeVerify = 3; constexpr uint64_t kKeyFormatRaw = 3; // json keys @@ -91,13 +96,19 @@ constexpr char kAdditionalCertChain[] = "additional_cert_chain"; constexpr char kSignerInfo[] = "signer_info"; constexpr char kProvisionStatus[] = "provision_status"; constexpr char kLockProvision[] = "lock_provision"; +constexpr char kOEMRootKey[] = "oem_root_key"; +constexpr char kSeFactoryProvisionLock[] = "se_factory_lock"; +constexpr char kUnLockProvision[] = "unlock_provision"; // Instruction constatnts // TODO Modify according to keymint constexpr int kAttestationIdsCmd = INS_BEGIN_KM_CMD + 1; constexpr int kPresharedSecretCmd = INS_BEGIN_KM_CMD + 2; -constexpr int kLockProvisionCmd = INS_BEGIN_KM_CMD + 3; +constexpr int kOemLockProvisionCmd = INS_BEGIN_KM_CMD + 3; constexpr int kGetProvisionStatusCmd = INS_BEGIN_KM_CMD + 4; constexpr int kBootParamsCmd = INS_BEGIN_KM_CMD + 5; constexpr int kDeviceUniqueKeyCmd = INS_BEGIN_KM_CMD + 6; constexpr int kAdditionalCertChainCmd = INS_BEGIN_KM_CMD + 7; +constexpr int kSeFactoryLockCmd = INS_BEGIN_KM_CMD + 9; +constexpr int kOemRootPublicKeyCmd = INS_BEGIN_KM_CMD + 10; +constexpr int kOemUnLockProvisionCmd = INS_BEGIN_KM_CMD + 11; diff --git a/ProvisioningTool/sample_json_keymint_cf.txt b/ProvisioningTool/sample_json_keymint_cf.txt index cb454584..af868d65 100644 --- a/ProvisioningTool/sample_json_keymint_cf.txt +++ b/ProvisioningTool/sample_json_keymint_cf.txt @@ -24,5 +24,6 @@ "test_resources/ca_key.der", "test_resources/intermediate_key.der" ] - } + }, + "oem_root_key": "test_resources/oem_root_key.der" } diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/src/construct_apdus.cpp index 190e3a78..5b5d50ad 100644 --- a/ProvisioningTool/src/construct_apdus.cpp +++ b/ProvisioningTool/src/construct_apdus.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #include #include "cppbor/cppbor.h" #include "cppcose/cppcose.h" +#include +#include // static globals. static std::string inputFileName; @@ -55,6 +58,8 @@ using cppcose::bytevec; static int processInputFile(); static int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& pub_x, std::vector& pub_y); +static int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, + std::vector& publicKey); static int processAttestationIds(); static int processSharedSecret(); static int processSetBootParameters(); @@ -66,7 +71,15 @@ static int getStringValue(Json::Value& Obj, const char* key, std::string& str); static int processDeviceUniqueKey(); static int processAdditionalCertificateChain(); static int getDeviceUniqueKey(bytevec& privKey, bytevec& x, bytevec& y); - +static int processOEMRootPublicKey(); +static int processSEFactoryLock(); +static int signEcdsaDigest(const std::vector& key, const std::vector& data, + std::vector& out); +static int sha256(const std::vector& data, std::vector& out); +static int sendOEMAuthenticationToken(const char* toBeSigned, int oemCmd, const char* mapKey); +static int processOEMFactoryProvisionLock(); +static int processOEMFactoryProvisionUnLock(); +static int processGetProvisionStatus(); // Print usage. void usage() { @@ -139,6 +152,47 @@ int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& pkcs8Blob, std::vector& secret, + std::vector& publicKey) { + + const uint8_t *data = pkcs8Blob.data(); + EVP_PKEY *evpkey = d2i_PrivateKey(EVP_PKEY_EC, nullptr, &data, pkcs8Blob.size()); + if(!evpkey) { + printf("\n Failed to decode private key from PKCS8, Error: %ld", ERR_peek_last_error()); + return FAILURE; + } + EVP_PKEY_Ptr pkey(evpkey); + + EC_KEY_Ptr ec_key(EVP_PKEY_get1_EC_KEY(pkey.get())); + if(!ec_key.get()) { + printf("\n Failed to create EC_KEY, Error: %ld", ERR_peek_last_error()); + return FAILURE; + } + + //Get EC Group + const EC_GROUP *group = EC_KEY_get0_group(ec_key.get()); + if(group == NULL) { + printf("\n Failed to get the EC_GROUP from ec_key."); + return FAILURE; + } + + //Extract private key. + const BIGNUM *privBn = EC_KEY_get0_private_key(ec_key.get()); + int privKeyLen = BN_num_bytes(privBn); + std::unique_ptr privKey(new uint8_t[privKeyLen]); + BN_bn2bin(privBn, privKey.get()); + secret.insert(secret.begin(), privKey.get(), privKey.get()+privKeyLen); + + //Extract public key. + const EC_POINT *point = EC_KEY_get0_public_key(ec_key.get()); + int pubKeyLen=0; + pubKeyLen = EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + std::unique_ptr pubKey(new uint8_t[pubKeyLen]); + EC_POINT_point2oct(group, point, POINT_CONVERSION_UNCOMPRESSED, pubKey.get(), pubKeyLen, NULL); + publicKey.insert(publicKey.begin(), pubKey.get(), pubKey.get()+pubKeyLen); + return SUCCESS; +} + int getIntValue(Json::Value& bootParamsObj, const char* key, uint32_t *value) { Json::Value val = bootParamsObj[key]; if(val.empty()) @@ -183,6 +237,53 @@ int getBlobValue(Json::Value& bootParamsObj, const char* key, std::vector sha256(const std::vector& data) { + std::vector ret(32); // SHA256 digest output len + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, data.data(), data.size()); + SHA256_Final((unsigned char*)ret.data(), &ctx); + return ret; +} + +// TODO use unique_ptr +int signEcdsaDigest(const std::vector& key, const std::vector& data, + std::vector& out) { + size_t len; + unsigned char* p = nullptr; + ECDSA_SIG *sig = nullptr; + EC_KEY *ec_key = nullptr; + std::vector signature; + int result = FAILURE; + BIGNUM *bn = BN_bin2bn(key.data(), key.size(), nullptr); + if (bn == nullptr) { + printf("Error creating BIGNUM"); + goto exit; + } + + ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + if (EC_KEY_set_private_key(ec_key, bn) != 1) { + printf("Error setting private key from BIGNUM"); + goto exit; + } + + sig = ECDSA_do_sign(data.data(), data.size(), ec_key); + if (sig == nullptr) { + printf("Error signing digest"); + goto exit; + } + len = i2d_ECDSA_SIG(sig, nullptr); + signature.resize(len); + p = (unsigned char*)signature.data(); + i2d_ECDSA_SIG(sig, &p); + out = signature; + result = SUCCESS; +exit: + if (bn != nullptr) BN_free(bn); + if (ec_key != nullptr) EC_KEY_free(ec_key); + if (sig != nullptr) ECDSA_SIG_free(sig); + return result; +} // Parses the input json file. Prepares the apdu for each entry in the json // file and dump all the apdus into the output json file. @@ -196,6 +297,11 @@ int processInputFile() { 0 != processAdditionalCertificateChain() || 0 != processAttestationIds() || 0 != processSharedSecret() || + 0 != processOEMRootPublicKey() || + 0 != processOEMFactoryProvisionLock() || + 0 != processOEMFactoryProvisionUnLock() || + 0 != processGetProvisionStatus() || + 0 != processSEFactoryLock() || 0 != processSetBootParameters()) { return FAILURE; } @@ -363,6 +469,118 @@ int processDeviceUniqueKey() { return SUCCESS; } +int sendOEMAuthenticationToken(const char* toBeSigned, int oemCmd, const char* mapKey) { + Json::Value keyFile = root.get(kOEMRootKey, Json::Value::nullRef); + if (!keyFile.isNull()) { + std::vector data; + std::vector privateKey; + std::vector publicKey; + std::vector signature; + std::vector plainMsg(toBeSigned, toBeSigned + strlen(toBeSigned)); + + std::string keyFileName = keyFile.asString(); + if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { + printf("\n Failed to read the OEM root key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, publicKey)) { + return FAILURE; + } + if (SUCCESS != signEcdsaDigest(privateKey, sha256(plainMsg), signature)) { + printf("\n Failed to sign the message.\n"); + return FAILURE; + } + // Prepare cbor input. + Array input; + input.add(signature); + std::vector cborData = input.encode(); + + if(SUCCESS != addApduHeader(oemCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[mapKey] = getHexString(cborData); + } else { + printf("\n Improper value for OEM_root_key in json file \n"); + return FAILURE; + } + const char *lockCmd = (oemCmd == kOemLockProvisionCmd) ? "lock" : "unlock"; + printf("\n Constructed OEM Factory provision %s successfully. \n", lockCmd); + return SUCCESS; +} + +int processOEMFactoryProvisionLock() { + return sendOEMAuthenticationToken(kOemProvisioningLock, kOemLockProvisionCmd, kLockProvision); +} + +int processOEMFactoryProvisionUnLock() { + return sendOEMAuthenticationToken(kEnableRma, kOemUnLockProvisionCmd, kUnLockProvision); +} + +int processGetProvisionStatus() { + std::vector cborData; + if (SUCCESS != addApduHeader(kGetProvisionStatusCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kProvisionStatus] = getHexString(cborData); + printf("\n Constructed get Provision status APDU successfully. \n"); + return SUCCESS; +} + +int processSEFactoryLock() { + std::vector cborData; + if (SUCCESS != addApduHeader(kSeFactoryLockCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kSeFactoryProvisionLock] = getHexString(cborData); + printf("\n Constructed SE factory lock APDU successfully. \n"); + return SUCCESS; +} + +int processOEMRootPublicKey() { + Json::Value keyFile = root.get(kOEMRootKey, Json::Value::nullRef); + if (!keyFile.isNull()) { + std::vector data; + std::vector privateKey; + std::vector publicKey; + + std::string keyFileName = keyFile.asString(); + if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { + printf("\n Failed to read the oem root key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, publicKey)) { + return FAILURE; + } + + // Prepare cbor input. + Array input; + Map map; + map.add(kTagAlgorithm, kAlgorithmEc); + map.add(kTagDigest, std::vector({kDigestSha256})); + map.add(kTagCurve, kCurveP256); + map.add(kTagPurpose, std::vector({kPurposeVerify})); + // Add elements inside cbor array. + input.add(std::move(map)); + input.add(kKeyFormatRaw); + input.add(publicKey); + std::vector cborData = input.encode(); + + if(SUCCESS != addApduHeader(kOemRootPublicKeyCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kOEMRootKey] = getHexString(cborData); + } else { + printf("\n Improper value for oem_root_key in json file \n"); + return FAILURE; + } + printf("\n Constructed OemRootPublicKey APDU successfully. \n"); + return SUCCESS; +} + int processAttestationIds() { //AttestIDParams params; diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index ef0c8534..4587a808 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -19,6 +19,7 @@ #include #include #include "socket.h" +#include #include #include #include @@ -30,14 +31,14 @@ // TODO keymint provision status enum ProvisionStatus { NOT_PROVISIONED = 0x00, - PROVISION_STATUS_ATTESTATION_KEY = 0x01, - PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x02, PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, PROVISION_STATUS_ATTEST_IDS = 0x08, PROVISION_STATUS_PRESHARED_SECRET = 0x10, PROVISION_STATUS_PROVISIONING_LOCKED = 0x20, PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x40, PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x80, + PROVISION_STATUS_SE_LOCKED = 0x01, + PROVISION_STATUS_OEM_PUBLIC_KEY = 0x02 }; // TODO keymint provision status and lock @@ -50,6 +51,7 @@ using cppbor::Item; using cppbor::Array; using cppbor::Uint; using cppbor::MajorType; +bool printProvisionStatus = false; // static function declarations static uint16_t getApduStatus(std::vector& inputData); @@ -57,6 +59,7 @@ static int sendData(std::shared_ptr& pSocket, std::string input static int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response); static int provisionData(std::shared_ptr& pSocket, const char* jsonKey); static int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t *value); +static int getProvisionStatus(uint64_t *provisionStatus); // Print usage. @@ -67,7 +70,9 @@ void usage() { printf("-h, --help show this help message and exit.\n"); printf("-i, --input jsonFile \t Input json file \n"); printf("-s, --provision_status jsonFile \t Gets the provision status of applet. \n"); - printf("-l, --lock_provision jsonFile \t Gets the provision status of applet. \n"); + printf("-l, --lock_provision jsonFile \t OEM provisioning lock. \n"); + printf("-f, --se_factory_lock jsonFile \t SE Factory provisioning lock. \n"); + printf("-u, --unlock_provision jsonFile \t Unlock OEM provisioning. \n"); } @@ -124,6 +129,14 @@ uint64_t unmaskPowerResetFlag(uint64_t errorCode) { return errorCode; } +bool isSEFactoryProvisionLocked(uint64_t provisionStatus) { + return (0 != (provisionStatus & PROVISION_STATUS_SE_LOCKED)); +} + +bool isOEMProvisionLocked(uint64_t provisionStatus) { + return (0 != (provisionStatus & PROVISION_STATUS_PROVISIONING_LOCKED)); +} + int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response) { if (SUCCESS != sendData(pSocket, apdu, response)) { return FAILURE; @@ -172,10 +185,10 @@ int provisionData(std::shared_ptr& pSocket, const char* jsonKey int openConnection(std::shared_ptr& pSocket) { if (!pSocket->isConnected()) { - if (!pSocket->openConnection()) + if (!pSocket->openConnection()) { + printf("\nFailed to open connection.\n"); return FAILURE; - } else { - printf("\n Socket already opened.\n"); + } } return SUCCESS; } @@ -191,24 +204,44 @@ int processInputFile() { printf("\n Failed to open connection \n"); return FAILURE; } - if (0 != provisionData(pSocket, kDeviceUniqueKey) || - 0 != provisionData(pSocket, kAdditionalCertChain) || - 0 != provisionData(pSocket, kAttestationIds) || - 0 != provisionData(pSocket, kSharedSecret) || - 0 != provisionData(pSocket, kBootParams)) { + + uint64_t provisionStatus = 0; + if (SUCCESS != getProvisionStatus(&provisionStatus)) { + return false; + } + if (!isSEFactoryProvisionLocked(provisionStatus) && + ((0 != provisionData(pSocket, kDeviceUniqueKey)) || + (0 != provisionData(pSocket, kAdditionalCertChain)))) { + return FAILURE; + } + if (!isOEMProvisionLocked(provisionStatus) && + ((0 != provisionData(pSocket, kAttestationIds)) || + (0 != provisionData(pSocket, kSharedSecret)) || + (0 != provisionData(pSocket, kOEMRootKey)))) { + return FAILURE; + } + + if (0 != provisionData(pSocket, kBootParams)) { return FAILURE; } + return SUCCESS; } int lockProvision() { std::vector response; std::shared_ptr pSocket = SocketTransport::getInstance(); + + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + if (SUCCESS != openConnection(pSocket)) { printf("\n Failed to open connection \n"); return FAILURE; } - if (SUCCESS != provisionData(pSocket, lockProvisionApdu, response)) { + if (SUCCESS != provisionData(pSocket, kLockProvision)){ printf("\n Failed to lock provision.\n"); return FAILURE; } @@ -216,45 +249,111 @@ int lockProvision() { return SUCCESS; } -int getProvisionStatus() { +int unlockProvision() { std::vector response; std::shared_ptr pSocket = SocketTransport::getInstance(); + + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } if (SUCCESS != openConnection(pSocket)) { printf("\n Failed to open connection \n"); return FAILURE; } + if (SUCCESS != provisionData(pSocket, kUnLockProvision)) { + printf("\n Failed to unlock provision.\n"); + return FAILURE; + } + return SUCCESS; +} - if (SUCCESS != provisionData(pSocket, provisionStatusApdu, response)) { - printf("\n Failed to get provision status \n"); +int seFactoryProvisionLock() { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + if (SUCCESS != provisionData(pSocket, kSeFactoryProvisionLock)) { + printf("\n Failed to lock SE factory provision.\n"); + return FAILURE; + } + return SUCCESS; +} + +int getProvisionStatus(uint64_t *provisionStatus) { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + + Json::Value val = root.get(kProvisionStatus, Json::Value::nullRef); + if (!val.isNull()) { + if (val.isString()) { + if (SUCCESS != provisionData(pSocket, hex2str(val.asString()), response)) { + printf("\n Error while provisioning %s \n", kProvisionStatus); + return FAILURE; + } + } else { + printf("\n Fail: Expected (%s) tag value is string. \n", kProvisionStatus); + return FAILURE; + } + } else { return FAILURE; } auto [item, pos, message] = cppbor::parse(response); + uint64_t status; if(item != nullptr) { - uint64_t status; if(SUCCESS != getUint64(item, 1, &status)) { printf("\n Failed to get the provision status.\n"); return FAILURE; - } - // TODO Handle Keymint Provision status once added. - if ( (0 != (status & ProvisionStatus::PROVISION_STATUS_DEVICE_UNIQUE_KEY)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) && - (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET))) { + } + if (printProvisionStatus) { + if ((0 != (status & ProvisionStatus::PROVISION_STATUS_DEVICE_UNIQUE_KEY)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTEST_IDS)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET))) { printf("\n SE is provisioned \n"); - } else { - if (0 == (status & ProvisionStatus::PROVISION_STATUS_DEVICE_UNIQUE_KEY)) { - printf("\n Device Unique key is not provisioned \n"); - } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) { - printf("\n Additional certificate chain is not provisioned \n"); } - if (0 == (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) { - printf("\n Shared secret is not provisioned \n"); + else { + if (0 == (status & ProvisionStatus::PROVISION_STATUS_DEVICE_UNIQUE_KEY)) { + printf("\n Attestation key is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) { + printf("\n Attestation certificate chain is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTEST_IDS)) { + printf("\n Attestation certificate params are not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) { + printf("\n Shared secret is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_OEM_PUBLIC_KEY)) { + printf("\n OEM Root Public Key is not provisioned \n"); + } } - } + printf("\n provisionStatus:%ld\n", status); + printProvisionStatus = false; + } } else { printf("\n Fail to parse the response \n"); return FAILURE; } + if (provisionStatus != nullptr) { + *provisionStatus = status; + } return SUCCESS; } @@ -262,11 +361,15 @@ int main(int argc, char* argv[]) { int c; bool provisionStatusSet = false; bool lockProvisionSet = false; + bool unlockProvisionSet = false; + bool seFactoryLockSet = false; struct option longOpts[] = { {"input", required_argument, NULL, 'i'}, {"provision_status", no_argument, NULL, 's'}, - {"lock_provision", no_argument, NULL, 'l'}, + {"oem_lock_provision", no_argument, NULL, 'l'}, + {"oem_unlock_provision", no_argument, NULL, 'u'}, + {"se_factory_lock", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {0,0,0,0} }; @@ -278,7 +381,7 @@ int main(int argc, char* argv[]) { } /* getopt_long stores the option index here. */ - while ((c = getopt_long(argc, argv, ":hlsi:", longOpts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, ":hlufsvi:", longOpts, NULL)) != -1) { switch(c) { case 'i': // input file @@ -291,6 +394,12 @@ int main(int argc, char* argv[]) { case 'l': lockProvisionSet = true; break; + case 'u': + unlockProvisionSet = true; + break; + case 'f': + seFactoryLockSet = true; + break; case 'h': // help usage(); @@ -306,16 +415,29 @@ int main(int argc, char* argv[]) { return FAILURE; } } - // Process input file; send apuds to JCServer over socket. - if (argc >= 3) { - if (SUCCESS != processInputFile()) { - return FAILURE; + + if (argc < 3) { + usage(); + return FAILURE; + } + if (argc == 4) { + if (provisionStatusSet) { + printProvisionStatus = true; + getProvisionStatus(nullptr); + return SUCCESS; } } - if (provisionStatusSet) - getProvisionStatus(); + // Process input file; send apuds to JCServer over socket. + if (SUCCESS != processInputFile()) { + return FAILURE; + } + + if (seFactoryLockSet) + seFactoryProvisionLock(); if (lockProvisionSet) lockProvision(); + if (unlockProvisionSet) + unlockProvision(); return SUCCESS; } diff --git a/ProvisioningTool/src/utils.cpp b/ProvisioningTool/src/utils.cpp index 41ad8a6c..39e2bf0f 100644 --- a/ProvisioningTool/src/utils.cpp +++ b/ProvisioningTool/src/utils.cpp @@ -63,8 +63,8 @@ int readJsonFile(Json::Value& root, std::string& inputFileName) { std::string errorMessage; if(!root.empty()) { - printf("\n Already parsed \n"); - return 1; + // Already parsed. + return 0; } std::ifstream stream(inputFileName); if (Json::parseFromStream(builder, stream, &root, &errorMessage)) { @@ -93,4 +93,4 @@ int writeJsonFile(Json::Value& writerRoot, std::string& outputFileName) { ofs.close(); return SUCCESS; -} \ No newline at end of file +} diff --git a/ProvisioningTool/test_resources/oem_root_key.der b/ProvisioningTool/test_resources/oem_root_key.der new file mode 100644 index 0000000000000000000000000000000000000000..6940e35eaccfc4d36f5259fa656e0a58a5020787 GIT binary patch literal 138 zcmV;50CoQ`frkPC05B5<2P%e0&OHJF1_&yKNX|V20S5$aFlzz<0R$kG!sXGU%44q_ z&pX6tq3dx|vlC?*{9|cI5q-NzSy61EL<2$q1gG~-@*&+H+Zl(yI0fxaf`qSp}$R|k&jda#07gy(hpvb}sgDgXcg literal 0 HcmV?d00001 From fb47be7b6436f741b2e4bc5c5744951186c7410b Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Mon, 30 May 2022 06:51:05 +0000 Subject: [PATCH 02/15] Bug fixes --- .../javacard/keymaster/KMAndroidSEApplet.java | 23 ++- .../seprovider/KMAndroidSEProvider.java | 17 +- .../javacard/seprovider/KMSEProvider.java | 22 +++ .../javacard/keymaster/KMKeymasterApplet.java | 50 ++++-- HAL/Android.bp | 1 + HAL/ITransport.h | 10 +- HAL/JavacardSecureElement.cpp | 7 +- HAL/JavacardSharedSecret.cpp | 6 +- HAL/OmapiTransport.cpp | 162 ++++++++++++------ HAL/OmapiTransport.h | 25 +-- HAL/SocketTransport.cpp | 26 +-- HAL/SocketTransport.h | 6 +- 12 files changed, 254 insertions(+), 101 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 98f6975b..8fb66a7f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -135,6 +135,10 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); + break; + + case INS_SET_BOOT_ENDED_CMD: + processSetBootEndedCmd(apdu); break; case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: @@ -519,9 +523,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 diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index 4dde7acf..d2f8738e 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -117,7 +117,7 @@ public KMAndroidSEProvider() { rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); androidSEProvider = this; resetFlag = JCSystem.makeTransientByteArray((short) 1, - JCSystem.CLEAR_ON_DESELECT); + JCSystem.CLEAR_ON_RESET); resetFlag[0] = (byte) POWER_RESET_FALSE; } @@ -1197,5 +1197,20 @@ public short getBackupObjectCount(byte interfaceType) { } 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/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 6e209daa..d641e4c6 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/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. diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 2b1736a3..813d148f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -1030,6 +1030,7 @@ private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, 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)); @@ -1044,12 +1045,12 @@ private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, && KMInteger.compare(tagValue, systemParam) == 1 && KMInteger.compare(systemParam, zero) == 0)) { // Key needs upgrade. - return true; + 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. - return true; + isKeyUpgradeRequired = true; } else if (KMInteger.compare(tagValue, systemParam) == 1) { KMException.throwIt(KMError.INVALID_ARGUMENT); } @@ -1058,7 +1059,7 @@ private boolean isKeyUpgradeRequired(short keyBlob, short appId, short appData, } index += 4; } - return false; + return isKeyUpgradeRequired; } private void processUpgradeKeyCmd(APDU apdu) { @@ -2359,7 +2360,8 @@ private void authorizeDigest(KMOperationState op) { break; case KMType.EC: case KMType.HMAC: - if (op.getPurpose() != KMType.AGREE_KEY && param == KMType.INVALID_VALUE) { + if ((param == KMType.INVALID_VALUE && op.getPurpose() != KMType.AGREE_KEY)|| + !isDigestSupported(op.getAlgorithm(), op.getDigest())) { KMException.throwIt(KMError.UNSUPPORTED_DIGEST); } break; @@ -2408,6 +2410,13 @@ private void authorizePadding(KMOperationState op) { && 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); + } + } //TODO d to verify whether javacard support MGF1 = SHA1 or is it equal to the OAEP scheme // digest. There is no way to define any other digest. if(param == KMType.RSA_OAEP){ @@ -3915,17 +3924,17 @@ private void readKeyBlobParams(short version, short parsedKeyBlob) { data[KEY_BLOB_VERSION_DATA_OFFSET] = KMInteger.uint_8((byte) 0); break; case (short) 1: - 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[KEY_BLOB_VERSION_DATA_OFFSET] = KMArray.cast(parsedKeyBlob).get( - (short) 4); - data[CUSTOM_TAGS] = KMType.INVALID_VALUE; - data[PUB_KEY] = KMType.INVALID_VALUE; + (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); if (KMArray.cast(parsedKeyBlob).length() == ASYM_KEY_BLOB_SIZE_V1) { data[PUB_KEY] = KMArray.cast(parsedKeyBlob).get((short) 5); } + data[CUSTOM_TAGS] = KMType.INVALID_VALUE; + data[PUB_KEY] = KMType.INVALID_VALUE; break; case (short) 2: data[SECRET] = KMArray.cast(parsedKeyBlob).get(KEY_BLOB_SECRET); @@ -4507,4 +4516,23 @@ private void finishTrustedConfirmationOperation(KMOperationState op) { } } } + + 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/HAL/Android.bp b/HAL/Android.bp index 557f204d..ad1619be 100644 --- a/HAL/Android.bp +++ b/HAL/Android.bp @@ -66,6 +66,7 @@ cc_library { "liblog", "libbinder_ndk", "android.se.omapi-V1-ndk", + "libhardware" ], } diff --git a/HAL/ITransport.h b/HAL/ITransport.h index 672f5f50..0e74dc46 100644 --- a/HAL/ITransport.h +++ b/HAL/ITransport.h @@ -17,11 +17,13 @@ #pragma once #include #include +#include namespace keymint::javacard { using std::shared_ptr; using std::vector; - +constexpr int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE = -68; +constexpr int KM_ERROR_HARDWARE_NOT_YET_AVAILABLE = -85; /** * ITransport is an interface with a set of virtual methods that allow communication between the * HAL and the applet on the secure element. @@ -33,15 +35,15 @@ class ITransport { /** * Opens connection. */ - virtual bool openConnection() = 0; + virtual keymaster_error_t openConnection() = 0; /** * Send data over communication channel and receives data back from the remote end. */ - virtual bool sendData(const vector& inData, vector& output) = 0; + virtual keymaster_error_t sendData(const vector& inData, vector& output) = 0; /** * Closes the connection. */ - virtual bool closeConnection() = 0; + virtual keymaster_error_t closeConnection() = 0; /** * Returns the state of the connection status. Returns true if the connection is active, false * if connection is broken. diff --git a/HAL/JavacardSecureElement.cpp b/HAL/JavacardSecureElement.cpp index 57ec6181..252131da 100644 --- a/HAL/JavacardSecureElement.cpp +++ b/HAL/JavacardSecureElement.cpp @@ -83,9 +83,10 @@ keymaster_error_t JavacardSecureElement::sendData(Instruction ins, std::vectorsendData(apdu, response)) { - LOG(ERROR) << "Error in sending data in sendData."; - return (KM_ERROR_SECURE_HW_COMMUNICATION_FAILED); + ret = transport_->sendData(apdu, response); + if (ret != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending data in sendData. " << static_cast(ret); + return ret; } // Response size should be greater than 2. Cbor output data followed by two bytes of APDU diff --git a/HAL/JavacardSharedSecret.cpp b/HAL/JavacardSharedSecret.cpp index 2f4dfe4b..a779c1ad 100644 --- a/HAL/JavacardSharedSecret.cpp +++ b/HAL/JavacardSharedSecret.cpp @@ -14,7 +14,11 @@ using std::vector; ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) { card_->initializeJavacard(); auto [item, err] = card_->sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD); - if (err != KM_ERROR_OK || !cbor_.getSharedSecretParameters(item, 1, *params)) { + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in getSharedSecretParameters.!!!!!!!!!!!!!!!"; + return km_utils::kmError2ScopedAStatus(err); + } + if (!cbor_.getSharedSecretParameters(item, 1, *params)) { LOG(ERROR) << "Error in sending in getSharedSecretParameters."; return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); } diff --git a/HAL/OmapiTransport.cpp b/HAL/OmapiTransport.cpp index 7c353d8c..fc13744a 100644 --- a/HAL/OmapiTransport.cpp +++ b/HAL/OmapiTransport.cpp @@ -29,11 +29,14 @@ namespace keymint::javacard { constexpr uint8_t SELECTABLE_AID[] = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31}; +std::string const ESE_READER_PREFIX = "eSE"; +constexpr const char omapiServiceName[] = + "android.system.omapi.ISecureElementService/default"; class SEListener : public ::aidl::android::se::omapi::BnSecureElementListener {}; -bool OmapiTransport::initialize() { - std::vector readers = {}; + +keymaster_error_t OmapiTransport::initialize() { LOG(DEBUG) << "Initialize the secure element connection"; @@ -43,19 +46,21 @@ bool OmapiTransport::initialize() { if (omapiSeService == nullptr) { LOG(ERROR) << "Failed to start omapiSeService null"; - return false; + return static_cast(KM_ERROR_HARDWARE_NOT_YET_AVAILABLE); } + int size = sizeof(SELECTABLE_AID) / sizeof(SELECTABLE_AID[0]); // reset readers, clear readers if already existing if (mVSReaders.size() > 0) { closeConnection(); } + std::vector readers = {}; // Get available readers auto status = omapiSeService->getReaders(&readers); if (!status.isOk()) { LOG(ERROR) << "getReaders failed to get available readers: " << status.getMessage(); - return false; + return static_cast(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE); } // Get SE readers handlers @@ -65,9 +70,8 @@ bool OmapiTransport::initialize() { if (!status.isOk()) { LOG(ERROR) << "getReader for " << readerName.c_str() << " Failed: " << status.getMessage(); - return false; + return static_cast(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE); } - mVSReaders[readerName] = reader; } @@ -79,79 +83,125 @@ bool OmapiTransport::initialize() { if (name.find(ESE_READER_PREFIX, 0) != std::string::npos) { LOG(DEBUG) << "eSE reader found: " << name; eSEReader = reader; + break; } } } if (eSEReader == nullptr) { LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; - return false; + return static_cast(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE); } - return true; + bool isSecureElementPresent = false; + auto res = eSEReader->isSecureElementPresent(&isSecureElementPresent); + if (!res.isOk()) { + eSEReader = nullptr; + LOG(ERROR) << "isSecureElementPresent error: " << res.getMessage(); + return static_cast(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE); + } + if (!isSecureElementPresent) { + LOG(ERROR) << "secure element not found"; + eSEReader = nullptr; + return static_cast(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE); + } + + status = eSEReader->openSession(&session); + if (!status.isOk()) { + LOG(ERROR) << "openSession error: " << status.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + if (session == nullptr) { + LOG(ERROR) << "Could not open session null"; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + + std::vector aid(SELECTABLE_AID, SELECTABLE_AID + size); + auto mSEListener = ndk::SharedRefBase::make(); + status = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); + if (!status.isOk()) { + LOG(ERROR) << "openLogicalChannel error: " << status.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + if (channel == nullptr) { + LOG(ERROR) << "Could not open channel null"; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + + return KM_ERROR_OK; } bool OmapiTransport::internalTransmitApdu( std::shared_ptr reader, std::vector apdu, std::vector& transmitResponse) { - std::shared_ptr session; - std::shared_ptr channel; - auto mSEListener = ndk::SharedRefBase::make(); - std::vector selectResponse = {}; - int size = sizeof(SELECTABLE_AID) / sizeof(SELECTABLE_AID[0]); - std::vector aid(SELECTABLE_AID, SELECTABLE_AID + size); LOG(DEBUG) << "internalTransmitApdu: trasmitting data to secure element"; - if (reader == nullptr) { LOG(ERROR) << "eSE reader is null"; return false; } - bool status = false; - auto res = reader->isSecureElementPresent(&status); - if (!res.isOk()) { - LOG(ERROR) << "isSecureElementPresent error: " << res.getMessage(); - return false; + bool result = true; + auto res = ndk::ScopedAStatus::ok(); + if(session != nullptr) { + res = session->isClosed(&result); + if (!res.isOk()) { + LOG(ERROR) << "isClosed error: " << res.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } } - if (!status) { - LOG(ERROR) << "secure element not found"; - return false; + if(result) { + res = reader->openSession(&session); + if (!res.isOk()) { + LOG(ERROR) << "openSession error: " << res.getMessage(); + return false; + } + if (session == nullptr) { + LOG(ERROR) << "Could not open session null"; + return false; + } } - res = reader->openSession(&session); - if (!res.isOk()) { - LOG(ERROR) << "openSession error: " << res.getMessage(); - return false; - } - if (session == nullptr) { - LOG(ERROR) << "Could not open session null"; - return false; + result = true; + if(channel != nullptr) { + res = channel->isClosed(&result); + if (!res.isOk()) { + LOG(ERROR) << "isClosed error: " << res.getMessage(); + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } } - res = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); - if (!res.isOk()) { - LOG(ERROR) << "openLogicalChannel error: " << res.getMessage(); - return false; - } - if (channel == nullptr) { - LOG(ERROR) << "Could not open channel null"; - return false; + int size = sizeof(SELECTABLE_AID) / sizeof(SELECTABLE_AID[0]); + std::vector aid(SELECTABLE_AID, SELECTABLE_AID + size); + if(result) { + auto mSEListener = ndk::SharedRefBase::make(); + res = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); + if (!res.isOk()) { + LOG(ERROR) << "openLogicalChannel error: " << res.getMessage(); + return false; + } + if (channel == nullptr) { + LOG(ERROR) << "Could not open channel null"; + return false; + } } + std::vector selectResponse = {}; res = channel->getSelectResponse(&selectResponse); if (!res.isOk()) { LOG(ERROR) << "getSelectResponse error: " << res.getMessage(); return false; } - if (selectResponse.size() < 2) { - LOG(ERROR) << "getSelectResponse size error"; + + if ((selectResponse.size() < 2) + || ((selectResponse[selectResponse.size() -1] & 0xFF) == 0x00) + || ((selectResponse[selectResponse.size() -2] & 0xFF) == 0x90)) + { + LOG(ERROR) << "Failed to select the Applet."; return false; } res = channel->transmit(apdu, &transmitResponse); - if (channel != nullptr) channel->close(); - if (session != nullptr) session->close(); LOG(INFO) << "STATUS OF TRNSMIT: " << res.getExceptionCode() << " Message: " << res.getMessage(); @@ -163,38 +213,42 @@ bool OmapiTransport::internalTransmitApdu( return true; } -bool OmapiTransport::openConnection() { +keymaster_error_t OmapiTransport::openConnection() { // if already conection setup done, no need to initialise it again. if (isConnected()) { - return true; + return KM_ERROR_OK; } - return initialize(); } -bool OmapiTransport::sendData(const vector& inData, vector& output) { +keymaster_error_t OmapiTransport::sendData(const vector& inData, vector& output) { if (!isConnected()) { // Try to initialize connection to eSE LOG(INFO) << "Failed to send data, try to initialize connection SE connection"; - if (!initialize()) { + auto res = initialize(); + if (res != KM_ERROR_OK) { LOG(ERROR) << "Failed to send data, initialization not completed"; closeConnection(); - return false; + return res; } } if (eSEReader != nullptr) { LOG(DEBUG) << "Sending apdu data to secure element: " << ESE_READER_PREFIX; - return internalTransmitApdu(eSEReader, inData, output); + if(internalTransmitApdu(eSEReader, inData, output)) { + return KM_ERROR_OK; + } else { + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } } else { LOG(ERROR) << "secure element reader " << ESE_READER_PREFIX << " not found"; - return false; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; } } -bool OmapiTransport::closeConnection() { +keymaster_error_t OmapiTransport::closeConnection() { LOG(DEBUG) << "Closing all connections"; if (omapiSeService != nullptr) { if (mVSReaders.size() > 0) { @@ -204,7 +258,9 @@ bool OmapiTransport::closeConnection() { mVSReaders.clear(); } } - return true; + if (channel != nullptr) channel->close(); + if (session != nullptr) session->close(); + return KM_ERROR_OK; } bool OmapiTransport::isConnected() { diff --git a/HAL/OmapiTransport.h b/HAL/OmapiTransport.h index 2a537871..6c71a081 100644 --- a/HAL/OmapiTransport.h +++ b/HAL/OmapiTransport.h @@ -23,20 +23,23 @@ using std::vector; class OmapiTransport : public ITransport { public: + OmapiTransport() : omapiSeService(nullptr), eSEReader(nullptr), session(nullptr), + channel(nullptr), mVSReaders({}) { + } /** - * Gets the binder instance of ISEService, gets the reader corresponding to secure element, + * Gets the binder instance of ISEService, gets te reader corresponding to secure element, * establishes a session and opens a basic channel. */ - bool openConnection() override; + keymaster_error_t openConnection() override; /** * Transmists the data over the opened basic channel and receives the data back. */ - bool sendData(const vector& inData, vector& output) override; + keymaster_error_t sendData(const vector& inData, vector& output) override; /** * Closes the connection. */ - bool closeConnection() override; + keymaster_error_t closeConnection() override; /** * Returns the state of the connection status. Returns true if the connection is active, false * if connection is broken. @@ -44,15 +47,13 @@ class OmapiTransport : public ITransport { bool isConnected() override; private: - std::shared_ptr omapiSeService = nullptr; - std::shared_ptr eSEReader = nullptr; + std::shared_ptr omapiSeService; + std::shared_ptr eSEReader; + std::shared_ptr session; + std::shared_ptr channel; std::map> - mVSReaders = {}; - std::string const ESE_READER_PREFIX = "eSE"; - constexpr static const char omapiServiceName[] = - "android.system.omapi.ISecureElementService/default"; - - bool initialize(); + mVSReaders; + keymaster_error_t initialize(); bool internalTransmitApdu(std::shared_ptr reader, std::vector apdu, std::vector& transmitResponse); diff --git a/HAL/SocketTransport.cpp b/HAL/SocketTransport.cpp index edb8e464..698024e5 100644 --- a/HAL/SocketTransport.cpp +++ b/HAL/SocketTransport.cpp @@ -31,12 +31,12 @@ namespace keymint::javacard { using std::shared_ptr; using std::vector; -bool SocketTransport::openConnection() { +keymaster_error_t SocketTransport::openConnection() { struct sockaddr_in serv_addr; if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { LOG(ERROR) << "Socket creation failed" << " Error: " << strerror(errno); - return false; + return static_cast(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE); } serv_addr.sin_family = AF_INET; @@ -45,19 +45,19 @@ bool SocketTransport::openConnection() { // Convert IPv4 and IPv6 addresses from text to binary form if (inet_pton(AF_INET, IPADDR, &serv_addr.sin_addr) <= 0) { LOG(ERROR) << "Invalid address/ Address not supported."; - return false; + return static_cast(KM_ERROR_HARDWARE_TYPE_UNAVAILABLE); } if (connect(mSocket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { close(mSocket); LOG(ERROR) << "Connection failed. Error: " << strerror(errno); - return false; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; } socketStatus = true; - return true; + return KM_ERROR_OK; } -bool SocketTransport::sendData(const vector& inData, vector& output) { +keymaster_error_t SocketTransport::sendData(const vector& inData, vector& output) { int count = 1; while (!socketStatus && count++ < 5) { sleep(1); @@ -67,7 +67,7 @@ bool SocketTransport::sendData(const vector& inData, vector& o if (count >= 5) { LOG(ERROR) << "Failed to open socket connection"; - return false; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; } // Prepend the input length to the inputData before sending. vector inDataPrependedLength; @@ -85,15 +85,19 @@ bool SocketTransport::sendData(const vector& inData, vector& o } LOG(ERROR) << "Failed to send data over socket err: " << errno; connectionResetCnt = 0; - return false; + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; } - return readData(output); + + if (!readData(output)) { + return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; + } + return KM_ERROR_OK; } -bool SocketTransport::closeConnection() { +keymaster_error_t SocketTransport::closeConnection() { close(mSocket); socketStatus = false; - return true; + return KM_ERROR_OK; } bool SocketTransport::isConnected() { diff --git a/HAL/SocketTransport.h b/HAL/SocketTransport.h index 097694d9..ac8103f6 100644 --- a/HAL/SocketTransport.h +++ b/HAL/SocketTransport.h @@ -30,15 +30,15 @@ class SocketTransport : public ITransport { /** * Creates a socket instance and connects to the provided server IP and port. */ - bool openConnection() override; + keymaster_error_t openConnection() override; /** * Sends data over socket and receives data back. */ - bool sendData(const vector& inData, vector& output) override; + keymaster_error_t sendData(const vector& inData, vector& output) override; /** * Closes the connection. */ - bool closeConnection() override; + keymaster_error_t closeConnection() override; /** * Returns the state of the connection status. Returns true if the connection is active, * false if connection is broken. From 06e994fb464d28b86048cd087013555cb560c3c3 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Mon, 30 May 2022 11:34:01 +0000 Subject: [PATCH 03/15] NVM optimization --- .../seprovider/KMAndroidSEProvider.java | 36 +++--- .../android/javacard/keymaster/KMCose.java | 77 ++++++----- .../javacard/keymaster/KMCoseHeaders.java | 17 +-- .../android/javacard/keymaster/KMCoseKey.java | 26 ++-- .../javacard/keymaster/KMKeyParameters.java | 103 ++++++++------- .../javacard/keymaster/KMKeymasterApplet.java | 45 +++---- .../RemotelyProvisionedComponentDevice.java | 122 +++++++++--------- 7 files changed, 221 insertions(+), 205 deletions(-) diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index d2f8738e..86c3e7c7 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -407,13 +407,18 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta final byte n = 2; // hardcoded // [L] 256 bits - hardcoded 32 bits as per // reference impl in keymaster. - final byte[] L = { - 0, 0, 1, 0 - }; - // byte - final byte[] zero = { - 0 - }; + short zeroIndex = 0; + short LIndex = 1; + short bufIndex = 5; + short keyIndex = 9; + //1st byte in tempArray is reserved for zero + //next 4 bytes in tempArray is reserved for L + tmpArray[0] = 0; + tmpArray[1] = 0; + tmpArray[2] = 0; + tmpArray[3] = 1; + tmpArray[4] = 0; + // [i] counter - 32 bits short iBufLen = 4; short keyOutLen = n * 16; @@ -434,12 +439,12 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta while (i <= n) { tmpArray[3] = i; // 4 bytes of iBuf with counter in it - kdf.update(tmpArray, (short) 0, (short) iBufLen); + kdf.update(tmpArray, bufIndex, iBufLen); kdf.update(label, labelStart, (short) labelLen); // label - kdf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00 + kdf.update(tmpArray, zeroIndex, (short) 1); // 1 byte of 0x00 kdf.update(context, contextStart, contextLength); // context // 4 bytes of L - signature of 16 bytes - pos = kdf.sign(L, (short) 0, (short) 4, tmpArray, + pos = kdf.sign(tmpArray, LIndex, (short) 4, tmpArray, (short) (iBufLen + pos)); i++; } @@ -969,22 +974,21 @@ private short hkdfExpand(byte[] prk, short prkOff, short prkLen, byte[] info, sh CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen); - Util.arrayFill(tmpArray, (short) 0, (short) 32, (byte) 0); - byte[] cnt = {(byte) 0}; + Util.arrayFill(tmpArray, (short) 0, (short) 33, (byte) 0); short bytesCopied = 0; short len = 0; for (short i = 0; i < n; i++) { - cnt[0]++; + tmpArray[0]++; hmacSignature.init(hmacKey, Signature.MODE_SIGN); if (i != 0) { - hmacSignature.update(tmpArray, (short) 0, (short) 32); + hmacSignature.update(tmpArray, (short) 1, (short) 32); } hmacSignature.update(info, infoOff, infoLen); - len = hmacSignature.sign(cnt, (short) 0, (short) 1, tmpArray, (short) 0); + len = hmacSignature.sign(tmpArray, (short) 0, (short) 1, tmpArray, (short) 1); if ((short) (bytesCopied + len) > outLen) { len = (short) (outLen - bytesCopied); } - Util.arrayCopyNonAtomic(tmpArray, (short) 0, out, (short) (outOff + bytesCopied), len); + Util.arrayCopyNonAtomic(tmpArray, (short) 1, out, (short) (outOff + bytesCopied), len); bytesCopied += len; } return outLen; diff --git a/Applet/src/com/android/javacard/keymaster/KMCose.java b/Applet/src/com/android/javacard/keymaster/KMCose.java index 6720d8df..94648ca2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCose.java +++ b/Applet/src/com/android/javacard/keymaster/KMCose.java @@ -120,6 +120,9 @@ public class KMCose { {0x4B, 0x65, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x4D, 0x41, 0x43, 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x20, 0x6B, 0x65, 0x79, 0x73}; // "Key to MAC public keys" + public static final short[] coseKeyConst = {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[] coseHeaderConst = {KMCose.COSE_LABEL_ALGORITHM, KMCose.COSE_LABEL_KEYID, KMCose.COSE_LABEL_IV, KMCose.COSE_LABEL_COSE_KEY}; /** * Constructs the Cose MAC structure. * @@ -240,32 +243,33 @@ public static short constructCoseSign1(short protectedHeader, short unProtectedH /** * Constructs array based on the tag values provided. * - * @param tags array of tag values to be constructed. + * @param tags 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[] tags, boolean includeTestMode) { + 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; - while (index < tags.length) { - if (tags[(short) (index + 1)] != KMType.INVALID_VALUE) { - tags[(short) (index + 2)] = - buildCosePairTag((byte) tags[index], tags[(short) (index + 1)]); - var++; + 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 += 3; + index++; } var += includeTestMode ? 1 : 0; short arrPtr = KMArray.instance(var); index = 0; // var is used to index the array. var = 0; - while (index < tags.length) { - if (tags[(short) (index + 2)] != KMType.INVALID_VALUE) { - KMArray.cast(arrPtr).add(var++, tags[(short) (index + 2)]); + while (index < tagLen) { + if (keyValues[(short)(index + valueIndex)] != KMType.INVALID_VALUE) { + KMArray.cast(arrPtr).add(var++, keyValues[(short)(index + valueIndex)]); } - index += 3; + index++; } return arrPtr; } @@ -300,14 +304,15 @@ public static short constructCoseCertPayload(short issuer, short subject, short * @param ephemeralKey instance of KMCoseKey. * @return instance of KMCoseHeaders. */ - public static short constructHeaders(short alg, short keyId, short iv, short ephemeralKey) { - short[] coseHeaderTags = { - KMCose.COSE_LABEL_ALGORITHM, alg, KMType.INVALID_VALUE, - KMCose.COSE_LABEL_KEYID, keyId, KMType.INVALID_VALUE, - KMCose.COSE_LABEL_IV, iv, KMType.INVALID_VALUE, - KMCose.COSE_LABEL_COSE_KEY, ephemeralKey, KMType.INVALID_VALUE - }; - short ptr = handleCosePairTags(coseHeaderTags, false); + 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(coseHeaderConst, buff, (short)4, false); ptr = KMCoseHeaders.instance(ptr); KMCoseHeaders.cast(ptr).canonicalize(); return ptr; @@ -443,7 +448,7 @@ public static short buildCosePairTag(byte key, short valuePtr) { * @param testMode Represents if key is used in test mode or production mode. * @return Instance of the CoseKey structure. */ - public static short constructCoseKey(short keyType, short keyId, short keyAlg, short keyOps, + 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 @@ -453,7 +458,7 @@ public static short constructCoseKey(short keyType, short keyId, short keyAlg, s pubKeyLen = (short) (pubKeyLen / 2); short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen); short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen); - short coseKey = constructCoseKey(keyType, keyId, keyAlg, keyOps, curve, xPtr, yPtr, privKeyPtr, testMode); + short coseKey = constructCoseKey(buff, keyType, keyId, keyAlg, keyOps, curve, xPtr, yPtr, privKeyPtr, testMode); KMCoseKey.cast(coseKey).canonicalize(); return coseKey; } @@ -473,19 +478,21 @@ public static short constructCoseKey(short keyType, short keyId, short keyAlg, s * @param includeTestKey flag which identifies whether to construct test key or production key. * @return instance of the KMCoseKey object. */ - public static short constructCoseKey(short keyType, short keyId, short keyAlg, short keyOps, short curve, + 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[] coseKeyTags = { - KMCose.COSE_KEY_KEY_TYPE, keyType, KMType.INVALID_VALUE, - KMCose.COSE_KEY_KEY_ID, keyId, KMType.INVALID_VALUE, - KMCose.COSE_KEY_ALGORITHM, keyAlg, KMType.INVALID_VALUE, - KMCose.COSE_KEY_KEY_OPS, keyOps, KMType.INVALID_VALUE, - KMCose.COSE_KEY_CURVE, curve, KMType.INVALID_VALUE, - KMCose.COSE_KEY_PUBKEY_X, pubX, KMType.INVALID_VALUE, - KMCose.COSE_KEY_PUBKEY_Y, pubY, KMType.INVALID_VALUE, - KMCose.COSE_KEY_PRIV_KEY, priv, KMType.INVALID_VALUE - }; - short arrPtr = handleCosePairTags(coseKeyTags, 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(coseKeyConst, buff, valueIndex, includeTestKey); if (includeTestKey) { short testKey = KMCosePairSimpleValueTag.instance(KMNInteger.uint_32(KMCose.COSE_TEST_KEY, (short) 0), @@ -546,4 +553,4 @@ public static short constructKdfContext(byte[] publicKeyA, short publicKeyAOff, return arrPtr; } -} \ No newline at end of file +} diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java b/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java index 0b82d6cb..d02a3975 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java +++ b/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java @@ -152,20 +152,21 @@ public short getAlgorithm() { return getValueType(KMCose.COSE_LABEL_ALGORITHM); } - public boolean isDataValid(short alg, short keyIdPtr) { - short[] headerTags = { - KMCose.COSE_LABEL_ALGORITHM, alg, - KMCose.COSE_LABEL_KEYID, keyIdPtr, - }; + 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 < headerTags.length) { - value = headerTags[(short) (tagIndex + 1)]; + while (tagIndex < bufLen) { + value = buff[(short) (tagIndex + 1)]; if (value != KMType.INVALID_VALUE) { valid = false; - ptr = getValueType(headerTags[tagIndex]); + ptr = getValueType(buff[tagIndex]); switch (KMType.getType(ptr)) { case KMType.BYTE_BLOB_TYPE: if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length()) && diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseKey.java b/Applet/src/com/android/javacard/keymaster/KMCoseKey.java index eafa3006..53a14c34 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCoseKey.java +++ b/Applet/src/com/android/javacard/keymaster/KMCoseKey.java @@ -179,23 +179,27 @@ public boolean isTestKey() { * @param curve value of the curve. * @return true if valid, otherwise false. */ - public boolean isDataValid(short keyType, short keyIdPtr, short keyAlg, short keyOps, short curve) { - short[] coseKeyTags = { - KMCose.COSE_KEY_KEY_TYPE, keyType, - KMCose.COSE_KEY_KEY_ID, keyIdPtr, - KMCose.COSE_KEY_ALGORITHM, keyAlg, - KMCose.COSE_KEY_KEY_OPS, keyOps, - KMCose.COSE_KEY_CURVE, curve, - }; + 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 < coseKeyTags.length) { - value = coseKeyTags[(short) (tagIndex + 1)]; + while (tagIndex < buffLen) { + value = buff[(short) (tagIndex + 1)]; if (value != KMType.INVALID_VALUE) { valid = false; - ptr = getValueType(coseKeyTags[tagIndex], KMType.INVALID_VALUE); + 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()) && diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 89805dc7..5e2a0437 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -34,6 +34,60 @@ public class KMKeyParameters extends KMType { 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.UNLOCKED_DEVICE_REQUIRED, + KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, + KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, + KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, + }; + + 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, + }; + + 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 KMKeyParameters() { } @@ -132,11 +186,6 @@ public short findTag(short tagType, short tagKey) { } public static boolean hasUnsupportedTags(short keyParamsPtr) { - final short[] tagArr = { - // Unsupported tags. - KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, - KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS - }; byte index = 0; short tagInd; short tagPtr; @@ -165,29 +214,6 @@ public static boolean hasUnsupportedTags(short keyParamsPtr) { public static short makeSbEnforced(short keyParamsPtr, byte origin, short osVersionObjPtr, short osPatchObjPtr, short vendorPatchObjPtr, short bootPatchObjPtr, byte[] scratchPad) { - 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.UNLOCKED_DEVICE_REQUIRED, - KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, - KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, - KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, - KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, - }; byte index = 0; short tagInd; short arrInd = 0; @@ -263,16 +289,6 @@ public static short makeHwEnforced(short sb, short tee){ } // ALL_USERS, EXPORTABLE missing from types.hal public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) { - 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, - }; byte index = 0; short tagInd; short arrInd = 0; @@ -304,11 +320,6 @@ public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) } public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) { - final short[] teeEnforcedTagsArr = { - KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, - KMType.UINT_TAG, KMType.AUTH_TIMEOUT, - KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, - }; byte index = 0; short tagInd; short arrInd = 0; @@ -380,12 +391,6 @@ public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfT } public static boolean isValidTag(short tagType, short tagKey) { - 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, - }; short index = 0; if (tagKey == KMType.INVALID_TAG) { return false; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 813d148f..462c42df 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -113,7 +113,25 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe protected static final byte[] OEM_UNLOCK_PROVISION_VERIFICATION_LABEL = { // "Enable RMA" 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x52, 0x4d, 0x41 }; - + + 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; @@ -713,12 +731,6 @@ public static short receiveIncoming(APDU apdu, short reqExp) { private void processGetHwInfoCmd(APDU apdu) { // No arguments expected final byte version = 1; - final byte[] JavacardKeymintDevice = { - 0x4a,0x61,0x76,0x61,0x63,0x61,0x72,0x64, - 0x4b,0x65,0x79,0x6d,0x69,0x6e,0x74, - 0x44,0x65,0x76,0x69,0x63,0x65, - }; - final byte[] Google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; // Make the response short respPtr = KMArray.instance((short) 6); KMArray resp = KMArray.cast(respPtr); @@ -1507,17 +1519,6 @@ protected short getVerifiedBootHash(byte[] scratchPad){ // id values of both the requested parameters and the provisioned parameters // then throw INVALID_TAG error. private void addAttestationIds(KMAttestationCert cert, byte[] scratchPad) { - final short[] attTags = - new short[]{ - 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 - }; byte index = 0; short attIdTag; short attIdTagValue; @@ -4339,7 +4340,7 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, 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(alg, KMType.INVALID_VALUE)) { + if (!KMCoseHeaders.cast(ptr2).isDataValid(rkp.rkpTmpVariables, alg, KMType.INVALID_VALUE)) { KMException.throwIt(KMError.STATUS_FAILED); } @@ -4350,7 +4351,7 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, if ((index == (short) (len - 1)) && len > 1) { alg = expLeafCertAlg; } - if (!KMCoseKey.cast(ptr2).isDataValid(KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, alg, + 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); } @@ -4410,7 +4411,7 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { KMDeviceUniqueKeyPair deviceUniqueKey = kmDataStore.getRkpDeviceUniqueKeyPair(testMode); short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0); short coseKey = - KMCose.constructCoseKey( + KMCose.constructCoseKey(rkp.rkpTmpVariables, KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), KMType.INVALID_VALUE, KMNInteger.uint_8(KMCose.COSE_ALG_ES256), @@ -4446,7 +4447,7 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { // protected header short protectedHeader = - KMCose.constructHeaders(KMNInteger.uint_8(KMCose.COSE_ALG_ES256), KMType.INVALID_VALUE, + 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, diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 578cbcdb..10304cab 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -91,6 +91,8 @@ public class RemotelyProvisionedComponentDevice { public static final byte[] ATTEST_ID_LOCKED = {0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64}; public static final byte[] ATTEST_ID_OPEN = {0x6f, 0x70, 0x65, 0x6e}; private static final short MAX_SEND_DATA = 1024; + + private static final byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; // 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; @@ -143,6 +145,7 @@ public class RemotelyProvisionedComponentDevice { private Object[] operation; private short[] dataIndex; public static Object[] authorizedEekRoots; + public short[] rkpTmpVariables; public RemotelyProvisionedComponentDevice(KMEncoder encoder, KMDecoder decoder, KMRepository repository, KMSEProvider seProvider, KMKeymintDataStore storeDInst) { @@ -151,6 +154,7 @@ public RemotelyProvisionedComponentDevice(KMEncoder encoder, KMDecoder 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); @@ -235,7 +239,6 @@ private short getEntryLength(short index) { private void processGetRkpHwInfoCmd(APDU apdu) { // Make the response // Author name - Google. - final byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; short respPtr = KMArray.instance((short) 4); KMArray resp = KMArray.cast(respPtr); resp.add((short) 0, KMInteger.uint_16(KMError.OK)); @@ -596,7 +599,7 @@ private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { ptr = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - if (!KMCoseHeaders.cast(ptr).isDataValid(KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) { + if (!KMCoseHeaders.cast(ptr).isDataValid(rkpTmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) { KMException.throwIt(KMError.STATUS_FAILED); } @@ -605,7 +608,7 @@ private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { ptr = decoder.decode(coseKeyExp, KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - if (!KMCoseKey.cast(ptr).isDataValid(KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, + 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); } @@ -706,7 +709,7 @@ private void constructPartialPubKeysToSignMac(byte[] scratchPad, short arrayLeng short encodedCoseKeysLen) { short ptr; short len; - short headerPtr = KMCose.constructHeaders( + short headerPtr = KMCose.constructHeaders(rkpTmpVariables, KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), KMType.INVALID_VALUE, KMType.INVALID_VALUE, @@ -762,7 +765,7 @@ private short createSignedMac(KMDeviceUniqueKeyPair deviceUniqueKeyPair, byte[] aad = KMByteBlob.instance(scratchPad, (short) 0, aad); /* construct protected header */ - short protectedHeaders = KMCose.constructHeaders( + short protectedHeaders = KMCose.constructHeaders(rkpTmpVariables, KMNInteger.uint_8(KMCose.COSE_ALG_ES256), KMType.INVALID_VALUE, KMType.INVALID_VALUE, @@ -801,7 +804,8 @@ private short createSignedMac(KMDeviceUniqueKeyPair deviceUniqueKeyPair, byte[] private KMDeviceUniqueKeyPair createDeviceUniqueKeyPair(boolean testMode, byte[] scratchPad) { KMDeviceUniqueKeyPair deviceUniqueKeyPair; - short[] lengths = {0, 0}; + rkpTmpVariables[0] = 0; + rkpTmpVariables[1] = 0; if (testMode) { seProvider.createAsymmetricKey( KMType.EC, @@ -811,10 +815,10 @@ private KMDeviceUniqueKeyPair createDeviceUniqueKeyPair(boolean testMode, byte[] scratchPad, (short) 128, (short) 128, - lengths); + rkpTmpVariables); deviceUniqueKeyPair = - storeDataInst.createRkpTestDeviceUniqueKeyPair(scratchPad, (short) 128, lengths[1], - scratchPad, (short) 0, lengths[0]); + storeDataInst.createRkpTestDeviceUniqueKeyPair(scratchPad, (short) 128, rkpTmpVariables[1], + scratchPad, (short) 0, rkpTmpVariables[0]); } else { deviceUniqueKeyPair = storeDataInst.getRkpDeviceUniqueKeyPair(false); } @@ -844,52 +848,41 @@ private KMDeviceUniqueKeyPair createDeviceUniqueKeyPair(boolean testMode, byte[] */ private short createDeviceInfo(byte[] scratchpad) { // Device Info Key Value pairs. - short[] deviceIds = { - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, - }; - short[] out = {0/* index */, 0 /* length */}; - updateItem(deviceIds, out, BRAND, getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad)); - updateItem(deviceIds, out, MANUFACTURER, + for (short i = 0; i < 30; 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(deviceIds, out, PRODUCT, + updateItem(rkpTmpVariables, metaOffset, PRODUCT, getAttestationId(KMType.ATTESTATION_ID_PRODUCT, scratchpad)); - updateItem(deviceIds, out, MODEL, getAttestationId(KMType.ATTESTATION_ID_MODEL, scratchpad)); - updateItem(deviceIds, out, VB_STATE, getVbState()); - updateItem(deviceIds, out, BOOTLOADER_STATE, getBootloaderState()); - updateItem(deviceIds, out, VB_META_DIGEST, getVerifiedBootHash(scratchpad)); - updateItem(deviceIds, out, OS_VERSION, getBootParams(OS_VERSION_ID, scratchpad)); - updateItem(deviceIds, out, SYSTEM_PATCH_LEVEL, + updateItem(rkpTmpVariables, metaOffset, MODEL, getAttestationId(KMType.ATTESTATION_ID_MODEL, 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(deviceIds, out, BOOT_PATCH_LEVEL, getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad)); - updateItem(deviceIds, out, VENDOR_PATCH_LEVEL, + 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(deviceIds, out, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION)); - updateItem(deviceIds, out, SECURITY_LEVEL, + 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)); byte[] attestIdState = storeDataInst.isProvisionLocked() ? ATTEST_ID_LOCKED : ATTEST_ID_OPEN; - updateItem(deviceIds, out, ATTEST_ID_STATE, + updateItem(rkpTmpVariables, metaOffset, ATTEST_ID_STATE, KMTextString.instance(attestIdState, (short) 0, (short) attestIdState.length)); // Create device info map. - short map = KMMap.instance(out[1]); + short map = KMMap.instance(rkpTmpVariables[1]); short mapIndex = 0; - short index = 0; - while (index < (short) deviceIds.length) { - if (deviceIds[index] != KMType.INVALID_VALUE) { - KMMap.cast(map).add(mapIndex++, deviceIds[index], deviceIds[(short) (index + 1)]); + 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; } @@ -908,12 +901,12 @@ private short createDeviceInfo(byte[] scratchpad) { * @param item Key info to be updated. * @param value value to be updated. */ - private void updateItem(short[] deviceIds, short[] meta, byte[] item, short value) { + private void updateItem(short[] deviceIds, short metaOffset, byte[] item, short value) { if (KMType.INVALID_VALUE != value) { - deviceIds[meta[0]++] = + deviceIds[deviceIds[metaOffset]++] = KMTextString.instance(item, (short) 0, (short) item.length); - deviceIds[meta[0]++] = value; - meta[1]++; + deviceIds[deviceIds[metaOffset]++] = value; + deviceIds[(short)(metaOffset+1)]++; } } @@ -1066,7 +1059,8 @@ private short ecdhHkdfDeriveKey(byte[] privKeyA, short privKeyAOff, short privKe // data table for later usage. private short generateEphemeralEcKey(byte[] scratchPad) { // Generate ephemeral ec key. - short[] lengths = {0/* Private key Length*/, 0 /* Public key length*/}; + rkpTmpVariables[0] = 0; + rkpTmpVariables[1] = 0; seProvider.createAsymmetricKey( KMType.EC, scratchPad, @@ -1075,18 +1069,18 @@ private short generateEphemeralEcKey(byte[] scratchPad) { scratchPad, (short) 128, (short) 128, - lengths); + rkpTmpVariables); // Copy the ephemeral private key from scratch pad - short ptr = KMByteBlob.instance(lengths[0]); + short ptr = KMByteBlob.instance(rkpTmpVariables[0]); Util.arrayCopyNonAtomic( scratchPad, (short) 0, KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), - lengths[0]); + rkpTmpVariables[0]); //Store ephemeral public key in data table for later usage. - short dataEntryIndex = createEntry(EPHEMERAL_PUB_KEY, lengths[1]); - Util.arrayCopyNonAtomic(scratchPad, (short) 128, data, dataEntryIndex, lengths[1]); + short dataEntryIndex = createEntry(EPHEMERAL_PUB_KEY, rkpTmpVariables[1]); + Util.arrayCopyNonAtomic(scratchPad, (short) 128, data, dataEntryIndex, rkpTmpVariables[1]); return ptr; } @@ -1154,7 +1148,7 @@ private void initAesGcmOperation(byte[] scratchPad, short nonce) { } private short processRecipientStructure(byte[] scratchPad) { - short protectedHeaderRecipient = KMCose.constructHeaders( + short protectedHeaderRecipient = KMCose.constructHeaders(rkpTmpVariables, KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256), KMType.INVALID_VALUE, KMType.INVALID_VALUE, @@ -1169,7 +1163,7 @@ private short processRecipientStructure(byte[] scratchPad) { short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); // prepare cosekey short coseKey = - KMCose.constructCoseKey( + KMCose.constructCoseKey(rkpTmpVariables, KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), KMType.INVALID_VALUE, KMNInteger.uint_8(KMCose.COSE_ALG_ES256), @@ -1184,7 +1178,7 @@ private short processRecipientStructure(byte[] scratchPad) { short keyIdentifierPtr = KMByteBlob .instance(data, getEntry(EEK_KEY_ID), getEntryLength(EEK_KEY_ID)); short unprotectedHeaderRecipient = - KMCose.constructHeaders(KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, + KMCose.constructHeaders(rkpTmpVariables, KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, coseKey); // Construct recipients structure. @@ -1258,7 +1252,7 @@ private short processBcc(byte[] scratchPad) { // AAD is the CoseEncrypt structure private void processAesGcmUpdateAad(byte[] scratchPad) { - short protectedHeader = KMCose.constructHeaders( + short protectedHeader = KMCose.constructHeaders(rkpTmpVariables, KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), KMType.INVALID_VALUE, KMType.INVALID_VALUE, @@ -1304,7 +1298,7 @@ private short processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short private short getCoseEncryptProtectedHeader(byte[] scratchPad) { // CoseEncrypt protected headers. - short protectedHeader = KMCose.constructHeaders( + short protectedHeader = KMCose.constructHeaders(rkpTmpVariables, KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), KMType.INVALID_VALUE, KMType.INVALID_VALUE, @@ -1318,13 +1312,13 @@ private short getCoseEncryptProtectedHeader(byte[] scratchPad) { private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) { /* CoseEncrypt unprotected headers */ return KMCose - .constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE); + .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( + KMCose.constructCoseKey(rkpTmpVariables, KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), KMType.INVALID_VALUE, KMNInteger.uint_8(KMCose.COSE_ALG_ES256), @@ -1340,7 +1334,7 @@ private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, sho .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( + short headerPtr = KMCose.constructHeaders(rkpTmpVariables, KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), KMType.INVALID_VALUE, KMType.INVALID_VALUE, From c5efc6ad1453a78c138d0df6a5ba18dafd03c1cc Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Tue, 31 May 2022 06:03:18 +0000 Subject: [PATCH 04/15] Updated fixes in NVM optimization --- .../android/javacard/keymaster/KMKeymintDataStore.java | 3 +++ .../keymaster/RemotelyProvisionedComponentDevice.java | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index 34a477c7..0d50284f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -788,6 +788,9 @@ public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLengt if (inLength != 65) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } + if(oemRootPublicKey == null) { + oemRootPublicKey = new byte[65]; + } Util.arrayCopy(inBuff, inOffset, oemRootPublicKey, (short) 0, inLength); } diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 10304cab..282a480b 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -437,9 +437,9 @@ public void processFinishSendData(APDU apdu) throws Exception { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // PubKeysToSignMac - byte[] empty = {}; + short empty = repository.alloc((short)0) ; short len = - ((KMOperation) operation[0]).sign(empty, (short) 0, + ((KMOperation) operation[0]).sign(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0); // release operation releaseOperation(); @@ -561,9 +561,9 @@ private boolean isAdditionalCertificateChainPresent() { private short processFinalData(byte[] scratchPad) { // Call finish on AES GCM Cipher - byte[] empty = {}; + short empty = repository.alloc((short)0) ; short len = - ((KMOperation) operation[0]).finish(empty, (short) 0, (short) 0, scratchPad, (short) 0); + ((KMOperation) operation[0]).finish(repository.getHeap(), (short) empty, (short) 0, scratchPad, (short) 0); return len; } @@ -848,7 +848,7 @@ private KMDeviceUniqueKeyPair createDeviceUniqueKeyPair(boolean testMode, byte[] */ private short createDeviceInfo(byte[] scratchpad) { // Device Info Key Value pairs. - for (short i = 0; i < 30; i++) { + for (short i = 0; i < 32; i++) { rkpTmpVariables[i] = KMType.INVALID_VALUE; } short dataOffset = 2; From 000e74945d69462620d01906559aa87bbfab019b Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Wed, 1 Jun 2022 04:12:11 +0000 Subject: [PATCH 05/15] 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(); From b90e22c6fe069425149699ad3bcf66dc2ca492dc Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Fri, 3 Jun 2022 11:08:55 +0000 Subject: [PATCH 06/15] updated bug fixes in RMA support --- .../javacard/keymaster/KMAndroidSEApplet.java | 99 ++++++++----------- .../javacard/seprovider/KMSEProvider.java | 1 - .../javacard/keymaster/KMJCardSimApplet.java | 90 +++++++---------- .../keymaster/KMKeymintDataStore.java | 29 +++--- ProvisioningTool/src/provision.cpp | 20 ++-- 5 files changed, 104 insertions(+), 135 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 8fb66a7f..3f557d0d 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -65,17 +65,17 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis public static final byte BOOT_HASH_MAX_SIZE = 32; // Provision reporting status - private static final byte NOT_PROVISIONED = 0x00; - 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; + private static final short NOT_PROVISIONED = 0x0000; + private static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001; + private static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002; + private static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004; + private static final short PROVISION_STATUS_ATTEST_IDS = 0x0008; + private static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010; + private static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020; + private static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040; + private static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080; + private static final short PROVISION_STATUS_SE_LOCKED = 0x0100; + private static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200; public static final short SHARED_SECRET_KEY_SIZE = 32; @@ -206,7 +206,7 @@ private boolean isCommandAllowed(short apduIns) { break; case INS_SE_FACTORY_PROVISIONING_LOCK_CMD: - if(!isSeFactoryProvisioningComplete()) { + if(isSeFactoryProvisioningLocked() || !isSeFactoryProvisioningComplete()) { result = false; } break; @@ -216,7 +216,7 @@ private boolean isCommandAllowed(short apduIns) { // 1. All the necessary provisioning commands are succcessfully executed // 2. SE provision is locked // 3. OEM Root Public is provisioned. - if (!(isProvisioningComplete() && isSeFactoryProvisioningLocked())) { + if (kmDataStore.isProvisionLocked() || !(isProvisioningComplete() && isSeFactoryProvisioningLocked())) { result = false; } break; @@ -243,44 +243,48 @@ private boolean isCommandAllowed(short apduIns) { } private boolean isSeFactoryProvisioningLocked() { - short dInex = repository.allocReclaimableMemory((short)1); - byte data[] = repository.getHeap(); + short dInex = repository.allocReclaimableMemory((short)2); + byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); + short pStatus = Util.getShort(data, dInex); boolean result = false; - if ((0 != (data[dInex] & PROVISION_STATUS_SE_LOCKED))) { + if ((0 != (pStatus & PROVISION_STATUS_SE_LOCKED))) { result = true; } - repository.reclaimMemory((short)1); + repository.reclaimMemory((short)2); return result; } private boolean isSeFactoryProvisioningComplete() { - short dIndex = repository.allocReclaimableMemory((short)1); + short dIndex = repository.allocReclaimableMemory((short)2); byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dIndex); + short pStatus = Util.getShort(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; + if ((0 != (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) + && (0 != ((pStatus & PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)))) { + result = true; } - repository.reclaimMemory((short)1); + repository.reclaimMemory((short)2); return result; } private void processOEMUnlockProvisionCmd(APDU apdu) { - handleOEMLockUnlockCmd(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu); + authenticateOEM(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu); + kmDataStore.setProvisionLock(false); kmDataStore.unlockProvision(PROVISION_STATUS_PROVISIONING_LOCKED); sendError(apdu, KMError.OK); } private void processOEMLockProvisionCmd(APDU apdu) { - handleOEMLockUnlockCmd(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu); + authenticateOEM(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu); // Enable the lock bit in provision status. + kmDataStore.setProvisionLock(true); kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED); sendError(apdu, KMError.OK); } - private void handleOEMLockUnlockCmd(byte[] plainMsg, APDU apdu) { + private void authenticateOEM(byte[] plainMsg, APDU apdu) { tmpVariables[0] = KMArray.instance((short) 1); KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); @@ -519,7 +523,7 @@ private void processGetProvisionStatusCmd(APDU apdu) { kmDataStore.getProvisionStatus(scratchpad, (short) 0); short resp = KMArray.instance((short) 2); KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(resp).add((short) 1, KMInteger.uint_8(scratchpad[0])); + KMArray.cast(resp).add((short) 1, KMInteger.instance(scratchpad, (short)0, (short)2)); sendOutgoing(apdu, resp); } @@ -586,53 +590,30 @@ 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); - + super.reboot(); sendError(apdu, KMError.OK); } private boolean isProvisioningComplete() { - short dInex = repository.allocReclaimableMemory((short)1); - byte data[] = repository.getHeap(); + short dInex = repository.allocReclaimableMemory((short)2); + byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); + short pStatus = Util.getShort(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)))) { + if (kmDataStore.isProvisionLocked() || ((0 != (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) + && (0 != (pStatus & PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) + && (0 != (pStatus & PROVISION_STATUS_PRESHARED_SECRET)) + && (0 != (pStatus & PROVISION_STATUS_ATTEST_IDS)))) { result = true; } - repository.reclaimMemory((short)1); + repository.reclaimMemory((short)2); 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); - } - } - @Override public void onCleanup() { } @@ -704,5 +685,5 @@ private short validateApdu(APDU apdu) { } return apduBuffer[ISO7816.OFFSET_INS]; } -} +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index d641e4c6..d77f2167 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -698,7 +698,6 @@ KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(KMDeviceUniqueKeyPair key, short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset); - /** * This function generates a HMAC key from the provided key buffers. * diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 904a44ae..66f80148 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -54,17 +54,17 @@ public class KMJCardSimApplet extends KMKeymasterApplet { public static final byte BOOT_HASH_MAX_SIZE = 32; // Provision reporting status - private static final byte NOT_PROVISIONED = 0x00; - 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; + private static final short NOT_PROVISIONED = 0x0000; + private static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001; + private static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002; + private static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004; + private static final short PROVISION_STATUS_ATTEST_IDS = 0x0008; + private static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010; + private static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020; + private static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040; + private static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080; + private static final short PROVISION_STATUS_SE_LOCKED = 0x0100; + private static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200; public static final short SHARED_SECRET_KEY_SIZE = 32; @@ -194,7 +194,7 @@ private boolean isCommandAllowed(short apduIns) { break; case INS_SE_FACTORY_PROVISIONING_LOCK_CMD: - if(!isSeFactoryProvisioningComplete()) { + if(isSeFactoryProvisioningLocked() || !isSeFactoryProvisioningComplete()) { result = false; } break; @@ -204,7 +204,7 @@ private boolean isCommandAllowed(short apduIns) { // 1. All the necessary provisioning commands are succcessfully executed // 2. SE provision is locked // 3. OEM Root Public is provisioned. - if (!(isProvisioningComplete() && isSeFactoryProvisioningLocked())) { + if (kmDataStore.isProvisionLocked() || !(isProvisioningComplete() && isSeFactoryProvisioningLocked())) { result = false; } break; @@ -231,44 +231,48 @@ private boolean isCommandAllowed(short apduIns) { } private boolean isSeFactoryProvisioningLocked() { - short dInex = repository.allocReclaimableMemory((short)1); + short dInex = repository.allocReclaimableMemory((short)2); byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); + short pStatus = Util.getShort(data, dInex); boolean result = false; - if ((0 != (data[dInex] & PROVISION_STATUS_SE_LOCKED))) { + if ((0 != (pStatus & PROVISION_STATUS_SE_LOCKED))) { result = true; } - repository.reclaimMemory((short)1); + repository.reclaimMemory((short)2); return result; } private boolean isSeFactoryProvisioningComplete() { - short dIndex = repository.allocReclaimableMemory((short)1); + short dIndex = repository.allocReclaimableMemory((short)2); byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dIndex); + short pStatus = Util.getShort(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; + if ((0 != (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) + && (0 != ((pStatus & PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)))) { + result = true; } - repository.reclaimMemory((short)1); + repository.reclaimMemory((short)2); return result; } private void processOEMUnlockProvisionCmd(APDU apdu) { - handleOEMLockUnlockCmd(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu); + authenticateOEM(OEM_UNLOCK_PROVISION_VERIFICATION_LABEL, apdu); + kmDataStore.setProvisionLock(false); kmDataStore.unlockProvision(PROVISION_STATUS_PROVISIONING_LOCKED); sendError(apdu, KMError.OK); } private void processOEMLockProvisionCmd(APDU apdu) { - handleOEMLockUnlockCmd(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu); + authenticateOEM(OEM_LOCK_PROVISION_VERIFICATION_LABEL, apdu); // Enable the lock bit in provision status. + kmDataStore.setProvisionLock(true); kmDataStore.setProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED); sendError(apdu, KMError.OK); } - private void handleOEMLockUnlockCmd(byte[] plainMsg, APDU apdu) { + private void authenticateOEM(byte[] plainMsg, APDU apdu) { tmpVariables[0] = KMArray.instance((short) 1); KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); @@ -507,7 +511,7 @@ private void processGetProvisionStatusCmd(APDU apdu) { kmDataStore.getProvisionStatus(scratchpad, (short) 0); short resp = KMArray.instance((short) 2); KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(resp).add((short) 1, KMInteger.uint_8(scratchpad[0])); + KMArray.cast(resp).add((short) 1, KMInteger.instance(scratchpad, (short)0, (short)2)); sendOutgoing(apdu, resp); } @@ -583,43 +587,21 @@ private void processSetBootParamsCmd(APDU apdu) { } 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); + short dInex = repository.allocReclaimableMemory((short)2); byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); + short pStatus = Util.getShort(data, dInex); boolean result = false; - if ((0 != (data[dInex] & PROVISION_STATUS_OEM_PUBLIC_KEY)) - && (0 != (data[dInex] & PROVISION_STATUS_SE_LOCKED))) { + if (kmDataStore.isProvisionLocked() || ((0 != (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) + && (0 != (pStatus & PROVISION_STATUS_ADDITIONAL_CERT_CHAIN)) + && (0 != (pStatus & PROVISION_STATUS_PRESHARED_SECRET)) + && (0 != (pStatus & PROVISION_STATUS_ATTEST_IDS)))) { result = true; } - repository.reclaimMemory((short)1); + repository.reclaimMemory((short)2); 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(); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index 0d50284f..570648ad 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -462,7 +462,6 @@ public void persistAdditionalCertChain(byte[] buf, short offset, short len) { Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, (short) 2, len); JCSystem.commitTransaction(); - } public short getAdditionalCertChainLength() { @@ -749,8 +748,8 @@ public void setBootPatchLevel(byte[] buffer, short start, short length) { Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) length); } - public void setProvisionLocked() { - writeBoolean(PROVISIONED_LOCKED, true); + public void setProvisionLock(boolean lockValue) { + writeBoolean(PROVISIONED_LOCKED, lockValue); } public boolean isProvisionLocked() { @@ -763,25 +762,28 @@ public boolean isProvisionLocked() { return false; } - public void setProvisionStatus(byte provisionStatus) { - short offset = repository.alloc((short) 1); + public void setProvisionStatus(short provisionStatus) { + short offset = repository.alloc((short) 2); byte[] buf = repository.getHeap(); getProvisionStatus(buf, offset); - buf[offset] |= provisionStatus; - writeDataEntry(PROVISIONED_STATUS, buf, offset, (short) 1); + provisionStatus |= Util.getShort(buf, offset); + Util.setShort(buf, offset, provisionStatus); + writeDataEntry(PROVISIONED_STATUS, buf, offset, (short) 2); } public void getProvisionStatus(byte[] scratchpad, short offset) { - scratchpad[offset] = 0; + Util.setShort(scratchpad, offset, (short)0); readDataEntry(PROVISIONED_STATUS, scratchpad, offset); } - public void unlockProvision(byte unlockOffset) { - short offset = repository.alloc((short) 1); + public void unlockProvision(short unlockOffset) { + short offset = repository.alloc((short) 2); byte[] buf = repository.getHeap(); getProvisionStatus(buf, offset); - buf[offset] &= ~unlockOffset; - writeDataEntry(PROVISIONED_STATUS, buf, offset, (short) 1); + short temp = Util.getShort(buf, offset); + temp &= ~unlockOffset; + Util.setShort(buf, offset, temp); + writeDataEntry(PROVISIONED_STATUS, buf, offset, (short) 2); } public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLength) { @@ -795,6 +797,9 @@ public void persistOEMRootPublicKey(byte[] inBuff, short inOffset, short inLengt } public byte[] getOEMRootPublicKey() { + if(oemRootPublicKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } return oemRootPublicKey; } diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index 4587a808..6284f686 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -30,15 +30,17 @@ #define SE_POWER_RESET_STATUS_FLAG (1 << 30) // TODO keymint provision status enum ProvisionStatus { - NOT_PROVISIONED = 0x00, - PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x04, - PROVISION_STATUS_ATTEST_IDS = 0x08, - PROVISION_STATUS_PRESHARED_SECRET = 0x10, - PROVISION_STATUS_PROVISIONING_LOCKED = 0x20, - PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x40, - PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x80, - PROVISION_STATUS_SE_LOCKED = 0x01, - PROVISION_STATUS_OEM_PUBLIC_KEY = 0x02 + NOT_PROVISIONED = 0x0000, + PROVISION_STATUS_ATTESTATION_KEY = 0x0001, + PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002, + PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004, + PROVISION_STATUS_ATTEST_IDS = 0x0008, + PROVISION_STATUS_PRESHARED_SECRET = 0x0010, + PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020, + PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x0040, + PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080, + PROVISION_STATUS_SE_LOCKED = 0x0100, + PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200 }; // TODO keymint provision status and lock From 7dda70169c74bb822dd5b92ab468e73b4cba9000 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Fri, 3 Jun 2022 20:06:37 +0000 Subject: [PATCH 07/15] updated bug fixes and provision status in RMA support --- .../javacard/keymaster/KMAndroidSEApplet.java | 15 ----------- .../javacard/keymaster/KMKeymasterApplet.java | 13 ++++++++++ .../keymaster/KMKeymintDataStore.java | 26 +++++++++++++++++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 3f557d0d..adb18e05 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -59,24 +59,9 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis 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; public static final byte BOOT_HASH_MAX_SIZE = 32; - - // Provision reporting status - private static final short NOT_PROVISIONED = 0x0000; - private static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001; - private static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002; - private static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004; - private static final short PROVISION_STATUS_ATTEST_IDS = 0x0008; - private static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010; - private static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020; - private static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040; - private static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080; - private static final short PROVISION_STATUS_SE_LOCKED = 0x0100; - private static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200; - public static final short SHARED_SECRET_KEY_SIZE = 32; // Package version. diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 462c42df..74d66f0f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -266,6 +266,19 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe // 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; + protected static RemotelyProvisionedComponentDevice rkp; protected static KMEncoder encoder; protected static KMDecoder decoder; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index 570648ad..3d6760ec 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -866,8 +866,34 @@ public void onRestore(Element element, short oldVersion, short currentVersion) { preSharedKey = (KMPreSharedKey) seProvider.onRestore(element); deviceUniqueKeyPair = (KMDeviceUniqueKeyPair) seProvider.onRestore(element); rkpMacKey = (KMRkpMacKey) seProvider.onRestore(element); + handleDataUpgrade(oldVersion, currentVersion); } + void handleDataUpgrade(short oldVersion, short currentVersion) { + if(oldVersion == 0x0100 && currentVersion == 0x0200) { + handleProvisionStatusUpgrade(); + } + } + + void handleProvisionStatusUpgrade( ){ + short dInex = repository.allocReclaimableMemory((short)2); + byte data[] = repository.getHeap(); + getProvisionStatus(data, dInex); + short newStatus = (short)( data[dInex] & 0x00ff); + if( KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED + == (newStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) { + newStatus |= KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED; + } + Util.setShort(data, dInex, newStatus); + short pStatusOff = (short) (PROVISIONED_STATUS * DATA_INDEX_ENTRY_SIZE); + JCSystem.beginTransaction(); + Util.setShort(dataTable, (short) (pStatusOff + DATA_INDEX_ENTRY_OFFSET), (short)0); + Util.setShort(dataTable, (short) (pStatusOff + DATA_INDEX_ENTRY_LENGTH), (short)0); + JCSystem.commitTransaction(); + writeDataEntry(PROVISIONED_STATUS, data, dInex, (short) 2); + repository.reclaimMemory((short)2); + } + @Override public short getBackupPrimitiveByteCount() { // dataIndex - 2 bytes From f778194b8017aa8adf5afe655804bbb337bc52a3 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Sun, 5 Jun 2022 17:45:06 +0000 Subject: [PATCH 08/15] Updated NVM optimization changes --- .../javacard/keymaster/KMAndroidSEApplet.java | 2 +- .../seprovider/KMAndroidSEProvider.java | 30 ++++++++----------- .../android/javacard/keymaster/KMCose.java | 14 ++++----- .../javacard/keymaster/KMCoseHeaders.java | 8 ++--- .../android/javacard/keymaster/KMCoseKey.java | 22 +++++++------- .../RemotelyProvisionedComponentDevice.java | 4 +-- 6 files changed, 38 insertions(+), 42 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index adb18e05..115abe41 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -123,7 +123,7 @@ public void process(APDU apdu) { break; case INS_SET_BOOT_ENDED_CMD: - processSetBootEndedCmd(apdu); + processSetBootEndedCmd(apdu); break; case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index 86c3e7c7..e5ede6e9 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -52,6 +52,8 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final byte POWER_RESET_FALSE = (byte) 0xAA; public static final byte POWER_RESET_TRUE = (byte) 0x00; private static final short COMPUTED_HMAC_KEY_SIZE = 32; + private static byte[] CMAC_KDF_CONSTANT_L; + private static byte[] CMAC_KDF_CONSTANT_ZERO; private static KeyAgreement keyAgreement; @@ -88,6 +90,7 @@ public static KMAndroidSEProvider getInstance() { } public KMAndroidSEProvider() { + initStatics(); // Re-usable AES,DES and HMAC keys in persisted memory. aesKeys = new AESKey[2]; aesKeys[KEYSIZE_128_OFFSET] = (AESKey) KeyBuilder.buildKey( @@ -120,6 +123,12 @@ public KMAndroidSEProvider() { 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, (short) 256, (byte) 0); @@ -401,23 +410,10 @@ public boolean aesGCMDecrypt(byte[] aesKey, short aesKeyStart, public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelStart, short labelLen, byte[] context, short contextStart, short contextLength) { - try { + try { // This is hardcoded to requirement - 32 byte output with two concatenated // 16 bytes K1 and K2. final byte n = 2; // hardcoded - // [L] 256 bits - hardcoded 32 bits as per - // reference impl in keymaster. - short zeroIndex = 0; - short LIndex = 1; - short bufIndex = 5; - short keyIndex = 9; - //1st byte in tempArray is reserved for zero - //next 4 bytes in tempArray is reserved for L - tmpArray[0] = 0; - tmpArray[1] = 0; - tmpArray[2] = 0; - tmpArray[3] = 1; - tmpArray[4] = 0; // [i] counter - 32 bits short iBufLen = 4; @@ -439,12 +435,12 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta while (i <= n) { tmpArray[3] = i; // 4 bytes of iBuf with counter in it - kdf.update(tmpArray, bufIndex, iBufLen); + kdf.update(tmpArray, (short) 0, (short) iBufLen); kdf.update(label, labelStart, (short) labelLen); // label - kdf.update(tmpArray, zeroIndex, (short) 1); // 1 byte of 0x00 + 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(tmpArray, LIndex, (short) 4, tmpArray, + pos = kdf.sign(CMAC_KDF_CONSTANT_L, (short) 0, (short) CMAC_KDF_CONSTANT_L.length, tmpArray, (short) (iBufLen + pos)); i++; } diff --git a/Applet/src/com/android/javacard/keymaster/KMCose.java b/Applet/src/com/android/javacard/keymaster/KMCose.java index 94648ca2..ca72db64 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCose.java +++ b/Applet/src/com/android/javacard/keymaster/KMCose.java @@ -305,13 +305,13 @@ public static short constructCoseCertPayload(short issuer, short subject, short * @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[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(coseHeaderConst, buff, (short)4, false); ptr = KMCoseHeaders.instance(ptr); KMCoseHeaders.cast(ptr).canonicalize(); @@ -490,7 +490,7 @@ public static short constructCoseKey(short []buff, short keyType, short keyId, s buff[6] = pubY; buff[7] = priv; for (short i = valueIndex; i < 16; i++) { - buff[i] = KMType.INVALID_VALUE; + buff[i] = KMType.INVALID_VALUE; } short arrPtr = handleCosePairTags(coseKeyConst, buff, valueIndex, includeTestKey); if (includeTestKey) { diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java b/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java index d02a3975..e8939f1e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java +++ b/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java @@ -154,10 +154,10 @@ public short getAlgorithm() { 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; + 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; diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseKey.java b/Applet/src/com/android/javacard/keymaster/KMCoseKey.java index 53a14c34..a39d3225 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCoseKey.java +++ b/Applet/src/com/android/javacard/keymaster/KMCoseKey.java @@ -180,17 +180,17 @@ public boolean isTestKey() { * @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; + 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; diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 282a480b..12dc4b83 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -1059,8 +1059,8 @@ private short ecdhHkdfDeriveKey(byte[] privKeyA, short privKeyAOff, short privKe // data table for later usage. private short generateEphemeralEcKey(byte[] scratchPad) { // Generate ephemeral ec key. - rkpTmpVariables[0] = 0; - rkpTmpVariables[1] = 0; + rkpTmpVariables[0] = 0; + rkpTmpVariables[1] = 0; seProvider.createAsymmetricKey( KMType.EC, scratchPad, From b19cfdf41c0d5425cdd610bbdb1ebb557a276fba Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Sun, 5 Jun 2022 18:56:23 +0000 Subject: [PATCH 09/15] added system property validations --- .../javacard/keymaster/KMAndroidSEApplet.java | 9 +- .../javacard/keymaster/KMKeymasterApplet.java | 191 ++++++++------- .../keymaster/KMKeymintDataStore.java | 220 ++++++++++++------ HAL/JavacardSecureElement.cpp | 2 +- HAL/JavacardSecureElement.h | 2 +- HAL/JavacardSharedSecret.cpp | 14 +- 6 files changed, 269 insertions(+), 169 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 115abe41..23a72e56 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -220,8 +220,8 @@ private boolean isCommandAllowed(short apduIns) { default: // Allow other commands only if provision is completed. - if (!isProvisioningComplete()) { - result = false; + if (!(isProvisioningComplete())) { + result = false; } } return result; @@ -518,7 +518,7 @@ private void processSetBootEndedCmd(APDU apdu) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } //set the flag to mark boot ended - kmDataStore.setBootEndedStatus(true); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.DEVICE_BOOT_ENDED_DONE); seProvider.clearDeviceBooted(false); sendError(apdu, KMError.OK); } @@ -528,7 +528,7 @@ private void processSetBootParamsCmd(APDU apdu) { && (!seProvider.isDeviceRebooted())) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - + kmDataStore.clearSBInitStatus(); short argsProto = KMArray.instance((short) 5); byte[] scratchPad = apdu.getBuffer(); // Array of 4 expected arguments @@ -580,6 +580,7 @@ private void processSetBootParamsCmd(APDU apdu) { kmDataStore.createComputedHmacKey(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE); super.reboot(); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.SET_BOOT_PARAMS_DONE); sendError(apdu, KMError.OK); } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 74d66f0f..3622f622 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -457,89 +457,93 @@ public void process(APDU apdu) { } byte[] apduBuffer = apdu.getBuffer(); byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; - switch (apduIns) { - case INS_INIT_STRONGBOX_CMD: - processInitStrongBoxCmd(apdu); - sendError(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; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + if (isCommandAllowed(apduIns)) { + switch (apduIns) { + case INS_INIT_STRONGBOX_CMD: + processInitStrongBoxCmd(apdu); + sendError(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; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } else { + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } catch (KMException exception) { freeOperations(); @@ -562,6 +566,22 @@ public void process(APDU apdu) { } } + private boolean isCommandAllowed(short apduIns) { + boolean result = true; + switch(apduIns) { + case INS_INIT_STRONGBOX_CMD: + case INS_GET_HMAC_SHARING_PARAM_CMD: + case INS_COMPUTE_SHARED_HMAC_CMD: + break; + + default: + if(!(kmDataStore.isSBInitCompleted())) { + result = false; + } + } + return result; + } + private void generateUniqueOperationHandle(byte[] buf, short offset, short len) { do { seProvider.newRandomNumber(buf, offset, len); @@ -579,6 +599,7 @@ private void freeOperations() { private void processEarlyBootEndedCmd(APDU apdu) { kmDataStore.setEarlyBootEndedStatus(true); + sendError(apdu, KMError.OK); } private short deviceLockedCmd(APDU apdu){ @@ -1015,6 +1036,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { (short) sharingCheck.length, scratchPad, keyLen); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_DONE); // verification signature blob - 32 bytes //tmpVariables[1] short signature = KMByteBlob.instance(scratchPad, keyLen, signLen); @@ -3422,12 +3444,11 @@ private void processInitStrongBoxCmd(APDU apdu) { setOsVersion(osVersion); setOsPatchLevel(osPatchLevel); setVendorPatchLevel(vendorPatchLevel); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.INIT_STRONGBOX_DONE); } public void reboot() { kmDataStore.clearHmacNonce(); - //flag to maintain the boot state - kmDataStore.setBootEndedStatus(false); //flag to maintain early boot ended state kmDataStore.setEarlyBootEndedStatus(false); //Clear all the operation state. diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index 3d6760ec..c89077d9 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -73,6 +73,12 @@ public class KMKeymintDataStore implements KMUpgradable { private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 512;//First 2 bytes for length. private static final short BCC_MAX_SIZE = 512; + public static final byte RESET_SB_INIT_STATUS = 0x00; + public static final byte SET_BOOT_PARAMS_DONE = 0x01; + public static final byte DEVICE_BOOT_ENDED_DONE = 0x02; + public static final byte INIT_STRONGBOX_DONE = 0x04; + public static final byte NEGOTIATED_SHARED_SECRET_DONE = 0x08; + // Data - originally was in repository private byte[] attIdBrand; private byte[] attIdDevice; @@ -206,32 +212,29 @@ public short getHmacNonce() { public short getOsVersion() { short blob = readData(BOOT_OS_VERSION); - if (blob != KMType.INVALID_VALUE) { - return KMInteger.uint_32( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_32(zero, (short) 0); + 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) { - return KMInteger.uint_32( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_32(zero, (short) 0); + 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) { - return KMInteger.uint_32( - KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_32(zero, (short) 0); + 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) { @@ -255,17 +258,24 @@ public boolean getEarlyBootEndedStatus() { } public boolean getBootEndedStatus() { - return readBoolean(BOOT_ENDED_FLAG); + boolean result = false; + short offset = repository.allocReclaimableMemory((short) 1); + byte[] buf = repository.getHeap(); + getSBInitStatus(buf, offset); + if(DEVICE_BOOT_ENDED_DONE == (buf[offset] & DEVICE_BOOT_ENDED_DONE)) { + result = true; + } + repository.reclaimMemory((short)1); + return result; } public short getDeviceTimeStamp() { short blob = readData(DEVICE_LOCKED_TIME); - if (blob != KMType.INVALID_VALUE) { - return KMInteger.uint_64(KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff()); - } else { - return KMInteger.uint_64(zero, (short) 0); + 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) { @@ -311,8 +321,36 @@ public void setEarlyBootEndedStatus(boolean flag) { writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); } - public void setBootEndedStatus(boolean flag) { - writeBoolean(BOOT_ENDED_FLAG, flag); + public void clearSBInitStatus() { + clearDataEntry(BOOT_ENDED_FLAG); + } + + public void updateSBInitStatus(byte initStatus) { + short offset = repository.allocReclaimableMemory((short) 1); + byte[] buf = repository.getHeap(); + getSBInitStatus(buf, offset); + buf[offset] |= initStatus; + writeDataEntry(BOOT_ENDED_FLAG, buf, offset, (short) 1); + repository.reclaimMemory((short)1); + } + + public boolean isSBInitCompleted() { + boolean result = false; + short offset = repository.allocReclaimableMemory((short) 1); + byte[] buf = repository.getHeap(); + getSBInitStatus(buf, offset); + if ((0 != (buf[offset] & SET_BOOT_PARAMS_DONE)) + && (0 != (buf[offset] & INIT_STRONGBOX_DONE)) + && (0 != (buf[offset] & NEGOTIATED_SHARED_SECRET_DONE))) { + result = true; + } + repository.reclaimMemory((short)1); + return result; + } + + public void getSBInitStatus(byte[] scratchpad, short offset) { + scratchpad[offset] = 0; + readDataEntry(BOOT_ENDED_FLAG, scratchpad, offset); } public void clearDeviceLockTimeStamp() { @@ -512,6 +550,9 @@ public void createPresharedKey(byte[] keyData, short offset, short length) { } public KMPreSharedKey getPresharedKey() { + if (preSharedKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } return preSharedKey; } @@ -527,6 +568,9 @@ public void createComputedHmacKey(byte[] keyData, short offset, short length) { } public KMComputedHmacKey getComputedHmacKey() { + if (computedHmacKey == null) { + KMException.throwIt(KMError.INVALID_DATA); + } return computedHmacKey; } @@ -571,106 +615,112 @@ public void createRkpMacKey(byte[] keydata, short offset, short length) { } public KMRkpMacKey getRkpMacKey() { - return rkpMacKey; + 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: - Util.arrayCopyNonAtomic(attIdBrand, (short) 0, buffer, start, (short) attIdBrand.length); - return (short) attIdBrand.length; + attestId = attIdBrand; + break; // Attestation Id Device case KMType.ATTESTATION_ID_DEVICE: - Util.arrayCopyNonAtomic(attIdDevice, (short) 0, buffer, start, (short) attIdDevice.length); - return (short) attIdDevice.length; + attestId = attIdDevice; + break; // Attestation Id Product case KMType.ATTESTATION_ID_PRODUCT: - Util.arrayCopyNonAtomic(attIdProduct, (short) 0, buffer, start, - (short) attIdProduct.length); - return (short) attIdProduct.length; + attestId = attIdProduct; + break; // Attestation Id Serial case KMType.ATTESTATION_ID_SERIAL: - Util.arrayCopyNonAtomic(attIdSerial, (short) 0, buffer, start, (short) attIdSerial.length); - return (short) attIdSerial.length; + attestId = attIdSerial; + break; // Attestation Id IMEI case KMType.ATTESTATION_ID_IMEI: - Util.arrayCopyNonAtomic(attIdImei, (short) 0, buffer, start, (short) attIdImei.length); - return (short) attIdImei.length; + attestId = attIdImei; + break; // Attestation Id MEID case KMType.ATTESTATION_ID_MEID: - Util.arrayCopyNonAtomic(attIdMeId, (short) 0, buffer, start, (short) attIdMeId.length); - return (short) attIdMeId.length; + attestId = attIdMeId; + break; // Attestation Id Manufacturer case KMType.ATTESTATION_ID_MANUFACTURER: - Util.arrayCopyNonAtomic(attIdManufacturer, (short) 0, buffer, start, - (short) attIdManufacturer.length); - return (short) attIdManufacturer.length; + attestId = attIdManufacturer; + break; // Attestation Id Model case KMType.ATTESTATION_ID_MODEL: - Util.arrayCopyNonAtomic(attIdModel, (short) 0, buffer, start, (short) attIdModel.length); - return (short) attIdModel.length; + attestId = attIdModel; + break; } - return (short) 0; + 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(); + JCSystem.beginTransaction(); attIdBrand = new byte[length]; Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length); - JCSystem.commitTransaction(); + 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.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(); + 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(); + 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(); + 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(); + 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]; + 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(); + JCSystem.beginTransaction(); + attIdModel = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length); + JCSystem.commitTransaction(); break; } } @@ -687,11 +737,17 @@ public void deleteAttestationIds() { } 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 (verifiedHash == null) { + KMException.throwIt(KMError.INVALID_DATA); + } Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length); return (short) bootKey.length; } @@ -705,6 +761,9 @@ public boolean isDeviceBootLocked() { } public short getBootPatchLevel(byte[] buffer, short start) { + if (bootPatchLevel == null) { + KMException.throwIt(KMError.INVALID_DATA); + } Util.arrayCopyNonAtomic(bootPatchLevel, (short) 0, buffer, start, (short) bootPatchLevel.length); return (short) bootPatchLevel.length; @@ -717,7 +776,7 @@ public void setVerifiedBootHash(byte[] buffer, short start, short length) { if (length != 32) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - Util.arrayCopyNonAtomic(buffer, start, verifiedHash, (short) 0, (short) 32); + Util.arrayCopy(buffer, start, verifiedHash, (short) 0, (short) 32); } public void setBootKey(byte[] buffer, short start, short length) { @@ -727,7 +786,7 @@ public void setBootKey(byte[] buffer, short start, short length) { if (length != 32) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - Util.arrayCopyNonAtomic(buffer, start, bootKey, (short) 0, (short) 32); + Util.arrayCopy(buffer, start, bootKey, (short) 0, (short) 32); } public void setBootState(short state) { @@ -745,7 +804,7 @@ public void setBootPatchLevel(byte[] buffer, short start, short length) { if (length > 4 || length < 0) { KMException.throwIt(KMError.UNKNOWN_ERROR); } - Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, (short) length); + Util.arrayCopy(buffer, start, bootPatchLevel, (short) 0, (short) length); } public void setProvisionLock(boolean lockValue) { @@ -802,7 +861,7 @@ public byte[] getOEMRootPublicKey() { } return oemRootPublicKey; } - + @Override public void onSave(Element element) { // Prmitives @@ -872,10 +931,11 @@ public void onRestore(Element element, short oldVersion, short currentVersion) { void handleDataUpgrade(short oldVersion, short currentVersion) { if(oldVersion == 0x0100 && currentVersion == 0x0200) { handleProvisionStatusUpgrade(); + handleSBInitStatusUpgrade(); } } - void handleProvisionStatusUpgrade( ){ + void handleProvisionStatusUpgrade(){ short dInex = repository.allocReclaimableMemory((short)2); byte data[] = repository.getHeap(); getProvisionStatus(data, dInex); @@ -893,6 +953,16 @@ void handleProvisionStatusUpgrade( ){ writeDataEntry(PROVISIONED_STATUS, data, dInex, (short) 2); repository.reclaimMemory((short)2); } + + void handleSBInitStatusUpgrade(){ + short dInex = repository.allocReclaimableMemory((short)1); + byte data[] = repository.getHeap(); + getSBInitStatus(data, dInex); + if(data[dInex] == 0x01) { + updateSBInitStatus(DEVICE_BOOT_ENDED_DONE); + } + repository.reclaimMemory((short)1); + } @Override public short getBackupPrimitiveByteCount() { diff --git a/HAL/JavacardSecureElement.cpp b/HAL/JavacardSecureElement.cpp index 252131da..0b5cef82 100644 --- a/HAL/JavacardSecureElement.cpp +++ b/HAL/JavacardSecureElement.cpp @@ -37,7 +37,7 @@ keymaster_error_t JavacardSecureElement::initializeJavacard() { request.add(Uint(getOsVersion())); request.add(Uint(getOsPatchlevel())); request.add(Uint(getVendorPatchlevel())); - auto [item, err] = sendRequest(Instruction::INS_SET_BOOT_PARAMS_CMD, request); + auto [item, err] = sendRequest(Instruction::INS_INIT_STRONGBOX_CMD, request); return err; } diff --git a/HAL/JavacardSecureElement.h b/HAL/JavacardSecureElement.h index 160d3d4d..d483a2d2 100644 --- a/HAL/JavacardSecureElement.h +++ b/HAL/JavacardSecureElement.h @@ -59,7 +59,7 @@ enum class Instruction { INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23, INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24, INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25, - INS_SET_BOOT_PARAMS_CMD = KEYMINT_CMD_APDU_START + 26, + INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26, // RKP Commands INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27, INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28, diff --git a/HAL/JavacardSharedSecret.cpp b/HAL/JavacardSharedSecret.cpp index a779c1ad..d6fd0541 100644 --- a/HAL/JavacardSharedSecret.cpp +++ b/HAL/JavacardSharedSecret.cpp @@ -12,10 +12,14 @@ using std::shared_ptr; using std::vector; ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) { - card_->initializeJavacard(); + auto error = card_->initializeJavacard(); + if(error != KM_ERROR_OK) { + LOG(ERROR) << "Error in initializing javacard."; + return km_utils::kmError2ScopedAStatus(error); + } auto [item, err] = card_->sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD); if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in sending in getSharedSecretParameters.!!!!!!!!!!!!!!!"; + LOG(ERROR) << "Error in sending in getSharedSecretParameters."; return km_utils::kmError2ScopedAStatus(err); } if (!cbor_.getSharedSecretParameters(item, 1, *params)) { @@ -29,7 +33,11 @@ ScopedAStatus JavacardSharedSecret::computeSharedSecret(const std::vector& params, std::vector* secret) { - card_->initializeJavacard(); + auto error = card_->initializeJavacard(); + if(error != KM_ERROR_OK) { + LOG(ERROR) << "Error in initializing javacard."; + return km_utils::kmError2ScopedAStatus(error); + } cppbor::Array request; cbor_.addSharedSecretParameters(request, params); auto [item, err] = card_->sendRequest(Instruction::INS_COMPUTE_SHARED_SECRET_CMD, request); From cc7cec19b76b59fdabd415797f7ee833a0327bc7 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Mon, 6 Jun 2022 07:30:27 +0000 Subject: [PATCH 10/15] updated system properties validation --- .../javacard/keymaster/KMAndroidSEApplet.java | 4 +- .../javacard/keymaster/KMKeymasterApplet.java | 202 +++++++++--------- .../keymaster/KMKeymintDataStore.java | 30 +-- 3 files changed, 120 insertions(+), 116 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 23a72e56..300fabb7 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -518,7 +518,7 @@ private void processSetBootEndedCmd(APDU apdu) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } //set the flag to mark boot ended - kmDataStore.updateSBInitStatus(KMKeymintDataStore.DEVICE_BOOT_ENDED_DONE); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.SET_BOOT_ENDED_SUCCESS); seProvider.clearDeviceBooted(false); sendError(apdu, KMError.OK); } @@ -580,7 +580,7 @@ private void processSetBootParamsCmd(APDU apdu) { kmDataStore.createComputedHmacKey(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE); super.reboot(); - kmDataStore.updateSBInitStatus(KMKeymintDataStore.SET_BOOT_PARAMS_DONE); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS); sendError(apdu, KMError.OK); } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 3622f622..07de2c97 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -457,93 +457,92 @@ public void process(APDU apdu) { } byte[] apduBuffer = apdu.getBuffer(); byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; - if (isCommandAllowed(apduIns)) { - switch (apduIns) { - case INS_INIT_STRONGBOX_CMD: - processInitStrongBoxCmd(apdu); - sendError(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; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - } else { - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + if (!isStrongBoxKeymasterCmdAllowed(apduIns)) { + ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); + } + switch (apduIns) { + case INS_INIT_STRONGBOX_CMD: + processInitStrongBoxCmd(apdu); + sendError(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; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } } catch (KMException exception) { freeOperations(); @@ -566,20 +565,21 @@ public void process(APDU apdu) { } } - private boolean isCommandAllowed(short apduIns) { - boolean result = true; - switch(apduIns) { - case INS_INIT_STRONGBOX_CMD: - case INS_GET_HMAC_SHARING_PARAM_CMD: - case INS_COMPUTE_SHARED_HMAC_CMD: - break; - + private boolean isStrongBoxKeymasterCmdAllowed(byte apduIns) { + if(kmDataStore.isSBInitCompleted()) { + return true; + } + // Data Dirty. + switch (apduIns) { + case INS_GENERATE_KEY_CMD: + case INS_IMPORT_KEY_CMD: + case INS_IMPORT_WRAPPED_KEY_CMD: + case INS_ATTEST_KEY_CMD: + return false; default: - if(!(kmDataStore.isSBInitCompleted())) { - result = false; - } + break; } - return result; + return true; } private void generateUniqueOperationHandle(byte[] buf, short offset, short len) { @@ -1036,7 +1036,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { (short) sharingCheck.length, scratchPad, keyLen); - kmDataStore.updateSBInitStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_DONE); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_SUCCESS); // verification signature blob - 32 bytes //tmpVariables[1] short signature = KMByteBlob.instance(scratchPad, keyLen, signLen); @@ -3444,7 +3444,7 @@ private void processInitStrongBoxCmd(APDU apdu) { setOsVersion(osVersion); setOsPatchLevel(osPatchLevel); setVendorPatchLevel(vendorPatchLevel); - kmDataStore.updateSBInitStatus(KMKeymintDataStore.INIT_STRONGBOX_DONE); + kmDataStore.updateSBInitStatus(KMKeymintDataStore.SET_SYSTEM_PROPERTIES_SUCCESS); } public void reboot() { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index c89077d9..84f3ae75 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -74,10 +74,10 @@ public class KMKeymintDataStore implements KMUpgradable { private static final short BCC_MAX_SIZE = 512; public static final byte RESET_SB_INIT_STATUS = 0x00; - public static final byte SET_BOOT_PARAMS_DONE = 0x01; - public static final byte DEVICE_BOOT_ENDED_DONE = 0x02; - public static final byte INIT_STRONGBOX_DONE = 0x04; - public static final byte NEGOTIATED_SHARED_SECRET_DONE = 0x08; + public static final byte SET_BOOT_PARAMS_SUCCESS = 0x01; + public static final byte SET_BOOT_ENDED_SUCCESS = 0x02; + public static final byte SET_SYSTEM_PROPERTIES_SUCCESS = 0x04; + public static final byte NEGOTIATED_SHARED_SECRET_SUCCESS = 0x08; // Data - originally was in repository private byte[] attIdBrand; @@ -262,7 +262,7 @@ public boolean getBootEndedStatus() { short offset = repository.allocReclaimableMemory((short) 1); byte[] buf = repository.getHeap(); getSBInitStatus(buf, offset); - if(DEVICE_BOOT_ENDED_DONE == (buf[offset] & DEVICE_BOOT_ENDED_DONE)) { + if(SET_BOOT_ENDED_SUCCESS == (buf[offset] & SET_BOOT_ENDED_SUCCESS)) { result = true; } repository.reclaimMemory((short)1); @@ -339,18 +339,19 @@ public boolean isSBInitCompleted() { short offset = repository.allocReclaimableMemory((short) 1); byte[] buf = repository.getHeap(); getSBInitStatus(buf, offset); - if ((0 != (buf[offset] & SET_BOOT_PARAMS_DONE)) - && (0 != (buf[offset] & INIT_STRONGBOX_DONE)) - && (0 != (buf[offset] & NEGOTIATED_SHARED_SECRET_DONE))) { + if ((0 != (buf[offset] & SET_BOOT_PARAMS_SUCCESS)) + && (0 != (buf[offset] & SET_BOOT_ENDED_SUCCESS)) + && (0 != (buf[offset] & SET_SYSTEM_PROPERTIES_SUCCESS)) + && (0 != (buf[offset] & NEGOTIATED_SHARED_SECRET_SUCCESS))) { result = true; } repository.reclaimMemory((short)1); return result; } - public void getSBInitStatus(byte[] scratchpad, short offset) { + public short getSBInitStatus(byte[] scratchpad, short offset) { scratchpad[offset] = 0; - readDataEntry(BOOT_ENDED_FLAG, scratchpad, offset); + return readDataEntry(BOOT_ENDED_FLAG, scratchpad, offset); } public void clearDeviceLockTimeStamp() { @@ -957,9 +958,12 @@ void handleProvisionStatusUpgrade(){ void handleSBInitStatusUpgrade(){ short dInex = repository.allocReclaimableMemory((short)1); byte data[] = repository.getHeap(); - getSBInitStatus(data, dInex); - if(data[dInex] == 0x01) { - updateSBInitStatus(DEVICE_BOOT_ENDED_DONE); + short len = getSBInitStatus(data, dInex); + if(len != 0) { + updateSBInitStatus((byte)(SET_BOOT_PARAMS_SUCCESS | + SET_BOOT_ENDED_SUCCESS | + SET_SYSTEM_PROPERTIES_SUCCESS | + NEGOTIATED_SHARED_SECRET_SUCCESS)); } repository.reclaimMemory((short)1); } From 8d56d611e7a10e88e1a648bc8e9f4d63a314b372 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Tue, 7 Jun 2022 05:31:56 +0000 Subject: [PATCH 11/15] Updated system properties validation --- .../javacard/keymaster/KMAndroidSEApplet.java | 26 ++----- .../javacard/keymaster/KMKeymasterApplet.java | 50 ++++++------ .../keymaster/KMKeymintDataStore.java | 77 +++++++------------ 3 files changed, 61 insertions(+), 92 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 300fabb7..1d75d262 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -54,7 +54,7 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis 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; + INS_KEYMINT_PROVIDER_APDU_START + 8; //unused 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; @@ -120,11 +120,7 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - break; - - case INS_SET_BOOT_ENDED_CMD: - processSetBootEndedCmd(apdu); - break; + break; case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: processProvisionRkpDeviceUniqueKeyPair(apdu); @@ -214,7 +210,6 @@ private boolean isCommandAllowed(short apduIns) { break; case INS_SET_BOOT_PARAMS_CMD: - case INS_SET_BOOT_ENDED_CMD: case INS_GET_PROVISION_STATUS_CMD: break; @@ -512,23 +507,13 @@ 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.updateSBInitStatus(KMKeymintDataStore.SET_BOOT_ENDED_SUCCESS); - seProvider.clearDeviceBooted(false); - sendError(apdu, KMError.OK); - } - private void processSetBootParamsCmd(APDU apdu) { if (seProvider.isBootSignalEventSupported() && (!seProvider.isDeviceRebooted())) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - kmDataStore.clearSBInitStatus(); + // clear the device reboot status + kmDataStore.clearDeviceBootStatus(); short argsProto = KMArray.instance((short) 5); byte[] scratchPad = apdu.getBuffer(); // Array of 4 expected arguments @@ -580,7 +565,8 @@ private void processSetBootParamsCmd(APDU apdu) { kmDataStore.createComputedHmacKey(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE); super.reboot(); - kmDataStore.updateSBInitStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS); + kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS); + seProvider.clearDeviceBooted(false); sendError(apdu, KMError.OK); } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 07de2c97..caeffac7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -457,7 +457,7 @@ public void process(APDU apdu) { } byte[] apduBuffer = apdu.getBuffer(); byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; - if (!isStrongBoxKeymasterCmdAllowed(apduIns)) { + if (!isKeymintReady(apduIns)) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } switch (apduIns) { @@ -565,21 +565,26 @@ public void process(APDU apdu) { } } - private boolean isStrongBoxKeymasterCmdAllowed(byte apduIns) { - if(kmDataStore.isSBInitCompleted()) { - return true; - } - // Data Dirty. - switch (apduIns) { - case INS_GENERATE_KEY_CMD: - case INS_IMPORT_KEY_CMD: - case INS_IMPORT_WRAPPED_KEY_CMD: - case INS_ATTEST_KEY_CMD: - return false; - default: - break; - } - return true; + //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_ADD_RNG_ENTROPY_CMD: + case INS_GET_HMAC_SHARING_PARAM_CMD: + case INS_COMPUTE_SHARED_HMAC_CMD: + case INS_INIT_STRONGBOX_CMD: + return true; + default: + break; + } + return false; } private void generateUniqueOperationHandle(byte[] buf, short offset, short len) { @@ -1036,7 +1041,7 @@ private void processComputeSharedHmacCmd(APDU apdu) { (short) sharingCheck.length, scratchPad, keyLen); - kmDataStore.updateSBInitStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_SUCCESS); + kmDataStore.setDeviceBootStatus(KMKeymintDataStore.NEGOTIATED_SHARED_SECRET_SUCCESS); // verification signature blob - 32 bytes //tmpVariables[1] short signature = KMByteBlob.instance(scratchPad, keyLen, signLen); @@ -2592,6 +2597,9 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] 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 eary boot ended tag is present if (kmDataStore.getEarlyBootEndedStatus()) { @@ -2599,12 +2607,6 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) KMError.EARLY_BOOT_ENDED); } - //Validate bootloader only - if (kmDataStore.getBootEndedStatus()) { - KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, - KMError.INVALID_KEY_BLOB); - } - // 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; @@ -3444,7 +3446,7 @@ private void processInitStrongBoxCmd(APDU apdu) { setOsVersion(osVersion); setOsPatchLevel(osPatchLevel); setVendorPatchLevel(vendorPatchLevel); - kmDataStore.updateSBInitStatus(KMKeymintDataStore.SET_SYSTEM_PROPERTIES_SUCCESS); + kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_SYSTEM_PROPERTIES_SUCCESS); } public void reboot() { diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index 84f3ae75..e8dac348 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -49,9 +49,10 @@ public class KMKeymintDataStore implements KMUpgradable { public static final byte DEVICE_LOCKED_TIME = 5; public static final byte DEVICE_LOCKED = 6; public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 7; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 public static final byte AUTH_TAG_1 = 8; - public static final byte BOOT_ENDED_FLAG = 15; + public static final byte DEVICE_STATUS_FLAG = 15; public static final byte EARLY_BOOT_ENDED_FLAG = 16; private static final byte PROVISIONED_LOCKED = 17; private static final byte PROVISIONED_STATUS = 18; @@ -69,15 +70,22 @@ public class KMKeymintDataStore implements KMUpgradable { public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); private static final short MASTER_KEY_SIZE = 16; private static final short SHARED_SECRET_KEY_SIZE = 32; + private static final byte DEVICE_STATUS_FLAG_SIZE = 1; private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 512;//First 2 bytes for length. private static final short BCC_MAX_SIZE = 512; - public static final byte RESET_SB_INIT_STATUS = 0x00; - public static final byte SET_BOOT_PARAMS_SUCCESS = 0x01; - public static final byte SET_BOOT_ENDED_SUCCESS = 0x02; - public static final byte SET_SYSTEM_PROPERTIES_SUCCESS = 0x04; - public static final byte NEGOTIATED_SHARED_SECRET_SUCCESS = 0x08; + //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; // Data - originally was in repository private byte[] attIdBrand; @@ -257,18 +265,6 @@ public boolean getEarlyBootEndedStatus() { return readBoolean(EARLY_BOOT_ENDED_FLAG); } - public boolean getBootEndedStatus() { - boolean result = false; - short offset = repository.allocReclaimableMemory((short) 1); - byte[] buf = repository.getHeap(); - getSBInitStatus(buf, offset); - if(SET_BOOT_ENDED_SUCCESS == (buf[offset] & SET_BOOT_ENDED_SUCCESS)) { - result = true; - } - repository.reclaimMemory((short)1); - return result; - } - public short getDeviceTimeStamp() { short blob = readData(DEVICE_LOCKED_TIME); if (blob == KMType.INVALID_VALUE) { @@ -320,38 +316,37 @@ public void setDeviceLockTimestamp(byte[] buf, short start, short len) { public void setEarlyBootEndedStatus(boolean flag) { writeBoolean(EARLY_BOOT_ENDED_FLAG, flag); } - - public void clearSBInitStatus() { - clearDataEntry(BOOT_ENDED_FLAG); - } - public void updateSBInitStatus(byte initStatus) { - short offset = repository.allocReclaimableMemory((short) 1); + public void clearDeviceBootStatus() { + clearDataEntry(DEVICE_STATUS_FLAG); + } + + public void setDeviceBootStatus(byte initStatus) { + short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE); byte[] buf = repository.getHeap(); - getSBInitStatus(buf, offset); + getDeviceBootStatus(buf, offset); buf[offset] |= initStatus; - writeDataEntry(BOOT_ENDED_FLAG, buf, offset, (short) 1); - repository.reclaimMemory((short)1); + writeDataEntry(DEVICE_STATUS_FLAG, buf, offset, DEVICE_STATUS_FLAG_SIZE); + repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE); } - public boolean isSBInitCompleted() { + public boolean isDeviceReady() { boolean result = false; - short offset = repository.allocReclaimableMemory((short) 1); + short offset = repository.allocReclaimableMemory(DEVICE_STATUS_FLAG_SIZE); byte[] buf = repository.getHeap(); - getSBInitStatus(buf, offset); + getDeviceBootStatus(buf, offset); if ((0 != (buf[offset] & SET_BOOT_PARAMS_SUCCESS)) - && (0 != (buf[offset] & SET_BOOT_ENDED_SUCCESS)) && (0 != (buf[offset] & SET_SYSTEM_PROPERTIES_SUCCESS)) && (0 != (buf[offset] & NEGOTIATED_SHARED_SECRET_SUCCESS))) { result = true; } - repository.reclaimMemory((short)1); + repository.reclaimMemory(DEVICE_STATUS_FLAG_SIZE); return result; } - public short getSBInitStatus(byte[] scratchpad, short offset) { + public short getDeviceBootStatus(byte[] scratchpad, short offset) { scratchpad[offset] = 0; - return readDataEntry(BOOT_ENDED_FLAG, scratchpad, offset); + return readDataEntry(DEVICE_STATUS_FLAG, scratchpad, offset); } public void clearDeviceLockTimeStamp() { @@ -932,7 +927,6 @@ public void onRestore(Element element, short oldVersion, short currentVersion) { void handleDataUpgrade(short oldVersion, short currentVersion) { if(oldVersion == 0x0100 && currentVersion == 0x0200) { handleProvisionStatusUpgrade(); - handleSBInitStatusUpgrade(); } } @@ -954,19 +948,6 @@ void handleProvisionStatusUpgrade(){ writeDataEntry(PROVISIONED_STATUS, data, dInex, (short) 2); repository.reclaimMemory((short)2); } - - void handleSBInitStatusUpgrade(){ - short dInex = repository.allocReclaimableMemory((short)1); - byte data[] = repository.getHeap(); - short len = getSBInitStatus(data, dInex); - if(len != 0) { - updateSBInitStatus((byte)(SET_BOOT_PARAMS_SUCCESS | - SET_BOOT_ENDED_SUCCESS | - SET_SYSTEM_PROPERTIES_SUCCESS | - NEGOTIATED_SHARED_SECRET_SUCCESS)); - } - repository.reclaimMemory((short)1); - } @Override public short getBackupPrimitiveByteCount() { From 5f279ab0ce1cd23e1e7a8e9ce9c63f644da921f7 Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Tue, 7 Jun 2022 05:41:49 +0000 Subject: [PATCH 12/15] Updated system properties validation --- .../com/android/javacard/keymaster/KMAndroidSEApplet.java | 4 ++-- .../com/android/javacard/keymaster/KMKeymasterApplet.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 1d75d262..f320dac4 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -120,7 +120,7 @@ public void process(APDU apdu) { case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); - break; + break; case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: processProvisionRkpDeviceUniqueKeyPair(apdu); @@ -215,7 +215,7 @@ private boolean isCommandAllowed(short apduIns) { default: // Allow other commands only if provision is completed. - if (!(isProvisioningComplete())) { + if (!isProvisioningComplete()) { result = false; } } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index caeffac7..0cf17077 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -570,9 +570,9 @@ public void process(APDU apdu) { // 2. system properties are set and // 3. computed the shared secret successfully. private boolean isKeymintReady(byte apduIns) { - if(kmDataStore.isDeviceReady()) { - return true; - } + if(kmDataStore.isDeviceReady()) { + return true; + } // Below commands are allowed even if the Keymaster is not ready. switch (apduIns) { case INS_GET_HW_INFO_CMD: @@ -2598,7 +2598,7 @@ private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) authorizeKeyUsageForCount(scratchPad); KMTag.assertAbsence(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, - KMError.INVALID_KEY_BLOB); + KMError.INVALID_KEY_BLOB); //Validate early boot //VTS expects error code EARLY_BOOT_ONLY during begin operation if eary boot ended tag is present From a85fb8ec92e2ec3905dc60ed7cf829c27dbd531c Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Tue, 7 Jun 2022 07:45:18 +0000 Subject: [PATCH 13/15] added CLA validation --- .../javacard/keymaster/KMAndroidSEApplet.java | 6 ++- .../javacard/keymaster/KMJCardSimApplet.java | 52 ++++++------------- .../javacard/keymaster/KMKeymasterApplet.java | 1 - 3 files changed, 20 insertions(+), 39 deletions(-) diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index f320dac4..848eeefd 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -647,9 +647,13 @@ public Element onSave() { 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 CLA + if (!apdu.isValidCLA()) { + ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); + } + // Validate P1P2. if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { sendError(apdu, KMError.INVALID_P1P2); diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 66f80148..2b8e55fe 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -43,29 +43,14 @@ public class KMJCardSimApplet extends KMKeymasterApplet { 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; + INS_KEYMINT_PROVIDER_APDU_START + 8; //unused 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; public static final byte BOOT_HASH_MAX_SIZE = 32; - - // Provision reporting status - private static final short NOT_PROVISIONED = 0x0000; - private static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001; - private static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002; - private static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004; - private static final short PROVISION_STATUS_ATTEST_IDS = 0x0008; - private static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010; - private static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020; - private static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040; - private static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080; - private static final short PROVISION_STATUS_SE_LOCKED = 0x0100; - private static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200; - public static final short SHARED_SECRET_KEY_SIZE = 32; // Package version. @@ -125,10 +110,6 @@ public void process(APDU apdu) { processSetBootParamsCmd(apdu); break; - case INS_SET_BOOT_ENDED_CMD: - processSetBootEndedCmd(apdu); - break; - case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: processProvisionRkpDeviceUniqueKeyPair(apdu); break; @@ -217,14 +198,13 @@ private boolean isCommandAllowed(short apduIns) { 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; + result = false; } } return result; @@ -515,23 +495,13 @@ 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) { if (seProvider.isBootSignalEventSupported() && (!seProvider.isDeviceRebooted())) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - + // clear the device reboot status + kmDataStore.clearDeviceBootStatus(); short argsProto = KMArray.instance((short) 5); byte[] scratchPad = apdu.getBuffer(); // Array of 4 expected arguments @@ -583,12 +553,14 @@ private void processSetBootParamsCmd(APDU apdu) { kmDataStore.createComputedHmacKey(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE); super.reboot(); + kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS); + seProvider.clearDeviceBooted(false); sendError(apdu, KMError.OK); } private boolean isProvisioningComplete() { - short dInex = repository.allocReclaimableMemory((short)2); - byte data[] = repository.getHeap(); + short dInex = repository.allocReclaimableMemory((short)2); + byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); short pStatus = Util.getShort(data, dInex); boolean result = false; @@ -605,9 +577,15 @@ private boolean isProvisioningComplete() { private short validateApdu(APDU apdu) { // Read the apdu header and buffer. byte[] apduBuffer = apdu.getBuffer(); - byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; + short apduClass = (short) (apduBuffer[ISO7816.OFFSET_CLA] & 0x00FF); short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + // Validate CLA. + if (((apduClass & 0x00E0) == 0x0020) || + (apduClass == 0x00FF)) { + ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); + } + // Validate P1P2. if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { sendError(apdu, KMError.INVALID_P1P2); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 0cf17077..ed51caa0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -48,7 +48,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final short VERIFIED_BOOT_HASH_SIZE = 32; public static final short BOOT_PATCH_LVL_SIZE = 4; - protected static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; protected static final short KM_HAL_VERSION = (short) 0x5000; private static final short MAX_AUTH_DATA_SIZE = (short) 512; private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; From 076f6632ed150c4876d8215b4cda381ab2635c7f Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Tue, 7 Jun 2022 07:50:34 +0000 Subject: [PATCH 14/15] updated system properties validation changes in Jcard --- .../javacard/keymaster/KMJCardSimApplet.java | 44 ++++--------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 66f80148..094b5238 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -43,29 +43,14 @@ public class KMJCardSimApplet extends KMKeymasterApplet { 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; + INS_KEYMINT_PROVIDER_APDU_START + 8; //unused 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; public static final byte BOOT_HASH_MAX_SIZE = 32; - - // Provision reporting status - private static final short NOT_PROVISIONED = 0x0000; - private static final short PROVISION_STATUS_ATTESTATION_KEY = 0x0001; - private static final short PROVISION_STATUS_ATTESTATION_CERT_CHAIN = 0x0002; - private static final short PROVISION_STATUS_ATTESTATION_CERT_PARAMS = 0x0004; - private static final short PROVISION_STATUS_ATTEST_IDS = 0x0008; - private static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010; - private static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020; - private static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040; - private static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080; - private static final short PROVISION_STATUS_SE_LOCKED = 0x0100; - private static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200; - public static final short SHARED_SECRET_KEY_SIZE = 32; // Package version. @@ -125,10 +110,6 @@ public void process(APDU apdu) { processSetBootParamsCmd(apdu); break; - case INS_SET_BOOT_ENDED_CMD: - processSetBootEndedCmd(apdu); - break; - case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: processProvisionRkpDeviceUniqueKeyPair(apdu); break; @@ -217,14 +198,13 @@ private boolean isCommandAllowed(short apduIns) { 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; + result = false; } } return result; @@ -515,23 +495,13 @@ 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) { if (seProvider.isBootSignalEventSupported() && (!seProvider.isDeviceRebooted())) { ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); } - + // clear the device reboot status + kmDataStore.clearDeviceBootStatus(); short argsProto = KMArray.instance((short) 5); byte[] scratchPad = apdu.getBuffer(); // Array of 4 expected arguments @@ -583,12 +553,14 @@ private void processSetBootParamsCmd(APDU apdu) { kmDataStore.createComputedHmacKey(scratchPad, (short) 0, KMKeymintDataStore.COMPUTED_HMAC_KEY_SIZE); super.reboot(); + kmDataStore.setDeviceBootStatus(KMKeymintDataStore.SET_BOOT_PARAMS_SUCCESS); + seProvider.clearDeviceBooted(false); sendError(apdu, KMError.OK); } private boolean isProvisioningComplete() { - short dInex = repository.allocReclaimableMemory((short)2); - byte data[] = repository.getHeap(); + short dInex = repository.allocReclaimableMemory((short)2); + byte data[] = repository.getHeap(); kmDataStore.getProvisionStatus(data, dInex); short pStatus = Util.getShort(data, dInex); boolean result = false; From af572cf1013801d583ef101c6fdc714bdca9b25f Mon Sep 17 00:00:00 2001 From: "avinash.hedage" Date: Wed, 8 Jun 2022 08:01:17 +0000 Subject: [PATCH 15/15] Bug fix in RKP and updated upgrade implementation --- .../android/javacard/seprovider/KMPoolManager.java | 5 ++--- .../javacard/keymaster/KMKeymintDataStore.java | 2 +- .../keymaster/RemotelyProvisionedComponentDevice.java | 11 +++++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java index 7b65303a..de9d9f37 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -184,7 +184,7 @@ public static void initStatics() { } private KMPoolManager() { - initStatics(); + initStatics(); cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. signerPool = new Object[(short) ((SIG_ALGS.length * 4) + 4)]; @@ -202,9 +202,8 @@ private KMPoolManager() { 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); diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index e8dac348..a68cbbbd 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -925,7 +925,7 @@ public void onRestore(Element element, short oldVersion, short currentVersion) { } void handleDataUpgrade(short oldVersion, short currentVersion) { - if(oldVersion == 0x0100 && currentVersion == 0x0200) { + if(oldVersion != currentVersion) { handleProvisionStatusUpgrade(); } } diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 12dc4b83..8cd8aab1 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -159,10 +159,13 @@ public RemotelyProvisionedComponentDevice(KMEncoder encoder, KMDecoder decoder, operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); // Initialize RKP mac key - short offset = repository.alloc((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); + 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(); }