diff --git a/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMAndroidSEApplet.java b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMAndroidSEApplet.java new file mode 100644 index 00000000..0338b004 --- /dev/null +++ b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMAndroidSEApplet.java @@ -0,0 +1,428 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmapplet; + +import org.globalplatform.upgrade.Element; +import org.globalplatform.upgrade.OnUpgradeListener; +import org.globalplatform.upgrade.UpgradeManager; + +import com.android.javacard.kmdevice.KMArray; +import com.android.javacard.kmdevice.KMBootDataStore; +import com.android.javacard.kmdevice.KMByteBlob; +import com.android.javacard.kmdevice.KMDecoder; +import com.android.javacard.kmdevice.KMEncoder; +import com.android.javacard.kmdevice.KMEnum; +import com.android.javacard.kmdevice.KMInteger; +import com.android.javacard.kmdevice.KMKeymasterDevice; +import com.android.javacard.kmdevice.KMKeymintDevice; +import com.android.javacard.kmdevice.KMRepository; +import com.android.javacard.kmdevice.KMRkpDataStore; +import com.android.javacard.seprovider.KMAndroidSEProvider; +import com.android.javacard.seprovider.KMError; +import com.android.javacard.kmdevice.KMException; +import com.android.javacard.kmdevice.KMDataStore; +import com.android.javacard.seprovider.KMKeymasterProvision; +import com.android.javacard.seprovider.KMKeymintProvision; +import com.android.javacard.kmdevice.KMSEProvider; +import com.android.javacard.kmdevice.KMDataStoreConstants; +import com.android.javacard.seprovider.KMType; + +import javacard.framework.APDU; +import javacard.framework.Applet; +import javacard.framework.AppletEvent; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacardx.apdu.ExtendedLength; + +public class KMAndroidSEApplet extends Applet implements AppletEvent, OnUpgradeListener, + ExtendedLength { + + // 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 CURRENT_PACKAGE_VERSION = 0x0009; // 0.9 + + public static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; + private static final byte KM_BEGIN_STATE = 0x00; + private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; + + // Provider specific Commands + private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 1; //0x01 + private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 2; //0x02 + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; + private static final byte INS_PROVISION_PRESHARED_SECRET_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_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 6; + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7; + private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 8; //0x08 + private static final byte INS_SET_BOOT_ENDED_CMD = INS_KEYMINT_PROVIDER_APDU_START + 9; //0x09 + private static final byte INS_PROVISION_DEVICE_UNIQUE_KEY_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 10; + private static final byte INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 11; + + // Keymaster versions + public static final byte KM_40 = 0x00; + public static final byte KM_41 = 0x01; + public static final byte KM_100 = 0x03; + + private static final byte BOOT_KEY_MAX_SIZE = 32; + private static final byte BOOT_HASH_MAX_SIZE = 32; + private static final byte COMPUTED_HMAC_KEY_SIZE = 32; + + + private byte kmDevice; + private KMSEProvider seProvider; + private KMKeymasterProvision seProvisionInst; + private KMDecoder decoderInst; + private KMEncoder encoderInst; + private KMRepository repositoryInst; + private KMKeymasterDevice kmDeviceInst; + private KMDataStore kmDataStore; + private KMRkpDataStore kmRkpDataStore; + private KMBootDataStore bootParamsProvider; + + // Package version. + protected short packageVersion; + + KMAndroidSEApplet(byte device) { + kmDevice = device; + seProvider = (KMSEProvider) new KMAndroidSEProvider(); + repositoryInst = new KMRepository(UpgradeManager.isUpgrading()); + decoderInst = new KMDecoder(); + encoderInst = new KMEncoder(); + if (!UpgradeManager.isUpgrading()) { + packageVersion = CURRENT_PACKAGE_VERSION; + initLibraries(); + } + } + + private void initLibraries() { + kmRkpDataStore = new KMRkpDataStoreImpl(seProvider); + kmDataStore = new KMKeymintDataStore(seProvider, + !(kmDevice == KM_100) /* Factory attest flag*/); + bootParamsProvider = new KMBootParamsProviderImpl((KMKeymintDataStore) kmDataStore); + if (kmDevice == KM_40 || kmDevice == KM_41) { + kmDeviceInst = new KMKeymasterDevice(seProvider, repositoryInst, encoderInst, decoderInst, + kmDataStore, + bootParamsProvider); + seProvisionInst = new KMKeymasterProvision(kmDeviceInst, seProvider, decoderInst, + repositoryInst, kmDataStore); + } else { + kmDeviceInst = new KMKeymintDevice(seProvider, repositoryInst, encoderInst, decoderInst, + kmDataStore, + bootParamsProvider, kmRkpDataStore); + seProvisionInst = new KMKeymintProvision(kmDeviceInst, seProvider, decoderInst, + repositoryInst, kmDataStore, + kmRkpDataStore); + } + } + + /** + * Installs this applet. + * + * @param bArray the array containing installation parameters + * @param bOffset the starting offset in bArray + * @param bLength the length in bytes of the parameter data in bArray + */ + public static void install(byte[] bArray, short bOffset, byte bLength) { + byte kmDevice = KM_100; + if (!UpgradeManager.isUpgrading()) { + byte Li = bArray[bOffset]; // Length of AID + byte Lc = bArray[(short) (bOffset + Li + 1)]; // Length of ControlInfo + byte La = bArray[(short) (bOffset + Li + Lc + 2)]; // Length of application data + if (La != 1) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + kmDevice = bArray[(short) (bOffset + Li + Lc + 3)]; + } + new KMAndroidSEApplet(kmDevice).register(bArray, (short) (bOffset + 1), bArray[bOffset]); + } + + private boolean isProvisionLocked() { + short offset = repositoryInst.alloc((short) 1); + short len = kmDataStore.getData(KMDataStoreConstants.PROVISIONED_LOCKED, + repositoryInst.getHeap(), offset); + if (len == 0) { + return false; + } + return ((byte[]) repositoryInst.getHeap())[offset] == 0x01; + } + + + @Override + public void process(APDU apdu) { + try { + // If this is select applet apdu which is selecting this applet then return + if (apdu.isISOInterindustryCLA()) { + if (selectingApplet()) { + return; + } + } + short apduIns = validateApdu(apdu); + if (((KMAndroidSEProvider) seProvider).isPowerReset(false)) { + kmDeviceInst.powerReset(); + } + + if (isProvisionLocked()) { + switch (apduIns) { + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; + + case INS_SET_BOOT_ENDED_CMD: + // set the flag to mark boot ended + byte[] buffer = apdu.getBuffer(); + buffer[0] = 0x01; + kmDataStore.storeData(KMDataStoreConstants.BOOT_ENDED_STATUS, buffer, (short) 0, + (short) 1); + kmDeviceInst.sendError(apdu, KMError.OK); + break; + + default: + kmDeviceInst.process(apdu); + break; + } + return; + } + + if (apduIns == KMType.INVALID_VALUE) { + return; + } + switch (apduIns) { + case INS_PROVISION_ATTESTATION_KEY_CMD: // only keymaster + seProvisionInst.processProvisionAttestationKey(apdu); + break; + case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: // only keymaster + seProvisionInst.processProvisionAttestationCertDataCmd(apdu); + break; + case INS_PROVISION_ATTEST_IDS_CMD: + seProvisionInst.processProvisionAttestIdsCmd(apdu); + break; + + case INS_PROVISION_PRESHARED_SECRET_CMD: + seProvisionInst.processProvisionPreSharedSecretCmd(apdu); + break; + + case INS_GET_PROVISION_STATUS_CMD: + seProvisionInst.processGetProvisionStatusCmd(apdu); + break; + + case INS_LOCK_PROVISIONING_CMD: + seProvisionInst.processLockProvisioningCmd(apdu); + break; + + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; + + case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: // only keymint + seProvisionInst.processProvisionDeviceUniqueKey(apdu); + break; + + case INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD:// only keymint + seProvisionInst.processProvisionAdditionalCertChain(apdu); + break; + + default: + kmDeviceInst.process(apdu); + break; + } + } catch (KMException exception) { + kmDeviceInst.sendError(apdu, KMException.reason()); + } catch (ISOException exp) { + kmDeviceInst.sendError(apdu, kmDeviceInst.mapISOErrorToKMError(exp.getReason())); + } catch (CryptoException e) { + kmDeviceInst.sendError(apdu, kmDeviceInst.mapCryptoErrorToKMError(e.getReason())); + } catch (Exception e) { + kmDeviceInst.sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); + } finally { + kmDeviceInst.clean(); + } + } + + + private boolean isUpgradeAllowed(short version) { + boolean upgradeAllowed = false; + short oldMajorVersion = (short) ((version >> 8) & 0x00FF); + short oldMinorVersion = (short) (version & 0x00FF); + short currentMajorVersion = (short) (CURRENT_PACKAGE_VERSION >> 8 & 0x00FF); + short currentMinorVersion = (short) (CURRENT_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) { + if (currentMinorVersion == 0) { + upgradeAllowed = true; + } + } else if ((short) (currentMajorVersion - oldMajorVersion) == 0) { + if ((short) (currentMinorVersion - oldMinorVersion) == 1) { + upgradeAllowed = true; + } + } + return upgradeAllowed; + } + + @Override + public void onCleanup() { + } + + @Override + public void onConsolidate() { + } + + @Override + public void onRestore(Element element) { + element.initRead(); + byte magicNumber = element.readByte(); + if (magicNumber != KM_MAGIC_NUMBER) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short packageVersion = element.readShort(); + // Validate version. + if (0 != packageVersion && !isUpgradeAllowed(packageVersion)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + kmDevice = element.readByte(); + // Initialize libraries after reading kmDevice flag. + initLibraries(); + kmDataStore.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + kmRkpDataStore.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); + } + + @Override + public Element onSave() { + short primitiveCount = 4; + primitiveCount += kmDataStore.getBackupPrimitiveByteCount(); + short objectCount = kmDataStore.getBackupObjectCount(); + + primitiveCount += kmRkpDataStore.getBackupPrimitiveByteCount(); + objectCount += kmRkpDataStore.getBackupObjectCount(); + + // Create element. + Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, + primitiveCount, objectCount); + + element.write(KM_MAGIC_NUMBER); + element.write(packageVersion); + element.write(kmDevice); + kmDataStore.onSave(element); + kmRkpDataStore.onSave(element); + return element; + } + + private short validateApdu(APDU apdu) { + // Read the apdu header and buffer. + byte[] apduBuffer = apdu.getBuffer(); + byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; + + // Validate APDU Header. + if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { + ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); + } + + kmDeviceInst.validateP1P2(apdu); + return apduBuffer[ISO7816.OFFSET_INS]; + } + + private void processSetBootParamsCmd(APDU apdu) { + short argsProto = KMArray.instance((short) 5); + + byte[] scratchPad = apdu.getBuffer(); + // Array of 4 expected arguments + // Argument 0 Boot Patch level + KMArray.add(argsProto, (short) 0, KMInteger.exp()); + // Argument 1 Verified Boot Key + KMArray.add(argsProto, (short) 1, KMByteBlob.exp()); + // Argument 2 Verified Boot Hash + KMArray.add(argsProto, (short) 2, KMByteBlob.exp()); + // Argument 3 Verified Boot State + KMArray.add(argsProto, (short) 3, KMEnum.instance(KMType.VERIFIED_BOOT_STATE)); + // Argument 4 Device Locked + KMArray.add(argsProto, (short) 4, KMEnum.instance(KMType.DEVICE_LOCKED)); + + short args = kmDeviceInst.receiveIncoming(apdu, argsProto); + + short bootParam = KMArray.get(args, (short) 0); + + ((KMKeymintDataStore) kmDataStore).setBootPatchLevel(KMInteger.getBuffer(bootParam), + KMInteger.getStartOff(bootParam), + KMInteger.length(bootParam)); + + bootParam = KMArray.get(args, (short) 1); + if (KMByteBlob.length(bootParam) > BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMKeymintDataStore) kmDataStore).setBootKey(KMByteBlob.getBuffer(bootParam), + KMByteBlob.getStartOff(bootParam), + KMByteBlob.length(bootParam)); + + bootParam = KMArray.get(args, (short) 2); + if (KMByteBlob.length(bootParam) > BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMKeymintDataStore) kmDataStore).setVerifiedBootHash(KMByteBlob.getBuffer(bootParam), + KMByteBlob.getStartOff(bootParam), + KMByteBlob.length(bootParam)); + + bootParam = KMArray.get(args, (short) 3); + byte enumVal = KMEnum.getVal(bootParam); + ((KMKeymintDataStore) kmDataStore).setBootState(enumVal); + + bootParam = KMArray.get(args, (short) 4); + enumVal = KMEnum.getVal(bootParam); + ((KMKeymintDataStore) kmDataStore).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + + // Clear the Computed SharedHmac and Hmac nonce from persistent memory. + Util.arrayFillNonAtomic(scratchPad, (short) 0, COMPUTED_HMAC_KEY_SIZE, (byte) 0); + kmDataStore.storeData(KMDataStoreConstants.COMPUTED_HMAC_KEY, scratchPad, (short) 0, + COMPUTED_HMAC_KEY_SIZE); + + kmDeviceInst.reboot(scratchPad, (short) 0); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + @Override + public void uninstall() { + kmDeviceInst.onUninstall(); + } + + /** + * Selects this applet. + * + * @return Returns true if the keymaster is in correct state + */ + @Override + public boolean select() { + return kmDeviceInst.onSelect(); + + } + + /** + * De-selects this applet. + */ + @Override + public void deselect() { + kmDeviceInst.onDeselect(); + } +} + diff --git a/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMBootParamsProviderImpl.java b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMBootParamsProviderImpl.java new file mode 100644 index 00000000..467ce7a5 --- /dev/null +++ b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMBootParamsProviderImpl.java @@ -0,0 +1,53 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmapplet; + +import com.android.javacard.kmdevice.KMBootDataStore; + +public class KMBootParamsProviderImpl implements KMBootDataStore { + + KMKeymintDataStore kmStoreDataInst; + + public KMBootParamsProviderImpl(KMKeymintDataStore storeData) { + kmStoreDataInst = storeData; + } + + @Override + public short getVerifiedBootHash(byte[] buffer, short start) { + return kmStoreDataInst.getVerifiedBootHash(buffer, start); + } + + @Override + public short getBootKey(byte[] buffer, short start) { + return kmStoreDataInst.getBootKey(buffer, start); + } + + @Override + public short getBootState() { + return kmStoreDataInst.getBootState(); + } + + @Override + public boolean isDeviceBootLocked() { + return kmStoreDataInst.isDeviceBootLocked(); + } + + @Override + public short getBootPatchLevel(byte[] buffer, short start) { + return kmStoreDataInst.getBootPatchLevel(buffer, start); + } + +} diff --git a/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMConfigurations.java b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMConfigurations.java new file mode 100644 index 00000000..99f4d894 --- /dev/null +++ b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMConfigurations.java @@ -0,0 +1,32 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmapplet; + +public class KMConfigurations { + + // Machine types + public static final byte LITTLE_ENDIAN = 0x00; + public static final byte BIG_ENDIAN = 0x01; + public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN; + + // Maximum cert chain size + public static final short CERT_CHAIN_MAX_SIZE = 2500; + public static final short CERT_ISSUER_MAX_SIZE = 250; + public static final short CERT_EXPIRY_MAX_SIZE = 20; + public static final short TOTAL_ATTEST_IDS_SIZE = 300; + public static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 512; + public static final short BOOT_CERT_CHAIN_MAX_SIZE = 512; +} diff --git a/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMKeymintDataStore.java b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMKeymintDataStore.java new file mode 100644 index 00000000..cf19c0b2 --- /dev/null +++ b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMKeymintDataStore.java @@ -0,0 +1,890 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmapplet; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.kmdevice.KMAttestationKey; +import com.android.javacard.kmdevice.KMComputedHmacKey; +import com.android.javacard.kmdevice.KMDeviceUniqueKey; +import com.android.javacard.kmdevice.KMError; +import com.android.javacard.kmdevice.KMException; +import com.android.javacard.kmdevice.KMDataStore; +import com.android.javacard.kmdevice.KMMasterKey; +import com.android.javacard.kmdevice.KMPreSharedKey; +import com.android.javacard.kmdevice.KMSEProvider; +import com.android.javacard.kmdevice.KMDataStoreConstants; +import com.android.javacard.kmdevice.KMType; +import com.android.javacard.kmdevice.KMUpgradable; +import com.android.javacard.seprovider.KMConfigurations; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * KMRepository class manages persistent and volatile memory usage by the applet. Note the + * repository is only used by applet and it is not intended to be used by seProvider. + */ +public class KMKeymintDataStore implements KMDataStore { + + + // Data table configuration + private static final short DATA_INDEX_SIZE = 19; + private static final short DATA_INDEX_ENTRY_SIZE = 4; + private static final short DATA_MEM_SIZE = 300; + private static final short DATA_INDEX_ENTRY_LENGTH = 0; + private static final short DATA_INDEX_ENTRY_OFFSET = 2; + + // Data table offsets + private static final byte HMAC_NONCE = 0; + private static final byte OS_VERSION = 1; + private static final byte OS_PATCH_LEVEL = 2; + private static final byte VENDOR_PATCH_LEVEL = 3; + private static final byte DEVICE_LOCKED_TIME = 4; + private static final byte DEVICE_LOCKED = 5; + private static final byte DEVICE_LOCKED_PASSWORD_ONLY = 6; + // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 + private static final byte AUTH_TAG_1 = 7; + private static final byte AUTH_TAG_2 = 8; + private static final byte AUTH_TAG_3 = 9; + private static final byte AUTH_TAG_4 = 10; + private static final byte AUTH_TAG_5 = 11; + private static final byte AUTH_TAG_6 = 12; + private static final byte AUTH_TAG_7 = 13; + private static final byte AUTH_TAG_8 = 14; + private static final byte BOOT_ENDED_STATUS = 15; + private static final byte EARLY_BOOT_ENDED_STATUS = 16; + private static final byte PROVISIONED_LOCKED = 17; + private static final byte PROVISIONED_STATUS = 18; + + // Data Item sizes + private static final short MASTER_KEY_SIZE = 16; + private static final short SHARED_SECRET_KEY_SIZE = 32; + private static final short HMAC_SEED_NONCE_SIZE = 32; + private static final short COMPUTED_HMAC_KEY_SIZE = 32; + private static final short SB_PROP_SIZE = 4; + private static final short DEVICE_LOCK_TS_SIZE = 8; + private static final short BOOT_DEVICE_LOCK_FLAG_SIZE = 1; + private static final short DEVICE_LOCKED_FLAG_SIZE = 1; + private static final short DEVICE_LOCKED_PASSWORD_ONLY_SIZE = 1; + private static final short BOOT_STATE_SIZE = 1; + private static final byte BOOT_KEY_MAX_SIZE = 32; + private static final byte BOOT_HASH_MAX_SIZE = 32; + private static final short MAX_BLOB_STORAGE = 8; + private static final short AUTH_TAG_LENGTH = 16; + private static final short AUTH_TAG_COUNTER_SIZE = 4; + private static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); + private static final short BOOT_ENDED_FLAG_SIZE = 1; + private static final short EARLY_BOOT_ENDED_FLAG_SIZE = 1; + private static final short PROVISIONED_LOCKED_SIZE = 1; + private static final short PROVISIONED_STATUS_SIZE = 1; + + // certificate data constants. + private static final short CERT_CHAIN_OFFSET = 0; + private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; + private static final short CERT_EXPIRY_OFFSET = (short) (CERT_ISSUER_OFFSET + + KMConfigurations.CERT_ISSUER_MAX_SIZE); + + // data table + private byte[] dataTable; + private short dataIndex; + + // certificate data + protected byte[] certificateData; + + // Keys + private KMComputedHmacKey computedHmacKey; + private KMMasterKey masterKey; + private KMPreSharedKey preSharedKey; + private KMAttestationKey attestationKey; + protected KMSEProvider seProvider; + + // Data - originally was in repository + private byte[] attIdBrand; + private byte[] attIdDevice; + private byte[] attIdProduct; + private byte[] attIdSerial; + private byte[] attIdImei; + private byte[] attIdMeId; + private byte[] attIdManufacturer; + private byte[] attIdModel; + + + // Boot parameters + private byte[] verifiedHash; + private byte[] bootKey; + private byte[] bootPatchLevel; + private boolean deviceBootLocked; + private short bootState; + + public KMKeymintDataStore(KMSEProvider provider, boolean factoryAttestSupport) { + seProvider = provider; + boolean isUpgrading = provider.isUpgrading(); + initDataTable(isUpgrading); + initializeCertificateDataBuffer(isUpgrading, factoryAttestSupport); + } + + private short mapTodataTableId(byte kmStoreId) { + switch (kmStoreId) { + case KMDataStoreConstants.HMAC_NONCE: + return HMAC_NONCE; + case KMDataStoreConstants.OS_VERSION: + return OS_VERSION; + case KMDataStoreConstants.OS_PATCH_LEVEL: + return OS_PATCH_LEVEL; + case KMDataStoreConstants.VENDOR_PATCH_LEVEL: + return VENDOR_PATCH_LEVEL; + case KMDataStoreConstants.DEVICE_LOCKED_TIME: + return DEVICE_LOCKED_TIME; + case KMDataStoreConstants.DEVICE_LOCKED: + return DEVICE_LOCKED; + case KMDataStoreConstants.DEVICE_LOCKED_PASSWORD_ONLY: + return DEVICE_LOCKED_PASSWORD_ONLY; + case KMDataStoreConstants.BOOT_ENDED_STATUS: + return BOOT_ENDED_STATUS; + case KMDataStoreConstants.EARLY_BOOT_ENDED_STATUS: + return EARLY_BOOT_ENDED_STATUS; + case KMDataStoreConstants.PROVISIONED_LOCKED: + return PROVISIONED_LOCKED; + case KMDataStoreConstants.PROVISIONED_STATUS: + return PROVISIONED_STATUS; + case KMDataStoreConstants.AUTH_TAG_1: + return AUTH_TAG_1; + case KMDataStoreConstants.AUTH_TAG_2: + return AUTH_TAG_2; + case KMDataStoreConstants.AUTH_TAG_3: + return AUTH_TAG_3; + case KMDataStoreConstants.AUTH_TAG_4: + return AUTH_TAG_4; + case KMDataStoreConstants.AUTH_TAG_5: + return AUTH_TAG_5; + case KMDataStoreConstants.AUTH_TAG_6: + return AUTH_TAG_6; + case KMDataStoreConstants.AUTH_TAG_7: + return AUTH_TAG_7; + case KMDataStoreConstants.AUTH_TAG_8: + return AUTH_TAG_8; + default: + break; + } + return KMType.INVALID_VALUE; + } + + @Override + public void storeData(byte storeDataIndex, byte[] data, short offset, short length) { + short maxLen = 0; + switch (storeDataIndex) { + case KMDataStoreConstants.ATT_ID_BRAND: + case KMDataStoreConstants.ATT_ID_DEVICE: + case KMDataStoreConstants.ATT_ID_PRODUCT: + case KMDataStoreConstants.ATT_ID_SERIAL: + case KMDataStoreConstants.ATT_ID_IMEI: + case KMDataStoreConstants.ATT_ID_MEID: + case KMDataStoreConstants.ATT_ID_MANUFACTURER: + case KMDataStoreConstants.ATT_ID_MODEL: + setAttestationId(storeDataIndex, data, offset, length); + return; + case KMDataStoreConstants.COMPUTED_HMAC_KEY: + persistComputedHmacKey(data, offset, length); + return; + case KMDataStoreConstants.MASTER_KEY: + persistMasterKey(data, offset, length); + return; + case KMDataStoreConstants.PRE_SHARED_KEY: + persistPresharedKey(data, offset, length); + return; + case KMDataStoreConstants.ATTESTATION_KEY: + persistAttestationKey(data, offset, length); + return; + case KMDataStoreConstants.HMAC_NONCE: + maxLen = HMAC_SEED_NONCE_SIZE; + break; + case KMDataStoreConstants.OS_VERSION: + case KMDataStoreConstants.OS_PATCH_LEVEL: + case KMDataStoreConstants.VENDOR_PATCH_LEVEL: + maxLen = SB_PROP_SIZE; + break; + case KMDataStoreConstants.DEVICE_LOCKED_TIME: + maxLen = DEVICE_LOCK_TS_SIZE; + break; + case KMDataStoreConstants.DEVICE_LOCKED: + maxLen = DEVICE_LOCKED_FLAG_SIZE; + break; + case KMDataStoreConstants.DEVICE_LOCKED_PASSWORD_ONLY: + maxLen = DEVICE_LOCKED_PASSWORD_ONLY_SIZE; + break; + case KMDataStoreConstants.BOOT_ENDED_STATUS: + maxLen = BOOT_ENDED_FLAG_SIZE; + break; + case KMDataStoreConstants.EARLY_BOOT_ENDED_STATUS: + maxLen = EARLY_BOOT_ENDED_FLAG_SIZE; + break; + case KMDataStoreConstants.PROVISIONED_LOCKED: + maxLen = PROVISIONED_LOCKED_SIZE; + break; + case KMDataStoreConstants.PROVISIONED_STATUS: + maxLen = PROVISIONED_STATUS_SIZE; + break; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + return; + } + short dataTableId = mapTodataTableId(storeDataIndex); + if (dataTableId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (length != maxLen) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + writeDataEntry(dataTableId, data, offset, length); + } + + @Override + public short getData(byte storeDataIndex, byte[] data, short offset) { + switch (storeDataIndex) { + case KMDataStoreConstants.ATT_ID_BRAND: + case KMDataStoreConstants.ATT_ID_DEVICE: + case KMDataStoreConstants.ATT_ID_PRODUCT: + case KMDataStoreConstants.ATT_ID_SERIAL: + case KMDataStoreConstants.ATT_ID_IMEI: + case KMDataStoreConstants.ATT_ID_MEID: + case KMDataStoreConstants.ATT_ID_MANUFACTURER: + case KMDataStoreConstants.ATT_ID_MODEL: + return getAttestationId(storeDataIndex, data, offset); + default: + break; + } + short dataTableId = mapTodataTableId(storeDataIndex); + if (dataTableId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return readDataEntry(dataTableId, data, offset); + } + + @Override + public void clearData(byte storeDataIndex) { + switch (storeDataIndex) { + case KMDataStoreConstants.ATT_ID_BRAND: + attIdBrand = null; + return; + case KMDataStoreConstants.ATT_ID_DEVICE: + attIdDevice = null; + return; + case KMDataStoreConstants.ATT_ID_PRODUCT: + attIdProduct = null; + return; + case KMDataStoreConstants.ATT_ID_SERIAL: + attIdSerial = null; + return; + case KMDataStoreConstants.ATT_ID_IMEI: + attIdImei = null; + return; + case KMDataStoreConstants.ATT_ID_MEID: + attIdMeId = null; + return; + case KMDataStoreConstants.ATT_ID_MANUFACTURER: + attIdManufacturer = null; + return; + case KMDataStoreConstants.ATT_ID_MODEL: + attIdModel = null; + return; + default: + break; + } + short dataTableId = mapTodataTableId(storeDataIndex); + if (dataTableId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + clearDataEntry(dataTableId); + } + + private short dataAlloc(short length) { + if (length < 0) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (((short) (dataIndex + length)) > DATA_MEM_SIZE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + JCSystem.beginTransaction(); + dataIndex += length; + JCSystem.commitTransaction(); + return (short) (dataIndex - length); + } + + protected void initDataTable(boolean isUpgrading) { + if (!isUpgrading) { + if (dataTable == null) { + dataTable = new byte[DATA_MEM_SIZE]; + dataIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + } + } + } + + private void initializeCertificateDataBuffer(boolean isUpgrading, + boolean isFactoryAttestSupported) { + if (!isUpgrading) { + if (isFactoryAttestSupported && certificateData == null) { + // First 2 bytes is reserved for length for all the 3 buffers. + short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + + KMConfigurations.CERT_EXPIRY_MAX_SIZE + + KMConfigurations.CERT_ISSUER_MAX_SIZE); + certificateData = new byte[totalLen]; + } + } + } + + private void clearDataEntry(short id) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (dataLen != 0) { + short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + JCSystem.beginTransaction(); + Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0); + JCSystem.commitTransaction(); + } + } + + private short readDataEntry(short id, byte[] buf, short offset) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (len != 0) { + Util.arrayCopyNonAtomic(dataTable, + Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), buf, offset, + len); + } + return len; + } + + private void writeDataEntry(short id, byte[] buf, short offset, short len) { + short dataPtr; + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (dataLen == 0) { + dataPtr = dataAlloc(len); + // Begin Transaction + JCSystem.beginTransaction(); + Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); + Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len); + JCSystem.commitTransaction(); + Util.arrayCopy(buf, offset, dataTable, dataPtr, len); + // End Transaction + } else { + if (len != dataLen) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); + Util.arrayCopy(buf, offset, dataTable, dataPtr, len); + } + } + + private short dataLength(short id) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + } + + public short readData(byte[] dataTable, short id, byte[] buf, short startOff, short bufLen) { + id = (short) (id * DATA_INDEX_ENTRY_SIZE); + short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); + if (len > bufLen) { + return KMType.INVALID_VALUE; + } + if (len != 0) { + Util.arrayCopyNonAtomic(dataTable, + Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), buf, + startOff, len); + } + return len; + } + + private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) { + readDataEntry(tagId, buf, offset); + return (0 == buf[offset]); + } + + private void writeAuthTagState(byte[] buf, short offset, byte state) { + buf[offset] = state; + } + + @Override + public boolean storeAuthTag(byte[] data, short offset, short length, byte[] scratchPad, + short scratchPadOff) { + if (length != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + + short index = 0; + while (index < MAX_BLOB_STORAGE) { + if ((dataLength((short) (index + AUTH_TAG_1)) == 0) + || isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) { + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, AUTH_TAG_ENTRY_SIZE, (byte) 0); + // prepare auth tag buffer + writeAuthTagState(scratchPad, scratchPadOff, (byte) 1); + Util.arrayCopyNonAtomic(data, offset, scratchPad, (short) (scratchPadOff + 1), + AUTH_TAG_LENGTH); + Util.setShort(scratchPad, (short) (scratchPadOff + AUTH_TAG_LENGTH + 1 + 2), (short) 1); + // write the auth tag buffer to persistent memroy. + writeDataEntry((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff, + AUTH_TAG_ENTRY_SIZE); + return true; + } + index++; + } + return false; + } + + @Override + public void clearAllAuthTags() { + short index = 0; + while (index < MAX_BLOB_STORAGE) { + clearDataEntry((short) (index + AUTH_TAG_1)); + index++; + } + } + + @Override + public boolean isAuthTagPersisted(byte[] data, short offset, short length, byte[] scratchPad, + short scratchPadOff) { + return (KMType.INVALID_VALUE != findTag(data, offset, length, scratchPad, scratchPadOff)); + } + + private short findTag(byte[] data, short offset, short length, byte[] scratchPad, + short scratchPadOff) { + if (length != AUTH_TAG_LENGTH) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + short index = 0; + short found; + // short offset = alloc(AUTH_TAG_ENTRY_SIZE); + while (index < MAX_BLOB_STORAGE) { + if (dataLength((short) (index + AUTH_TAG_1)) != 0) { + readDataEntry((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff); + found = Util.arrayCompare(scratchPad, (short) (scratchPadOff + 1), data, offset, + AUTH_TAG_LENGTH); + if (found == 0) { + return (short) (index + AUTH_TAG_1); + } + } + index++; + } + return KMType.INVALID_VALUE; + } + + @Override + public short getRateLimitedKeyCount(byte[] data, short offset, short length, byte[] scratchPad, + short scratchPadOff) { + short tag = findTag(data, offset, length, scratchPad, scratchPadOff); + short blob; + if (tag != KMType.INVALID_VALUE) { + readDataEntry(tag, scratchPad, scratchPadOff); + Util.arrayCopyNonAtomic(scratchPad, (short) (scratchPadOff + AUTH_TAG_LENGTH + 1), scratchPad, + scratchPadOff, + AUTH_TAG_COUNTER_SIZE); + return AUTH_TAG_COUNTER_SIZE; + } + return (short) 0; + } + + @Override + public void setRateLimitedKeyCount(byte[] data, short dataOffset, short dataLen, byte[] counter, + short counterOff, + short counterLen, byte[] scratchPad, short scratchPadOff) { + short tag = findTag(data, dataOffset, dataLen, scratchPad, scratchPadOff); + if (tag != KMType.INVALID_VALUE) { + short len = readDataEntry(tag, scratchPad, scratchPadOff); + Util.arrayCopyNonAtomic(counter, counterOff, scratchPad, + (short) (scratchPadOff + AUTH_TAG_LENGTH + 1), + counterLen); + writeDataEntry(tag, scratchPad, scratchPadOff, len); + } + } + + private short getcertificateDataBufferOffset(byte dataType) { + switch (dataType) { + case KMDataStoreConstants.CERTIFICATE_CHAIN: + return CERT_CHAIN_OFFSET; + case KMDataStoreConstants.CERTIFICATE_ISSUER: + return CERT_ISSUER_OFFSET; + case KMDataStoreConstants.CERTIFICATE_EXPIRY: + return CERT_EXPIRY_OFFSET; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return 0; + } + + private void persistcertificateData(byte[] buf, short off, short len, short maxSize, + short copyToOff) { + if (len > maxSize) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.arrayCopyNonAtomic(buf, off, certificateData, + Util.setShort(certificateData, copyToOff, len), len); + JCSystem.commitTransaction(); + } + + private void persistCertificateChain(byte[] certChain, short certChainOff, short certChainLen) { + persistcertificateData(certChain, certChainOff, certChainLen, + KMConfigurations.CERT_CHAIN_MAX_SIZE, + CERT_CHAIN_OFFSET); + } + + private void persistCertficateIssuer(byte[] certIssuer, short certIssuerOff, + short certIssuerLen) { + persistcertificateData(certIssuer, certIssuerOff, certIssuerLen, + KMConfigurations.CERT_ISSUER_MAX_SIZE, + CERT_ISSUER_OFFSET); + } + + private void persistCertificateExpiryTime(byte[] certExpiry, short certExpiryOff, + short certExpiryLen) { + persistcertificateData(certExpiry, certExpiryOff, certExpiryLen, + KMConfigurations.CERT_EXPIRY_MAX_SIZE, + CERT_EXPIRY_OFFSET); + } + + @Override + public void persistCertificateData(byte[] buffer, short certChainOff, short certChainLen, + short certIssuerOff, + short certIssuerLen, short certExpiryOff, short certExpiryLen) { + // All the buffers uses first two bytes for length. The certificate chain + // is stored as shown below. + // _____________________________________________________ + // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... + // |_________|________|_________|_______|________|_______ + // First two bytes holds the length of the total buffer. + // CBOR format: + // Next single byte holds the byte string header. + // Next 3 bytes holds the total length of the certificate chain. + // clear buffer. + JCSystem.beginTransaction(); + Util.arrayFillNonAtomic(certificateData, (short) 0, (short) certificateData.length, (byte) 0); + JCSystem.commitTransaction(); + // Persist data. + persistCertificateChain(buffer, certChainOff, certChainLen); + persistCertficateIssuer(buffer, certIssuerOff, certIssuerLen); + persistCertificateExpiryTime(buffer, certExpiryOff, certExpiryLen); + } + + @Override + public short readCertificateData(byte dataType, byte[] buf, short offset) { + short provisionBufOffset = getcertificateDataBufferOffset(dataType); + short len = Util.getShort(certificateData, provisionBufOffset); + Util.arrayCopyNonAtomic(certificateData, (short) (2 + provisionBufOffset), buf, offset, len); + return len; + } + + @Override + public short getCertificateDataLength(byte dataType) { + short provisionBufOffset = getcertificateDataBufferOffset(dataType); + return Util.getShort(certificateData, provisionBufOffset); + } + + private void persistComputedHmacKey(byte[] keydata, short offset, short length) { + if (length != COMPUTED_HMAC_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if (computedHmacKey == null) { + computedHmacKey = seProvider.createComputedHmacKey(computedHmacKey, keydata, offset, length); + } else { + seProvider.createComputedHmacKey(computedHmacKey, keydata, offset, length); + } + } + + private void persistMasterKey(byte[] keydata, short offset, short length) { + if (length != MASTER_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if (masterKey == null) { + masterKey = seProvider.createMasterKey(masterKey, keydata, offset, length); + } + } + + private void persistPresharedKey(byte[] keydata, short offset, short length) { + if (length != SHARED_SECRET_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + if (preSharedKey == null) { + preSharedKey = seProvider.createPreSharedKey(preSharedKey, keydata, offset, length); + } + } + + private void persistAttestationKey(byte[] privateKey, short privateKeyOff, short privateKeyLen) { + if (attestationKey == null) { + attestationKey = seProvider.createAttestationKey(attestationKey, privateKey, privateKeyOff, + privateKeyLen); + } else { + seProvider.createAttestationKey(attestationKey, privateKey, privateKeyOff, privateKeyLen); + } + } + + @Override + public KMComputedHmacKey getComputedHmacKey() { + return computedHmacKey; + } + + @Override + public KMPreSharedKey getPresharedKey() { + return preSharedKey; + } + + @Override + public KMMasterKey getMasterKey() { + return masterKey; + } + + @Override + public KMAttestationKey getAttestationKey() { + return attestationKey; + } + + public short getAttestationId(short id, byte[] buffer, short start) { + switch (id) { + // Attestation Id Brand + case KMDataStoreConstants.ATT_ID_BRAND: + Util.arrayCopyNonAtomic(attIdBrand, (short) 0, buffer, start, (short) attIdBrand.length); + return (short) attIdBrand.length; + // Attestation Id Device + case KMDataStoreConstants.ATT_ID_DEVICE: + Util.arrayCopyNonAtomic(attIdDevice, (short) 0, buffer, start, (short) attIdDevice.length); + return (short) attIdDevice.length; + // Attestation Id Product + case KMDataStoreConstants.ATT_ID_PRODUCT: + Util.arrayCopyNonAtomic(attIdProduct, (short) 0, buffer, start, + (short) attIdProduct.length); + return (short) attIdProduct.length; + // Attestation Id Serial + case KMDataStoreConstants.ATT_ID_SERIAL: + Util.arrayCopyNonAtomic(attIdSerial, (short) 0, buffer, start, (short) attIdSerial.length); + return (short) attIdSerial.length; + // Attestation Id IMEI + case KMDataStoreConstants.ATT_ID_IMEI: + Util.arrayCopyNonAtomic(attIdImei, (short) 0, buffer, start, (short) attIdImei.length); + return (short) attIdImei.length; + // Attestation Id MEID + case KMDataStoreConstants.ATT_ID_MEID: + Util.arrayCopyNonAtomic(attIdMeId, (short) 0, buffer, start, (short) attIdMeId.length); + return (short) attIdMeId.length; + // Attestation Id Manufacturer + case KMDataStoreConstants.ATT_ID_MANUFACTURER: + Util.arrayCopyNonAtomic(attIdManufacturer, (short) 0, buffer, start, + (short) attIdManufacturer.length); + return (short) attIdManufacturer.length; + // Attestation Id Model + case KMDataStoreConstants.ATT_ID_MODEL: + Util.arrayCopyNonAtomic(attIdModel, (short) 0, buffer, start, (short) attIdModel.length); + return (short) attIdModel.length; + } + return (short) 0; + } + + public void setAttestationId(short id, byte[] buffer, short start, short length) { + switch (id) { + // Attestation Id Brand + case KMDataStoreConstants.ATT_ID_BRAND: + attIdBrand = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdBrand, (short) 0, length); + break; + // Attestation Id Device + case KMDataStoreConstants.ATT_ID_DEVICE: + attIdDevice = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdDevice, (short) 0, length); + break; + // Attestation Id Product + case KMDataStoreConstants.ATT_ID_PRODUCT: + attIdProduct = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdProduct, (short) 0, length); + break; + // Attestation Id Serial + case KMDataStoreConstants.ATT_ID_SERIAL: + attIdSerial = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdSerial, (short) 0, length); + break; + // Attestation Id IMEI + case KMDataStoreConstants.ATT_ID_IMEI: + attIdImei = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdImei, (short) 0, length); + break; + // Attestation Id MEID + case KMDataStoreConstants.ATT_ID_MEID: + attIdMeId = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdMeId, (short) 0, length); + break; + // Attestation Id Manufacturer + case KMDataStoreConstants.ATT_ID_MANUFACTURER: + attIdManufacturer = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdManufacturer, (short) 0, length); + break; + // Attestation Id Model + case KMDataStoreConstants.ATT_ID_MODEL: + attIdModel = new byte[length]; + Util.arrayCopy(buffer, (short) start, attIdModel, (short) 0, length); + break; + } + } + + @Override + public void onSave(Element element) { + // Prmitives + element.write(dataIndex); + element.write(deviceBootLocked); + element.write(bootState); + // Objects + element.write(dataTable); + element.write(certificateData); + element.write(attIdBrand); + element.write(attIdDevice); + element.write(attIdProduct); + element.write(attIdSerial); + element.write(attIdImei); + element.write(attIdMeId); + element.write(attIdManufacturer); + element.write(attIdModel); + element.write(verifiedHash); + element.write(bootKey); + element.write(bootPatchLevel); + // Key Objects + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, masterKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY, + computedHmacKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY, preSharedKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY, attestationKey); + } + + @Override + public void onRestore(Element element, short oldVersion, short currentVersion) { + // Read Primitives + dataIndex = element.readShort(); + deviceBootLocked = element.readBoolean(); + bootState = element.readShort(); + // Read Objects + dataTable = (byte[]) element.readObject(); + certificateData = (byte[]) element.readObject(); + attIdBrand = (byte[]) element.readObject(); + attIdDevice = (byte[]) element.readObject(); + attIdProduct = (byte[]) element.readObject(); + attIdSerial = (byte[]) element.readObject(); + attIdImei = (byte[]) element.readObject(); + attIdMeId = (byte[]) element.readObject(); + attIdManufacturer = (byte[]) element.readObject(); + attIdModel = (byte[]) element.readObject(); + verifiedHash = (byte[]) element.readObject(); + bootKey = (byte[]) element.readObject(); + bootPatchLevel = (byte[]) element.readObject(); + // Read Key Objects + masterKey = (KMMasterKey) seProvider.onResore(element); + computedHmacKey = (KMComputedHmacKey) seProvider.onResore(element); + preSharedKey = (KMPreSharedKey) seProvider.onResore(element); + attestationKey = (KMAttestationKey) seProvider.onResore(element); + } + + @Override + public short getBackupPrimitiveByteCount() { + // Magic Number - 1 byte + // Package Version - 2 bytes + // dataIndex - 2 bytes + // deviceLocked - 1 byte + // deviceState = 2 bytes + // interface types - 4 bytes + return (short) (12 + + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + + seProvider.getBackupPrimitiveByteCount( + KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY) + + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + + seProvider.getBackupPrimitiveByteCount( + KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY)); + } + + @Override + public short getBackupObjectCount() { + // dataTable - 1 + // CertificateData - 1 + // AttestationIds - 8 + // bootParameters - 3 + return (short) (13 + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY)); + } + + // Below functions are related boot paramters. + public void setVerifiedBootHash(byte[] buffer, short start, short length) { + if (verifiedHash == null) { + verifiedHash = new byte[32]; + } + if (length != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, verifiedHash, (short) 0, (short) 32); + } + + public void setBootKey(byte[] buffer, short start, short length) { + if (bootKey == null) { + bootKey = new byte[32]; + } + if (length != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootKey, (short) 0, (short) 32); + } + + public void setBootState(short state) { + bootState = state; + } + + public void setDeviceLocked(boolean state) { + deviceBootLocked = state; + } + + public void setBootPatchLevel(byte[] buffer, short start, short length) { + if (bootPatchLevel == null) { + bootPatchLevel = new byte[4]; + } + if (length > 4 || length < 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + Util.arrayCopyNonAtomic(buffer, start, bootPatchLevel, (short) 0, length); + } + + public short getVerifiedBootHash(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length); + return (short) verifiedHash.length; + } + + public short getBootKey(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length); + return (short) bootKey.length; + } + + public short getBootState() { + return bootState; + } + + public boolean isDeviceBootLocked() { + return deviceBootLocked; + } + + public short getBootPatchLevel(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootPatchLevel, (short) 0, buffer, start, + (short) bootPatchLevel.length); + return (short) bootPatchLevel.length; + } + +} diff --git a/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMRkpDataStoreImpl.java b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMRkpDataStoreImpl.java new file mode 100644 index 00000000..e1d4d275 --- /dev/null +++ b/Applet/AndroidSEApplet/src/com/android/javacard/kmapplet/KMRkpDataStoreImpl.java @@ -0,0 +1,193 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmapplet; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.kmdevice.KMDeviceUniqueKey; +import com.android.javacard.kmdevice.KMError; +import com.android.javacard.kmdevice.KMException; +import com.android.javacard.kmdevice.KMRkpDataStore; +import com.android.javacard.kmdevice.KMPreSharedKey; +import com.android.javacard.kmdevice.KMSEProvider; +import com.android.javacard.kmdevice.KMDataStoreConstants; +import com.android.javacard.kmdevice.KMType; + +import javacard.framework.JCSystem; +import javacard.framework.Util; + +public class KMRkpDataStoreImpl implements KMRkpDataStore { + + private byte[] bcc; + private byte[] additionalCertData; + private KMDeviceUniqueKey deviceUniqueKey; + private KMDeviceUniqueKey testDeviceUniqueKey; + private KMSEProvider seProvider; + + + public KMRkpDataStoreImpl(KMSEProvider provider) { + seProvider = provider; + initializeAdditionalBuffers(provider.isUpgrading()); + } + + private void initializeAdditionalBuffers(boolean isUpgrading) { + if (!isUpgrading) { + // use certificateData as Additional certficate chain. + if (additionalCertData == null) { + // First 2 bytes is reserved for length for all the 3 buffers. + additionalCertData = new byte[(short) (2 + + KMConfigurations.ADDITIONAL_CERT_CHAIN_MAX_SIZE)]; + } + + if (bcc == null) { + bcc = new byte[(short) (2 + KMConfigurations.BOOT_CERT_CHAIN_MAX_SIZE)]; + } + } + } + + @Override + public void storeData(byte storeDataIndex, byte[] data, short offset, short length) { + switch (storeDataIndex) { + case KMDataStoreConstants.ADDITIONAL_CERT_CHAIN: + persistAdditionalCertChain(data, offset, length); + break; + case KMDataStoreConstants.BOOT_CERT_CHAIN: + persistBootCertificateChain(data, offset, length); + break; + } + } + + private void persistAdditionalCertChain(byte[] buf, short offset, short len) { + // Input buffer contains encoded additional certificate chain as shown below. + // AdditionalDKSignatures = { + // + SignerName => DKCertChain + // } + // SignerName = tstr + // DKCertChain = [ + // 2* Certificate // Root -> Leaf. Root is the vendo r + // // self-signed cert, leaf contains DK_pu b + // ] + // Certificate = COSE_Sign1 of a public key + if ((short) (len + 2) > KMConfigurations.ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(additionalCertData, (short) 0, (short) len); + JCSystem.commitTransaction(); + Util.arrayCopy(buf, offset, additionalCertData, (short) 2, len); + } + + private void persistBootCertificateChain(byte[] buf, short offset, short len) { + if ((short) (len + 2) > KMConfigurations.BOOT_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(bcc, (short) 0, (short) len); + JCSystem.commitTransaction(); + Util.arrayCopy(buf, offset, bcc, (short) 2, len); + } + + + @Override + public void createDeviceUniqueKey(boolean testMode, byte[] pubKey, short pubKeyOff, + short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen) { + if (testMode) { + createTestDeviceUniqueKey(pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen); + } else { + createDeviceUniqueKey(pubKey, pubKeyOff, pubKeyLen, privKey, privKeyOff, privKeyLen); + } + } + + @Override + public KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode) { + if (testMode) { + return testDeviceUniqueKey; + } else { + return deviceUniqueKey; + } + } + + private void createTestDeviceUniqueKey(byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, + short privKeyOff, short privKeyLen) { + if (testDeviceUniqueKey == null) { + testDeviceUniqueKey = seProvider.createDeviceUniqueKey(testDeviceUniqueKey, pubKey, pubKeyOff, + pubKeyLen, privKey, + privKeyOff, privKeyLen); + } else { + seProvider.createDeviceUniqueKey(testDeviceUniqueKey, pubKey, pubKeyOff, pubKeyLen, privKey, + privKeyOff, + privKeyLen); + } + } + + private void createDeviceUniqueKey(byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, short privKeyOff, + short privKeyLen) { + if (deviceUniqueKey == null) { + deviceUniqueKey = seProvider.createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, + pubKeyLen, privKey, + privKeyOff, privKeyLen); + } else { + seProvider.createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, pubKeyLen, privKey, + privKeyOff, privKeyLen); + } + } + + @Override + public void onSave(Element ele) { + ele.write(additionalCertData); + ele.write(bcc); + // Key Object + seProvider.onSave(ele, KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY, deviceUniqueKey); + } + + @Override + public void onRestore(Element ele, short oldVersion, short currentVersion) { + additionalCertData = (byte[]) ele.readObject(); + bcc = (byte[]) ele.readObject(); + deviceUniqueKey = (KMDeviceUniqueKey) seProvider.onResore(ele); + } + + @Override + public short getBackupPrimitiveByteCount() { + return seProvider.getBackupPrimitiveByteCount( + KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY); + } + + @Override + public short getBackupObjectCount() { + // AdditionalCertificateChain - 1 + // BCC - 1 + return (short) (2 + seProvider.getBackupObjectCount( + KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY)); + } + + @Override + public byte[] getData(byte dataStoreId) { + switch (dataStoreId) { + case KMDataStoreConstants.ADDITIONAL_CERT_CHAIN: + return additionalCertData; + case KMDataStoreConstants.BOOT_CERT_CHAIN: + return bcc; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return null; + } + +} diff --git a/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt b/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt deleted file mode 100644 index ba998254..00000000 --- a/Applet/AndroidSEProvider/AndroidSE_3_0_5.opt +++ /dev/null @@ -1,5 +0,0 @@ --out EXP JCA CAP --exportpath ../../AndroidSEProvider/api_export_files_3.0.5 --applet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1:0x1 com.android.javacard.keymaster.KMAndroidSEApplet -com.android.javacard.keymaster -0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1 1.0 diff --git a/Applet/AndroidSEProvider/AndroidSE_3_1_0.opt b/Applet/AndroidSEProvider/AndroidSE_3_1_0.opt deleted file mode 100644 index 3de07eb5..00000000 --- a/Applet/AndroidSEProvider/AndroidSE_3_1_0.opt +++ /dev/null @@ -1,5 +0,0 @@ --out EXP JCA CAP --exportpath ../../AndroidSEProvider/api_export_files_3.1.0 --applet 0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1:0x1 com.android.javacard.keymaster.KMAndroidSEApplet -com.android.javacard.keymaster -0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1 1.0 diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/io/javacard/io.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/io/javacard/io.exp deleted file mode 100644 index 931133af..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/io/javacard/io.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/lang/javacard/lang.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/lang/javacard/lang.exp deleted file mode 100644 index f3498186..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/lang/javacard/lang.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/rmi/javacard/rmi.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/java/rmi/javacard/rmi.exp deleted file mode 100644 index 209cdf37..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/java/rmi/javacard/rmi.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/javacard/framework.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/javacard/framework.exp deleted file mode 100644 index fd14eac7..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/javacard/framework.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp deleted file mode 100644 index 8fbef2f6..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/framework/service/javacard/service.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/security/javacard/security.exp deleted file mode 100644 index 39e57627..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacard/security/javacard/security.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp deleted file mode 100644 index c9183d4f..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/javacard/apdu.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp deleted file mode 100644 index 6a1e9e1e..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/apdu/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp deleted file mode 100644 index fb46fa30..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry/javacard/biometry.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp deleted file mode 100644 index 88527985..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/biometry1toN/javacard/biometry1toN.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp deleted file mode 100644 index e1ff132d..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/crypto/javacard/crypto.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/external/javacard/external.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/external/javacard/external.exp deleted file mode 100644 index 4af91e53..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/external/javacard/external.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp deleted file mode 100644 index 89a1ac6c..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/math/javacard/math.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp deleted file mode 100644 index 7a3b73c3..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/string/javacard/string.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp deleted file mode 100644 index 58495fb0..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/tlv/javacard/tlv.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp deleted file mode 100644 index 881a9612..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/intx/javacard/intx.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp deleted file mode 100644 index a1ce7e96..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/framework/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp deleted file mode 100644 index 5153a68d..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/javacardx/security/javacard/security.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.exp b/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.exp deleted file mode 100644 index 110117b2..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.jca b/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.jca deleted file mode 100644 index 9bb1e3b8..00000000 --- a/Applet/AndroidSEProvider/api_export_files_3.0.5/org/globalplatform/upgrade/javacard/upgrade.jca +++ /dev/null @@ -1,245 +0,0 @@ -// converted by version 1.3 -// on Wed Feb 14 10:43:54 CET 2018 - -.package org/globalplatform/upgrade { - .aid 0xA0:0x0:0x0:0x1:0x51:0x7; - .version 1.1; - - .imports { - 0xA0:0x0:0x0:0x0:0x62:0x0:0x1 1.0; //java/lang - } - - .constantPool { - // 0 - staticMethodRef 0.0.0()V; // java/lang/Object.()V - } - - .interface public abstract Element 0 { - - .fields { - public static final byte TYPE_SIMPLE = 1; // B - public static final byte TYPE_MAPPED = 2; // B - public static final short SIZE_BOOLEAN = 1; // S - public static final short SIZE_BYTE = 1; // S - public static final short SIZE_SHORT = 2; // S - } - - .method public abstract write(Z)Lorg/globalplatform/upgrade/Element; 0 { - } - - .method public abstract write(B)Lorg/globalplatform/upgrade/Element; 1 { - } - - .method public abstract write(S)Lorg/globalplatform/upgrade/Element; 2 { - } - - .method public abstract write(Ljava/lang/Object;)Lorg/globalplatform/upgrade/Element; 3 { - .descriptor Ljava/lang/Object; 0.0; - - } - - .method public abstract canWriteBoolean()S 4 { - } - - .method public abstract canWriteByte()S 5 { - } - - .method public abstract canWriteShort()S 6 { - } - - .method public abstract canWriteObject()S 7 { - } - - .method public abstract initRead()V 8 { - } - - .method public abstract readBoolean()Z 9 { - } - - .method public abstract readByte()B 10 { - } - - .method public abstract readShort()S 11 { - } - - .method public abstract readObject()Ljava/lang/Object; 12 { - .descriptor Ljava/lang/Object; 0.0; - - } - - .method public abstract canReadBoolean()S 13 { - } - - .method public abstract canReadByte()S 14 { - } - - .method public abstract canReadShort()S 15 { - } - - .method public abstract canReadObject()S 16 { - } - - } - - .interface public abstract MappedElement 1 { - - .superInterfaces { - Element; - } - - .method public abstract write(Z)Lorg/globalplatform/upgrade/Element; 0 { - } - - .method public abstract write(B)Lorg/globalplatform/upgrade/Element; 1 { - } - - .method public abstract write(S)Lorg/globalplatform/upgrade/Element; 2 { - } - - .method public abstract write(Ljava/lang/Object;)Lorg/globalplatform/upgrade/Element; 3 { - .descriptor Ljava/lang/Object; 0.0; - - } - - .method public abstract canWriteBoolean()S 4 { - } - - .method public abstract canWriteByte()S 5 { - } - - .method public abstract canWriteShort()S 6 { - } - - .method public abstract canWriteObject()S 7 { - } - - .method public abstract initRead()V 8 { - } - - .method public abstract readBoolean()Z 9 { - } - - .method public abstract readByte()B 10 { - } - - .method public abstract readShort()S 11 { - } - - .method public abstract readObject()Ljava/lang/Object; 12 { - .descriptor Ljava/lang/Object; 0.0; - - } - - .method public abstract canReadBoolean()S 13 { - } - - .method public abstract canReadByte()S 14 { - } - - .method public abstract canReadShort()S 15 { - } - - .method public abstract canReadObject()S 16 { - } - - .method public abstract getMappedObject()Ljava/lang/Object; 17 { - .descriptor Ljava/lang/Object; 0.0; - - } - - .method public abstract setMappedObject(Ljava/lang/Object;)Lorg/globalplatform/upgrade/Element; 18 { - .descriptor Ljava/lang/Object; 0.0; - - } - - } - - .interface public abstract OnUpgradeListener 2 { - - .method public abstract onSave()Lorg/globalplatform/upgrade/Element; 0 { - } - - .method public abstract onCleanup()V 1 { - } - - .method public abstract onRestore(Lorg/globalplatform/upgrade/Element;)V 2 { - } - - .method public abstract onConsolidate()V 3 { - } - - } - - .class public final UpgradeManager 3 extends 0.0 { // extends java/lang/Object - - .publicMethodTable 1 { - equals(Ljava/lang/Object;)Z; - } - - .packageMethodTable 0 { - } - - .method private ()V { - .stack 1; - .locals 0; - - L0: aload_0; - invokespecial 0; // java/lang/Object.()V - return; - } - - .method public static isUpgrading()Z 0 { - .stack 1; - .locals 0; - - L0: sconst_0; - sreturn; - } - - .method public static getPreviousPackageVersion()S 1 { - .stack 1; - .locals 0; - - L0: sconst_0; - sreturn; - } - - .method public static checkPreviousPackageAID([BSB)Z 2 { - .stack 1; - .locals 0; - - L0: sconst_0; - sreturn; - } - - .method public static createElement(BSS)Lorg/globalplatform/upgrade/Element; 3 { - .stack 1; - .locals 0; - - L0: aconst_null; - areturn; - } - - .method public static matchMappedElement(Ljava/lang/Object;)Lorg/globalplatform/upgrade/MappedElement; 4 { - .stack 1; - .locals 0; - - .descriptor Ljava/lang/Object; 0.0; - - L0: aconst_null; - areturn; - } - - .method public static nonNullReference()Ljava/lang/Object; 5 { - .stack 1; - .locals 0; - - .descriptor Ljava/lang/Object; 0.0; - - L0: aconst_null; - areturn; - } - - } - -} diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp deleted file mode 100644 index 36b9d18b..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/io/javacard/io.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp deleted file mode 100644 index 272eebba..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/lang/javacard/lang.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp deleted file mode 100644 index 8c695237..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/java/rmi/javacard/rmi.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/javacard/framework.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/javacard/framework.exp deleted file mode 100644 index e360b447..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/javacard/framework.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp deleted file mode 100644 index 69fd1b28..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/framework/service/javacard/service.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/security/javacard/security.exp deleted file mode 100644 index 11514947..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacard/security/javacard/security.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp deleted file mode 100644 index ce4ac0c9..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/javacard/apdu.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp deleted file mode 100644 index cf2fc718..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/apdu/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp deleted file mode 100644 index 2bdf2c40..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry/javacard/biometry.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp deleted file mode 100644 index 3b93e625..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/biometry1toN/javacard/biometry1toN.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp deleted file mode 100644 index 679c29d6..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/crypto/javacard/crypto.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/external/javacard/external.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/external/javacard/external.exp deleted file mode 100644 index aca62699..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/external/javacard/external.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp deleted file mode 100644 index bf463690..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/event/javacard/event.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp deleted file mode 100644 index af2f608c..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/math/javacard/math.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp deleted file mode 100644 index 667743fd..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/nio/javacard/nio.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp deleted file mode 100644 index 47df6293..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/string/javacard/string.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp deleted file mode 100644 index 8621ad8f..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/time/javacard/time.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp deleted file mode 100644 index 142d9923..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/tlv/javacard/tlv.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp deleted file mode 100644 index 39008657..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/intx/javacard/intx.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp deleted file mode 100644 index 35523adf..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/framework/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp deleted file mode 100644 index 4312b4d4..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/cert/javacard/cert.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp deleted file mode 100644 index 5c740cff..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/derivation/javacard/derivation.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp deleted file mode 100644 index 30b3ed6e..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/javacard/security.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp b/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp deleted file mode 100644 index 7f443343..00000000 Binary files a/Applet/AndroidSEProvider/api_export_files_3.1.0/javacardx/security/util/javacard/util.exp and /dev/null differ diff --git a/Applet/AndroidSEProvider/build.xml b/Applet/AndroidSEProvider/build.xml deleted file mode 100644 index 137c169a..00000000 --- a/Applet/AndroidSEProvider/build.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java deleted file mode 100644 index befab92f..00000000 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import org.globalplatform.upgrade.Element; -import org.globalplatform.upgrade.OnUpgradeListener; -import org.globalplatform.upgrade.UpgradeManager; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { - - KMAndroidSEApplet() { - super(new KMAndroidSEProvider()); - } - - /** - * Installs this applet. - * - * @param bArray the array containing installation parameters - * @param bOffset the starting offset in bArray - * @param bLength the length in bytes of the parameter data in bArray - */ - public static void install(byte[] bArray, short bOffset, byte bLength) { - new KMAndroidSEApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]); - } - - @Override - public void onCleanup() { - } - - @Override - public void onConsolidate() { - } - - @Override - public void onRestore(Element element) { - element.initRead(); - byte firstByte = element.readByte(); - short packageVersion_ = 0; - byte provisionStatus_ = firstByte; - if (firstByte == KMKeymasterApplet.KM_MAGIC_NUMBER) { - packageVersion_ = element.readShort(); - provisionStatus_ = element.readByte(); - } - if (0 != packageVersion_ && !isUpgradeAllowed(packageVersion_)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - packageVersion = packageVersion_; - provisionStatus = provisionStatus_; - keymasterState = element.readByte(); - repository.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); - seProvider.onRestore(element, packageVersion, CURRENT_PACKAGE_VERSION); - handleDataUpgradeToVersion2_0(); - } - - @Override - public Element onSave() { - // SEProvider count - short primitiveCount = seProvider.getBackupPrimitiveByteCount(); - short objectCount = seProvider.getBackupObjectCount(); - //Repository count - primitiveCount += repository.getBackupPrimitiveByteCount(); - objectCount += repository.getBackupObjectCount(); - //KMKeymasterApplet count - primitiveCount += computePrimitveDataSize(); - objectCount += computeObjectCount(); - - // Create element. - Element element = UpgradeManager.createElement(Element.TYPE_SIMPLE, - primitiveCount, objectCount); - element.write(KM_MAGIC_NUMBER); - element.write(packageVersion); - element.write(provisionStatus); - element.write(keymasterState); - repository.onSave(element); - seProvider.onSave(element); - return element; - } - - private short computePrimitveDataSize() { - // provisionStatus + keymasterState + magic byte + version - return (short) 5; - } - - private short computeObjectCount() { - return (short) 0; - } - - public boolean isUpgradeAllowed(short version) { - boolean upgradeAllowed = false; - short oldMajorVersion = (short) ((version >> 8) & 0x00FF); - short oldMinorVersion = (short) (version & 0x00FF); - short currentMajorVersion = (short) (CURRENT_PACKAGE_VERSION >> 8 & 0x00FF); - short currentMinorVersion = (short) (CURRENT_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) { - if (currentMinorVersion == 0) { - upgradeAllowed = true; - } - } else if ((short) (currentMajorVersion - oldMajorVersion) == 0) { - if ((short) (currentMinorVersion - oldMinorVersion) == 1) { - upgradeAllowed = true; - } - } - return upgradeAllowed; - } - - public void handleDataUpgradeToVersion2_0() { - - if (packageVersion != 0) { - // No Data upgrade required. - return; - } - byte status = provisionStatus; - // In the current version of the applet set boot parameters is removed from - // provision status so readjust the provision locked flag. - // 0x40 is provision locked flag in the older applet. - // Unset the 5th bit. setboot parameters flag. - status = (byte) (status & 0xDF); - // Readjust the lock provisioned status flag. - if ((status & 0x40) == 0x40) { - // 0x40 to 0x20 - // Unset 6th bit - status = (byte) (status & 0xBF); - // set the 5th bit - status = (byte) (status | 0x20); - } - provisionStatus = status; - packageVersion = CURRENT_PACKAGE_VERSION; - - short certExpiryLen = 0; - short issuerLen = 0; - short certExpiry = repository.getCertExpiryTime(); - if (certExpiry != KMType.INVALID_VALUE) { - certExpiryLen = KMByteBlob.cast(certExpiry).length(); - } - short issuer = repository.getIssuer(); - if (issuer != KMType.INVALID_VALUE) { - issuerLen = KMByteBlob.cast(issuer).length(); - } - short certChainLen = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); - short offset = repository.allocReclaimableMemory((short) (certExpiryLen + issuerLen + certChainLen)); - // Get the start offset of the certificate chain. - short certChaionOff = - decoder.getCborBytesStartOffset( - repository.getHeap(), - offset, - seProvider.readProvisionedData(KMSEProvider.CERTIFICATE_CHAIN, repository.getHeap(), offset)); - certChainLen -= (short) (certChaionOff - offset); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(issuer).getBuffer(), - KMByteBlob.cast(issuer).getStartOff(), - repository.getHeap(), - (short) (certChaionOff + certChainLen), - issuerLen); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(certExpiry).getBuffer(), - KMByteBlob.cast(certExpiry).getStartOff(), - repository.getHeap(), - (short) (certChaionOff + certChainLen + issuerLen), - certExpiryLen); - - seProvider.persistProvisionData( - repository.getHeap(), - certChaionOff, // cert chain offset - certChainLen, - (short) (certChaionOff + certChainLen), // issuer offset - issuerLen, - (short) (certChaionOff + certChainLen + issuerLen), // cert expiry offset - certExpiryLen); - - // Update computed HMAC key. - short blob = repository.getComputedHmacKey(); - if (blob != KMType.INVALID_VALUE) { - seProvider.createComputedHmacKey( - KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), - KMByteBlob.cast(blob).length() - ); - } else { - // Initialize the Key object. - Util.arrayFillNonAtomic(repository.getHeap(), offset, (short) 32, (byte) 0); - seProvider.createComputedHmacKey(repository.getHeap(), offset,(short) 32); - } - repository.reclaimMemory((short) (certExpiryLen + issuerLen + certChainLen)); - } -} - diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java deleted file mode 100644 index 7e5eb5cc..00000000 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ /dev/null @@ -1,909 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import com.android.javacard.keymaster.KMAESKey; -import com.android.javacard.keymaster.KMByteBlob; -import com.android.javacard.keymaster.KMECPrivateKey; -import com.android.javacard.keymaster.KMMasterKey; - -import javacard.framework.JCSystem; -import javacard.framework.Util; -import javacard.security.AESKey; - -// The class encodes strongbox generated amd signed attestation certificate. This only encodes -// required fields of the certificates. It is not meant to be generic X509 cert encoder. -// Whatever fields that are fixed are added as byte arrays. The Extensions are encoded as per -// the values. -// The certificate is assembled with leafs first and then the sequences. - -public class KMAttestationCertImpl implements KMAttestationCert { - - private static final byte MAX_PARAMS = 30; - // DER encoded object identifiers required by the cert. - // rsaEncryption - 1.2.840.113549.1.1.1 - private static final byte[] rsaEncryption = { - 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01 - }; - // ecPublicKey - 1.2.840.10045.2.1 - private static final byte[] eccPubKey = { - 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01 - }; - // prime256v1 curve - 1.2.840.10045.3.1.7 - private static final byte[] prime256v1 = { - 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07 - }; - // Key Usage Extn - 2.5.29.15 - private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F}; - // Android Extn - 1.3.6.1.4.1.11129.2.1.17 - private static final byte[] androidExtn = { - 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11 - }; - - private static final short ECDSA_MAX_SIG_LEN = 72; - //Signature algorithm identifier - always ecdsaWithSha256 - 1.2.840.10045.4.3.2 - //SEQUENCE of alg OBJ ID and parameters = NULL. - private static final byte[] X509SignAlgIdentifier = { - 0x30, - 0x0A, - 0x06, - 0x08, - 0x2A, - (byte) 0x86, - 0x48, - (byte) 0xCE, - (byte) 0x3D, - 0x04, - 0x03, - 0x02 - }; - - // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. - private static final short[] swTagIds = { - KMType.ATTESTATION_APPLICATION_ID, - KMType.CREATION_DATETIME, - KMType.USAGE_EXPIRE_DATETIME, - KMType.ORIGINATION_EXPIRE_DATETIME, - KMType.ACTIVE_DATETIME, - KMType.UNLOCKED_DEVICE_REQUIRED - }; - - // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. - private static final short[] hwTagIds = { - KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, - KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER, - KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI, - KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT, - KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND, - KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, - KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, - KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID, - KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH, - KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE, - KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE}; - - // Validity is not fixed field - // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys - private static final byte[] X509Subject = { - 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, - 0x64, - 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, - 0x65, - 0x79 - }; - - private static final byte keyUsageSign = (byte) 0x80; // 0 bit - private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit - private static final byte keyUsageDataEncipher = (byte) 0x10; // 3rd- bit - - private static final byte KEYMASTER_VERSION = 41; - private static final byte ATTESTATION_VERSION = 4; - private static final byte[] pubExponent = {0x01, 0x00, 0x01}; - private static final byte SERIAL_NUM = (byte) 0x01; - private static final byte X509_VERSION = (byte) 0x02; - - private static short certStart; - private static short signatureOffset; - private static short tbsOffset; - private static short tbsLength; - - private static short stackPtr; - private static byte[] stack; - private static short start; - private static short length; - // private static KMRepository repo; - private static short uniqueId; - private static short attChallenge; - private static short notBefore; - private static short notAfter; - private static short pubKey; - private static short[] swParams; - private static short swParamsIndex; - private static short[] hwParams; - private static short hwParamsIndex; - private static byte keyUsage; - private static byte unusedBits; - private static KMAttestationCert inst; - private static boolean rsaCert; - private static byte deviceLocked; - private static short verifiedBootKey; - private static byte verifiedState; - private static short verifiedHash; - private static short issuer; - private static short signPriv; - - private KMAttestationCertImpl() { - } - - public static KMAttestationCert instance(boolean rsaCert) { - if (inst == null) { - inst = new KMAttestationCertImpl(); - } - init(); - KMAttestationCertImpl.rsaCert = rsaCert; - return inst; - } - - private static void init() { - stack = null; - stackPtr = 0; - certStart = 0; - signatureOffset = 0; - start = 0; - length = 0; - tbsLength = 0; - if (swParams == null) { - swParams = JCSystem.makeTransientShortArray((short) MAX_PARAMS, JCSystem.CLEAR_ON_RESET); - } - if (hwParams == null) { - hwParams = JCSystem.makeTransientShortArray((short) MAX_PARAMS, JCSystem.CLEAR_ON_RESET); - } - - swParamsIndex = 0; - hwParamsIndex = 0; - keyUsage = 0; - unusedBits = 8; - attChallenge = 0; - notBefore = 0; - notAfter = 0; - pubKey = 0; - uniqueId = 0; - verifiedBootKey = 0; - verifiedHash = 0; - verifiedState = 0; - rsaCert = true; - deviceLocked = 0; - signPriv = 0; - } - - @Override - public KMAttestationCert verifiedBootHash(short obj) { - if (obj == KMType.INVALID_VALUE) - KMException.throwIt(KMError.INVALID_DATA); - verifiedHash = obj; - return this; - } - - @Override - public KMAttestationCert verifiedBootKey(short obj) { - if (obj == KMType.INVALID_VALUE) - KMException.throwIt(KMError.INVALID_DATA); - verifiedBootKey = obj; - return this; - } - - @Override - public KMAttestationCert verifiedBootState(byte val) { - verifiedState = val; - return this; - } - - private KMAttestationCert uniqueId(short obj) { - uniqueId = obj; - return this; - } - - @Override - public KMAttestationCert notBefore(short obj, byte[] scratchpad) { - // convert milliseconds to UTC date - notBefore = KMUtils.convertToDate(obj, scratchpad, true); - return this; - } - - @Override - public KMAttestationCert notAfter(short usageExpiryTimeObj, - short certExpirtyTimeObj, byte[] scratchPad, short tmpVar) { - if (usageExpiryTimeObj != KMType.INVALID_VALUE) { - // compare if the expiry time is greater then 2051 then use generalized - // time format else use utc time format. - usageExpiryTimeObj = KMIntegerTag.cast(usageExpiryTimeObj).getValue(); - tmpVar = KMInteger.uint_64(KMUtils.firstJan2051, (short) 0); - if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) { - usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, - false); - } else { - usageExpiryTimeObj = KMUtils - .convertToDate(usageExpiryTimeObj, scratchPad, true); - } - notAfter = usageExpiryTimeObj; - } else { - notAfter = certExpirtyTimeObj; - } - return this; - } - - @Override - public KMAttestationCert deviceLocked(boolean val) { - if (val) { - deviceLocked = (byte) 0xFF; - } else { - deviceLocked = 0; - } - return this; - } - - @Override - public KMAttestationCert publicKey(short obj) { - pubKey = obj; - return this; - } - - @Override - public KMAttestationCert attestationChallenge(short obj) { - attChallenge = obj; - return this; - } - - @Override - public KMAttestationCert extensionTag(short tag, boolean hwEnforced) { - if (hwEnforced) { - hwParams[hwParamsIndex] = tag; - hwParamsIndex++; - } else { - swParams[swParamsIndex] = tag; - swParamsIndex++; - } - if (KMTag.getKey(tag) == KMType.PURPOSE) { - createKeyUsage(tag); - } - return this; - } - - @Override - public KMAttestationCert issuer(short obj) { - if (obj == KMType.INVALID_VALUE) - KMException.throwIt(KMError.INVALID_DATA); - issuer = obj; - return this; - } - - private void createKeyUsage(short tag) { - short len = KMEnumArrayTag.cast(tag).length(); - byte index = 0; - while (index < len) { - if (KMEnumArrayTag.cast(tag).get(index) == KMType.SIGN) { - keyUsage = (byte) (keyUsage | keyUsageSign); - } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.WRAP_KEY) { - keyUsage = (byte) (keyUsage | keyUsageKeyEncipher); - } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) { - keyUsage = (byte) (keyUsage | keyUsageDataEncipher); - } - index++; - } - index = keyUsage; - while (index != 0) { - index = (byte) (index << 1); - unusedBits--; - } - } - - private static void pushTbsCert(boolean rsaCert) { - short last = stackPtr; - pushExtensions(); - // subject public key info - if (rsaCert) { - pushRsaSubjectKeyInfo(); - } else { - pushEccSubjectKeyInfo(); - } - // subject - pushBytes(X509Subject, (short) 0, (short) X509Subject.length); - pushValidity(); - // issuer - der encoded - pushBytes( - KMByteBlob.cast(issuer).getBuffer(), - KMByteBlob.cast(issuer).getStartOff(), - KMByteBlob.cast(issuer).length()); - // Algorithm Id - pushAlgorithmId(X509SignAlgIdentifier); - // Serial Number - pushByte(SERIAL_NUM); - pushIntegerHeader((short) 1); - // Version - pushByte(X509_VERSION); - pushIntegerHeader((short) 1); - pushByte((byte) 0x03); - pushByte((byte) 0xA0); - // Finally sequence header. - pushSequenceHeader((short) (last - stackPtr)); - } - - private static void pushExtensions() { - short last = stackPtr; - if (keyUsage != 0) { - pushKeyUsage(keyUsage, unusedBits); - } - pushKeyDescription(); - pushSequenceHeader((short) (last - stackPtr)); - // Extensions have explicit tag of [3] - pushLength((short) (last - stackPtr)); - pushByte((byte) 0xA3); - } - - // Time SEQUENCE{UTCTime, UTC or Generalized Time) - private static void pushValidity() { - short last = stackPtr; - if (notAfter != KMType.INVALID_VALUE) { - pushBytes( - KMByteBlob.cast(notAfter).getBuffer(), - KMByteBlob.cast(notAfter).getStartOff(), - KMByteBlob.cast(notAfter).length()); - } else { - KMException.throwIt(KMError.INVALID_DATA); - } - pushTimeHeader(KMByteBlob.cast(notAfter).length()); - pushBytes( - KMByteBlob.cast(notBefore).getBuffer(), - KMByteBlob.cast(notBefore).getStartOff(), - KMByteBlob.cast(notBefore).length()); - pushTimeHeader(KMByteBlob.cast(notBefore).length()); - pushSequenceHeader((short) (last - stackPtr)); - } - - private static void pushTimeHeader(short len) { - if (len == 13) { // UTC Time - pushLength((short) 0x0D); - pushByte((byte) 0x17); - } else if (len == 15) { // Generalized Time - pushLength((short) 0x0F); - pushByte((byte) 0x18); - } else { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } - - // SEQUENCE{SEQUENCE{algId, NULL}, bitString{SEQUENCE{ modulus as positive integer, public - // exponent - // as positive integer} - private static void pushRsaSubjectKeyInfo() { - short last = stackPtr; - pushBytes(pubExponent, (short) 0, (short) pubExponent.length); - pushIntegerHeader((short) pubExponent.length); - pushBytes( - KMByteBlob.cast(pubKey).getBuffer(), - KMByteBlob.cast(pubKey).getStartOff(), - KMByteBlob.cast(pubKey).length()); - - // encode modulus as positive if the MSB is 1. - if (KMByteBlob.cast(pubKey).get((short) 0) < 0) { - pushByte((byte) 0x00); - pushIntegerHeader((short) (KMByteBlob.cast(pubKey).length() + 1)); - } else { - pushIntegerHeader(KMByteBlob.cast(pubKey).length()); - } - pushSequenceHeader((short) (last - stackPtr)); - pushBitStringHeader((byte) 0x00, (short) (last - stackPtr)); - pushRsaEncryption(); - pushSequenceHeader((short) (last - stackPtr)); - } - - // SEQUENCE{SEQUENCE{ecPubKey, prime256v1}, bitString{pubKey}} - private static void pushEccSubjectKeyInfo() { - short last = stackPtr; - pushBytes( - KMByteBlob.cast(pubKey).getBuffer(), - KMByteBlob.cast(pubKey).getStartOff(), - KMByteBlob.cast(pubKey).length()); - pushBitStringHeader((byte) 0x00, KMByteBlob.cast(pubKey).length()); - pushEcDsa(); - pushSequenceHeader((short) (last - stackPtr)); - } - - private static void pushEcDsa() { - short last = stackPtr; - pushBytes(prime256v1, (short) 0, (short) prime256v1.length); - pushBytes(eccPubKey, (short) 0, (short) eccPubKey.length); - pushSequenceHeader((short) (last - stackPtr)); - } - - private static void pushRsaEncryption() { - short last = stackPtr; - pushNullHeader(); - pushBytes(rsaEncryption, (short) 0, (short) rsaEncryption.length); - pushSequenceHeader((short) (last - stackPtr)); - } - - // KeyDescription ::= SEQUENCE { - // attestationVersion INTEGER, # Value 3 - // attestationSecurityLevel SecurityLevel, # See below - // keymasterVersion INTEGER, # Value 4 - // keymasterSecurityLevel SecurityLevel, # See below - // attestationChallenge OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams - // uniqueId OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID - // softwareEnforced AuthorizationList, # See below - // hardwareEnforced AuthorizationList, # See below - // } - private static void pushKeyDescription() { - short last = stackPtr; - pushHWParams(); - pushSWParams(); - if (uniqueId != 0) { - pushOctetString( - KMByteBlob.cast(uniqueId).getBuffer(), - KMByteBlob.cast(uniqueId).getStartOff(), - KMByteBlob.cast(uniqueId).length()); - } else { - pushOctetStringHeader((short) 0); - } - pushOctetString( - KMByteBlob.cast(attChallenge).getBuffer(), - KMByteBlob.cast(attChallenge).getStartOff(), - KMByteBlob.cast(attChallenge).length()); - pushEnumerated(KMType.STRONGBOX); - pushByte(KEYMASTER_VERSION); - pushIntegerHeader((short) 1); - pushEnumerated(KMType.STRONGBOX); - pushByte(ATTESTATION_VERSION); - pushIntegerHeader((short) 1); - pushSequenceHeader((short) (last - stackPtr)); - pushOctetStringHeader((short) (last - stackPtr)); - pushBytes(androidExtn, (short) 0, (short) androidExtn.length); - pushSequenceHeader((short) (last - stackPtr)); - } - - private static void pushSWParams() { - short last = stackPtr; - byte index = 0; - short length = (short) swTagIds.length; - do { - pushParams(swParams, swParamsIndex, swTagIds[index]); - } while (++index < length); - pushSequenceHeader((short) (last - stackPtr)); - } - - private static void pushHWParams() { - short last = stackPtr; - byte index = 0; - short length = (short) hwTagIds.length; - do { - if (hwTagIds[index] == KMType.ROOT_OF_TRUST) { - pushRoT(); - continue; - } - if (pushParams(hwParams, hwParamsIndex, hwTagIds[index])) { - continue; - } - } while (++index < length); - pushSequenceHeader((short) (last - stackPtr)); - } - - private static boolean pushParams(short[] params, short len, short tagId) { - short index = 0; - while (index < len) { - if (tagId == KMTag.getKey(params[index])) { - pushTag(params[index]); - return true; - } - index++; - } - return false; - } - - private static void pushTag(short tag) { - short type = KMTag.getTagType(tag); - short tagId = KMTag.getKey(tag); - short val; - switch (type) { - case KMType.BYTES_TAG: - val = KMByteTag.cast(tag).getValue(); - pushBytesTag( - tagId, - KMByteBlob.cast(val).getBuffer(), - KMByteBlob.cast(val).getStartOff(), - KMByteBlob.cast(val).length()); - break; - case KMType.ENUM_TAG: - val = KMEnumTag.cast(tag).getValue(); - pushEnumTag(tagId, (byte) val); - break; - case KMType.ENUM_ARRAY_TAG: - val = KMEnumArrayTag.cast(tag).getValues(); - pushEnumArrayTag( - tagId, - KMByteBlob.cast(val).getBuffer(), - KMByteBlob.cast(val).getStartOff(), - KMByteBlob.cast(val).length()); - break; - case KMType.UINT_TAG: - case KMType.ULONG_TAG: - case KMType.DATE_TAG: - val = KMIntegerTag.cast(tag).getValue(); - pushIntegerTag( - tagId, - KMInteger.cast(val).getBuffer(), - KMInteger.cast(val).getStartOff(), - KMInteger.cast(val).length()); - break; - case KMType.UINT_ARRAY_TAG: - case KMType.ULONG_ARRAY_TAG: - // According to keymaster hal only one user secure id is used but this conflicts with - // tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS - val = KMIntegerArrayTag.cast(tag).getValues(); - pushIntegerArrayTag(tagId, val); - break; - case KMType.BOOL_TAG: - val = KMBoolTag.cast(tag).getVal(); - pushBoolTag(tagId); - break; - default: - KMException.throwIt(KMError.INVALID_TAG); - break; - } - } - - // RootOfTrust ::= SEQUENCE { - // verifiedBootKey OCTET_STRING, - // deviceLocked BOOLEAN, - // verifiedBootState VerifiedBootState, - // verifiedBootHash OCTET_STRING, - // } - // VerifiedBootState ::= ENUMERATED { - // Verified (0), - // SelfSigned (1), - // Unverified (2), - // Failed (3), - // } - private static void pushRoT() { - short last = stackPtr; - // verified boot hash - pushOctetString( - KMByteBlob.cast(verifiedHash).getBuffer(), - KMByteBlob.cast(verifiedHash).getStartOff(), - KMByteBlob.cast(verifiedHash).length()); - - pushEnumerated(verifiedState); - - pushBoolean(deviceLocked); - // verified boot Key - pushOctetString( - KMByteBlob.cast(verifiedBootKey).getBuffer(), - KMByteBlob.cast(verifiedBootKey).getStartOff(), - KMByteBlob.cast(verifiedBootKey).length()); - - // Finally sequence header - pushSequenceHeader((short) (last - stackPtr)); - // ... and tag Id - pushTagIdHeader(KMType.ROOT_OF_TRUST, (short) (last - stackPtr)); - } - - private static void pushOctetString(byte[] buf, short start, short len) { - pushBytes(buf, start, len); - pushOctetStringHeader(len); - } - - private static void pushBoolean(byte val) { - pushByte(val); - pushBooleanHeader((short) 1); - } - - private static void pushBooleanHeader(short len) { - pushLength(len); - pushByte((byte) 0x01); - } - - // Only SET of INTEGERS supported are padding, digest, purpose and blockmode - // All of these are enum array tags i.e. byte long values - private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short len) { - short last = stackPtr; - short index = 0; - while (index < len) { - pushByte(buf[(short) (start + index)]); - pushIntegerHeader((short) 1); - index++; - } - pushSetHeader((short) (last - stackPtr)); - pushTagIdHeader(tagId, (short) (last - stackPtr)); - } - - // Only SET of INTEGERS supported are padding, digest, purpose and blockmode - // All of these are enum array tags i.e. byte long values - private static void pushIntegerArrayTag(short tagId, short arr) { - short last = stackPtr; - short index = 0; - short len = KMArray.cast(arr).length(); - short ptr; - while (index < len) { - ptr = KMArray.cast(arr).get(index); - pushInteger( - KMInteger.cast(ptr).getBuffer(), - KMInteger.cast(ptr).getStartOff(), - KMInteger.cast(ptr).length()); - index++; - } - pushSetHeader((short) (last - stackPtr)); - pushTagIdHeader(tagId, (short) (last - stackPtr)); - } - - private static void pushSetHeader(short len) { - pushLength(len); - pushByte((byte) 0x31); - } - - private static void pushEnumerated(byte val) { - short last = stackPtr; - pushByte(val); - pushEnumeratedHeader((short) (last - stackPtr)); - } - - private static void pushEnumeratedHeader(short len) { - pushLength(len); - pushByte((byte) 0x0A); - } - - private static void pushBoolTag(short tagId) { - short last = stackPtr; - pushNullHeader(); - pushTagIdHeader(tagId, (short) (last - stackPtr)); - } - - private static void pushNullHeader() { - pushByte((byte) 0); - pushByte((byte) 0x05); - } - - private static void pushEnumTag(short tagId, byte val) { - short last = stackPtr; - pushByte(val); - pushIntegerHeader((short) (last - stackPtr)); - pushTagIdHeader(tagId, (short) (last - stackPtr)); - } - - private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) { - short last = stackPtr; - pushInteger(buf, start, len); - pushTagIdHeader(tagId, (short) (last - stackPtr)); - } - - // Ignore leading zeros. Only Unsigned Integers are required hence if MSB is set then add 0x00 - // as most significant byte. - private static void pushInteger(byte[] buf, short start, short len) { - short last = stackPtr; - byte index = 0; - while (index < (byte) len) { - if (buf[(short) (start + index)] != 0) { - break; - } - index++; - } - if (index == (byte) len) { - pushByte((byte) 0x00); - } else { - pushBytes(buf, (short) (start + index), (short) (len - index)); - if (buf[(short) (start + index)] < 0) { // MSB is 1 - pushByte((byte) 0x00); // always unsigned int - } - } - pushIntegerHeader((short) (last - stackPtr)); - } - - // Bytes Tag is a octet string and tag id is added explicitly - private static void pushBytesTag(short tagId, byte[] buf, short start, short len) { - short last = stackPtr; - pushBytes(buf, start, len); - pushOctetStringHeader((short) (last - stackPtr)); - pushTagIdHeader(tagId, (short) (last - stackPtr)); - } - - // tag id <= 30 ---> 0xA0 | {tagId} - // 30 < tagId < 128 ---> 0xBF 0x{tagId} - // tagId >= 128 ---> 0xBF 0x80+(tagId/128) 0x{tagId - (128*(tagId/128))} - private static void pushTagIdHeader(short tagId, short len) { - pushLength(len); - short count = (short) (tagId / 128); - if (count > 0) { - pushByte((byte) (tagId - (128 * count))); - pushByte((byte) (0x80 + count)); - pushByte((byte) 0xBF); - } else if (tagId > 30) { - pushByte((byte) tagId); - pushByte((byte) 0xBF); - } else { - pushByte((byte) (0xA0 | (byte) tagId)); - } - } - - // SEQUENCE {ObjId, OCTET STRING{BIT STRING{keyUsage}}} - private static void pushKeyUsage(byte keyUsage, byte unusedBits) { - short last = stackPtr; - pushByte(keyUsage); - pushBitStringHeader(unusedBits, (short) (last - stackPtr)); - pushOctetStringHeader((short) (last - stackPtr)); - pushBytes(keyUsageExtn, (short) 0, (short) keyUsageExtn.length); - pushSequenceHeader((short) (last - stackPtr)); - } - - private static void pushAlgorithmId(byte[] algId) { - pushBytes(algId, (short) 0, (short) algId.length); - } - - private static void pushIntegerHeader(short len) { - pushLength(len); - pushByte((byte) 0x02); - } - - private static void pushOctetStringHeader(short len) { - pushLength(len); - pushByte((byte) 0x04); - } - - private static void pushSequenceHeader(short len) { - pushLength(len); - pushByte((byte) 0x30); - } - - private static void pushBitStringHeader(byte unusedBits, short len) { - pushByte(unusedBits); - pushLength((short) (len + 1)); // 1 extra byte for unused bits byte - pushByte((byte) 0x03); - } - - private static void pushLength(short len) { - if (len < 128) { - pushByte((byte) len); - } else if (len < 256) { - pushByte((byte) len); - pushByte((byte) 0x81); - } else { - pushShort(len); - pushByte((byte) 0x82); - } - } - - private static void pushShort(short val) { - decrementStackPtr((short) 2); - Util.setShort(stack, stackPtr, val); - } - - private static void pushByte(byte val) { - decrementStackPtr((short) 1); - stack[stackPtr] = val; - } - - private static void pushBytes(byte[] buf, short start, short len) { - decrementStackPtr(len); - if (buf != null) { - Util.arrayCopyNonAtomic(buf, start, stack, stackPtr, len); - } - } - - private static void decrementStackPtr(short cnt) { - stackPtr = (short) (stackPtr - cnt); - if (start > stackPtr) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } - - @Override - public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { - stack = buf; - start = bufStart; - length = maxLen; - stackPtr = (short) (start + length); - return this; - } - - @Override - public short getCertStart() { - return certStart; - } - - @Override - public short getCertEnd() { - return (short) (start + length - 1); - } - - @Override - public short getCertLength() { - return (short) (getCertEnd() - getCertStart() + 1); - } - - @Override - public void build() { - short last = stackPtr; - decrementStackPtr((short) ECDSA_MAX_SIG_LEN); - signatureOffset = stackPtr; - pushBitStringHeader((byte) 0, (short) (last - stackPtr)); - pushAlgorithmId(X509SignAlgIdentifier); - tbsLength = stackPtr; - pushTbsCert(rsaCert); - tbsOffset = stackPtr; - tbsLength = (short) (tbsLength - tbsOffset); - pushSequenceHeader((short) (last - stackPtr)); - certStart = stackPtr; - KMAndroidSEProvider androidSeProvider = KMAndroidSEProvider.getInstance(); - short sigLen = androidSeProvider - .ecSign256( - androidSeProvider.getAttestationKey(), - stack, - tbsOffset, - tbsLength, - stack, - signatureOffset); - if (sigLen != ECDSA_MAX_SIG_LEN) { - // Update the lengths appropriately. - stackPtr = (short) (signatureOffset - 1); - pushLength((short) (sigLen + 1)); - stackPtr = tbsOffset; - last -= (short) (ECDSA_MAX_SIG_LEN - sigLen); - pushLength((short) (last - stackPtr)); - length -= (short) (ECDSA_MAX_SIG_LEN - sigLen); - } - } - - @Override - public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, - byte[] creationTime, short timeOffset, short creationTimeLen, - byte[] attestAppId, short appIdOff, short attestAppIdLen, - byte resetSinceIdRotation, KMMasterKey masterKey) { - // Concatenate T||C||R - // temporal count T - short temp = KMUtils.countTemporalCount(creationTime, timeOffset, - creationTimeLen, scratchPad, scratchPadOff); - Util.setShort(scratchPad, (short) scratchPadOff, temp); - temp = scratchPadOff; - scratchPadOff += 2; - - // Application Id C - Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, - attestAppIdLen); - scratchPadOff += attestAppIdLen; - - // Reset After Rotation R - scratchPad[scratchPadOff] = resetSinceIdRotation; - scratchPadOff++; - - timeOffset = KMByteBlob.instance((short) 32); - //Get the key data from the master key and use it for HMAC Sign. - AESKey aesKey = ((KMAESKey) masterKey).getKey(); - short mKeyData = KMByteBlob.instance((short) (aesKey.getSize() / 8)); - aesKey.getKey( - KMByteBlob.cast(mKeyData).getBuffer(), - KMByteBlob.cast(mKeyData).getStartOff()); - appIdOff = KMAndroidSEProvider.getInstance().hmacSign( - KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ - KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/ - KMByteBlob.cast(mKeyData).length(), /* Key length*/ - scratchPad, /* data */ - temp, /* data start */ - scratchPadOff, /* data length */ - KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */ - KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */ - if (appIdOff != 32) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - return uniqueId(timeOffset); - } -} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java deleted file mode 100644 index de304d8f..00000000 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.framework.JCSystem; -import javacard.framework.Util; -import javacard.security.Signature; -import javacardx.crypto.AEADCipher; -import javacardx.crypto.Cipher; - -public class KMOperationImpl implements KMOperation { - - private static final short CIPHER_ALG_OFFSET = 0x00; - private static final short PADDING_OFFSET = 0x01; - private static final short OPER_MODE_OFFSET = 0x02; - private static final short BLOCK_MODE_OFFSET = 0x03; - private static final short MAC_LENGTH_OFFSET = 0x04; - private static final byte[] EMPTY = {}; - //This will hold the length of the buffer stored inside the - //Java Card after the GCM update operation. - private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05; - private short[] parameters; - // Either one of Cipher/Signature instance is stored. - private Object[] operationInst; - - public KMOperationImpl() { - parameters = JCSystem.makeTransientShortArray((short) 6, JCSystem.CLEAR_ON_RESET); - operationInst = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - } - - public short getMode() { - return parameters[OPER_MODE_OFFSET]; - } - - public void setMode(short mode) { - parameters[OPER_MODE_OFFSET] = mode; - } - - public short getMacLength() { - return parameters[MAC_LENGTH_OFFSET]; - } - - public void setMacLength(short macLength) { - parameters[MAC_LENGTH_OFFSET] = macLength; - } - - public short getPaddingAlgorithm() { - return parameters[PADDING_OFFSET]; - } - - public void setPaddingAlgorithm(short alg) { - parameters[PADDING_OFFSET] = alg; - } - - public void setBlockMode(short mode) { - parameters[BLOCK_MODE_OFFSET] = mode; - } - - public short getBlockMode() { - return parameters[BLOCK_MODE_OFFSET]; - } - - public short getCipherAlgorithm() { - return parameters[CIPHER_ALG_OFFSET]; - } - - public void setCipherAlgorithm(short cipherAlg) { - parameters[CIPHER_ALG_OFFSET] = cipherAlg; - } - - public void setCipher(Cipher cipher) { - operationInst[0] = cipher; - } - - public void setSignature(Signature signer) { - operationInst[0] = signer; - } - - public boolean isResourceMatches(Object object) { - return operationInst[0] == object; - } - - private void reset() { - operationInst[0] = null; - parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE; - parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; - parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE;; - parameters[OPER_MODE_OFFSET] = KMType.INVALID_VALUE;; - parameters[CIPHER_ALG_OFFSET] = KMType.INVALID_VALUE;; - parameters[PADDING_OFFSET] = KMType.INVALID_VALUE;; - } - - @Override - public short update(byte[] inputDataBuf, short inputDataStart, - short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - short len = ((Cipher) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength, - outputDataBuf, outputDataStart); - if (parameters[CIPHER_ALG_OFFSET] == KMType.AES && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) { - // Every time Block size data is stored as intermediate result. - parameters[AES_GCM_UPDATE_LEN_OFFSET] += (short) (inputDataLength - len); - } - return len; - } - - @Override - public short update(byte[] inputDataBuf, short inputDataStart, - short inputDataLength) { - ((Signature) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength); - return 0; - } - - @Override - public short finish(byte[] inputDataBuf, short inputDataStart, - short inputDataLen, byte[] outputDataBuf, short outputDataStart) { - byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; - Cipher cipher = (Cipher) operationInst[0]; - short cipherAlg = parameters[CIPHER_ALG_OFFSET]; - short blockMode = parameters[BLOCK_MODE_OFFSET]; - short mode = parameters[OPER_MODE_OFFSET]; - short macLength = parameters[MAC_LENGTH_OFFSET]; - short padding = parameters[PADDING_OFFSET]; - short len = 0; - try { - if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { - if (mode == KMType.DECRYPT) { - inputDataLen = (short) (inputDataLen - macLength); - } - } else if ((cipherAlg == KMType.DES || cipherAlg == KMType.AES) && - padding == KMType.PKCS7 && mode == KMType.ENCRYPT) { - byte blkSize = 16; - byte paddingBytes; - short inputlen = inputDataLen; - if (cipherAlg == KMType.DES) { - blkSize = 8; - } - // padding bytes - if (inputlen % blkSize == 0) { - paddingBytes = blkSize; - } else { - paddingBytes = (byte) (blkSize - (inputlen % blkSize)); - } - // final len with padding - inputlen = (short) (inputlen + paddingBytes); - // intermediate buffer to copy input data+padding - // fill in the padding - Util.arrayFillNonAtomic(tmpArray, (short) 0, inputlen, paddingBytes); - // copy the input data - Util.arrayCopyNonAtomic(inputDataBuf, inputDataStart, tmpArray, - (short) 0, inputDataLen); - inputDataBuf = tmpArray; - inputDataLen = inputlen; - inputDataStart = 0; - } - len = cipher.doFinal(inputDataBuf, inputDataStart, inputDataLen, - outputDataBuf, outputDataStart); - if ((cipherAlg == KMType.AES || cipherAlg == KMType.DES) && - padding == KMType.PKCS7 && mode == KMType.DECRYPT) { - byte blkSize = 16; - if (cipherAlg == KMType.DES) { - blkSize = 8; - } - if (len > 0) { - // verify if padding is corrupted. - byte paddingByte = outputDataBuf[(short) (outputDataStart + len - 1)]; - // padding byte always should be <= block size - if ((short) paddingByte > blkSize || (short) paddingByte <= 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - for (short j = 1; j <= paddingByte; ++j) { - if (outputDataBuf[(short) (outputDataStart + len - j)] != paddingByte) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - len = (short) (len - (short) paddingByte);// remove the padding bytes - } - } else if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { - if (mode == KMType.ENCRYPT) { - len += ((AEADCipher) cipher).retrieveTag(outputDataBuf, - (short) (outputDataStart + len), macLength); - } else { - boolean verified = ((AEADCipher) cipher).verifyTag(inputDataBuf, - (short) (inputDataStart + inputDataLen), macLength, macLength); - if (!verified) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - } - } - } finally { - KMAndroidSEProvider.getInstance().clean(); - reset(); - } - return len; - } - - @Override - public short sign(byte[] inputDataBuf, short inputDataStart, - short inputDataLength, byte[] signBuf, short signStart) { - short len = 0; - try { - len = ((Signature) operationInst[0]).sign(inputDataBuf, inputDataStart, inputDataLength, - signBuf, signStart); - } finally { - reset(); - } - return len; - } - - @Override - public boolean verify(byte[] inputDataBuf, short inputDataStart, - short inputDataLength, byte[] signBuf, short signStart, short signLength) { - boolean ret = false; - try { - ret = ((Signature) operationInst[0]).verify(inputDataBuf, inputDataStart, inputDataLength, - signBuf, signStart, signLength); - } finally { - reset(); - } - return ret; - } - - @Override - public void abort() { - // Few simulators does not reset the Hmac signer instance on init so as - // a workaround to reset the hmac signer instance in case of abort/failure of the operation - // the corresponding sign / verify function is called. - if (operationInst[0] != null) { - if ((parameters[OPER_MODE_OFFSET] == KMType.SIGN || parameters[OPER_MODE_OFFSET] == KMType.VERIFY) && - (((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { - Signature signer = (Signature) operationInst[0]; - try { - if (parameters[OPER_MODE_OFFSET] == KMType.SIGN) { - signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0); - } else { - signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0); - } - } catch(Exception e) { - // Ignore. - } - } - } - reset(); - } - - @Override - public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { - ((AEADCipher) operationInst[0]).updateAAD(dataBuf, dataStart, dataLength); - } - - @Override - public short getAESGCMOutputSize(short dataSize, short macLength) { - if (parameters[OPER_MODE_OFFSET] == KMType.ENCRYPT) { - return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize + macLength); - } else { - return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength); - } - } -} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMAESKey.java similarity index 78% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMAESKey.java index cec6388e..fd85435b 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAESKey.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMAESKey.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; -import com.android.javacard.keymaster.KMMasterKey; +import com.android.javacard.kmdevice.KMMasterKey; import javacard.security.AESKey; +import javacard.security.HMACKey; public class KMAESKey implements KMMasterKey { @@ -33,22 +34,23 @@ public void setKey(byte[] keyData, short kOff) { aesKey.setKey(keyData, kOff); } - public AESKey getKey() { - return aesKey; + public byte getKey(byte[] keyData, short kOff) { + return aesKey.getKey(keyData, kOff); } public short getKeySizeBits() { return aesKey.getSize(); } - public static void onSave(Element element, KMAESKey kmKey) { - element.write(kmKey.aesKey); + public static KMAESKey onRestore(AESKey aesKey) { + if (aesKey == null) { + return null; + } + return new KMAESKey(aesKey); } - public static KMAESKey onRestore(Element element) { - AESKey aesKey = (AESKey) element.readObject(); - KMAESKey kmKey = new KMAESKey(aesKey); - return kmKey; + public static void onSave(Element element, KMAESKey kmKey) { + element.write(kmKey.aesKey); } public static short getBackupPrimitiveByteCount() { diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMAndroidSEProvider.java similarity index 52% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index 41f468ed..9c554dde 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -13,11 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; import org.globalplatform.upgrade.UpgradeManager; +import com.android.javacard.kmdevice.KMAttestationKey; +import com.android.javacard.kmdevice.KMComputedHmacKey; +import com.android.javacard.kmdevice.KMDataStoreConstants; +import com.android.javacard.kmdevice.KMDeviceUniqueKey; +import com.android.javacard.kmdevice.KMException; +import com.android.javacard.kmdevice.KMMasterKey; +import com.android.javacard.kmdevice.KMOperation; +import com.android.javacard.kmdevice.KMPreSharedKey; +import com.android.javacard.kmdevice.KMSEProvider; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.AESKey; @@ -35,78 +47,22 @@ import javacard.security.Signature; import javacardx.crypto.AEADCipher; import javacardx.crypto.Cipher; - -import com.android.javacard.keymaster.KMAESKey; -import com.android.javacard.keymaster.KMAttestationKey; -import com.android.javacard.keymaster.KMECPrivateKey; -import com.android.javacard.keymaster.KMError; -import com.android.javacard.keymaster.KMException; -import com.android.javacard.keymaster.KMHmacKey; -import com.android.javacard.keymaster.KMMasterKey; -import com.android.javacard.keymaster.KMPreSharedKey; +import javacard.security.KeyAgreement; public class KMAndroidSEProvider implements KMSEProvider { // static final variables // -------------------------------------------------------------- // P-256 Curve Parameters - static final byte[] secp256r1_P = { - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF}; - - static final byte[] secp256r1_A = { - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFC}; - - static final byte[] secp256r1_B = { - (byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, (byte) 0xAA, - (byte) 0x3A, (byte) 0x93, (byte) 0xE7, (byte) 0xB3, (byte) 0xEB, - (byte) 0xBD, (byte) 0x55, (byte) 0x76, (byte) 0x98, (byte) 0x86, - (byte) 0xBC, (byte) 0x65, (byte) 0x1D, (byte) 0x06, (byte) 0xB0, - (byte) 0xCC, (byte) 0x53, (byte) 0xB0, (byte) 0xF6, (byte) 0x3B, - (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, (byte) 0x27, (byte) 0xD2, - (byte) 0x60, (byte) 0x4B}; - - static final byte[] secp256r1_S = { - (byte) 0xC4, (byte) 0x9D, (byte) 0x36, (byte) 0x08, (byte) 0x86, - (byte) 0xE7, (byte) 0x04, (byte) 0x93, (byte) 0x6A, (byte) 0x66, - (byte) 0x78, (byte) 0xE1, (byte) 0x13, (byte) 0x9D, (byte) 0x26, - (byte) 0xB7, (byte) 0x81, (byte) 0x9F, (byte) 0x7E, (byte) 0x90}; + static byte[] secp256r1_P; + static byte[] secp256r1_A; + + static byte[] secp256r1_B; + static byte[] secp256r1_S; // Uncompressed form - static final byte[] secp256r1_UCG = { - (byte) 0x04, (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, - (byte) 0xE1, (byte) 0x2C, (byte) 0x42, (byte) 0x47, (byte) 0xF8, - (byte) 0xBC, (byte) 0xE6, (byte) 0xE5, (byte) 0x63, (byte) 0xA4, - (byte) 0x40, (byte) 0xF2, (byte) 0x77, (byte) 0x03, (byte) 0x7D, - (byte) 0x81, (byte) 0x2D, (byte) 0xEB, (byte) 0x33, (byte) 0xA0, - (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, (byte) 0xD8, - (byte) 0x98, (byte) 0xC2, (byte) 0x96, (byte) 0x4F, (byte) 0xE3, - (byte) 0x42, (byte) 0xE2, (byte) 0xFE, (byte) 0x1A, (byte) 0x7F, - (byte) 0x9B, (byte) 0x8E, (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, - (byte) 0x7C, (byte) 0x0F, (byte) 0x9E, (byte) 0x16, (byte) 0x2B, - (byte) 0xCE, (byte) 0x33, (byte) 0x57, (byte) 0x6B, (byte) 0x31, - (byte) 0x5E, (byte) 0xCE, (byte) 0xCB, (byte) 0xB6, (byte) 0x40, - (byte) 0x68, (byte) 0x37, (byte) 0xBF, (byte) 0x51, (byte) 0xF5}; - - static final byte[] secp256r1_N = { - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, - (byte) 0xFF, (byte) 0xBC, (byte) 0xE6, (byte) 0xFA, (byte) 0xAD, - (byte) 0xA7, (byte) 0x17, (byte) 0x9E, (byte) 0x84, (byte) 0xF3, - (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, (byte) 0xFC, (byte) 0x63, - (byte) 0x25, (byte) 0x51}; + static byte[] secp256r1_UCG; + static byte[] secp256r1_N; static final short secp256r1_H = 1; // -------------------------------------------------------------- public static final short AES_GCM_TAG_LENGTH = 16; @@ -115,44 +71,12 @@ public class KMAndroidSEProvider implements KMSEProvider { public static final byte KEYSIZE_256_OFFSET = 0x01; public static final short TMP_ARRAY_SIZE = 300; private static final short RSA_KEY_SIZE = 256; - private static final short MAX_OPERATIONS = 4; - private static final short HMAC_MAX_OPERATIONS = 8; - private static final short COMPUTED_HMAC_KEY_SIZE = 32; - public static final short INVALID_DATA_VERSION = 0x7FFF; - - private static final short CERT_CHAIN_OFFSET = 0; - private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; - private static final short CERT_EXPIRY_OFFSET = - (short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE); - - private static final byte[] CIPHER_ALGS = { - Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, - Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, - Cipher.ALG_DES_CBC_NOPAD, - Cipher.ALG_DES_ECB_NOPAD, - Cipher.ALG_AES_CTR, - Cipher.ALG_RSA_PKCS1, - KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1, - Cipher.ALG_RSA_NOPAD, - AEADCipher.ALG_AES_GCM}; - - private static final byte[] SIG_ALGS = { - Signature.ALG_RSA_SHA_256_PKCS1, - Signature.ALG_RSA_SHA_256_PKCS1_PSS, - Signature.ALG_ECDSA_SHA_256, - Signature.ALG_HMAC_SHA_256, - KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD, - KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST, - KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST}; - - // [L] 256 bits - hardcoded 32 bits as per - // reference impl in keymaster. - private static final byte[] CMAC_KDF_CONSTANT_L = { - 0, 0, 1, 0 - }; - private static final byte[] CMAC_KDF_CONSTANT_ZERO = { - 0 - }; + public static final short SHARED_SECRET_KEY_SIZE = 32; + public static final byte POWER_RESET_FALSE = (byte) 0xAA; + public static final byte POWER_RESET_TRUE = (byte) 0x00; + + private KeyAgreement keyAgreement; + // AESKey private AESKey aesKeys[]; // DES3Key @@ -166,30 +90,18 @@ public class KMAndroidSEProvider implements KMSEProvider { // Temporary array. public byte[] tmpArray; // This is used for internal encryption/decryption operations. - private static AEADCipher aesGcmCipher; - // Cipher pool - private Object[] cipherPool; - // Signature pool - private Object[] sigPool; - // KMOperationImpl pool - private Object[] operationPool; - // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. - private Object[] hmacSignOperationPool; - + private AEADCipher aesGcmCipher; + private Signature kdf; + public byte[] resetFlag; private Signature hmacSignature; //For ImportwrappedKey operations. private KMRsaOAEPEncoding rsaOaepDecipher; + private KMPoolManager poolMgr; // Entropy private RandomData rng; - //For storing root certificate and intermediate certificates. - private byte[] provisionData; - private KMAESKey masterKey; - private KMECPrivateKey attestationKey; - private KMHmacKey preSharedKey; - private KMHmacKey computedHmacKey; private static KMAndroidSEProvider androidSEProvider = null; @@ -198,34 +110,22 @@ 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( - KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_128, false); aesKeys[KEYSIZE_256_OFFSET] = (AESKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - triDesKey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, + KeyBuilder.TYPE_AES_TRANSIENT_RESET, KeyBuilder.LENGTH_AES_256, false); + triDesKey = (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_3KEY, false); - hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) 512, + hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC_TRANSIENT_RESET, (short) 512, false); rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + keyAgreement = KeyAgreement.getInstance(KeyAgreement.ALG_EC_SVDP_DH_PLAIN, false); initECKey(ecKeyPair); - - // Re-usable cipher and signature instances - cipherPool = new Object[(short) (CIPHER_ALGS.length * 4)]; - // Extra 4 algorithms are used to support TRUSTED_CONFIRMATION_REQUIRED feature. - sigPool = new Object[(short) ((SIG_ALGS.length * 4) + 4)]; - operationPool = new Object[4]; - - //maintain seperate operation pool for hmac signer used to support trusted confirmation - hmacSignOperationPool = new Object[4]; - // Creates an instance of each cipher algorithm once. - initializeCipherPool(); - // Creates an instance of each signature algorithm once. - initializeSigPool(); - initializeOperationPool(); - initializeHmacSignOperationPool(); + poolMgr = KMPoolManager.getInstance(); //RsaOAEP Decipher rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1); @@ -238,23 +138,78 @@ public KMAndroidSEProvider() { // Random number generator initialisation. rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); - //Allocate buffer for certificate chain. - if (!isUpgrading()) { - // First 2 bytes is reserved for length for all the 3 buffers. - short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + - KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); - provisionData = new byte[totalLen]; - - // Initialize attestationKey and preShared key with zeros. - Util.arrayFillNonAtomic(tmpArray, (short) 0, TMP_ARRAY_SIZE, (byte) 0); - // Create attestation key of P-256 curve. - createAttestationKey(tmpArray, (short)0, (short) 32); - // Pre-shared secret key length is 32 bytes. - createPresharedKey(tmpArray, (short)0, (short) 32); - // Initialize the Computed Hmac Key object. - createComputedHmacKey(tmpArray, (short)0, (short) 32); - } androidSEProvider = this; + resetFlag = JCSystem.makeTransientByteArray((short) 2, + JCSystem.CLEAR_ON_DESELECT); + resetFlag[0] = (byte) POWER_RESET_FALSE; + resetFlag[1] = (byte) POWER_RESET_FALSE; + } + + public static void initStatics() { + secp256r1_P = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, + (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF}; + + secp256r1_A = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, + (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, (byte) 0xFC}; + + secp256r1_B = new byte[]{(byte) 0x5A, (byte) 0xC6, (byte) 0x35, (byte) 0xD8, (byte) 0xAA, + (byte) 0x3A, + (byte) 0x93, (byte) 0xE7, (byte) 0xB3, (byte) 0xEB, (byte) 0xBD, (byte) 0x55, (byte) 0x76, + (byte) 0x98, + (byte) 0x86, (byte) 0xBC, (byte) 0x65, (byte) 0x1D, (byte) 0x06, (byte) 0xB0, (byte) 0xCC, + (byte) 0x53, + (byte) 0xB0, (byte) 0xF6, (byte) 0x3B, (byte) 0xCE, (byte) 0x3C, (byte) 0x3E, (byte) 0x27, + (byte) 0xD2, + (byte) 0x60, (byte) 0x4B}; + + secp256r1_S = new byte[]{(byte) 0xC4, (byte) 0x9D, (byte) 0x36, (byte) 0x08, (byte) 0x86, + (byte) 0xE7, + (byte) 0x04, (byte) 0x93, (byte) 0x6A, (byte) 0x66, (byte) 0x78, (byte) 0xE1, (byte) 0x13, + (byte) 0x9D, + (byte) 0x26, (byte) 0xB7, (byte) 0x81, (byte) 0x9F, (byte) 0x7E, (byte) 0x90}; + + // Uncompressed form + secp256r1_UCG = new byte[]{(byte) 0x04, (byte) 0x6B, (byte) 0x17, (byte) 0xD1, (byte) 0xF2, + (byte) 0xE1, + (byte) 0x2C, (byte) 0x42, (byte) 0x47, (byte) 0xF8, (byte) 0xBC, (byte) 0xE6, (byte) 0xE5, + (byte) 0x63, + (byte) 0xA4, (byte) 0x40, (byte) 0xF2, (byte) 0x77, (byte) 0x03, (byte) 0x7D, (byte) 0x81, + (byte) 0x2D, + (byte) 0xEB, (byte) 0x33, (byte) 0xA0, (byte) 0xF4, (byte) 0xA1, (byte) 0x39, (byte) 0x45, + (byte) 0xD8, + (byte) 0x98, (byte) 0xC2, (byte) 0x96, (byte) 0x4F, (byte) 0xE3, (byte) 0x42, (byte) 0xE2, + (byte) 0xFE, + (byte) 0x1A, (byte) 0x7F, (byte) 0x9B, (byte) 0x8E, (byte) 0xE7, (byte) 0xEB, (byte) 0x4A, + (byte) 0x7C, + (byte) 0x0F, (byte) 0x9E, (byte) 0x16, (byte) 0x2B, (byte) 0xCE, (byte) 0x33, (byte) 0x57, + (byte) 0x6B, + (byte) 0x31, (byte) 0x5E, (byte) 0xCE, (byte) 0xCB, (byte) 0xB6, (byte) 0x40, (byte) 0x68, + (byte) 0x37, + (byte) 0xBF, (byte) 0x51, (byte) 0xF5}; + + secp256r1_N = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x00, + (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xBC, (byte) 0xE6, (byte) 0xFA, (byte) 0xAD, (byte) 0xA7, + (byte) 0x17, + (byte) 0x9E, (byte) 0x84, (byte) 0xF3, (byte) 0xB9, (byte) 0xCA, (byte) 0xC2, (byte) 0xFC, + (byte) 0x63, + (byte) 0x25, (byte) 0x51}; } public void clean() { @@ -279,172 +234,6 @@ private void initECKey(KeyPair ecKeyPair) { privKey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length); } - private boolean isCipherAlgorithm(byte alg) { - short index = 0; - while (index < CIPHER_ALGS.length) { - if (CIPHER_ALGS[index++] == alg) { - return true; - } - } - return false; - } - - private boolean isSignerAlgorithm(byte alg) { - short index = 0; - while (index < SIG_ALGS.length) { - if (SIG_ALGS[index++] == alg) { - return true; - } - } - return false; - } - - private void initializeOperationPool() { - short index = 0; - while (index < 4) { - operationPool[index] = new KMOperationImpl(); - index++; - } - } - - private void initializeHmacSignOperationPool() { - short index = 0; - while (index < 4) { - hmacSignOperationPool[index] = new KMOperationImpl(); - index++; - } - } - - // Create a signature instance of each algorithm once. - private void initializeSigPool() { - short index = 0; - while (index < SIG_ALGS.length) { - sigPool[index] = getSignatureInstance(SIG_ALGS[index]); - index++; - } - } - - private Signature getSignatureInstance(byte alg) { - if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg - || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) { - return new KMRsa2048NoDigestSignature(alg); - } else if (KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST == alg) { - return new KMEcdsa256NoDigestSignature(alg); - } else { - return Signature.getInstance(alg, false); - } - } - - private Cipher getCipherInstance(byte alg) { - if (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 == alg) { - return new KMRsaOAEPEncoding(alg); - } else { - return Cipher.getInstance(alg, false); - } - } - - // Create a cipher instance of each algorithm once. - private void initializeCipherPool() { - short index = 0; - while (index < CIPHER_ALGS.length) { - cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]); - index++; - } - } - - private KMOperationImpl getOperationInstanceFromPool() { - short index = 0; - KMOperationImpl impl; - while (index < operationPool.length) { - impl = (KMOperationImpl) operationPool[index]; - // Mode is always set. so compare using mode value. - if (impl.getMode() == KMType.INVALID_VALUE) { - return impl; - } - index++; - } - return null; - } - - private KMOperationImpl getHmacSignOperationInstanceFromPool() { - short index = 0; - KMOperationImpl impl; - while (index < hmacSignOperationPool.length) { - impl = (KMOperationImpl) hmacSignOperationPool[index]; - // Mode is always set. so compare using mode value. - if (impl.getMode() == KMType.INVALID_VALUE) { - return impl; - } - index++; - } - return null; - } - - private Signature getSignatureInstanceFromPool(byte alg) { - return (Signature) getInstanceFromPool(sigPool, alg); - } - - private Cipher getCipherInstanceFromPool(byte alg) { - return (Cipher) getInstanceFromPool(cipherPool, alg); - } - - private boolean isResourceBusy(Object obj) { - short index = 0; - while (index < MAX_OPERATIONS) { - if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj) - || ((KMOperationImpl) hmacSignOperationPool[index]).isResourceMatches(obj)) { - return true; - } - index++; - } - return false; - } - - // This pool implementation can create a maximum of total 4 instances per - // algorithm. This function returns the unreserved Cipher/Signature instance - // of type algorithm from pool. If there is no unreserved cipher/signature - // instance of algorithm type in the pool and Cipher/Signature algorithm - // instance count is less than 4 then it creates and returns a new - // Cipher/Signature instance of algorithm type. If there is no unreserved - // cipher/signature and maximum instance count reaches four it throws - // exception. - private Object getInstanceFromPool(Object[] pool, byte alg) { - short index = 0; - short instanceCount = 0; - boolean isCipher = isCipherAlgorithm(alg); - boolean isSigner = isSignerAlgorithm(alg); - short maxOperations = MAX_OPERATIONS; - if (Signature.ALG_HMAC_SHA_256 == alg) { - maxOperations = HMAC_MAX_OPERATIONS; - } - while (index < (short) pool.length) { - if (instanceCount >= maxOperations) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - break; - } - if (null == pool[index]) { - // No instance of cipher/signature with this algorithm is found - if (isCipher) { // Cipher - pool[index] = getCipherInstance(alg); - } else if (isSigner) { // Signature - pool[index] = getSignatureInstance(alg); - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - return pool[index]; - } - if ((isCipher && (alg == ((Cipher) pool[index]).getAlgorithm())) - || ((isSigner && (alg == ((Signature) pool[index]).getAlgorithm())))) { - if (!isResourceBusy(pool[index])) { - return pool[index]; - } - instanceCount++; - } - index++; - } - return null; - } - public AESKey createAESKey(short keysize) { try { newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); @@ -725,7 +514,15 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta // 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. + final byte[] L = { + 0, 0, 1, 0 + }; + // byte + final byte[] zero = { + 0 + }; // [i] counter - 32 bits short iBufLen = 4; short keyOutLen = n * 16; @@ -748,10 +545,10 @@ public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelSta // 4 bytes of iBuf with counter in it kdf.update(tmpArray, (short) 0, (short) iBufLen); kdf.update(label, labelStart, (short) labelLen); // label - kdf.update(CMAC_KDF_CONSTANT_ZERO, (short) 0, (short) CMAC_KDF_CONSTANT_ZERO.length); // 1 byte of 0x00 + kdf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00 kdf.update(context, contextStart, contextLength); // context // 4 bytes of L - signature of 16 bytes - pos = kdf.sign(CMAC_KDF_CONSTANT_L, (short) 0, (short) CMAC_KDF_CONSTANT_L.length, tmpArray, + pos = kdf.sign(L, (short) 0, (short) 4, tmpArray, (short) (iBufLen + pos)); i++; } @@ -767,15 +564,6 @@ public short hmacSign(HMACKey key, byte[] data, short dataStart, return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); } - @Override - public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, - short dataLength, byte[] mac, short macStart, short macLength) { - KMHmacKey hmacKey = (KMHmacKey) key; - hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); - return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, - macLength); - } - @Override public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, short dataStart, short dataLength, byte[] mac, short macStart) { @@ -787,17 +575,26 @@ public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, short dataLength, byte[] signature, short signatureStart) { try { - AESKey aesKey = ((KMAESKey) masterkey).getKey(); - aesKey.getKey(tmpArray, (short) 0); - HMACKey key = createHMACKey(tmpArray, (short) 0, - (short) (aesKey.getSize() / 8)); - return hmacSign(key, data, dataStart, dataLength, signature, - signatureStart); + KMAESKey aesKey = (KMAESKey) masterkey; + short keyLen = (short) (aesKey.getKeySizeBits() / 8); + byte[] keyData = new byte[keyLen]; + aesKey.getKey(keyData, (short) 0); + return hmacSign(keyData, (short) 0, keyLen, data, dataStart, dataLength, + signature, signatureStart); } finally { clean(); } } + @Override + public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart, short macLength) { + KMHmacKey hmacKey = (KMHmacKey) key; + hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); + return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, + macLength); + } + @Override public short rsaDecipherOAEP256(byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength, @@ -811,38 +608,6 @@ public short rsaDecipherOAEP256(byte[] secret, short secretStart, outputDataBuf, (short) outputDataStart); } - public short ecSign256(KMAttestationKey attestationKey, - byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart) { - Signature.OneShot signer = null; - try { - - signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, - Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); - signer.init(((KMECPrivateKey) attestationKey).getPrivateKey(), Signature.MODE_SIGN); - return signer.sign(inputDataBuf, inputDataStart, inputDataLength, - outputDataBuf, outputDataStart); - } finally { - if (signer != null) { - signer.close(); - } - } - } - - private byte mapPurpose(short purpose) { - switch (purpose) { - case KMType.ENCRYPT: - return Cipher.MODE_ENCRYPT; - case KMType.DECRYPT: - return Cipher.MODE_DECRYPT; - case KMType.SIGN: - return Signature.MODE_SIGN; - case KMType.VERIFY: - return Signature.MODE_VERIFY; - } - return -1; - } - private byte mapSignature256Alg(byte alg, byte padding, byte digest) { switch (alg) { case KMType.RSA: @@ -901,8 +666,10 @@ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { case KMType.RSA_PKCS1_1_5_ENCRYPT: return Cipher.ALG_RSA_PKCS1; case KMType.RSA_OAEP: { - if (digest == KMType.SHA2_256) { + if (digest == KMType.SHA1) { /* MGF Digest is SHA1 */ return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1; + } else if (digest == KMType.SHA2_256) { /* MGF Digest is SHA256 */ + return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256; } else { KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); } @@ -913,11 +680,10 @@ private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { return -1; } - public Cipher createSymmetricCipher(short alg, short purpose, + public KMOperation createSymmetricCipher(short alg, short purpose, short macLength, short blockMode, short padding, byte[] secret, short secretStart, short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { Key key = null; - Cipher symmCipher = null; switch (secretLength) { case 32: key = aesKeys[KEYSIZE_256_OFFSET]; @@ -936,46 +702,37 @@ public Cipher createSymmetricCipher(short alg, short purpose, break; } short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0); - symmCipher = getCipherInstanceFromPool((byte) cipherAlg); - switch (cipherAlg) { - case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: - case Cipher.ALG_AES_CTR: - symmCipher.init(key, mapPurpose(purpose), ivBuffer, ivStart, ivLength); - break; - case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: - case Cipher.ALG_DES_ECB_NOPAD: - symmCipher.init(key, mapPurpose(purpose)); - break; - case Cipher.ALG_DES_CBC_NOPAD: - // Consume only 8 bytes of iv. the random number for iv is of 16 bytes. - // While sending back the iv, send only 8 bytes. - symmCipher.init(key, mapPurpose(purpose), ivBuffer, ivStart, (short) 8); - break; - case AEADCipher.ALG_AES_GCM: - ((AEADCipher) symmCipher).init(key, mapPurpose(purpose), ivBuffer, - ivStart, ivLength); - break; - default:// This should never happen - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - return symmCipher; + KMOperation operation = + poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength, false); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength); + return operation; } - private Signature createHmacSignerVerifier(short purpose, short digest, + public KMOperation createHmacSignerVerifier(short purpose, short digest, byte[] secret, short secretStart, short secretLength) { + if (digest != KMType.SHA2_256) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + KMOperation operation = + poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, + KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); HMACKey key = createHMACKey(secret, secretStart, secretLength); - return createHmacSignerVerifier(purpose, digest, key); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; } - - private Signature createHmacSignerVerifier(short purpose, short digest, HMACKey key) { - byte alg = Signature.ALG_HMAC_SHA_256; + + private KMOperation createHmacSignerVerifier(short purpose, short digest, HMACKey key, + boolean isTrustedConf) { if (digest != KMType.SHA2_256) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - Signature hmacSignerVerifier = getSignatureInstanceFromPool(alg); - hmacSignerVerifier.init(key, (byte) mapPurpose(purpose)); - return hmacSignerVerifier; + KMOperation operation = + poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, + KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, + isTrustedConf); + + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; } @Override @@ -983,28 +740,17 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, short keyLength, byte[] ivBuf, short ivStart, short ivLength, short macLength) { - KMOperationImpl opr = null; + KMOperation opr = null; switch (alg) { case KMType.AES: case KMType.DES: - Cipher cipher = createSymmetricCipher(alg, purpose, blockMode, padding, - keyBuf, keyStart, keyLength, ivBuf, ivStart, ivLength); - opr = getOperationInstanceFromPool(); // Convert macLength to bytes macLength = (short) (macLength / 8); - opr.setCipher(cipher); - opr.setCipherAlgorithm(alg); - opr.setBlockMode(blockMode); - opr.setPaddingAlgorithm(padding); - opr.setMode(purpose); - opr.setMacLength(macLength); + opr = createSymmetricCipher(alg, purpose, macLength, blockMode, padding, keyBuf, keyStart, + keyLength, ivBuf, ivStart, ivLength); break; case KMType.HMAC: - Signature signerVerifier = createHmacSignerVerifier(purpose, digest, - keyBuf, keyStart, keyLength); - opr = getOperationInstanceFromPool(); - opr.setMode(purpose); - opr.setSignature(signerVerifier); + opr = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, keyLength); break; default: CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); @@ -1015,82 +761,74 @@ public KMOperation initSymmetricOperation(byte purpose, byte alg, @Override public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { - KMOperationImpl opr = null; KMHmacKey key = (KMHmacKey) computedHmacKey; - Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); - opr = getHmacSignOperationInstanceFromPool(); - opr.setMode(KMType.VERIFY); - opr.setSignature(signerVerifier); - return opr; + return createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey(), true); } - public Signature createRsaSigner(short digest, short padding, byte[] secret, + public KMOperation createRsaSigner(short digest, short padding, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); - byte opMode; - if (padding == KMType.PADDING_NONE - || (padding == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) { - opMode = Cipher.MODE_DECRYPT; - } else { - opMode = Signature.MODE_SIGN; - } - Signature rsaSigner = getSignatureInstanceFromPool(alg); + KMOperation operation = poolMgr.getOperationImpl(KMType.SIGN, alg, KMType.RSA, padding, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); - rsaSigner.init(key, opMode); - return rsaSigner; + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; } - public Cipher createRsaDecipher(short padding, short digest, byte[] secret, + public KMOperation createRsaDecipher(short padding, short mgfDigest, byte[] secret, short secretStart, short secretLength, byte[] modBuffer, short modOff, short modLength) { - byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) digest); - Cipher rsaCipher = getCipherInstanceFromPool(cipherAlg); + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest); + KMOperation operation = poolMgr.getOperationImpl(KMType.DECRYPT, cipherAlg, KMType.RSA, padding, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); key.setExponent(secret, secretStart, secretLength); key.setModulus(modBuffer, modOff, modLength); - rsaCipher.init(key, Cipher.MODE_DECRYPT); - return rsaCipher; + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; } - public Signature createEcSigner(short digest, byte[] secret, + public KMOperation createEcSigner(short digest, byte[] secret, short secretStart, short secretLength) { byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest); - Signature ecSigner = null; ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); key.setS(secret, secretStart, secretLength); - ecSigner = getSignatureInstanceFromPool(alg); - ecSigner.init(key, Signature.MODE_SIGN); - return ecSigner; + KMOperation operation = poolMgr + .getOperationImpl(KMType.SIGN, alg, KMType.EC, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + public KMOperation createKeyAgreement(byte[] secret, short secretStart, + short secretLength) { + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + KMOperation operation = poolMgr + .getOperationImpl(KMType.AGREE_KEY, KeyAgreement.ALG_EC_SVDP_DH_PLAIN, + KMType.EC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, false); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; } @Override public KMOperation initAsymmetricOperation(byte purpose, byte alg, - byte padding, byte digest, byte[] privKeyBuf, short privKeyStart, + byte padding, byte digest, byte mgfDigest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { - KMOperationImpl opr = null; + KMOperation opr = null; if (alg == KMType.RSA) { switch (purpose) { case KMType.SIGN: - Signature signer = createRsaSigner(digest, padding, privKeyBuf, + opr = createRsaSigner(digest, padding, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); - opr = getOperationInstanceFromPool(); - opr.setSignature(signer); - opr.setCipherAlgorithm(alg); - opr.setPaddingAlgorithm(padding); - opr.setMode(purpose); break; case KMType.DECRYPT: - Cipher decipher = createRsaDecipher(padding, digest, privKeyBuf, + opr = createRsaDecipher(padding, mgfDigest, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); - opr = getOperationInstanceFromPool(); - opr.setCipher(decipher); - opr.setCipherAlgorithm(alg); - opr.setPaddingAlgorithm(padding); - opr.setMode(purpose); break; default: KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); @@ -1099,11 +837,11 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, } else if (alg == KMType.EC) { switch (purpose) { case KMType.SIGN: - Signature signer = createEcSigner(digest, privKeyBuf, privKeyStart, - privKeyLength); - opr = getOperationInstanceFromPool(); - opr.setMode(purpose); - opr.setSignature(signer); + opr = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength); + break; + + case KMType.AGREE_KEY: + opr = createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength); break; default: KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); @@ -1116,16 +854,6 @@ public KMOperation initAsymmetricOperation(byte purpose, byte alg, } - @Override - public KMAttestationCert getAttestationCert(boolean rsaCert) { - return KMAttestationCertImpl.instance(rsaCert); - } - - @Override - public KMPKCS8Decoder getPKCS8DecoderInstance() { - return KMPKCS8DecoderImpl.instance(); - } - @Override public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, short labelStart, short labelLen, byte[] context, short contextStart, @@ -1134,248 +862,273 @@ public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, contextStart, contextLength); return key.getKey(keyBuf, keyStart); } - - private short getProvisionDataBufferOffset(byte dataType) { - switch(dataType) { - case CERTIFICATE_CHAIN: - return CERT_CHAIN_OFFSET; - case CERTIFICATE_ISSUER: - return CERT_ISSUER_OFFSET; - case CERTIFICATE_EXPIRY: - return CERT_EXPIRY_OFFSET; - default: - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - return 0; - } - - private void persistProvisionData(byte[] buf, short off, short len, short maxSize, short copyToOff) { - if (len > maxSize) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - JCSystem.beginTransaction(); - Util.arrayCopyNonAtomic(buf, off, provisionData, Util.setShort(provisionData, copyToOff, len), len); - JCSystem.commitTransaction(); - } - - private void persistCertificateChain(byte[] certChain, short certChainOff, short certChainLen) { - persistProvisionData(certChain, certChainOff, certChainLen, - KMConfigurations.CERT_CHAIN_MAX_SIZE, CERT_CHAIN_OFFSET); - } - - private void persistCertficateIssuer(byte[] certIssuer, short certIssuerOff, short certIssuerLen) { - persistProvisionData(certIssuer, certIssuerOff, certIssuerLen, - KMConfigurations.CERT_ISSUER_MAX_SIZE, CERT_ISSUER_OFFSET); - } - - private void persistCertificateExpiryTime(byte[] certExpiry, short certExpiryOff, short certExpiryLen) { - persistProvisionData(certExpiry, certExpiryOff, certExpiryLen, - KMConfigurations.CERT_EXPIRY_MAX_SIZE, CERT_EXPIRY_OFFSET); - } @Override - public void persistProvisionData(byte[] buffer, short certChainOff, short certChainLen, - short certIssuerOff, short certIssuerLen, short certExpiryOff ,short certExpiryLen) { - // All the buffers uses first two bytes for length. The certificate chain - // is stored as shown below. - // _____________________________________________________ - // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... - // |_________|________|_________|_______|________|_______ - // First two bytes holds the length of the total buffer. - // CBOR format: - // Next single byte holds the byte string header. - // Next 3 bytes holds the total length of the certificate chain. - // clear buffer. - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(provisionData, (short) 0, (short) provisionData.length, (byte) 0); - JCSystem.commitTransaction(); - // Persist data. - persistCertificateChain(buffer, certChainOff, certChainLen); - persistCertficateIssuer(buffer, certIssuerOff, certIssuerLen); - persistCertificateExpiryTime(buffer, certExpiryOff, certExpiryLen); + public boolean isUpgrading() { + return UpgradeManager.isUpgrading(); } @Override - public short readProvisionedData(byte dataType, byte[] buf, short offset) { - short provisionBufOffset = getProvisionDataBufferOffset(dataType); - short len = Util.getShort(provisionData, provisionBufOffset); - Util.arrayCopyNonAtomic(provisionData, (short) (2 + provisionBufOffset), buf, offset, len); - return len; + public KMMasterKey createMasterKey(KMMasterKey masterKey, byte[] keyData, short offset, + short length) { + if (masterKey == null) { + short keySizeBits = (short) (length * 8); + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, keySizeBits, false); + masterKey = new KMAESKey(key); + } + ((KMAESKey) masterKey).setKey(keyData, offset); + return (KMMasterKey) masterKey; } @Override - public short getProvisionedDataLength(byte dataType) { - short provisionBufOffset = getProvisionDataBufferOffset(dataType); - return Util.getShort(provisionData, provisionBufOffset); + public KMPreSharedKey createPreSharedKey(KMPreSharedKey presharedKey, byte[] keyData, + short offset, short length) { + if (presharedKey == null) { + short lengthInBits = (short) (length * 8); + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, lengthInBits, false); + presharedKey = new KMHmacKey(key); + } + ((KMHmacKey) presharedKey).setKey(keyData, offset, length); + return (KMPreSharedKey) presharedKey; } @Override - public boolean isBootSignalEventSupported() { - return false; + public KMAttestationKey createAttestationKey(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); + initECKey(ecKeyPair); + attestationKey = new KMECPrivateKey(ecKeyPair); + } + ((KMECPrivateKey) attestationKey).setS(keyData, offset, length); + return (KMAttestationKey) attestationKey; } @Override - public boolean isDeviceRebooted() { - return false; + public KMComputedHmacKey createComputedHmacKey(KMComputedHmacKey computedHmacKey, byte[] keyData, + short offset, short length) { + if (computedHmacKey == null) { + HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), + false); + computedHmacKey = new KMHmacKey(key); + } + ((KMHmacKey) computedHmacKey).setKey(keyData, offset, length); + return (KMComputedHmacKey) computedHmacKey; } @Override - public void clearDeviceBooted(boolean resetBootFlag) { - // To be filled - } + public short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { - @Override - public void onSave(Element element) { - element.write(provisionData); - KMAESKey.onSave(element, masterKey); - KMECPrivateKey.onSave(element, attestationKey); - KMHmacKey.onSave(element, preSharedKey); - KMHmacKey.onSave(element, computedHmacKey); - } + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); - @Override - public void onRestore(Element element, short oldVersion, short currentVersion) { - provisionData = (byte[]) element.readObject(); - masterKey = KMAESKey.onRestore(element); - attestationKey = KMECPrivateKey.onRestore(element); - preSharedKey = KMHmacKey.onRestore(element); - if (oldVersion == 0) { - // Previous versions does not contain version information. - handleDataUpgradeToVersion2_0(); - } else { - computedHmacKey = KMHmacKey.onRestore(element); + Signature.OneShot signer = null; + try { + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } } } @Override - public short getBackupPrimitiveByteCount() { - short count = - (short) (KMAESKey.getBackupPrimitiveByteCount() + - KMECPrivateKey.getBackupPrimitiveByteCount() + - KMHmacKey.getBackupPrimitiveByteCount() + - KMHmacKey.getBackupPrimitiveByteCount()); - return count; - } + public short ecSign256(KMAttestationKey ecPrivKey, byte[] inputDataBuf, short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + Signature.OneShot signer = null; + try { - @Override - public short getBackupObjectCount() { - short count = - (short) (1 + /* provisionData buffer */ - KMAESKey.getBackupObjectCount() + - KMECPrivateKey.getBackupObjectCount() + - KMHmacKey.getBackupObjectCount() + - KMHmacKey.getBackupObjectCount()); - return count; + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(((KMECPrivateKey) ecPrivKey).getPrivateKey(), Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } } @Override - public boolean isUpgrading() { - return UpgradeManager.isUpgrading(); - } + public short rsaSign256Pkcs1(byte[] secret, short secretStart, short secretLength, byte[] modBuf, + short modStart, + short modLength, byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { - @Override - public KMMasterKey createMasterKey(short keySizeBits) { + Signature.OneShot signer = null; try { - if (masterKey == null) { - AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, - keySizeBits, false); - masterKey = new KMAESKey(key); - short keyLen = (short) (keySizeBits / 8); - getTrueRandomNumber(tmpArray, (short) 0, keyLen); - masterKey.setKey(tmpArray, (short) 0); - } - return (KMMasterKey) masterKey; + + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_RSA, Cipher.PAD_PKCS1); + + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + ; + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuf, modStart, modLength); + + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); } finally { - clean(); + if (signer != null) { + signer.close(); + } } + } @Override - public KMAttestationKey createAttestationKey(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); - initECKey(ecKeyPair); - attestationKey = new KMECPrivateKey(ecKeyPair); - } - attestationKey.setS(keyData, offset, length); - return (KMAttestationKey) attestationKey; + public boolean isAttestationKeyProvisioned() { + return false; } - + @Override - public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { - if (length != COMPUTED_HMAC_KEY_SIZE) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (computedHmacKey == null) { - HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), - false); - computedHmacKey = new KMHmacKey(key); - } - computedHmacKey.setKey(keyData, offset, length); - return (KMComputedHmacKey) computedHmacKey; - } + public short getAttestationKeyAlgorithm() { + return KMType.INVALID_VALUE; + } @Override - public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length) { - short lengthInBits = (short) (length * 8); - if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (preSharedKey == null) { - HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, lengthInBits, - false); - preSharedKey = new KMHmacKey(key); + public boolean isPowerReset(boolean isForStatusUpdate) { + boolean flag = false; + if (isForStatusUpdate == false) { + if (resetFlag[0] == POWER_RESET_TRUE) { + resetFlag[0] = POWER_RESET_FALSE; + flag = true; + if (poolMgr != null) { + poolMgr.powerReset(); + } + } + } else { + if (resetFlag[1] == POWER_RESET_TRUE) { + resetFlag[1] = POWER_RESET_FALSE; + flag = true; + } } - preSharedKey.setKey(keyData, offset, length); - return (KMPreSharedKey) preSharedKey; + return flag; } @Override - public KMMasterKey getMasterKey() { - return (KMMasterKey) masterKey; + public short hkdf(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, + short saltOff, short saltLen, byte[] info, short infoOff, short infoLen, + byte[] out, short outOff, short outLen) { + // HMAC_extract + hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, tmpArray, (short) 0); + //HMAC_expand + return hkdfExpand(tmpArray, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen); + } + + private short hkdfExtract(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, short saltOff, + short saltLen, + byte[] out, short off) { + // https://tools.ietf.org/html/rfc5869#section-2.2 + HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen); + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off); + } + + private short hkdfExpand(byte[] prk, short prkOff, short prkLen, byte[] info, short infoOff, + short infoLen, + byte[] out, short outOff, short outLen) { + // https://tools.ietf.org/html/rfc5869#section-2.3 + short digestLen = (short) 32; // SHA256 digest length. + // Calculate no of iterations N. + short n = (short) ((short) (outLen + digestLen - 1) / digestLen); + if (n > 255) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen); + Util.arrayFill(tmpArray, (short) 0, (short) 32, (byte) 0); + byte[] cnt = {(byte) 0}; + short bytesCopied = 0; + short len = 0; + for (short i = 0; i < n; i++) { + cnt[0]++; + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + if (i != 0) { + hmacSignature.update(tmpArray, (short) 0, (short) 32); + } + hmacSignature.update(info, infoOff, infoLen); + len = hmacSignature.sign(cnt, (short) 0, (short) 1, tmpArray, (short) 0); + if ((short) (bytesCopied + len) > outLen) { + len = (short) (outLen - bytesCopied); + } + Util.arrayCopyNonAtomic(tmpArray, (short) 0, out, (short) (outOff + bytesCopied), len); + bytesCopied += len; + } + return outLen; } @Override - public KMAttestationKey getAttestationKey() { - return (KMAttestationKey) attestationKey; + public short ecdhKeyAgreement(byte[] privKey, short privKeyOff, + short privKeyLen, byte[] publicKey, short publicKeyOff, + short publicKeyLen, byte[] secret, short secretOff) { + keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen)); + return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff); } @Override - public KMPreSharedKey getPresharedKey() { - return (KMPreSharedKey) preSharedKey; + public boolean ecVerify256(byte[] pubKey, short pubKeyOffset, short pubKeyLen, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signatureDataBuf, short signatureDataStart, + short signatureDataLen) { + Signature.OneShot signer = null; + try { + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic(); + key.setW(pubKey, pubKeyOffset, pubKeyLen); + signer.init(key, Signature.MODE_VERIFY); + return signer.verify(inputDataBuf, inputDataStart, inputDataLength, + signatureDataBuf, signatureDataStart, + (short) (signatureDataBuf[(short) (signatureDataStart + 1)] + 2)); + } finally { + if (signer != null) { + signer.close(); + } + } } @Override - public void releaseAllOperations() { - short index = 0; - while (index < operationPool.length) { - ((KMOperationImpl) operationPool[index]).abort(); - ((KMOperationImpl) hmacSignOperationPool[index]).abort(); - index++; + public short ecSign256(KMDeviceUniqueKey ecPrivKey, byte[] inputDataBuf, + short inputDataStart, short inputDataLength, byte[] outputDataBuf, + short outputDataStart) { + Signature.OneShot signer = null; + try { + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(((KMECDeviceUniqueKey) ecPrivKey).getPrivateKey(), Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } } } @Override - public KMComputedHmacKey getComputedHmacKey() { - return computedHmacKey; - } - - private void handleDataUpgradeToVersion2_0() { - short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + - KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); - byte[] oldBuffer = provisionData; - provisionData = new byte[totalLen]; - persistCertificateChain( - oldBuffer, - (short) 2, - Util.getShort(oldBuffer, (short) 0)); - - // Request object deletion - oldBuffer = null; - JCSystem.requestObjectDeletion(); - + public KMDeviceUniqueKey createDeviceUniqueKey(KMDeviceUniqueKey key, + byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen) { + if (key == null) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + initECKey(ecKeyPair); + key = new KMECDeviceUniqueKey(ecKeyPair); + } + ((KMECDeviceUniqueKey) key).setS(privKey, privKeyOff, privKeyLen); + ((KMECDeviceUniqueKey) key).setW(pubKey, pubKeyOff, pubKeyLen); + return (KMDeviceUniqueKey) key; } @Override @@ -1394,5 +1147,93 @@ public short messageDigest256(byte[] inBuff, short inOffset, } return len; } - + + @Override + public void onSave(Element element, byte interfaceType, Object object) { + element.write(interfaceType); + if (object == null) { + element.write(null); + return; + } + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + KMHmacKey.onSave(element, (KMHmacKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + KMAESKey.onSave(element, (KMAESKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + KMHmacKey.onSave(element, (KMHmacKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + KMECPrivateKey.onSave(element, (KMECPrivateKey) object); + break; + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + KMECDeviceUniqueKey.onSave(element, (KMECDeviceUniqueKey) object); + break; + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + @Override + public Object onResore(Element element) { + if (element == null) { + return null; + } + byte interfaceType = element.readByte(); + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.onRestore((AESKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.onRestore((HMACKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.onRestore((KeyPair) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + return KMECDeviceUniqueKey.onRestore((KeyPair) element.readObject()); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return null; + } + + @Override + public short getBackupPrimitiveByteCount(byte interfaceType) { + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + return KMHmacKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + return KMECDeviceUniqueKey.getBackupPrimitiveByteCount(); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return 0; + } + + @Override + public short getBackupObjectCount(byte interfaceType) { + switch (interfaceType) { + case KMDataStoreConstants.INTERFACE_TYPE_COMPUTED_HMAC_KEY: + return KMHmacKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY: + return KMAESKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: + return KMHmacKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY: + return KMECDeviceUniqueKey.getBackupObjectCount(); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return 0; + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMConfigurations.java similarity index 96% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMConfigurations.java index 6e5090a1..7fe11ad9 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMConfigurations.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; public class KMConfigurations { + // Machine types public static final byte LITTLE_ENDIAN = 0x00; public static final byte BIG_ENDIAN = 0x01; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java new file mode 100644 index 00000000..d82b80d3 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java @@ -0,0 +1,78 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.kmdevice.KMDeviceUniqueKey; + +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +import javacard.security.HMACKey; +import javacard.security.KeyPair; + +public class KMECDeviceUniqueKey implements KMDeviceUniqueKey { + + private KeyPair ecKeyPair; + + @Override + public short getPublicKey(byte[] buf, short offset) { + ECPublicKey publicKey = getPublicKey(); + return publicKey.getW(buf, offset); + } + + public KMECDeviceUniqueKey(KeyPair ecPair) { + ecKeyPair = ecPair; + } + + public void setS(byte[] buffer, short offset, short length) { + ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); + ecPriv.setS(buffer, offset, length); + } + + public void setW(byte[] buffer, short offset, short length) { + ECPublicKey ecPublicKey = (ECPublicKey) ecKeyPair.getPublic(); + ecPublicKey.setW(buffer, offset, length); + } + + public ECPrivateKey getPrivateKey() { + return (ECPrivateKey) ecKeyPair.getPrivate(); + } + + public ECPublicKey getPublicKey() { + return (ECPublicKey) ecKeyPair.getPublic(); + } + + public static void onSave(Element element, KMECDeviceUniqueKey kmKey) { + element.write(kmKey.ecKeyPair); + } + + public static KMECDeviceUniqueKey onRestore(KeyPair ecKey) { + if (ecKey == null) { + return null; + } + return new KMECDeviceUniqueKey(ecKey); + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } + +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMECPrivateKey.java similarity index 80% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMECPrivateKey.java index 3188ad19..fea64d35 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMECPrivateKey.java @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; -import com.android.javacard.keymaster.KMAESKey; -import com.android.javacard.keymaster.KMAttestationCert; -import com.android.javacard.keymaster.KMAttestationKey; +import com.android.javacard.kmdevice.KMAttestationKey; import javacard.security.AESKey; import javacard.security.ECPrivateKey; @@ -51,10 +49,11 @@ public static void onSave(Element element, KMECPrivateKey kmKey) { element.write(kmKey.ecKeyPair); } - public static KMECPrivateKey onRestore(Element element) { - KeyPair ecKey = (KeyPair) element.readObject(); - KMECPrivateKey kmKey = new KMECPrivateKey(ecKey); - return kmKey; + public static KMECPrivateKey onRestore(KeyPair ecKey) { + if (ecKey == null) { + return null; + } + return new KMECPrivateKey(ecKey); } public static short getBackupPrimitiveByteCount() { diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java similarity index 98% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java index f90b834f..4707f637 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.security.CryptoException; import javacard.framework.Util; diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMError.java similarity index 71% rename from Applet/src/com/android/javacard/keymaster/KMError.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMError.java index e2c74dcb..e9164389 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMError.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; /** * KMError includes all the error codes from android keymaster hal specifications. The values are @@ -59,19 +59,27 @@ public class KMError { public static final short INVALID_NONCE = 52; public static final short MISSING_MAC_LENGTH = 53; public static final short CALLER_NONCE_PROHIBITED = 55; - public static final short KEY_MAX_OPS_EXCEEDED = 56; public static final short INVALID_MAC_LENGTH = 57; public static final short MISSING_MIN_MAC_LENGTH = 58; public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59; public static final short UNSUPPORTED_EC_CURVE = 61; public static final short KEY_REQUIRES_UPGRADE = 62; + public static final short ATTESTATION_CHALLENGE_MISSING = 63; public static final short ATTESTATION_APPLICATION_ID_MISSING = 65; public static final short CANNOT_ATTEST_IDS = 66; - public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; - public static final short NO_USER_CONFIRMATION = 71; + public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + public static final short DEVICE_LOCKED = 72; public static final short EARLY_BOOT_ENDED = 73; + public static final short ATTESTATION_KEYS_NOT_PROVISIONED = 74; + public static final short INCOMPATIBLE_MGF_DIGEST = 78; + public static final short UNSUPPORTED_MGF_DIGEST = 79; + public static final short MISSING_NOT_BEFORE = 80; + public static final short MISSING_NOT_AFTER = 81; + public static final short MISSING_ISSUER_SUBJECT_NAME = 82; + public static final short INVALID_ISSUER_SUBJECT_NAME = 83; + public static final short UNIMPLEMENTED = 100; public static final short UNKNOWN_ERROR = 1000; @@ -83,6 +91,7 @@ public class KMError { public static final short CMD_NOT_ALLOWED = 10005; public static final short SW_WRONG_LENGTH = 10006; public static final short INVALID_DATA = 10007; + //Crypto errors public static final short CRYPTO_ILLEGAL_USE = 10008; public static final short CRYPTO_ILLEGAL_VALUE = 10009; @@ -92,4 +101,34 @@ public class KMError { //Generic Unknown error. public static final short GENERIC_UNKNOWN_ERROR = 10013; + // Remote key provisioning error codes. + public static final short STATUS_FAILED = 32000; + public static final short STATUS_INVALID_MAC = 32001; + public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; + public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; + public static final short STATUS_INVALID_EEK = 32004; + public static final short INVALID_STATE = 32005; + + public static short translate(short err) { + switch (err) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + case UNKNOWN_ERROR: + return UNKNOWN_ERROR; + case CRYPTO_NO_SUCH_ALGORITHM: + return UNSUPPORTED_ALGORITHM; + case UNSUPPORTED_INSTRUCTION: + case CMD_NOT_ALLOWED: + case SW_WRONG_LENGTH: + return UNIMPLEMENTED; + } + return err; + } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMHmacKey.java similarity index 83% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMHmacKey.java index 98f623b2..791129e2 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMHmacKey.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import org.globalplatform.upgrade.Element; -import com.android.javacard.keymaster.KMPreSharedKey; +import com.android.javacard.kmdevice.KMComputedHmacKey; +import com.android.javacard.kmdevice.KMPreSharedKey; import javacard.security.HMACKey; @@ -36,7 +37,7 @@ public void setKey(byte[] keyData, short kOff, short length) { public byte getKey(byte[] keyData, short kOff) { return hmacKey.getKey(keyData, kOff); } - + public HMACKey getKey() { return hmacKey; } @@ -49,10 +50,11 @@ public static void onSave(Element element, KMHmacKey kmKey) { element.write(kmKey.hmacKey); } - public static KMHmacKey onRestore(Element element) { - HMACKey hmacKey = (HMACKey) element.readObject(); - KMHmacKey kmKey = new KMHmacKey(hmacKey); - return kmKey; + public static KMHmacKey onRestore(HMACKey hmacKey) { + if (hmacKey == null) { + return null; + } + return new KMHmacKey(hmacKey); } public static short getBackupPrimitiveByteCount() { diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMKeymasterProvision.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMKeymasterProvision.java new file mode 100644 index 00000000..77ada381 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMKeymasterProvision.java @@ -0,0 +1,341 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +import com.android.javacard.kmdevice.KMArray; +import com.android.javacard.kmdevice.KMInteger; +import com.android.javacard.kmdevice.KMKeyParameters; +import com.android.javacard.kmdevice.KMKeymasterDevice; +import com.android.javacard.kmdevice.KMRepository; +import com.android.javacard.kmdevice.KMSEProvider; +import com.android.javacard.kmdevice.KMDataStoreConstants; +import com.android.javacard.kmdevice.KMTag; +import com.android.javacard.kmdevice.KMByteBlob; +import com.android.javacard.kmdevice.KMByteTag; +import com.android.javacard.kmdevice.KMDataStore; +import com.android.javacard.kmdevice.KMDecoder; +import com.android.javacard.kmdevice.KMEnum; +import com.android.javacard.kmdevice.KMEnumArrayTag; +import com.android.javacard.kmdevice.KMEnumTag; +import com.android.javacard.kmdevice.KMException; + +import javacard.framework.APDU; +import javacard.framework.Util; + +public class KMKeymasterProvision { + + //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_ATTEST_IDS = 0x08; + private static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; + + private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + + public static final short SHARED_SECRET_KEY_SIZE = 32; + //protected static byte provisionStatus = NOT_PROVISIONED; + + protected KMKeymasterDevice kmDeviceInst; + protected KMSEProvider seProvider; + protected KMDecoder kmDecoder; + protected KMRepository kmRepositroyInst; + protected KMDataStore kmStoreDataInst; + + public KMKeymasterProvision(KMKeymasterDevice deviceInst, KMSEProvider provider, + KMDecoder decoder, KMRepository repoInst, + KMDataStore storeData) { + kmDeviceInst = deviceInst; + seProvider = provider; + kmDecoder = decoder; + kmRepositroyInst = repoInst; + kmStoreDataInst = storeData; + if (!seProvider.isUpgrading()) { + writeProvisionStatus(NOT_PROVISIONED); + } + } + + protected void writeProvisionStatus(byte provisionStatus) { + short offset = kmRepositroyInst.alloc((short) 1); + byte[] buffer = kmRepositroyInst.getHeap(); + buffer[offset] = 0; + short len = kmStoreDataInst.getData(KMDataStoreConstants.PROVISIONED_STATUS, buffer, offset); + buffer[offset] |= provisionStatus; + kmStoreDataInst.storeData(KMDataStoreConstants.PROVISIONED_STATUS, + buffer, offset, (short) 1); + } + + private byte getProvisionStatus(byte[] buffer, short offset) { + short len = kmStoreDataInst.getData(KMDataStoreConstants.PROVISIONED_STATUS, buffer, offset); + if (len == 0) { + return NOT_PROVISIONED; + } + return buffer[offset]; + } + + public void processProvisionAttestationKey(APDU apdu) { + // Arguments + short keyparams = KMKeyParameters.exp(); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT); + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 3); + KMArray.add(argsProto, (short) 0, keyparams); + KMArray.add(argsProto, (short) 1, keyFormatPtr); + KMArray.add(argsProto, (short) 2, blob); + + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + + short args = kmDeviceInst.receiveIncoming(apdu, argsProto); + + // key params should have os patch, os version and verified root of trust + short keyParams = KMArray.get(args, (short) 0); + keyFormatPtr = KMArray.get(args, (short) 1); + short rawBlob = KMArray.get(args, (short) 2); + // Key format must be RAW format + short keyFormat = KMEnum.getVal(keyFormatPtr); + if (keyFormat != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + //byte origin = KMType.IMPORTED; + + // get algorithm - only EC keys expected + KMTag.assertPresence(keyParams, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); + short alg = KMEnumTag.getValue(KMType.ALGORITHM, keyParams); + if (alg != KMType.EC) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // get digest - only SHA256 supported + KMTag.assertPresence(keyParams, KMType.ENUM_ARRAY_TAG, KMType.DIGEST, KMError.INVALID_ARGUMENT); + short len = KMEnumArrayTag.getValues(KMType.DIGEST, keyParams, scratchPad, (short) 0); + if (len != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (scratchPad[0] != KMType.SHA2_256) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + // Purpose should be ATTEST_KEY + KMTag.assertPresence(keyParams, KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, + KMError.INVALID_ARGUMENT); + len = KMEnumArrayTag.getValues(KMType.PURPOSE, keyParams, scratchPad, (short) 0); + if (len != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (scratchPad[0] != KMType.ATTEST_KEY) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + // validate Curve + KMTag.assertPresence(keyParams, KMType.ENUM_TAG, KMType.ECCURVE, KMError.INVALID_ARGUMENT); + short curve = KMEnumTag.getValue(KMType.ECCURVE, keyParams); + if (curve != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + } + // Decode EC Key + short arrPtr = kmDeviceInst.decodeRawECKey(rawBlob); + short secret = KMArray.get(arrPtr, (short) 0); + short pubKey = KMArray.get(arrPtr, (short) 1); + // Check whether key can be created + seProvider.importAsymmetricKey( + KMType.EC, + KMByteBlob.getBuffer(secret), + KMByteBlob.getStartOff(secret), + KMByteBlob.length(secret), + KMByteBlob.getBuffer(pubKey), + KMByteBlob.getStartOff(pubKey), + KMByteBlob.length(pubKey)); + + // persist key + kmStoreDataInst.storeData(KMDataStoreConstants.ATTESTATION_KEY, + KMByteBlob.getBuffer(secret), + KMByteBlob.getStartOff(secret), + KMByteBlob.length(secret)); + + writeProvisionStatus(PROVISION_STATUS_ATTESTATION_KEY); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + public void processProvisionAttestationCertDataCmd(APDU apdu) { + // Buffer holds the corresponding offsets and lengths of the certChain, certIssuer and certExpiry + // in the bufferRef[0] buffer. + short var = KMByteBlob.instance((short) 12); + // These variables point to the appropriate positions in the var buffer. + short certChainPos = KMByteBlob.getStartOff(var); + short certIssuerPos = (short) (KMByteBlob.getStartOff(var) + 4); + short certExpiryPos = (short) (KMByteBlob.getStartOff(var) + 8); + short recvLen = apdu.setIncomingAndReceive(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = kmRepositroyInst.allocReclaimableMemory(bufferLength); + byte[] buffer = kmRepositroyInst.getHeap(); + kmDeviceInst.receiveIncomingCertData(apdu, buffer, bufferLength, + bufferStartOffset, recvLen, KMByteBlob.getBuffer(var), KMByteBlob.getStartOff(var)); + // persist data + kmStoreDataInst.persistCertificateData( + (byte[]) buffer, + Util.getShort(KMByteBlob.getBuffer(var), certChainPos), // offset + Util.getShort(KMByteBlob.getBuffer(var), (short) (certChainPos + 2)), // length + Util.getShort(KMByteBlob.getBuffer(var), certIssuerPos), // offset + Util.getShort(KMByteBlob.getBuffer(var), (short) (certIssuerPos + 2)), // length + Util.getShort(KMByteBlob.getBuffer(var), certExpiryPos), // offset + Util.getShort(KMByteBlob.getBuffer(var), (short) (certExpiryPos + 2))); // length + + // reclaim memory + kmRepositroyInst.reclaimMemory(bufferLength); + writeProvisionStatus((byte) (PROVISION_STATUS_ATTESTATION_CERT_CHAIN | + PROVISION_STATUS_ATTESTATION_CERT_PARAMS)); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + public void processProvisionAttestIdsCmd(APDU apdu) { + short keyparams = KMKeyParameters.exp(); + short cmd = KMArray.instance((short) 1); + KMArray.add(cmd, (short) 0, keyparams); + short args = kmDeviceInst.receiveIncoming(apdu, cmd); + + short attData = KMArray.get(args, (short) 0); + // persist attestation Ids - if any is missing then exception occurs + setAttestationIds(attData); + writeProvisionStatus(PROVISION_STATUS_ATTEST_IDS); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + public void processProvisionPreSharedSecretCmd(APDU apdu) { + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 1); + KMArray.add(argsProto, (short) 0, blob); + short args = kmDeviceInst.receiveIncoming(apdu, argsProto); + + short val = KMArray.get(args, (short) 0); + + if (val != KMType.INVALID_VALUE + && KMByteBlob.length(val) != SHARED_SECRET_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Persist shared Hmac. + kmStoreDataInst.storeData(KMDataStoreConstants.PRE_SHARED_KEY, + KMByteBlob.getBuffer(val), + KMByteBlob.getStartOff(val), + KMByteBlob.length(val)); + writeProvisionStatus(PROVISION_STATUS_PRESHARED_SECRET); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + public void processGetProvisionStatusCmd(APDU apdu) { + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, kmDeviceInst.buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, + KMInteger.uint_16(getProvisionStatus(apdu.getBuffer(), (short) 0))); + kmDeviceInst.sendOutgoing(apdu, resp); + } + + public void processLockProvisioningCmd(APDU apdu) { + byte[] buffer = apdu.getBuffer(); + buffer[0] = 0x01; + kmStoreDataInst.storeData(KMDataStoreConstants.PROVISIONED_LOCKED, buffer, (short) 0, + (short) 1); + writeProvisionStatus(PROVISION_STATUS_PROVISIONING_LOCKED); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + public void processProvisionDeviceUniqueKey(APDU apdu) { + kmDeviceInst.sendError(apdu, KMError.CMD_NOT_ALLOWED); + } + + public void processProvisionAdditionalCertChain(APDU apdu) { + kmDeviceInst.sendError(apdu, KMError.CMD_NOT_ALLOWED); + } + + public short mapAttestIdToStoreId(short tag) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + return KMDataStoreConstants.ATT_ID_BRAND; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + return KMDataStoreConstants.ATT_ID_DEVICE; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + return KMDataStoreConstants.ATT_ID_PRODUCT; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + return KMDataStoreConstants.ATT_ID_SERIAL; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + return KMDataStoreConstants.ATT_ID_IMEI; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + return KMDataStoreConstants.ATT_ID_MEID; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + return KMDataStoreConstants.ATT_ID_MANUFACTURER; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + return KMDataStoreConstants.ATT_ID_MODEL; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return KMType.INVALID_VALUE; + } + + protected void setAttestationIds(short attIdVals) { + short vals = KMKeyParameters.getVals(attIdVals); + short index = 0; + short length = KMArray.length(vals); + short key; + short type; + short obj; + while (index < length) { + obj = KMArray.get(vals, index); + key = KMTag.getKMTagKey(obj); + type = KMTag.getKMTagType(obj); + + if (KMType.BYTES_TAG != type) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + obj = KMByteTag.getValue(obj); + kmStoreDataInst.storeData((byte) mapAttestIdToStoreId(key), KMByteBlob.getBuffer(obj), + KMByteBlob.getStartOff(obj), KMByteBlob.length(obj)); + index++; + } + } + + //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. + public short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 4); + short powerResetStatus = 0; + if (((KMAndroidSEProvider) seProvider).isPowerReset(true)) { + powerResetStatus = POWER_RESET_MASK_FLAG; + } + + Util.setShort(KMInteger.getBuffer(int32Ptr), + KMInteger.getStartOff(int32Ptr), + powerResetStatus); + + Util.setShort(KMInteger.getBuffer(int32Ptr), + (short) (KMInteger.getStartOff(int32Ptr) + 2), + err); + // reset power reset status flag to its default value. + //repository.restorePowerResetStatus(); //TODO + return int32Ptr; + } + +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMKeymintProvision.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMKeymintProvision.java new file mode 100644 index 00000000..936d2d08 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMKeymintProvision.java @@ -0,0 +1,141 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import com.android.javacard.kmdevice.KMArray; +import com.android.javacard.kmdevice.KMByteBlob; +import com.android.javacard.kmdevice.KMCose; +import com.android.javacard.kmdevice.KMCoseHeaders; +import com.android.javacard.kmdevice.KMCoseKey; +import com.android.javacard.kmdevice.KMDataStore; +import com.android.javacard.kmdevice.KMDataStoreConstants; +import com.android.javacard.kmdevice.KMDecoder; +import com.android.javacard.kmdevice.KMDeviceUniqueKey; +import com.android.javacard.kmdevice.KMException; +import com.android.javacard.kmdevice.KMInteger; +import com.android.javacard.kmdevice.KMKeymasterDevice; +import com.android.javacard.kmdevice.KMKeymintDevice; +import com.android.javacard.kmdevice.KMMap; +import com.android.javacard.kmdevice.KMRepository; +import com.android.javacard.kmdevice.KMRkpDataStore; +import com.android.javacard.kmdevice.KMSEProvider; +import com.android.javacard.kmdevice.KMTextString; +import com.android.javacard.kmdevice.RemotelyProvisionedComponentDevice; + +import javacard.framework.APDU; +import javacard.framework.Util; + +public class KMKeymintProvision extends KMKeymasterProvision { + + private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x40; + private static final byte PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = (byte) 0x80; + private KMRkpDataStore rkpDataStore; + + public KMKeymintProvision(KMKeymasterDevice deviceInst, KMSEProvider provider, + KMDecoder decoder, KMRepository repoInst, KMDataStore storeData, KMRkpDataStore rkpStore) { + super(deviceInst, provider, decoder, repoInst, storeData); + rkpDataStore = rkpStore; + } + + @Override + public void processProvisionAttestationKey(APDU apdu) { + kmDeviceInst.sendError(apdu, KMError.CMD_NOT_ALLOWED); + } + + @Override + public void processProvisionAttestationCertDataCmd(APDU apdu) { + kmDeviceInst.sendError(apdu, KMError.CMD_NOT_ALLOWED); + } + + @Override + public void processProvisionDeviceUniqueKey(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.add(arr, (short) 0, coseKeyExp); //[ CoseKey ] + arr = kmDeviceInst.receiveIncoming(apdu, arr); + // Get cose key. + short coseKey = KMArray.get(arr, (short) 0); + short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0); + short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen); + //Store the Device unique Key. + rkpDataStore.createDeviceUniqueKey(false, scratchPad, (short) 0, pubKeyLen, scratchPad, + pubKeyLen, privKeyLen); + short bcc = ((KMKeymintDevice) kmDeviceInst).generateBcc(false, scratchPad); + short len = kmDeviceInst.encodeToApduBuffer(bcc, scratchPad, (short) 0, + RemotelyProvisionedComponentDevice.MAX_COSE_BUF_SIZE); + rkpDataStore.storeData(KMDataStoreConstants.BOOT_CERT_CHAIN, scratchPad, (short) 0, len); + writeProvisionStatus(PROVISION_STATUS_DEVICE_UNIQUE_KEY); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + @Override + public void processProvisionAdditionalCertChain(APDU apdu) { + // Prepare the expression to decode + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.add(arrInst, (short) 0, KMByteBlob.exp()); + KMArray.add(arrInst, (short) 1, headers); + KMArray.add(arrInst, (short) 2, KMByteBlob.exp()); + KMArray.add(arrInst, (short) 3, KMByteBlob.exp()); + short coseSignArr = KMArray.exp(arrInst); + short map = KMMap.instance((short) 1); + KMMap.add(map, (short) 0, KMTextString.exp(), coseSignArr); + // receive incoming data and decode it. + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = apdu.setIncomingAndReceive(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = kmRepositroyInst.allocReclaimableMemory(bufferLength); + byte[] buffer = kmRepositroyInst.getHeap(); + map = kmDeviceInst.receiveIncoming(apdu, map, buffer, bufferLength, bufferStartOffset, recvLen); + arrInst = KMMap.getKeyValue(map, (short) 0); + // Validate Additional certificate chain. + short leafCoseKey = + ((KMKeymintDevice) kmDeviceInst).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); + KMDeviceUniqueKey uniqueKey = rkpDataStore.getDeviceUniqueKey(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); + } + rkpDataStore.storeData(KMDataStoreConstants.ADDITIONAL_CERT_CHAIN, buffer, bufferStartOffset, + bufferLength); + //reclaim memory + kmRepositroyInst.reclaimMemory(bufferLength); + writeProvisionStatus(PROVISION_STATUS_ADDITIONAL_CERT_CHAIN); + kmDeviceInst.sendError(apdu, KMError.OK); + } + + @Override + public short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 2); + + Util.setShort(KMInteger.getBuffer(int32Ptr), + (short) (KMInteger.getStartOff(int32Ptr)), + err); + + return int32Ptr; + } + +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMOperationImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMOperationImpl.java new file mode 100644 index 00000000..ec7d0ad1 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMOperationImpl.java @@ -0,0 +1,380 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import com.android.javacard.kmdevice.KMException; +import com.android.javacard.kmdevice.KMOperation; +import com.android.javacard.seprovider.KMError; +import com.android.javacard.seprovider.KMType; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.KeyAgreement; +import javacard.security.PrivateKey; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; +import javacard.security.CryptoException; +import javacard.security.Key; + +public class KMOperationImpl implements KMOperation { + + private static final short ALG_TYPE_OFFSET = 0x00; + private static final short PADDING_OFFSET = 0x01; + private static final short PURPOSE_OFFSET = 0x02; + private static final short BLOCK_MODE_OFFSET = 0x03; + private static final short MAC_LENGTH_OFFSET = 0x04; + private final byte[] EMPTY = {}; + //This will hold the length of the buffer stored inside the + //Java Card after the GCM update operation. + private static final short AES_GCM_UPDATE_LEN_OFFSET = 0x05; + private static final short PARAMETERS_LENGTH = 6; + private short[] parameters; + // Either one of Cipher/Signature instance is stored. + private Object[] operationInst; + + public KMOperationImpl() { + parameters = JCSystem.makeTransientShortArray(PARAMETERS_LENGTH, JCSystem.CLEAR_ON_RESET); + operationInst = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + reset(); + } + + public short getPurpose() { + return parameters[PURPOSE_OFFSET]; + } + + public void setPurpose(short mode) { + parameters[PURPOSE_OFFSET] = mode; + } + + public short getMacLength() { + return parameters[MAC_LENGTH_OFFSET]; + } + + public void setMacLength(short macLength) { + parameters[MAC_LENGTH_OFFSET] = macLength; + } + + public short getPaddingAlgorithm() { + return parameters[PADDING_OFFSET]; + } + + public void setPaddingAlgorithm(short alg) { + parameters[PADDING_OFFSET] = alg; + } + + public void setBlockMode(short mode) { + parameters[BLOCK_MODE_OFFSET] = mode; + } + + public short getBlockMode() { + return parameters[BLOCK_MODE_OFFSET]; + } + + public short getAlgorithmType() { + return parameters[ALG_TYPE_OFFSET]; + } + + public void setAlgorithmType(short cipherAlg) { + parameters[ALG_TYPE_OFFSET] = cipherAlg; + } + + public void setCipher(Cipher cipher) { + operationInst[0] = cipher; + } + + public void setSignature(Signature signer) { + operationInst[0] = signer; + } + + public void setKeyAgreement(KeyAgreement keyAgreement) { + operationInst[0] = keyAgreement; + } + + public boolean isResourceMatches(Object object) { + return operationInst[0] == object; + } + + private void reset() { + operationInst[0] = null; + parameters[MAC_LENGTH_OFFSET] = KMType.INVALID_VALUE; + parameters[AES_GCM_UPDATE_LEN_OFFSET] = 0; + parameters[BLOCK_MODE_OFFSET] = KMType.INVALID_VALUE; + parameters[PURPOSE_OFFSET] = KMType.INVALID_VALUE; + parameters[ALG_TYPE_OFFSET] = KMType.INVALID_VALUE; + parameters[PADDING_OFFSET] = KMType.INVALID_VALUE; + } + + private byte mapPurpose(short purpose) { + switch (purpose) { + case KMType.ENCRYPT: + return Cipher.MODE_ENCRYPT; + case KMType.DECRYPT: + return Cipher.MODE_DECRYPT; + case KMType.SIGN: + return Signature.MODE_SIGN; + case KMType.VERIFY: + return Signature.MODE_VERIFY; + } + return -1; + } + + private void initSymmetricCipher(Key key, byte[] ivBuffer, short ivStart, short ivLength) { + Cipher symmCipher = (Cipher) operationInst[0]; + byte cipherAlg = symmCipher.getAlgorithm(); + switch (cipherAlg) { + case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: + case Cipher.ALG_AES_CTR: + symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, ivLength); + break; + case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: + case Cipher.ALG_DES_ECB_NOPAD: + symmCipher.init(key, mapPurpose(getPurpose())); + break; + case Cipher.ALG_DES_CBC_NOPAD: + // Consume only 8 bytes of iv. the random number for iv is of 16 bytes. + // While sending back the iv, send only 8 bytes. + symmCipher.init(key, mapPurpose(getPurpose()), ivBuffer, ivStart, (short) 8); + break; + case AEADCipher.ALG_AES_GCM: + ((AEADCipher) symmCipher).init(key, mapPurpose(getPurpose()), ivBuffer, + ivStart, ivLength); + break; + default:// This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + private void initRsa(Key key, short digest) { + if (KMType.SIGN == getPurpose()) { + byte mode; + if (getPaddingAlgorithm() == KMType.PADDING_NONE || + (getPaddingAlgorithm() == KMType.RSA_PKCS1_1_5_SIGN && + digest == KMType.DIGEST_NONE)) { + mode = Cipher.MODE_DECRYPT; + } else { + mode = Signature.MODE_SIGN; + } + ((Signature) operationInst[0]).init((PrivateKey) key, mode); + } else { // RSA Cipher + ((Cipher) operationInst[0]).init((PrivateKey) key, mapPurpose(getPurpose())); + } + } + + private void initEc(Key key) { + if (KMType.AGREE_KEY == getPurpose()) { + ((KeyAgreement) operationInst[0]).init((PrivateKey) key); + } else { + ((Signature) operationInst[0]).init((PrivateKey) key, mapPurpose(getPurpose())); + } + } + + public void init(Key key, short digest, byte[] buf, short start, short length) { + switch (getAlgorithmType()) { + case KMType.AES: + case KMType.DES: + initSymmetricCipher(key, buf, start, length); + break; + case KMType.HMAC: + ((Signature) operationInst[0]).init(key, mapPurpose(getPurpose())); + break; + case KMType.RSA: + initRsa(key, digest); + break; + case KMType.EC: + initEc(key); + break; + default:// This should never happen + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + @Override + public short update(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] outputDataBuf, short outputDataStart) { + short len = ((Cipher) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + if (parameters[ALG_TYPE_OFFSET] == KMType.AES + && parameters[BLOCK_MODE_OFFSET] == KMType.GCM) { + // Every time Block size data is stored as intermediate result. + parameters[AES_GCM_UPDATE_LEN_OFFSET] += (short) (inputDataLength - len); + } + return len; + } + + @Override + public short update(byte[] inputDataBuf, short inputDataStart, + short inputDataLength) { + ((Signature) operationInst[0]).update(inputDataBuf, inputDataStart, inputDataLength); + return 0; + } + + private short finishKeyAgreement(byte[] publicKey, short start, short len, byte[] output, + short outputStart) { + return ((KeyAgreement) operationInst[0]).generateSecret(publicKey, start, len, + output, outputStart); + } + + private short finishCipher(byte[] inputDataBuf, short inputDataStart, short inputDataLen, + byte[] outputDataBuf, + short outputDataStart) { + short len = 0; + try { + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + Cipher cipher = (Cipher) operationInst[0]; + short cipherAlg = parameters[ALG_TYPE_OFFSET]; + short blockMode = parameters[BLOCK_MODE_OFFSET]; + short mode = parameters[PURPOSE_OFFSET]; + short macLength = parameters[MAC_LENGTH_OFFSET]; + short padding = parameters[PADDING_OFFSET]; + + if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + if (mode == KMType.DECRYPT) { + inputDataLen = (short) (inputDataLen - macLength); + } + } else if ((cipherAlg == KMType.DES || cipherAlg == KMType.AES) && padding == KMType.PKCS7 + && mode == KMType.ENCRYPT) { + byte blkSize = 16; + byte paddingBytes; + short inputlen = inputDataLen; + if (cipherAlg == KMType.DES) { + blkSize = 8; + } + // padding bytes + if (inputlen % blkSize == 0) { + paddingBytes = blkSize; + } else { + paddingBytes = (byte) (blkSize - (inputlen % blkSize)); + } + // final len with padding + inputlen = (short) (inputlen + paddingBytes); + // intermediate buffer to copy input data+padding + // fill in the padding + Util.arrayFillNonAtomic(tmpArray, (short) 0, inputlen, paddingBytes); + // copy the input data + Util.arrayCopyNonAtomic(inputDataBuf, inputDataStart, tmpArray, (short) 0, inputDataLen); + inputDataBuf = tmpArray; + inputDataLen = inputlen; + inputDataStart = 0; + } + len = cipher + .doFinal(inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, outputDataStart); + if ((cipherAlg == KMType.AES || cipherAlg == KMType.DES) && padding == KMType.PKCS7 + && mode == KMType.DECRYPT) { + byte blkSize = 16; + if (cipherAlg == KMType.DES) { + blkSize = 8; + } + if (len > 0) { + // verify if padding is corrupted. + byte paddingByte = outputDataBuf[(short) (outputDataStart + len - 1)]; + // padding byte always should be <= block size + if ((short) paddingByte > blkSize || (short) paddingByte <= 0) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + for (short j = 1; j <= paddingByte; ++j) { + if (outputDataBuf[(short) (outputDataStart + len - j)] != paddingByte) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + len = (short) (len - (short) paddingByte);// remove the padding bytes + } + } else if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { + if (mode == KMType.ENCRYPT) { + len += ((AEADCipher) cipher) + .retrieveTag(outputDataBuf, (short) (outputDataStart + len), macLength); + } else { + boolean verified = ((AEADCipher) cipher) + .verifyTag(inputDataBuf, (short) (inputDataStart + inputDataLen), + macLength, macLength); + if (!verified) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + } + } finally { + KMAndroidSEProvider.getInstance().clean(); + } + return len; + } + + @Override + public short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLen, + byte[] outputDataBuf, + short outputDataStart) { + if (parameters[PURPOSE_OFFSET] == KMType.AGREE_KEY) { + return finishKeyAgreement(inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, + outputDataStart); + } else { + return finishCipher(inputDataBuf, inputDataStart, inputDataLen, outputDataBuf, + outputDataStart); + } + } + + @Override + public short sign(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] signBuf, short signStart) { + return ((Signature) operationInst[0]).sign(inputDataBuf, inputDataStart, inputDataLength, + signBuf, signStart); + } + + @Override + public boolean verify(byte[] inputDataBuf, short inputDataStart, + short inputDataLength, byte[] signBuf, short signStart, short signLength) { + return ((Signature) operationInst[0]).verify(inputDataBuf, inputDataStart, inputDataLength, + signBuf, signStart, signLength); + } + + @Override + public void abort() { + // Few simulators does not reset the Hmac signer instance on init so as + // a workaround to reset the hmac signer instance in case of abort/failure of the operation + // the corresponding sign / verify function is called. + if (operationInst[0] != null) { + if ((parameters[PURPOSE_OFFSET] == KMType.SIGN || parameters[PURPOSE_OFFSET] == KMType.VERIFY) + && + (((Signature) operationInst[0]).getAlgorithm() == Signature.ALG_HMAC_SHA_256)) { + Signature signer = (Signature) operationInst[0]; + try { + if (parameters[PURPOSE_OFFSET] == KMType.SIGN) { + signer.sign(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0); + } else { + signer.verify(EMPTY, (short) 0, (short) 0, EMPTY, (short) 0, (short) 0); + } + } catch (Exception e) { + // Ignore. + } + } + } + reset(); + } + + @Override + public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { + ((AEADCipher) operationInst[0]).updateAAD(dataBuf, dataStart, dataLength); + } + + @Override + public short getAESGCMOutputSize(short dataSize, short macLength) { + if (parameters[PURPOSE_OFFSET] == KMType.ENCRYPT) { + return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize + macLength); + } else { + return (short) (parameters[AES_GCM_UPDATE_LEN_OFFSET] + dataSize - macLength); + } + } +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMPoolManager.java new file mode 100644 index 00000000..a9fd7008 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMPoolManager.java @@ -0,0 +1,335 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import com.android.javacard.kmdevice.KMException; +import com.android.javacard.kmdevice.KMOperation; + +import javacard.framework.JCSystem; +import javacard.security.KeyAgreement; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; + +/** + * This class manages all the pool instances. + */ +public class KMPoolManager { + + public static final short MAX_OPERATION_INSTANCES = 4; + private static final short HMAC_MAX_OPERATION_INSTANCES = 8; + // Cipher pool + private Object[] cipherPool; + // Signature pool + private Object[] signerPool; + // Keyagreement pool + private Object[] keyAgreementPool; + // KMOperationImpl pool + private Object[] operationPool; + // Hmac signer pool which is used to support TRUSTED_CONFIRMATION_REQUIRED tag. + private Object[] hmacSignOperationPool; + + final byte[] CIPHER_ALGS = { + Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, + Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, + Cipher.ALG_DES_CBC_NOPAD, + Cipher.ALG_DES_ECB_NOPAD, + Cipher.ALG_AES_CTR, + Cipher.ALG_RSA_PKCS1, + KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1, + KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256, + Cipher.ALG_RSA_NOPAD, + AEADCipher.ALG_AES_GCM}; + + final byte[] SIG_ALGS = { + Signature.ALG_RSA_SHA_256_PKCS1, + Signature.ALG_RSA_SHA_256_PKCS1_PSS, + Signature.ALG_ECDSA_SHA_256, + Signature.ALG_HMAC_SHA_256, + KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD, + KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST, + KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST}; + + final byte[] KEY_AGREE_ALGS = {KeyAgreement.ALG_EC_SVDP_DH_PLAIN}; + + + private static KMPoolManager poolManager; + + public static KMPoolManager getInstance() { + if (poolManager == null) { + poolManager = new KMPoolManager(); + } + return poolManager; + } + + private KMPoolManager() { + 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)]; + keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * 4)]; + operationPool = new Object[4]; + hmacSignOperationPool = new Object[4]; + /* Initialize pools */ + initializeOperationPool(); + initializeHmacSignOperationPool(); + initializeSignerPool(); + initializeCipherPool(); + initializeKeyAgreementPool(); + } + + private void initializeOperationPool() { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + operationPool[index] = new KMOperationImpl(); + index++; + } + } + + private void initializeHmacSignOperationPool() { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + hmacSignOperationPool[index] = new KMOperationImpl(); + index++; + } + } + + // Create a signature instance of each algorithm once. + private void initializeSignerPool() { + short index = 0; + while (index < SIG_ALGS.length) { + signerPool[index] = getSignatureInstance(SIG_ALGS[index]); + index++; + } + } + + //Create a cipher instance of each algorithm once. + private void initializeCipherPool() { + short index = 0; + while (index < CIPHER_ALGS.length) { + cipherPool[index] = getCipherInstance(CIPHER_ALGS[index]); + index++; + } + } + + private void initializeKeyAgreementPool() { + short index = 0; + while (index < KEY_AGREE_ALGS.length) { + keyAgreementPool[index] = getKeyAgreementInstance(KEY_AGREE_ALGS[index]); + index++; + } + } + + private Object[] getCryptoPoolInstance(short purpose) { + switch (purpose) { + case KMType.AGREE_KEY: + return keyAgreementPool; + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return cipherPool; + + case KMType.SIGN: + case KMType.VERIFY: + return signerPool; + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return null; + } + + private Object createInstance(short purpose, short alg) { + switch (purpose) { + case KMType.AGREE_KEY: + return getKeyAgreementInstance((byte) alg); + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return getCipherInstance((byte) alg); + + case KMType.SIGN: + case KMType.VERIFY: + return getSignatureInstance((byte) alg); + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return null; + } + + private KeyAgreement getKeyAgreementInstance(byte alg) { + return KeyAgreement.getInstance(alg, false); + } + + private Signature getSignatureInstance(byte alg) { + if (KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD == alg + || KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST == alg) { + return new KMRsa2048NoDigestSignature(alg); + } else if (KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST == alg) { + return new KMEcdsa256NoDigestSignature(alg); + } else { + return Signature.getInstance(alg, false); + } + } + + private Cipher getCipherInstance(byte alg) { + if ((KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 == alg) || + (KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 == alg)) { + return new KMRsaOAEPEncoding(alg); + } else { + return Cipher.getInstance(alg, false); + } + } + + /** + * Returns the first available resource from operation pool. + * + * @return instance of the available resource or null if no resource is available. + */ + public KMOperation getResourceFromOperationPool(boolean isTrustedConfOpr) { + short index = 0; + KMOperationImpl impl; + Object[] oprPool; + if (isTrustedConfOpr) { + oprPool = hmacSignOperationPool; + } else { + oprPool = operationPool; + } + while (index < oprPool.length) { + impl = (KMOperationImpl) oprPool[index]; + // Mode is always set. so compare using mode value. + if (impl.getPurpose() == KMType.INVALID_VALUE) { + return impl; + } + index++; + } + return null; + } + + private byte getAlgorithm(short purpose, Object object) { + switch (purpose) { + case KMType.AGREE_KEY: + return ((KeyAgreement) object).getAlgorithm(); + + case KMType.ENCRYPT: + case KMType.DECRYPT: + return ((Cipher) object).getAlgorithm(); + + case KMType.SIGN: + case KMType.VERIFY: + return ((Signature) object).getAlgorithm(); + + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + return 0; + } + + private boolean isResourceBusy(Object obj) { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + if (((KMOperationImpl) operationPool[index]).isResourceMatches(obj) + || ((KMOperationImpl) hmacSignOperationPool[index]).isResourceMatches(obj)) { + return true; + } + index++; + } + return false; + } + + private void setObject(short purpose, KMOperation operation, Object obj) { + switch (purpose) { + case KMType.AGREE_KEY: + ((KMOperationImpl) operation).setKeyAgreement((KeyAgreement) obj); + break; + case KMType.ENCRYPT: + case KMType.DECRYPT: + ((KMOperationImpl) operation).setCipher((Cipher) obj); + break; + case KMType.SIGN: + case KMType.VERIFY: + ((KMOperationImpl) operation).setSignature((Signature) obj); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } + + private void reserveOperation(KMOperation operation, short purpose, short strongboxAlgType, + short padding, short blockMode, short macLength, Object obj) { + ((KMOperationImpl) operation).setPurpose(purpose); + ((KMOperationImpl) operation).setAlgorithmType(strongboxAlgType); + ((KMOperationImpl) operation).setPaddingAlgorithm(padding); + ((KMOperationImpl) operation).setBlockMode(blockMode); + ((KMOperationImpl) operation).setMacLength(macLength); + setObject(purpose, operation, obj); + } + + + public KMOperation getOperationImpl(short purpose, short alg, short strongboxAlgType, + short padding, + short blockMode, short macLength, boolean isTrustedConfOpr) { + KMOperation operation; + // Throw exception if no resource from operation pool is available. + if (null == (operation = getResourceFromOperationPool(isTrustedConfOpr))) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + // Get one of the pool instances (cipher / signer / keyAgreement) based on purpose. + Object[] pool = getCryptoPoolInstance(purpose); + short index = 0; + short usageCount = 0; + short maxOperations = MAX_OPERATION_INSTANCES; + if (Signature.ALG_HMAC_SHA_256 == alg) { + maxOperations = HMAC_MAX_OPERATION_INSTANCES; + } + + while (index < pool.length) { + if (usageCount >= maxOperations) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + if (pool[index] == null) { + // Create one of the instance (Cipher / Signer / KeyAgreement] based on purpose. + JCSystem.beginTransaction(); + pool[index] = createInstance(purpose, alg); + JCSystem.commitTransaction(); + reserveOperation(operation, purpose, strongboxAlgType, padding, blockMode, macLength, + pool[index]); + break; + } + if (alg == getAlgorithm(purpose, pool[index])) { + // Check if the crypto instance is not busy and free to use. + if (!isResourceBusy(pool[index])) { + reserveOperation(operation, purpose, strongboxAlgType, padding, blockMode, macLength, + pool[index]); + break; + } + usageCount++; + } + index++; + } + return operation; + } + + public void powerReset() { + short index = 0; + while (index < operationPool.length) { + ((KMOperationImpl) operationPool[index]).abort(); + ((KMOperationImpl) hmacSignOperationPool[index]).abort(); + index++; + } + } + +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java similarity index 96% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java index 08e11436..89c390bd 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; + +import com.android.javacard.kmdevice.KMException; import javacard.framework.Util; import javacard.security.CryptoException; @@ -135,6 +137,7 @@ private boolean isValidData(byte[] buf, short start, short len) { } } else { // ALG_RSA_PKCS1_NODIGEST if (len > 245) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); return false; } } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java similarity index 99% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java index ac099bc5..2f2da22f 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMRsaOAEPEncoding.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.JCSystem; import javacard.framework.Util; diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMType.java similarity index 77% rename from Applet/src/com/android/javacard/keymaster/KMType.java rename to Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMType.java index 00704df2..0bfc0b1a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/seprovider/KMType.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -44,6 +44,20 @@ public abstract class KMType { public static final byte VERIFICATION_TOKEN_TYPE = 0x09; public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; public static final byte X509_CERT = 0x0B; + public static final byte NEG_INTEGER_TYPE = 0x0C; + public static final byte TEXT_STRING_TYPE = 0x0D; + public static final byte MAP_TYPE = 0x0E; + public static final byte COSE_KEY_TYPE = 0x0F; + public static final byte COSE_PAIR_TAG_TYPE = 0x10; + public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20; + public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30; + public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40; + public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60; + public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70; + public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80; + public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90; + public static final byte COSE_HEADERS_TYPE = (byte) 0xA0; + public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0; // Tag Types public static final short INVALID_TAG = 0x0000; public static final short ENUM_TAG = 0x1000; @@ -59,6 +73,11 @@ public abstract class KMType { public static final short TAG_TYPE_MASK = (short) 0xF000; // Enum Tag + // Internal tags + public static final short RULE = 0x7FFF; + public static final byte IGNORE_INVALID_TAGS = 0x00; + public static final byte FAIL_ON_INVALID_TAGS = 0x01; + // Algorithm Enum Tag key and values public static final short ALGORITHM = 0x0002; public static final byte RSA = 0x01; @@ -143,9 +162,10 @@ public abstract class KMType { public static final byte DECRYPT = 0x01; public static final byte SIGN = 0x02; public static final byte VERIFY = 0x03; + public static final byte DERIVE_KEY = 0x04; public static final byte WRAP_KEY = 0x05; - public static final byte ATTEST_KEY = (byte) 0x7F; - + public static final byte AGREE_KEY = 0x06; + public static final byte ATTEST_KEY = (byte) 0x07; // Block mode public static final short BLOCK_MODE = 0x0004; public static final byte ECB = 0x01; @@ -172,6 +192,9 @@ public abstract class KMType { public static final byte RSA_PKCS1_1_5_SIGN = 0x05; public static final byte PKCS7 = 0x40; + // OAEP MGF Digests - only SHA-1 is supported in Javacard + public static final short RSA_OAEP_MGF_DIGEST = 0xCB; + // Integer Tag - UINT, ULONG and DATE // UINT tags // Keysize @@ -186,8 +209,6 @@ public abstract class KMType { public static final short USERID = 0x01F5; // Auth Timeout public static final short AUTH_TIMEOUT = 0x01F9; - // Auth Timeout in Milliseconds - public static final short AUTH_TIMEOUT_MILLIS = 0x7FFF; // OS Version public static final short OS_VERSION = 0x02C1; // OS Patch Level @@ -198,6 +219,8 @@ public abstract class KMType { public static final short BOOT_PATCH_LEVEL = 0x02CF; // Mac Length public static final short MAC_LENGTH = 0x03EB; + // Usage Count Limit + public static final short USAGE_COUNT_LIMIT = 0x195; // ULONG tags // RSA Public Exponent @@ -207,8 +230,10 @@ public abstract class KMType { public static final short ACTIVE_DATETIME = 0x0190; public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; public static final short USAGE_EXPIRE_DATETIME = 0x0192; - public static final short CREATION_DATETIME = 0x02BD;//0x0193; - + public static final short CREATION_DATETIME = 0x02BD; + ; + public static final short CERTIFICATE_NOT_BEFORE = 0x03F0; + public static final short CERTIFICATE_NOT_AFTER = 0x03F1; // Integer Array Tags - ULONG_REP and UINT_REP. // User Secure Id public static final short USER_SECURE_ID = (short) 0x01F6; @@ -274,10 +299,16 @@ public abstract class KMType { public static final short NONCE = (short) 0x03E9; // Confirmation Token public static final short CONFIRMATION_TOKEN = (short) 0x03ED; + // Serial Number - this is a big num but in applet we handle it as byte blob + public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE; + // Subject Name + public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF; public static final short LENGTH_FROM_PDU = (short) 0xFFFF; public static final byte NO_VALUE = (byte) 0xff; + // Support Curves for Eek Chain validation. + public static final byte RKP_CURVE_P256 = 1; // Type offsets. public static final byte KM_TYPE_BASE_OFFSET = 0; public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET; @@ -295,46 +326,30 @@ public abstract class KMType { public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12; public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13; public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14; - - protected static KMRepository repository; - protected static byte[] heap; - // Instance table - public static final byte INSTANCE_TABLE_SIZE = 15; - protected static short[] instanceTable; - - public static void initialize() { - instanceTable = JCSystem.makeTransientShortArray(INSTANCE_TABLE_SIZE, JCSystem.CLEAR_ON_RESET); - KMType.repository = KMRepository.instance(); - KMType.heap = repository.getHeap(); - } - - public static byte getType(short ptr) { - return heap[ptr]; - } - - public static short length(short ptr) { - return Util.getShort(heap, (short) (ptr + 1)); - } - - public static short getValue(short ptr) { - return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - } - - protected static short instance(byte type, short length) { - if (length < 0) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE)); - heap[ptr] = type; - Util.setShort(heap, (short) (ptr + 1), length); - return ptr; - } - - protected static short exp(byte type) { - short ptr = repository.alloc(TLV_HEADER_SIZE); - heap[ptr] = type; - Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE); - return ptr; - } + public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15; + public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16; + public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17; + public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18; + public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19; + public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20; + public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21; + public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22; + public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23; + public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24; + public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25; + public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26; + public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27; + public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28; + + // Attestation types + public static final byte NO_CERT = 0; + public static final byte ATTESTATION_CERT = 1; + public static final byte SELF_SIGNED_CERT = 2; + public static final byte FAKE_CERT = 3; + // Buffering Mode + public static final byte BUF_NONE = 0; + public static final byte BUF_RSA_NO_DIGEST = 1; + public static final byte BUF_EC_NO_DIGEST = 2; + public static final byte BUF_BLOCK_ALIGN = 3; } diff --git a/Applet/JCardSimProvider/build.xml b/Applet/JCardSimProvider/build.xml deleted file mode 100644 index 3d06a8fd..00000000 --- a/Applet/JCardSimProvider/build.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar b/Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar deleted file mode 100644 index 9d5fe16e..00000000 Binary files a/Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar and /dev/null differ diff --git a/Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar b/Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar deleted file mode 100644 index d756d67b..00000000 Binary files a/Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar and /dev/null differ diff --git a/Applet/JCardSimProvider/lib/junit-4.13.jar b/Applet/JCardSimProvider/lib/junit-4.13.jar deleted file mode 100644 index acc3c432..00000000 Binary files a/Applet/JCardSimProvider/lib/junit-4.13.jar and /dev/null differ diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java deleted file mode 100644 index 420a775c..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -public abstract class KMCipher { - - public static final short SUN_JCE = 0xE9; - - public abstract short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, - short i); - - public abstract short update(byte[] buffer, short startOff, short length, byte[] scratchPad, - short i); - - public abstract void updateAAD(byte[] buffer, short startOff, short length); - - public abstract short getBlockMode(); - - public abstract void setBlockMode(short mode); - - public abstract short getPaddingAlgorithm(); - - public abstract short getCipherAlgorithm(); - - public abstract void setPaddingAlgorithm(short alg); - - public abstract void setCipherAlgorithm(short alg); - - public abstract short getCipherProvider(); - - public abstract short getAesGcmOutputSize(short len, short macLength); -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java deleted file mode 100644 index c4162b12..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.framework.Util; -import javacard.security.CryptoException; -import javacardx.crypto.Cipher; -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.ShortBufferException; - - -public class KMCipherImpl extends KMCipher { - - private Cipher cipher; - private javax.crypto.Cipher sunCipher; - private short cipherAlg; - private short padding; - private short mode; - private short blockMode; - - KMCipherImpl(Cipher c) { - cipher = c; - } - - KMCipherImpl(javax.crypto.Cipher c) { - sunCipher = c; - } - - @Override - public short doFinal(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { - if (cipherAlg == KMType.RSA && padding == KMType.RSA_OAEP) { - try { - return (short) sunCipher.doFinal(buffer, startOff, length, scratchPad, i); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - } else if (cipherAlg == KMType.AES && blockMode == KMType.GCM) { - try { - return (short) sunCipher.doFinal(buffer, startOff, length, scratchPad, i); - } catch (AEADBadTagException e) { - e.printStackTrace(); - KMException.throwIt(KMError.VERIFICATION_FAILED); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - } else if (cipherAlg == KMType.AES && blockMode == KMType.CTR) { - try { - return (short) sunCipher.doFinal(buffer, startOff, length, scratchPad, i); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - } else { - if ((cipherAlg == KMType.DES || cipherAlg == KMType.AES) && padding == KMType.PKCS7 - && mode == KMType.ENCRYPT) { - byte blkSize = 16; - byte paddingBytes; - short len = length; - if (cipherAlg == KMType.DES) { - blkSize = 8; - } - // padding bytes - if (len % blkSize == 0) { - paddingBytes = blkSize; - } else { - paddingBytes = (byte) (blkSize - (len % blkSize)); - } - // final len with padding - len = (short) (len + paddingBytes); - // intermediate buffer to copy input data+padding - byte[] tmp = new byte[len]; - // fill in the padding - Util.arrayFillNonAtomic(tmp, (short) 0, len, paddingBytes); - // copy the input data - Util.arrayCopyNonAtomic(buffer, startOff, tmp, (short) 0, length); - buffer = tmp; - length = len; - startOff = 0; - } - short len = cipher.doFinal(buffer, startOff, length, scratchPad, i); - // JCard Sim removes leading zeros during decryption in case of no padding - so add that back. - if (cipherAlg == KMType.RSA && padding == KMType.PADDING_NONE && mode == KMType.DECRYPT - && len < 256) { - byte[] tempBuf = new byte[256]; - Util.arrayFillNonAtomic(tempBuf, (short) 0, (short) 256, (byte) 0); - Util.arrayCopyNonAtomic(scratchPad, (short) 0, tempBuf, (short) (i + 256 - len), len); - Util.arrayCopyNonAtomic(tempBuf, (short) 0, scratchPad, i, (short) 256); - len = 256; - } else if ((cipherAlg == KMType.AES || cipherAlg == KMType.DES) // PKCS7 - && padding == KMType.PKCS7 - && mode == KMType.DECRYPT) { - byte blkSize = 16; - if (cipherAlg == KMType.DES) { - blkSize = 8; - } - if (len > 0) { - //verify if padding is corrupted. - byte paddingByte = scratchPad[i + len - 1]; - //padding byte always should be <= block size - if ((short) paddingByte > blkSize || - (short) paddingByte <= 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - for (short j = 1; j <= paddingByte; ++j) { - if (scratchPad[i + len - j] != paddingByte) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - len = (short) (len - (short) paddingByte);// remove the padding bytes - } - } - return len; - } - return KMType.INVALID_VALUE; - } - - @Override - public short getCipherAlgorithm() { - return cipherAlg; - } - - @Override - public void setCipherAlgorithm(short alg) { - cipherAlg = alg; - } - - @Override - public short update(byte[] buffer, short startOff, short length, byte[] scratchPad, short i) { - if (cipherAlg == KMType.AES && (blockMode == KMType.GCM || blockMode == KMType.CTR)) { - try { - return (short) sunCipher.update(buffer, startOff, length, scratchPad, i); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalStateException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - } else { - return cipher.update(buffer, startOff, length, scratchPad, i); - } - return KMType.INVALID_VALUE; - } - - @Override - public void updateAAD(byte[] buffer, short startOff, short length) { - try { - sunCipher.updateAAD(buffer, startOff, length); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalStateException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (UnsupportedOperationException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_USE); - } - } - - @Override - public short getPaddingAlgorithm() { - return padding; - } - - @Override - public void setPaddingAlgorithm(short alg) { - padding = alg; - } - - @Override - public void setBlockMode(short mode) { - blockMode = mode; - } - - @Override - public short getBlockMode() { - return blockMode; - } - - public short getMode() { - return mode; - } - - public void setMode(short mode) { - this.mode = mode; - } - - @Override - public short getCipherProvider() { - return KMCipher.SUN_JCE; - } - - @Override - public short getAesGcmOutputSize(short len, short macLength) { - if (sunCipher != null) { - return (short) sunCipher.getOutputSize(len); - } else { - if (mode == KMType.ENCRYPT) { - return (short) (len + macLength); - } else { - return (short) (len - macLength); - } - } - } - -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java deleted file mode 100644 index 62b26cce..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.security.ECPrivateKey; -import javacard.security.KeyPair; - -public class KMECPrivateKey implements KMAttestationKey { - - private KeyPair ecKeyPair; - - public KMECPrivateKey(KeyPair ecPair) { - ecKeyPair = ecPair; - } - - public void setS(byte[] buffer, short offset, short length) { - ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); - ecPriv.setS(buffer, offset, length); - } - - public ECPrivateKey getPrivateKey() { - return (ECPrivateKey) ecKeyPair.getPrivate(); - } - -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java deleted file mode 100644 index c382f3cb..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import java.math.BigInteger; -import java.security.AlgorithmParameters; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SignatureException; -import java.security.interfaces.ECPrivateKey; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.InvalidParameterSpecException; -import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPrivateKeySpec; -import java.security.spec.ECPublicKeySpec; -import javacard.framework.Util; -import javacard.security.CryptoException; -import javacard.security.Key; -import javacard.security.Signature; - - -public class KMEcdsa256NoDigestSignature extends Signature { - - private java.security.Signature sunSigner; - - public KMEcdsa256NoDigestSignature(byte mode, byte[] key, short keyStart, short keyLength) { - KeyFactory kf; - try { - sunSigner = java.security.Signature.getInstance("NONEwithECDSA", "SunEC"); - kf = KeyFactory.getInstance("EC"); - AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", "SunEC"); - //Supported curve secp256r1 - parameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class); - if (mode == Signature.MODE_SIGN) { - byte[] privKey = new byte[keyLength]; - for (short i = 0; i < keyLength; i++) { - privKey[i] = key[keyStart + i]; - } - BigInteger bI = new BigInteger(privKey); - ECPrivateKeySpec prikeyspec = new ECPrivateKeySpec(bI, ecParameters); - ECPrivateKey privkey = (ECPrivateKey) kf.generatePrivate(prikeyspec); - sunSigner.initSign(privkey); - } else { - //Check if the first byte is 04 and remove it. - if (key[keyStart] == 0x04) { - //uncompressed format. - keyStart++; - keyLength--; - } - short i = 0; - byte[] pubx = new byte[keyLength / 2]; - for (; i < keyLength / 2; i++) { - pubx[i] = key[keyStart + i]; - } - byte[] puby = new byte[keyLength / 2]; - for (i = 0; i < keyLength / 2; i++) { - puby[i] = key[keyStart + keyLength / 2 + i]; - } - BigInteger bIX = new BigInteger(pubx); - BigInteger bIY = new BigInteger(puby); - ECPoint point = new ECPoint(bIX, bIY); - ECPublicKeySpec pubkeyspec = new ECPublicKeySpec(point, ecParameters); - ECPublicKey pubkey = (ECPublicKey) kf.generatePublic(pubkeyspec); - sunSigner.initVerify(pubkey); - } - } catch (NoSuchAlgorithmException e) { - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (NoSuchProviderException e) { - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (InvalidParameterSpecException e) { - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidKeySpecException e) { - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidKeyException e) { - CryptoException.throwIt(CryptoException.INVALID_INIT); - } - } - - @Override - public void init(Key key, byte b) throws CryptoException { - - } - - @Override - public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException { - - } - - @Override - public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) - throws CryptoException { - - } - - @Override - public byte getAlgorithm() { - return 0; - } - - @Override - public byte getMessageDigestAlgorithm() { - return 0; - } - - @Override - public byte getCipherAlgorithm() { - return 0; - } - - @Override - public byte getPaddingAlgorithm() { - return 0; - } - - @Override - public short getLength() throws CryptoException { - return 0; - } - - @Override - public void update(byte[] message, short msgStart, short messageLength) throws CryptoException { - byte[] msgBytes = new byte[messageLength]; - for (int i = 0; i < messageLength; i++) { - msgBytes[i] = message[msgStart + i]; - } - try { - if (messageLength > 0) { - sunSigner.update(msgBytes); - } - } catch (SignatureException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - } - - @Override - public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) - throws CryptoException { - short len = 0; - try { - update(bytes, i, i1); - byte[] sig = sunSigner.sign(); - Util.arrayCopyNonAtomic(sig, (short) 0, bytes1, i2, (short) sig.length); - return (short) sig.length; - } catch (SignatureException e) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - return len; - } - - @Override - public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2) - throws CryptoException { - return 0; - } - - @Override - public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) - throws CryptoException { - // Public key operations not handled here. - return false; - } - - @Override - public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2, - short i3) throws CryptoException { - return false; - } -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java deleted file mode 100644 index 64837ace..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.security.HMACKey; - -public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { - - private HMACKey hmacKey; - - public KMHmacKey(HMACKey key) { - hmacKey = key; - } - - public void setKey(byte[] keyData, short kOff, short length) { - hmacKey.setKey(keyData, kOff, length); - } - - public byte getKey(byte[] keyData, short kOff) { - return hmacKey.getKey(keyData, kOff); - } - - public HMACKey getKey() { - return hmacKey; - } - - public short getKeySizeBits() { - return hmacKey.getSize(); - } -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java deleted file mode 100644 index a97377ea..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -public class KMJCardSimApplet extends KMKeymasterApplet { - - KMJCardSimApplet() { - super(new KMJCardSimulator()); - } - - /** - * Installs this applet. - * - * @param bArray the array containing installation parameters - * @param bOffset the starting offset in bArray - * @param bLength the length in bytes of the parameter data in bArray - */ - public static void install(byte[] bArray, short bOffset, byte bLength) { - new KMJCardSimApplet().register(); - } - -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java deleted file mode 100644 index 2086620f..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ /dev/null @@ -1,1408 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import java.math.BigInteger; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.MGF1ParameterSpec; -import java.security.spec.RSAPrivateKeySpec; -import java.security.spec.RSAPublicKeySpec; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.CryptoException; -import javacard.security.DESKey; -import javacard.security.ECPrivateKey; -import javacard.security.ECPublicKey; -import javacard.security.HMACKey; -import javacard.security.Key; -import javacard.security.KeyBuilder; -import javacard.security.KeyPair; -import javacard.security.MessageDigest; -import javacard.security.RSAPrivateKey; -import javacard.security.RSAPublicKey; -import javacard.security.RandomData; -import javacard.security.Signature; -import javacardx.crypto.AEADCipher; -import javacardx.crypto.Cipher; - -import javax.crypto.AEADBadTagException; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.OAEPParameterSpec; -import javax.crypto.spec.PSource; -import javax.crypto.spec.SecretKeySpec; - -import org.globalplatform.upgrade.Element; - -/** - * Simulator only supports 512 bit RSA key pair, 128 AES Key, 128 bit 3Des key, less then 256 bit EC - * Key, and upto 512 bit HMAC key. Also simulator does not support TRNG, so this implementation just - * creates its own RNG using PRNG. - */ -public class KMJCardSimulator implements KMSEProvider { - - public static final short AES_GCM_TAG_LENGTH = 16; - public static final short AES_GCM_NONCE_LENGTH = 12; - public static final short MAX_RND_NUM_SIZE = 64; - public static final short ENTROPY_POOL_SIZE = 16; // simulator does not support 256 bit aes keys - public static final byte[] aesICV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - private static final short RSA_KEY_SIZE = 256; - private static final short CERT_CHAIN_OFFSET = 0; - private static final short CERT_ISSUER_OFFSET = KMConfigurations.CERT_CHAIN_MAX_SIZE; - private static final short CERT_EXPIRY_OFFSET = - (short) (CERT_ISSUER_OFFSET + KMConfigurations.CERT_ISSUER_MAX_SIZE); - private static final short COMPUTED_HMAC_KEY_SIZE = 32; - - public static boolean jcardSim = false; - private static Signature kdf; - private static Signature hmacSignature; - - private static byte[] rngCounter; - private static AESKey aesRngKey; - private static Cipher aesRngCipher; - private static byte[] entropyPool; - private static byte[] rndNum; - private byte[] provisionData; - private KMAESKey masterKey; - private KMECPrivateKey attestationKey; - private KMHmacKey preSharedKey; - private KMHmacKey computedHmacKey; - - private static KMJCardSimulator jCardSimulator = null; - - public static KMJCardSimulator getInstance() { - return jCardSimulator; - } - - // Implements Oracle Simulator based restricted crypto provider - public KMJCardSimulator() { - // Various Keys - kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); - hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); - // RNG - rndNum = JCSystem.makeTransientByteArray(MAX_RND_NUM_SIZE, JCSystem.CLEAR_ON_RESET); - entropyPool = JCSystem.makeTransientByteArray(ENTROPY_POOL_SIZE, JCSystem.CLEAR_ON_RESET); - rngCounter = JCSystem.makeTransientByteArray((short) 8, JCSystem.CLEAR_ON_RESET); - initEntropyPool(entropyPool); - try { - aesRngCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false); - } catch (CryptoException exp) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - aesRngKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - // various ciphers - //Allocate buffer for certificate chain and cert parameters. - // First 2 bytes is reserved for length for all the 3 buffers. - short totalLen = (short) (6 + KMConfigurations.CERT_CHAIN_MAX_SIZE + - KMConfigurations.CERT_ISSUER_MAX_SIZE + KMConfigurations.CERT_EXPIRY_MAX_SIZE); - provisionData = new byte[totalLen]; - jCardSimulator = this; - } - - - public KeyPair createRsaKeyPair() { - KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); - rsaKeyPair.genKeyPair(); - return rsaKeyPair; - } - - - public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, short modLength, - byte[] privBuffer, short privOff, short privLength) { - KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); - RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate(); - privKey.setExponent(privBuffer, privOff, privLength); - privKey.setModulus(modBuffer, modOff, modLength); - return privKey; - - } - - - public KeyPair createECKeyPair() { - KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); - ecKeyPair.genKeyPair(); - return ecKeyPair; - } - - - public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, short privLength) { - KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); - ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); - privKey.setS(privBuffer, privOff, privLength); - return privKey; - } - - - public AESKey createAESKey(short keysize) { - byte[] rndNum = new byte[(short) (keysize / 8)]; - return createAESKey(rndNum, (short) 0, (short) rndNum.length); - } - - public AESKey createAESKey(byte[] buf, short startOff, short length) { - AESKey key = null; - short keysize = (short) (length * 8); - if (keysize == 128) { - key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false); - key.setKey(buf, (short) startOff); - } else if (keysize == 256) { - key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - key.setKey(buf, (short) startOff); - } - return key; - } - - - public DESKey createTDESKey() { - byte[] rndNum = new byte[24]; - newRandomNumber(rndNum, (short) 0, (short) rndNum.length); - return createTDESKey(rndNum, (short) 0, (short) rndNum.length); - } - - - public DESKey createTDESKey(byte[] secretBuffer, short secretOff, short secretLength) { - DESKey triDesKey = - (DESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_DES, KeyBuilder.LENGTH_DES3_3KEY, false); - triDesKey.setKey(secretBuffer, secretOff); - return triDesKey; - } - - - public HMACKey createHMACKey(short keysize) { - if ((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - byte[] rndNum = new byte[(short) (keysize / 8)]; - newRandomNumber(rndNum, (short) 0, (short) (keysize / 8)); - return createHMACKey(rndNum, (short) 0, (short) rndNum.length); - } - - @Override - public short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff) { - switch (alg) { - case KMType.AES: - AESKey aesKey = createAESKey(keysize); - return aesKey.getKey(buf, startOff); - case KMType.DES: - DESKey desKey = createTDESKey(); - return desKey.getKey(buf, startOff); - case KMType.HMAC: - HMACKey hmacKey = createHMACKey(keysize); - return hmacKey.getKey(buf, startOff); - default: - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - return 0; - } - - @Override - public void createAsymmetricKey(byte alg, byte[] privKeyBuf, short privKeyStart, - short privKeyLength, - byte[] pubModBuf, short pubModStart, short pubModLength, short[] lengths) { - switch (alg) { - case KMType.RSA: - if (RSA_KEY_SIZE != privKeyLength || RSA_KEY_SIZE != pubModLength) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - KeyPair rsaKey = createRsaKeyPair(); - RSAPrivateKey privKey = (RSAPrivateKey) rsaKey.getPrivate(); - //Copy exponent. - byte[] exp = new byte[RSA_KEY_SIZE]; - lengths[0] = privKey.getExponent(exp, (short) 0); - if (lengths[0] > privKeyLength) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - Util.arrayFillNonAtomic(privKeyBuf, privKeyStart, privKeyLength, (byte) 0); - Util.arrayCopyNonAtomic(exp, (short) 0, - privKeyBuf, (short) (privKeyStart + privKeyLength - lengths[0]), lengths[0]); - //Copy modulus - byte[] mod = new byte[RSA_KEY_SIZE]; - lengths[1] = privKey.getModulus(mod, (short) 0); - if (lengths[1] > pubModLength) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - Util.arrayFillNonAtomic(pubModBuf, pubModStart, pubModLength, (byte) 0); - Util.arrayCopyNonAtomic(mod, (short) 0, - pubModBuf, (short) (pubModStart + pubModLength - lengths[1]), lengths[1]); - break; - case KMType.EC: - KeyPair ecKey = createECKeyPair(); - ECPublicKey ecPubKey = (ECPublicKey) ecKey.getPublic(); - ECPrivateKey ecPrivKey = (ECPrivateKey) ecKey.getPrivate(); - lengths[0] = ecPrivKey.getS(privKeyBuf, privKeyStart); - lengths[1] = ecPubKey.getW(pubModBuf, pubModStart); - if (lengths[0] > privKeyLength || lengths[1] > pubModLength) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - break; - default: - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - } - - @Override - public boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, - short length) { - switch (alg) { - case KMType.AES: - AESKey aesKey = createAESKey(buf, startOff, length); - break; - case KMType.DES: - DESKey desKey = createTDESKey(buf, startOff, length); - break; - case KMType.HMAC: - HMACKey hmacKey = createHMACKey(buf, startOff, length); - break; - default: - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - return true; - } - - @Override - public boolean importAsymmetricKey(byte alg, byte[] privKeyBuf, short privKeyStart, - short privKeyLength, byte[] pubModBuf, short pubModStart, short pubModLength) { - switch (alg) { - case KMType.RSA: - RSAPrivateKey rsaKey = createRsaKey(pubModBuf, pubModStart, pubModLength, privKeyBuf, - privKeyStart, privKeyLength); - break; - case KMType.EC: - ECPrivateKey ecPrivKey = createEcKey(privKeyBuf, privKeyStart, privKeyLength); - break; - default: - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - return true; - } - - - public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, short secretLength) { - HMACKey key = null; - key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, - KeyBuilder.LENGTH_HMAC_SHA_256_BLOCK_64, false); - key.setKey(secretBuffer, secretOff, secretLength); - return key; - } - - @Override - public short aesGCMEncrypt( - byte[] keyBuf, - short keyStart, - short keyLen, - byte[] secret, - short secretStart, - short secretLen, - byte[] encSecret, - short encSecretStart, - byte[] nonce, - short nonceStart, - short nonceLen, - byte[] authData, - short authDataStart, - short authDataLen, - byte[] authTag, - short authTagStart, - short authTagLen) { - //Create the sun jce compliant aes key - if (keyLen != 32 && keyLen != 16) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - java.security.Key aesKey = new SecretKeySpec(keyBuf, keyStart, keyLen, "AES"); - // Create the cipher - javax.crypto.Cipher cipher = null; - try { - cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - // Copy nonce - if (nonceLen != AES_GCM_NONCE_LENGTH) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - byte[] iv = new byte[AES_GCM_NONCE_LENGTH]; - Util.arrayCopyNonAtomic(nonce, nonceStart, iv, (short) 0, AES_GCM_NONCE_LENGTH); - // Init Cipher - GCMParameterSpec spec = new GCMParameterSpec(AES_GCM_TAG_LENGTH * 8, nonce, nonceStart, - AES_GCM_NONCE_LENGTH); - try { - cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, spec); - } catch (InvalidKeyException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } - // Create auth data - byte[] aad = new byte[authDataLen]; - Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, authDataLen); - cipher.updateAAD(aad); - // Encrypt secret - short len = 0; - byte[] outputBuf = new byte[cipher.getOutputSize(secretLen)]; - try { - len = (short) (cipher.doFinal(secret, secretStart, secretLen, outputBuf, (short) 0)); - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - // Extract Tag appended at the end. - Util.arrayCopyNonAtomic(outputBuf, (short) (len - AES_GCM_TAG_LENGTH), authTag, authTagStart, - AES_GCM_TAG_LENGTH); - //Copy the encrypted data - Util.arrayCopyNonAtomic(outputBuf, (short) 0, encSecret, encSecretStart, - (short) (len - AES_GCM_TAG_LENGTH)); - return (short) (len - AES_GCM_TAG_LENGTH); - } - - public boolean aesGCMDecrypt( - byte[] keyBuf, - short keyStart, - short keyLen, - byte[] encSecret, - short encSecretStart, - short encSecretLen, - byte[] secret, - short secretStart, - byte[] nonce, - short nonceStart, - short nonceLen, - byte[] authData, - short authDataStart, - short authDataLen, - byte[] authTag, - short authTagStart, - short authTagLen) { - // Create the sun jce compliant aes key - if (keyLen != 32 && keyLen != 16) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - java.security.Key aesKey = new SecretKeySpec(keyBuf, keyStart, keyLen, - "AES"); - // Create the cipher - javax.crypto.Cipher cipher = null; - try { - cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - // Copy nonce - if (nonceLen != AES_GCM_NONCE_LENGTH) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - byte[] iv = new byte[AES_GCM_NONCE_LENGTH]; - Util.arrayCopyNonAtomic(nonce, nonceStart, iv, (short) 0, - AES_GCM_NONCE_LENGTH); - // Init Cipher - GCMParameterSpec spec = new GCMParameterSpec(authTagLen * 8, nonce, - nonceStart, AES_GCM_NONCE_LENGTH); - try { - cipher.init(javax.crypto.Cipher.DECRYPT_MODE, aesKey, spec); - } catch (InvalidKeyException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } - // Create auth data - byte[] aad = new byte[authDataLen]; - Util.arrayCopyNonAtomic(authData, authDataStart, aad, (short) 0, - authDataLen); - cipher.updateAAD(aad); - // Append the auth tag at the end of data - byte[] inputBuf = new byte[(short) (encSecretLen + authTagLen)]; - Util.arrayCopyNonAtomic(encSecret, encSecretStart, inputBuf, (short) 0, - encSecretLen); - Util.arrayCopyNonAtomic(authTag, authTagStart, inputBuf, encSecretLen, - authTagLen); - // Decrypt - short len = 0; - byte[] outputBuf = new byte[cipher.getOutputSize((short) inputBuf.length)]; - try { - len = (short) (cipher.doFinal(inputBuf, (short) 0, - (short) inputBuf.length, outputBuf, (short) 0)); - } catch (AEADBadTagException e) { - e.printStackTrace(); - return false; - } catch (ShortBufferException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (BadPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - // Copy the decrypted data - Util.arrayCopyNonAtomic(outputBuf, (short) 0, secret, secretStart, len); - return true; - } - - @Override - public void getTrueRandomNumber(byte[] buf, short start, short length) { - Util.arrayCopy(entropyPool, (short) 0, buf, start, length); - } - - public HMACKey cmacKdf(byte[] keyMaterial, short keyMaterialStart, short keyMaterialLen, - byte[] label, - short labelStart, short labelLen, byte[] context, short contextStart, short contextLength) { - // This is hardcoded to requirement - 32 byte output with two concatenated 16 bytes K1 and K2. - final byte n = 2; // hardcoded - final byte[] L = {0, 0, 1, - 0}; // [L] 256 bits - hardcoded 32 bits as per reference impl in keymaster. - final byte[] zero = {0}; // byte - byte[] iBuf = new byte[]{0, 0, 0, 0}; // [i] counter - 32 bits - byte[] keyOut = new byte[(short) (n * 16)]; - Signature prf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); - AESKey key = (AESKey) KeyBuilder - .buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_256, false); - key.setKey(keyMaterial, keyMaterialStart); - prf.init(key, Signature.MODE_SIGN); - byte i = 1; - short pos = 0; - while (i <= n) { - iBuf[3] = i; - prf.update(iBuf, (short) 0, (short) 4); // 4 bytes of iBuf with counter in it - prf.update(label, labelStart, labelLen); // label - prf.update(zero, (short) 0, (short) 1); // 1 byte of 0x00 - prf.update(context, contextStart, contextLength); // context - pos = prf.sign(L, (short) 0, (short) 4, keyOut, pos); // 4 bytes of L - signature of 16 bytes - i++; - } - return createHMACKey(keyOut, (short) 0, (short) keyOut.length); - } - - @Override - public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, - short labelStart, short labelLen, byte[] context, short contextStart, short contextLength, - byte[] keyBuf, short keyStart) { - KMHmacKey key = (KMHmacKey) pSharedKey; - short keyMaterialLen = key.getKeySizeBits(); - keyMaterialLen = (short) (keyMaterialLen / 8); - short keyMaterialStart = 0; - byte[] keyMaterial = new byte[keyMaterialLen]; - key.getKey(keyMaterial, keyMaterialStart); - HMACKey hmacKey = cmacKdf(keyMaterial, keyMaterialStart, keyMaterialLen, label, labelStart, - labelLen, context, contextStart, contextLength); - return hmacKey.getKey(keyBuf, keyStart); - } - - - public short hmacSign(HMACKey key, byte[] data, short dataStart, short dataLength, byte[] mac, - short macStart) { - hmacSignature.init(key, Signature.MODE_SIGN); - return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); - } - - @Override - public short hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, - short dataLength, byte[] signature, short signatureStart) { - KMAESKey aesKey = (KMAESKey) masterkey; - short keyLen = (short) (aesKey.getKeySizeBits() / 8); - byte[] keyData = new byte[keyLen]; - aesKey.getKey(keyData, (short) 0); - return hmacSign(keyData, (short) 0, keyLen, data, dataStart, dataLength, - signature, signatureStart); - } - - @Override - public boolean hmacVerify(KMComputedHmacKey key, byte[] data, short dataStart, - short dataLength, byte[] mac, short macStart, short macLength) { - KMHmacKey hmacKey = (KMHmacKey) key; - hmacSignature.init(hmacKey.getKey(), Signature.MODE_VERIFY); - return hmacSignature.verify(data, dataStart, dataLength, mac, macStart, - macLength); - } - - @Override - public short hmacSign(byte[] keyBuf, short keyStart, short keyLength, byte[] data, - short dataStart, short dataLength, byte[] mac, short macStart) { - HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); - return hmacSign(key, data, dataStart, dataLength, mac, macStart); - } - - @Override - public short rsaDecipherOAEP256(byte[] secret, short secretStart, short secretLength, - byte[] modBuffer, short modOff, short modLength, - byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart) { - KMCipher cipher = createRsaDecipher( - KMType.RSA_OAEP, KMType.SHA2_256, secret, secretStart, secretLength, modBuffer, modOff, - modLength); - return cipher.doFinal( - inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); - } - - @Override - public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, - byte blockMode, - byte[] keyBuf, short keyStart, short keyLength, - byte[] ivBuf, short ivStart, short ivLength, short macLength) { - switch (alg) { - case KMType.AES: - case KMType.DES: - if (blockMode != KMType.GCM) { - KMCipher cipher = createSymmetricCipher(alg, purpose, blockMode, padding, keyBuf, - keyStart, keyLength, - ivBuf, ivStart, ivLength); - return new KMOperationImpl(cipher); - } else { - KMCipher aesGcm = createAesGcmCipher(purpose, macLength, keyBuf, keyStart, keyLength, - ivBuf, ivStart, ivLength); - return new KMOperationImpl(aesGcm); - } - case KMType.HMAC: - Signature signerVerifier = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, - keyLength); - return new KMOperationImpl(signerVerifier); - default: - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } - return null; - } - - @Override - public KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey) { - KMOperationImpl opr = null; - KMHmacKey key = (KMHmacKey) computedHmacKey; - Signature signerVerifier = createHmacSignerVerifier(KMType.VERIFY, KMType.SHA2_256, key.getKey()); - return new KMOperationImpl(signerVerifier); - } - - @Override - public KMOperation initAsymmetricOperation(byte purpose, byte alg, byte padding, byte digest, - byte[] privKeyBuf, short privKeyStart, short privKeyLength, - byte[] pubModBuf, short pubModStart, short pubModLength) { - if (alg == KMType.RSA) { - switch (purpose) { - case KMType.SIGN: - Signature signer = - createRsaSigner( - digest, - padding, - privKeyBuf, - privKeyStart, - privKeyLength, - pubModBuf, - pubModStart, - pubModLength); - return new KMOperationImpl(signer); - case KMType.DECRYPT: - KMCipher decipher = - createRsaDecipher( - padding, digest, privKeyBuf, privKeyStart, privKeyLength, pubModBuf, pubModStart, - pubModLength); - return new KMOperationImpl(decipher); - default: - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - } else if (alg == KMType.EC) { - switch (purpose) { - case KMType.SIGN: - Signature signer = - createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength); - return new KMOperationImpl(signer); - default: - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - } - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - return null; - } - - public KMCipher createRsaDecipher(short padding, short digest, byte[] secret, short secretStart, - short secretLength, byte[] modBuffer, short modOff, short modLength) { - byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0); - if (cipherAlg == Cipher.ALG_RSA_PKCS1_OAEP) { - return createRsaOAEP256Cipher(KMType.DECRYPT, (byte) digest, secret, secretStart, - secretLength, modBuffer, modOff, modLength); - } - Cipher rsaCipher = Cipher.getInstance(cipherAlg, false); - RSAPrivateKey key = (RSAPrivateKey) KeyBuilder - .buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); - key.setExponent(secret, secretStart, secretLength); - key.setModulus(modBuffer, modOff, modLength); - rsaCipher.init(key, Cipher.MODE_DECRYPT); - KMCipherImpl inst = new KMCipherImpl(rsaCipher); - inst.setCipherAlgorithm(KMType.RSA); - inst.setMode(KMType.DECRYPT); - inst.setPaddingAlgorithm(padding); - return inst; - } - - private KMCipher createRsaOAEP256Cipher(byte mode, byte digest, - byte[] secret, short secretStart, short secretLen, - byte[] modBuffer, short modOff, short modLength) { - // Convert byte arrays into keys - byte[] exp = null; - byte[] mod = new byte[modLength]; - if (secret != null) { - exp = new byte[secretLen]; - Util.arrayCopyNonAtomic(secret, secretStart, exp, (short) 0, secretLen); - } else { - exp = new byte[]{0x01, 0x00, 0x01}; - } - Util.arrayCopyNonAtomic(modBuffer, modOff, mod, (short) 0, modLength); - String modString = toHexString(mod); - String expString = toHexString(exp); - BigInteger modInt = new BigInteger(modString, 16); - BigInteger expInt = new BigInteger(expString, 16); - javax.crypto.Cipher rsaCipher = null; - try { - KeyFactory kf = KeyFactory.getInstance("RSA"); - // Create cipher with oaep padding - OAEPParameterSpec oaepSpec = null; - if (digest == KMType.SHA2_256) { - oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } else { - oaepSpec = new OAEPParameterSpec("SHA1", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } - rsaCipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPPadding", "SunJCE"); - if (mode == KMType.ENCRYPT) { - RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modInt, expInt); - java.security.interfaces.RSAPublicKey pubKey = (java.security.interfaces.RSAPublicKey) kf - .generatePublic(pubSpec); - rsaCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, pubKey, oaepSpec); - } else { - RSAPrivateKeySpec privSpec = new RSAPrivateKeySpec(modInt, expInt); - java.security.interfaces.RSAPrivateKey privKey = (java.security.interfaces.RSAPrivateKey) kf - .generatePrivate(privSpec); - rsaCipher.init(javax.crypto.Cipher.DECRYPT_MODE, privKey, oaepSpec); - } - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (InvalidKeySpecException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (InvalidKeyException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } - KMCipherImpl ret = new KMCipherImpl(rsaCipher); - ret.setCipherAlgorithm(KMType.RSA); - ret.setPaddingAlgorithm(KMType.RSA_OAEP); - ret.setMode(mode); - return ret; - } - - private String toHexString(byte[] num) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < num.length; i++) { - sb.append(String.format("%02X", num[i])); - } - return sb.toString(); - } - - public Signature createRsaSigner(short digest, short padding, byte[] secret, - short secretStart, short secretLength, byte[] modBuffer, - short modOff, short modLength) { - short alg = mapSignature256Alg(KMType.RSA, (byte) padding); - if (padding == KMType.PADDING_NONE || - (padding == KMType.RSA_PKCS1_1_5_SIGN && digest == KMType.DIGEST_NONE)) { - return createNoDigestSigner(padding, secret, secretStart, secretLength, - modBuffer, modOff, modLength); - } - Signature rsaSigner = Signature.getInstance((byte) alg, false); - RSAPrivateKey key = (RSAPrivateKey) KeyBuilder - .buildKey(KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_2048, false); - key.setExponent(secret, secretStart, secretLength); - key.setModulus(modBuffer, modOff, modLength); - rsaSigner.init(key, Signature.MODE_SIGN); - return rsaSigner; - } - - private Signature createNoDigestSigner(short padding, - byte[] secret, short secretStart, short secretLength, - byte[] modBuffer, short modOff, short modLength) { - Cipher rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); - RSAPrivateKey key = (RSAPrivateKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PRIVATE, - KeyBuilder.LENGTH_RSA_2048, false); - key.setExponent(secret, secretStart, secretLength); - key.setModulus(modBuffer, modOff, modLength); - rsaCipher.init(key, Cipher.MODE_DECRYPT); - KMRsa2048NoDigestSignature inst = new KMRsa2048NoDigestSignature(rsaCipher, (byte) padding, - modBuffer, modOff, modLength); - return inst; - } - - - public Signature createEcSigner(short digest, byte[] secret, short secretStart, - short secretLength) { - short alg = mapSignature256Alg(KMType.EC, (byte) 0); - Signature ecSigner; - if (digest == KMType.DIGEST_NONE) { - ecSigner = new KMEcdsa256NoDigestSignature(Signature.MODE_SIGN, secret, secretStart, - secretLength); - } else { - ECPrivateKey key = (ECPrivateKey) KeyBuilder - .buildKey(KeyBuilder.TYPE_EC_FP_PRIVATE, KeyBuilder.LENGTH_EC_FP_256, false); - key.setS(secret, secretStart, secretLength); - ecSigner = Signature.getInstance((byte) alg, false); - ecSigner.init(key, Signature.MODE_SIGN); - } - return ecSigner; - } - - - public KMCipher createSymmetricCipher( - short cipherAlg, short mode, short blockMode, short padding, byte[] secret, short secretStart, - short secretLength) { - return createSymmetricCipher(cipherAlg, mode, blockMode, padding, secret, secretStart, - secretLength, null, (short) 0, (short) 0); - } - - - public KMCipher createSymmetricCipher(short alg, short purpose, short blockMode, short padding, - byte[] secret, - short secretStart, short secretLength, - byte[] ivBuffer, short ivStart, short ivLength) { - Key key = null; - Cipher symmCipher = null; - short len = 0; - switch (secretLength) { - case 32: - len = KeyBuilder.LENGTH_AES_256; - break; - case 16: - len = KeyBuilder.LENGTH_AES_128; - break; - case 24: - len = KeyBuilder.LENGTH_DES3_3KEY; - break; - default: - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - break; - } - short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode); - switch (cipherAlg) { - case Cipher.ALG_AES_BLOCK_128_CBC_NOPAD: - key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES, len, false); - ((AESKey) key).setKey(secret, secretStart); - symmCipher = Cipher.getInstance((byte) cipherAlg, false); - symmCipher.init(key, mapPurpose(purpose), ivBuffer, ivStart, ivLength); - break; - case Cipher.ALG_AES_BLOCK_128_ECB_NOPAD: - key = KeyBuilder.buildKey(KeyBuilder.TYPE_AES, len, false); - ((AESKey) key).setKey(secret, secretStart); - symmCipher = Cipher.getInstance((byte) cipherAlg, false); - symmCipher.init(key, mapPurpose(purpose)); - break; - case Cipher.ALG_DES_CBC_NOPAD: - key = KeyBuilder.buildKey(KeyBuilder.TYPE_DES, len, false); - ((DESKey) key).setKey(secret, secretStart); - symmCipher = Cipher.getInstance((byte) cipherAlg, false); - //While sending back the iv send only 8 bytes. - symmCipher.init(key, mapPurpose(purpose), ivBuffer, ivStart, (short) 8); - break; - case Cipher.ALG_DES_ECB_NOPAD: - key = KeyBuilder.buildKey(KeyBuilder.TYPE_DES, len, false); - ((DESKey) key).setKey(secret, secretStart); - symmCipher = Cipher.getInstance((byte) cipherAlg, false); - symmCipher.init(key, mapPurpose(purpose)); - break; - case Cipher.ALG_AES_CTR: // uses SUNJCE - return createAesCtrCipherNoPad(purpose, secret, secretStart, secretLength, ivBuffer, - ivStart, ivLength); - default://This should never happen - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - KMCipherImpl cipher = new KMCipherImpl(symmCipher); - cipher.setCipherAlgorithm(alg); - cipher.setPaddingAlgorithm(padding); - cipher.setMode(purpose); - cipher.setBlockMode(blockMode); - return cipher; - } - - private byte mapPurpose(short purpose) { - switch (purpose) { - case KMType.ENCRYPT: - return Cipher.MODE_ENCRYPT; - case KMType.DECRYPT: - return Cipher.MODE_DECRYPT; - case KMType.SIGN: - return Signature.MODE_SIGN; - case KMType.VERIFY: - return Signature.MODE_VERIFY; - } - return -1; - } - - private byte mapSignature256Alg(byte alg, byte padding) { - switch (alg) { - case KMType.RSA: - switch (padding) { - case KMType.RSA_PKCS1_1_5_SIGN: - return Signature.ALG_RSA_SHA_256_PKCS1; - case KMType.RSA_PSS: - return Signature.ALG_RSA_SHA_256_PKCS1_PSS; - } - break; - case KMType.EC: - return Signature.ALG_ECDSA_SHA_256; - case KMType.HMAC: - return Signature.ALG_HMAC_SHA_256; - } - return -1; - } - - private byte mapCipherAlg(byte alg, byte padding, byte blockmode) { - switch (alg) { - case KMType.AES: - switch (blockmode) { - case KMType.ECB: - return Cipher.ALG_AES_BLOCK_128_ECB_NOPAD; - case KMType.CBC: - return Cipher.ALG_AES_BLOCK_128_CBC_NOPAD; - case KMType.CTR: - return Cipher.ALG_AES_CTR; - case KMType.GCM: - return AEADCipher.ALG_AES_GCM; - } - break; - case KMType.DES: - switch (blockmode) { - case KMType.ECB: - return Cipher.ALG_DES_ECB_NOPAD; - case KMType.CBC: - return Cipher.ALG_DES_CBC_NOPAD; - } - break; - case KMType.RSA: - switch (padding) { - case KMType.PADDING_NONE: - return Cipher.ALG_RSA_NOPAD; - case KMType.RSA_PKCS1_1_5_ENCRYPT: - return Cipher.ALG_RSA_PKCS1; - case KMType.RSA_OAEP: - return Cipher.ALG_RSA_PKCS1_OAEP; - } - break; - } - return -1; - } - - private KMCipher createAesCtrCipherNoPad(short mode, byte[] secret, short secretStart, - short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { - if (secretLength != 16 && secretLength != 32) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (ivLength != 16) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (mode != KMType.ENCRYPT && mode != KMType.DECRYPT) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - //Create the sun jce compliant aes key - byte[] keyMaterial = new byte[secretLength]; - Util.arrayCopyNonAtomic(secret, secretStart, keyMaterial, (short) 0, secretLength); - java.security.Key aesKey = new SecretKeySpec(keyMaterial, (short) 0, keyMaterial.length, "AES"); - // Create the cipher - javax.crypto.Cipher cipher = null; - try { - cipher = javax.crypto.Cipher.getInstance("AES/CTR/NoPadding", "SunJCE"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - // Copy nonce - byte[] iv = new byte[ivLength]; - Util.arrayCopyNonAtomic(ivBuffer, ivStart, iv, (short) 0, ivLength); - // Init Cipher - IvParameterSpec ivSpec = new IvParameterSpec(iv); - try { - if (mode == KMType.ENCRYPT) { - cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, aesKey, ivSpec); - } else { - cipher.init(javax.crypto.Cipher.DECRYPT_MODE, aesKey, ivSpec); - } - } catch (InvalidKeyException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } - KMCipherImpl ret = new KMCipherImpl(cipher); - ret.setCipherAlgorithm(KMType.AES); - ret.setMode(mode); - ret.setPaddingAlgorithm((short) 0); - ret.setBlockMode(KMType.CTR); - return ret; - } - - private Signature createHmacSignerVerifier(short purpose, short digest, - byte[] secret, short secretStart, short secretLength) { - HMACKey key = createHMACKey(secret, secretStart, secretLength); - return createHmacSignerVerifier(purpose, digest, key); - } - - private Signature createHmacSignerVerifier(short purpose, short digest, HMACKey key) { - byte alg = Signature.ALG_HMAC_SHA_256; - if (digest != KMType.SHA2_256) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - Signature hmacSignerVerifier = Signature.getInstance((byte) alg, false); - hmacSignerVerifier.init(key, (byte) purpose); - return hmacSignerVerifier; - } - - - public KMCipher createAesGcmCipher(short mode, short tagLen, byte[] secret, short secretStart, - short secretLength, - byte[] ivBuffer, short ivStart, short ivLength) { - if (secretLength != 16 && secretLength != 32) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (ivLength != AES_GCM_NONCE_LENGTH) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (mode != KMType.ENCRYPT && mode != KMType.DECRYPT) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - //Create the sun jce compliant aes key - byte[] keyMaterial = new byte[secretLength]; - Util.arrayCopyNonAtomic(secret, secretStart, keyMaterial, (short) 0, secretLength); - java.security.Key aesKey = new SecretKeySpec(keyMaterial, (short) 0, keyMaterial.length, "AES"); - // Create the cipher - javax.crypto.Cipher cipher = null; - try { - cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding", "SunJCE"); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - // Copy nonce - byte[] iv = new byte[AES_GCM_NONCE_LENGTH]; - Util.arrayCopyNonAtomic(ivBuffer, ivStart, iv, (short) 0, AES_GCM_NONCE_LENGTH); - // Init Cipher - GCMParameterSpec spec = new GCMParameterSpec(tagLen, iv, (short) 0, AES_GCM_NONCE_LENGTH); - try { - if (mode == KMType.ENCRYPT) { - mode = javax.crypto.Cipher.ENCRYPT_MODE; - } else { - mode = javax.crypto.Cipher.DECRYPT_MODE; - } - cipher.init(mode, aesKey, spec); - } catch (InvalidKeyException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.INVALID_INIT); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - } - KMCipherImpl ret = new KMCipherImpl(cipher); - ret.setCipherAlgorithm(KMType.AES); - ret.setMode(mode); - ret.setPaddingAlgorithm((short) 0); - ret.setBlockMode(KMType.GCM); - return ret; - } - - private void initEntropyPool(byte[] pool) { - byte index = 0; - RandomData trng; - while (index < rngCounter.length) { - rngCounter[index++] = 0; - } - try { - trng = RandomData.getInstance(RandomData.ALG_TRNG); - trng.nextBytes(pool, (short) 0, (short) pool.length); - } catch (CryptoException exp) { - if (exp.getReason() == CryptoException.NO_SUCH_ALGORITHM) { - // simulator does not support TRNG algorithm. So, PRNG algorithm (deprecated) is used. - trng = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM); - trng.nextBytes(pool, (short) 0, (short) pool.length); - } else { - ISOException.throwIt(ISO7816.SW_UNKNOWN); - } - } - } - - // Generate a secure random number from existing entropy pool. This uses aes ecb algorithm with - // 8 byte rngCounter and 16 byte block size. - @Override - public void newRandomNumber(byte[] num, short startOff, short length) { - KMRepository repository = KMRepository.instance(); - byte[] bufPtr = repository.getHeap(); - short countBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); - short randBufInd = repository.alloc(KMKeymasterApplet.AES_BLOCK_SIZE); - short len = KMKeymasterApplet.AES_BLOCK_SIZE; - aesRngKey.setKey(entropyPool, (short) 0); - aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); - while (length > 0) { - if (length < len) { - len = length; - } - // increment rngCounter by one - incrementCounter(); - // copy the 8 byte rngCounter into the 16 byte rngCounter buffer. - Util.arrayCopy(rngCounter, (short) 0, bufPtr, countBufInd, (short) rngCounter.length); - // encrypt the rngCounter buffer with existing entropy which forms the aes key. - aesRngCipher.doFinal( - bufPtr, countBufInd, KMKeymasterApplet.AES_BLOCK_SIZE, bufPtr, randBufInd); - // copy the encrypted rngCounter block to buffer passed in the argument - Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); - length = (short) (length - len); - startOff = (short) (startOff + len); - } - } - - // increment 8 byte rngCounter by one - private void incrementCounter() { - // start with least significant byte - short index = (short) (rngCounter.length - 1); - while (index >= 0) { - // if the msb of current byte is set then it will be negative - if (rngCounter[index] < 0) { - // then increment the rngCounter - rngCounter[index]++; - // is the msb still set? i.e. no carry over - if (rngCounter[index] < 0) { - break; // then break - } else { - index--; // else go to the higher order byte - } - } else { - // if msb is not set then increment the rngCounter - rngCounter[index]++; - break; - } - } - } - - @Override - public void addRngEntropy(byte[] num, short offset, short length) { - // Maximum length can be 256 bytes. But currently we support max 32 bytes seed. - // Get existing entropy pool. - if (length > 32) { - length = 32; - } - // Create new temporary pool. - // Populate the new pool with the entropy which is derived from current entropy pool. - newRandomNumber(rndNum, (short) 0, (short) entropyPool.length); - // Copy the entropy to the current pool - updates the entropy pool. - Util.arrayCopy(rndNum, (short) 0, entropyPool, (short) 0, (short) entropyPool.length); - short index = 0; - short randIndex = 0; - // XOR the seed received from the master in the entropy pool - 16 bytes (entPool.length). - // at a time. - while (index < length) { - entropyPool[randIndex] = (byte) (entropyPool[randIndex] ^ num[(short) (offset + index)]); - randIndex++; - index++; - if (randIndex >= entropyPool.length) { - randIndex = 0; - } - } - } - - @Override - public KMAttestationCert getAttestationCert(boolean rsaCert) { - return KMAttestationCertImpl.instance(rsaCert); - } - - @Override - public KMPKCS8Decoder getPKCS8DecoderInstance() { - return KMPKCS8DecoderImpl.instance(); - } - - private short getProvisionDataBufferOffset(byte dataType) { - switch(dataType) { - case CERTIFICATE_CHAIN: - return CERT_CHAIN_OFFSET; - case CERTIFICATE_ISSUER: - return CERT_ISSUER_OFFSET; - case CERTIFICATE_EXPIRY: - return CERT_EXPIRY_OFFSET; - default: - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - return 0; - } - - private void persistProvisionData(byte[] buf, short off, short len, short maxSize, short copyToOff) { - if (len > maxSize) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - JCSystem.beginTransaction(); - Util.setShort(provisionData, copyToOff, len); - Util.arrayCopyNonAtomic(buf, off, provisionData, (short) (copyToOff + 2), len); - JCSystem.commitTransaction(); - } - - private void persistCertificateChain(byte[] certChain, short certChainOff, short certChainLen) { - persistProvisionData(certChain, certChainOff, certChainLen, - KMConfigurations.CERT_CHAIN_MAX_SIZE, CERT_CHAIN_OFFSET); - } - - private void persistCertficateIssuer(byte[] certIssuer, short certIssuerOff, short certIssuerLen) { - persistProvisionData(certIssuer, certIssuerOff, certIssuerLen, - KMConfigurations.CERT_ISSUER_MAX_SIZE, CERT_ISSUER_OFFSET); - } - - private void persistCertificateExpiryTime(byte[] certExpiry, short certExpiryOff, short certExpiryLen) { - persistProvisionData(certExpiry, certExpiryOff, certExpiryLen, - KMConfigurations.CERT_EXPIRY_MAX_SIZE, CERT_EXPIRY_OFFSET); - } - - @Override - public void persistProvisionData(byte[] buffer, short certChainOff, short certChainLen, - short certIssuerOff, short certIssuerLen, short certExpiryOff ,short certExpiryLen) { - // All the buffers uses first two bytes for length. The certificate chain - // is stored as shown below. - // _____________________________________________________ - // | 2 Bytes | 1 Byte | 3 Bytes | Cert1 | Cert2 |... - // |_________|________|_________|_______|________|_______ - // First two bytes holds the length of the total buffer. - // CBOR format: - // Next single byte holds the byte string header. - // Next 3 bytes holds the total length of the certificate chain. - // clear buffer. - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(provisionData, (short) 0, (short) provisionData.length, (byte) 0); - JCSystem.commitTransaction(); - // Persist data. - persistCertificateChain(buffer, certChainOff, certChainLen); - persistCertficateIssuer(buffer, certIssuerOff, certIssuerLen); - persistCertificateExpiryTime(buffer, certExpiryOff, certExpiryLen); - } - - @Override - public short readProvisionedData(byte dataType, byte[] buf, short offset) { - short provisionBufOffset = getProvisionDataBufferOffset(dataType); - short len = Util.getShort(provisionData, provisionBufOffset); - Util.arrayCopyNonAtomic(provisionData, (short) (2 + provisionBufOffset), buf, offset, len); - return len; - } - - @Override - public short getProvisionedDataLength(byte dataType) { - short provisionBufOffset = getProvisionDataBufferOffset(dataType); - return Util.getShort(provisionData, provisionBufOffset); - } - - @Override - public short ecSign256(KMAttestationKey attestationKey, - byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart) { - - ECPrivateKey key = ((KMECPrivateKey) attestationKey).getPrivateKey(); - - Signature signer = Signature - .getInstance(Signature.ALG_ECDSA_SHA_256, false); - signer.init(key, Signature.MODE_SIGN); - return signer.sign(inputDataBuf, inputDataStart, inputDataLength, - outputDataBuf, outputDataStart); - } - - @Override - public boolean isBootSignalEventSupported() { - return false; - } - - @Override - public boolean isDeviceRebooted() { - return false; - } - - @Override - public void clearDeviceBooted(boolean resetBootFlag) { - } - - @Override - public void onSave(Element ele) { - } - - @Override - public void onRestore(Element ele, short oldVersion, short currentVersion) { - } - - @Override - public short getBackupPrimitiveByteCount() { - return 0; - } - - @Override - public short getBackupObjectCount() { - return 0; - } - - @Override - public boolean isUpgrading() { - return false; - } - - @Override - public KMMasterKey createMasterKey(short keySizeBits) { - if (masterKey == null) { - AESKey key = (AESKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_AES, keySizeBits, false); - masterKey = new KMAESKey(key); - short keyLen = (short) (keySizeBits / 8); - byte[] keyData = new byte[keyLen]; - getTrueRandomNumber(keyData, (short) 0, keyLen); - masterKey.setKey(keyData, (short) 0); - } - return (KMMasterKey) masterKey; - } - - @Override - public KMAttestationKey createAttestationKey(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); - } - attestationKey.setS(keyData, offset, length); - return (KMAttestationKey) attestationKey; - } - - @Override - public KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length) { - short lengthInBits = (short) (length * 8); - if ((lengthInBits % 8 != 0) || !(lengthInBits >= 64 && lengthInBits <= 512)) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (preSharedKey == null) { - HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, lengthInBits, - false); - preSharedKey = new KMHmacKey(key); - } - preSharedKey.setKey(keyData, offset, length); - return (KMPreSharedKey) preSharedKey; - } - - @Override - public KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length) { - if (length != COMPUTED_HMAC_KEY_SIZE) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - if (computedHmacKey == null) { - HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), - false); - computedHmacKey = new KMHmacKey(key); - } - computedHmacKey.setKey(keyData, offset, length); - return (KMComputedHmacKey) computedHmacKey; - } - - @Override - public KMMasterKey getMasterKey() { - return (KMMasterKey) masterKey; - } - - @Override - public KMAttestationKey getAttestationKey() { - return (KMAttestationKey) attestationKey; - } - - @Override - public KMPreSharedKey getPresharedKey() { - return (KMPreSharedKey) preSharedKey; - } - - @Override - public KMComputedHmacKey getComputedHmacKey() { - return (KMComputedHmacKey) computedHmacKey; - } - - @Override - public void releaseAllOperations() { - //Do nothing. - } - - @Override - public short messageDigest256(byte[] inBuff, short inOffset, - short inLength, byte[] outBuff, short outOffset) { - MessageDigest mDigest = null; - short len = 0; - try { - mDigest = MessageDigest.getInitializedMessageDigestInstance(MessageDigest.ALG_SHA_256, false); - len = mDigest.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); - } catch (Exception e) { - - } - return len; - } - -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java deleted file mode 100644 index 761b388a..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.security.Signature; - -public class KMOperationImpl implements KMOperation { - - private KMCipher cipher; - private Signature signature; - - public KMOperationImpl(KMCipher cipher) { - this.cipher = cipher; - this.signature = null; - } - - public KMOperationImpl(Signature sign) { - this.cipher = null; - this.signature = sign; - } - - @Override - public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart) { - return cipher - .update(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); - } - - @Override - public short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength) { - signature.update(inputDataBuf, inputDataStart, inputDataLength); - return 0; - } - - @Override - public short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] outputDataBuf, short outputDataStart) { - return cipher - .doFinal(inputDataBuf, inputDataStart, inputDataLength, outputDataBuf, outputDataStart); - } - - @Override - public short sign(byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] signBuf, short signStart) { - return signature.sign(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart); - } - - @Override - public boolean verify(byte[] inputDataBuf, short inputDataStart, short inputDataLength, - byte[] signBuf, short signStart, short signLength) { - return signature - .verify(inputDataBuf, inputDataStart, inputDataLength, signBuf, signStart, signLength); - } - - @Override - public void abort() { - // do nothing - } - - @Override - public void updateAAD(byte[] dataBuf, short dataStart, short dataLength) { - cipher.updateAAD(dataBuf, dataStart, dataLength); - } - - @Override - public short getAESGCMOutputSize(short dataSize, short macLength) { - return cipher.getAesGcmOutputSize(dataSize, macLength); - } -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java deleted file mode 100644 index 921cae28..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java +++ /dev/null @@ -1,223 +0,0 @@ -package com.android.javacard.keymaster; - -import javacard.framework.Util; - -public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { - - public static final byte ASN1_OCTET_STRING = 0x04; - public static final byte ASN1_SEQUENCE = 0x30; - public static final byte ASN1_INTEGER = 0x02; - public static final byte ASN1_A0_TAG = (byte) 0xA0; - public static final byte ASN1_A1_TAG = (byte) 0xA1; - public static final byte ASN1_BIT_STRING = 0x03; - public static final byte[] EC_CURVE = { - 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, - 0x01, 0x07 - }; - public static final byte[] RSA_ALGORITHM = { - 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, - (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 - }; - public static final byte[] EC_ALGORITHM = { - 0x06, 0x07, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, - 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, - (byte) 0xce, 0x3d, 0x03, 0x01, 0x07 - }; - private byte[] data; - private short start; - private short length; - private short cur; - private static KMPKCS8DecoderImpl inst; - - private KMPKCS8DecoderImpl() { - start = 0; - length = 0; - cur = 0; - } - - @Override - public short decodeRsa(short blob) { - init(blob); - decodeCommon((short) 0, RSA_ALGORITHM); - return decodeRsaPrivateKey((short) 0); - } - - @Override - public short decodeEc(short blob) { - init(blob); - decodeCommon((short) 0, EC_ALGORITHM); - return decodeEcPrivateKey((short) 1); - } - - //Seq[Int,Int,Int,Int,] - public short decodeRsaPrivateKey(short version) { - short resp = KMArray.instance((short) 3); - header(ASN1_OCTET_STRING); - header(ASN1_SEQUENCE); - short len = header(ASN1_INTEGER); - if (len != 1) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - short ver = getByte(); - if (ver != version) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - len = header(ASN1_INTEGER); - short modulus = getModulus(len); - len = header(ASN1_INTEGER); - short pubKey = KMByteBlob.instance(len); - getBytes(pubKey); - len = header(ASN1_INTEGER); - short privKey = KMByteBlob.instance(len); - getBytes(privKey); - KMArray.cast(resp).add((short) 0, modulus); - KMArray.cast(resp).add((short) 1, pubKey); - KMArray.cast(resp).add((short) 2, privKey); - return resp; - } - - // Seq [Int, Blob] - public void decodeCommon(short version, byte[] alg) { - short len = header(ASN1_SEQUENCE); - len = header(ASN1_INTEGER); - if (len != 1) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - short ver = getByte(); - if (ver != version) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - len = header(ASN1_SEQUENCE); - short blob = KMByteBlob.instance(len); - getBytes(blob); - if (Util.arrayCompare( - KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), - alg, - (short) 0, KMByteBlob.cast(blob).length()) != 0) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } - - //Seq[Int,blob,blob] - public short decodeEcPrivateKey(short version) { - short resp = KMArray.instance((short) 2); - header(ASN1_OCTET_STRING); - header(ASN1_SEQUENCE); - short len = header(ASN1_INTEGER); - if (len != 1) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - short ver = getByte(); - if (ver != version) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - len = header(ASN1_OCTET_STRING); - short privKey = KMByteBlob.instance(len); - getBytes(privKey); - validateTag0IfPresent(); - header(ASN1_A1_TAG); - len = header(ASN1_BIT_STRING); - if (len < 1) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - byte unusedBits = getByte(); - if (unusedBits != 0) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - short pubKey = KMByteBlob.instance((short) (len - 1)); - getBytes(pubKey); - KMArray.cast(resp).add((short) 0, pubKey); - KMArray.cast(resp).add((short) 1, privKey); - return resp; - } - - private void validateTag0IfPresent() { - if (data[cur] != ASN1_A0_TAG) { - return; - } - ; - short len = header(ASN1_A0_TAG); - if (len != EC_CURVE.length) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - if (Util.arrayCompare(data, cur, EC_CURVE, (short) 0, len) != 0) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - incrementCursor(len); - } - - private short header(short tag) { - short t = getByte(); - if (t != tag) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - return getLength(); - } - - private byte getByte() { - byte d = data[cur]; - incrementCursor((short) 1); - return d; - } - - private short getShort() { - short d = Util.getShort(data, cur); - incrementCursor((short) 2); - return d; - } - - private short getModulus(short modulusLen) { - if (0 == data[cur] && modulusLen == 257) { - incrementCursor((short) 1); - modulusLen--; - } - short blob = KMByteBlob.instance(modulusLen); - getBytes(blob); - return blob; - } - - private void getBytes(short blob) { - short len = KMByteBlob.cast(blob).length(); - Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), len); - incrementCursor(len); - } - - private short getLength() { - byte len = getByte(); - if (len >= 0) { - return len; - } - len = (byte) (len & 0x7F); - if (len == 1) { - return (short) (getByte() & 0xFF); - } else if (len == 2) { - return getShort(); - } else { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - return KMType.INVALID_VALUE; //should not come here - } - - public static KMPKCS8DecoderImpl instance() { - if (inst == null) { - inst = new KMPKCS8DecoderImpl(); - } - return inst; - } - - public void init(short blob) { - data = KMByteBlob.cast(blob).getBuffer(); - start = KMByteBlob.cast(blob).getStartOff(); - length = KMByteBlob.cast(blob).length(); - cur = start; - } - - public void incrementCursor(short n) { - cur += n; - if (cur > ((short) (start + length))) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - } -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java deleted file mode 100644 index 573be574..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.framework.Util; -import javacard.security.CryptoException; -import javacard.security.Key; -import javacard.security.Signature; -import javacardx.crypto.Cipher; - -public class KMRsa2048NoDigestSignature extends Signature { - - private Cipher inst; // ALG_RSA_NOPAD. - private byte padding; - private byte[] rsaModulus; // to compare with the data value - - public KMRsa2048NoDigestSignature(Cipher ciph, byte padding, byte[] mod, short start, short len) { - inst = ciph; - this.padding = padding; - if (len != 256) { - CryptoException.throwIt(CryptoException.INVALID_INIT); - } - rsaModulus = new byte[256]; - Util.arrayCopyNonAtomic(mod, start, rsaModulus, (short) 0, len); - } - - @Override - public void init(Key key, byte b) throws CryptoException { - - } - - @Override - public void init(Key key, byte b, byte[] bytes, short i, short i1) throws CryptoException { - } - - @Override - public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) - throws CryptoException { - } - - @Override - public byte getAlgorithm() { - return 0; - } - - @Override - public byte getMessageDigestAlgorithm() { - return 0; - } - - @Override - public byte getCipherAlgorithm() { - return 0; - } - - @Override - public byte getPaddingAlgorithm() { - return 0; - } - - @Override - public short getLength() throws CryptoException { - return 0; - } - - @Override - public void update(byte[] bytes, short i, short i1) throws CryptoException { - } - - @Override - public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) - throws CryptoException { - byte[] inputData = padData(bytes, i, i1); - return inst.doFinal(inputData, (short) 0, (short) 256, bytes1, i2); - } - - @Override - public short signPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2) - throws CryptoException { - return 0; - } - - @Override - public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, short i2, short i3) - throws CryptoException { - // Public key operations not handled here. - return false; - } - - @Override - public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, byte[] bytes1, short i2, - short i3) throws CryptoException { - return false; - } - - private byte[] padData(byte[] buf, short start, short len) { - byte[] inputData = new byte[256]; - if (!isValidData(buf, start, len)) { - CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); - } - Util.arrayFillNonAtomic(inputData, (short) 0, (short) 256, (byte) 0x00); - if (padding == KMType.PADDING_NONE) { // add zero to right - } else if (padding == KMType.RSA_PKCS1_1_5_SIGN) {// 0x00||0x01||PS||0x00 - inputData[0] = 0x00; - inputData[1] = 0x01; - Util.arrayFillNonAtomic(inputData, (short) 2, (short) (256 - len - 3), (byte) 0xFF); - inputData[(short) (256 - len - 1)] = 0x00; - } else { - CryptoException.throwIt(CryptoException.ILLEGAL_USE); - } - Util.arrayCopyNonAtomic(buf, start, inputData, (short) (256 - len), len); - return inputData; - } - - private boolean isValidData(byte[] buf, short start, short len) { - if (padding == KMType.PADDING_NONE) { - if (len > 256) { - return false; - } else if (len == 256) { - short v = KMInteger.unsignedByteArrayCompare(buf, start, rsaModulus, (short) 0, len); - if (v > 0) { - return false; - } - } - } else {//pkcs1 no digest - if (len > 245) { - return false; - } - } - return true; - } -} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java deleted file mode 100644 index 88b7b4d1..00000000 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.framework.Util; - -public class KMUtils { - - // 64 bit unsigned calculations for time - public static final byte[] oneSecMsec = { - 0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec - public static final byte[] oneMinMsec = { - 0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec - public static final byte[] oneHourMsec = { - 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80}; // 3600000 msec - public static final byte[] oneDayMsec = { - 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec - public static final byte[] oneMonthMsec = { - 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec - public static final byte[] leapYearMsec = { - 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; - public static final byte[] yearMsec = { - 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 - //Leap year(366) + 3 * 365 - public static final byte[] fourYrsMsec = { - 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 - public static final byte[] firstJan2020 = { - 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00}; // 1577836800000 msec - public static final byte[] firstJan2051 = { - 0, 0, 0x02, 0x53, 0x26, (byte) 0x0E, (byte) 0x1C, 0x00}; // 2556144000000 - // msec - public static final byte[] febMonthLeapMSec = { - 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00}; //2505600000 - public static final byte[] febMonthMsec = { - 0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00}; //2419200000 - public static final byte[] ThirtyOneDaysMonthMsec = { - 0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00};//2678400000 - public static final byte[] ThirtDaysMonthMsec = { - 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00};//2592000000 - public static final short year2051 = 2051; - public static final short year2020 = 2020; - - // -------------------------------------- - public static short convertToDate(short time, byte[] scratchPad, - boolean utcFlag) { - - short yrsCount = 0; - short monthCount = 1; - short dayCount = 1; - short hhCount = 0; - short mmCount = 0; - short ssCount = 0; - byte Z = 0x5A; - boolean from2020 = true; - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - Util.arrayCopyNonAtomic(KMInteger.cast(time).getBuffer(), - KMInteger.cast(time).getStartOff(), scratchPad, - (short) (8 - KMInteger.cast(time).length()), KMInteger.cast(time) - .length()); - // If the time is less then 1 Jan 2020 then it is an error - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, - (short) 8) < 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (utcFlag - && KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, - (short) 0, (short) 8) >= 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2051, (short) 0, - (short) 8) < 0) { - Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, - (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } else { - from2020 = false; - Util.arrayCopyNonAtomic(firstJan2051, (short) 0, scratchPad, (short) 8, - (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } - // divide the given time with four yrs msec count - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, - (short) 8) >= 0) { - Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient - // is - // multiple - // of 4 - yrsCount = (short) (yrsCount * 4); // number of yrs. - // copy reminder as new dividend - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } - - //Get the leap year index starting from the (base Year + yrsCount) Year. - short leapYrIdx = getLeapYrIndex(from2020, yrsCount); - - // if leap year index is 0, then the number of days for the 1st year will be 366 days. - // if leap year index is not 0, then the number of days for the 1st year will be 365 days. - if (((leapYrIdx == 0) && - (KMInteger - .unsignedByteArrayCompare(scratchPad, (short) 0, leapYearMsec, (short) 0, (short) 8) - >= 0)) || - ((leapYrIdx != 0) && - (KMInteger - .unsignedByteArrayCompare(scratchPad, (short) 0, yearMsec, (short) 0, (short) 8) - >= 0))) { - for (short i = 0; i < 4; i++) { - yrsCount++; - if (i == leapYrIdx) { - Util.arrayCopyNonAtomic(leapYearMsec, (short) 0, scratchPad, - (short) 8, (short) 8); - } else { - Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - } - subtract(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - if (((short) (i + 1) == leapYrIdx)) { - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, leapYearMsec, - (short) 0, (short) 8) < 0) { - break; - } - } else { - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, yearMsec, - (short) 0, (short) 8) < 0) { - break; - } - } - } - } - - // total yrs from 1970 - if (from2020) { - yrsCount = (short) (year2020 + yrsCount); - } else { - yrsCount = (short) (year2051 + yrsCount); - } - - // divide the given time with one month msec count - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, - (short) 8) >= 0) { - for (short i = 0; i < 12; i++) { - if (i == 1) { - // Feb month - if (isLeapYear(yrsCount)) { - // Leap year 29 days - Util.arrayCopyNonAtomic(febMonthLeapMSec, (short) 0, scratchPad, - (short) 8, (short) 8); - } else { - // 28 days - Util.arrayCopyNonAtomic(febMonthMsec, (short) 0, scratchPad, - (short) 8, (short) 8); - } - } else if (((i <= 6) && ((i % 2 == 0))) || ((i > 6) && ((i % 2 == 1)))) { - Util.arrayCopyNonAtomic(ThirtyOneDaysMonthMsec, (short) 0, - scratchPad, (short) 8, (short) 8); - } else { - // 30 Days - Util.arrayCopyNonAtomic(ThirtDaysMonthMsec, (short) 0, scratchPad, - (short) 8, (short) 8); - } - - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, scratchPad, (short) 8, - (short) 8) >= 0) { - subtract(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } else { - break; - } - monthCount++; - } - } - - // divide the given time with one day msec count - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, - (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - dayCount++; - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } - - // divide the given time with one hour msec count - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, - (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } - - // divide the given time with one minute msec count - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, - (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } - - // divide the given time with one second msec count - if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, - (short) 8) >= 0) { - Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, - (short) 8); - ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); - Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, - (short) 8); - } - - // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY - len += numberToString(monthCount, scratchPad, len); - len += numberToString(dayCount, scratchPad, len); - len += numberToString(hhCount, scratchPad, len); - len += numberToString(mmCount, scratchPad, len); - len += numberToString(ssCount, scratchPad, len); - scratchPad[len] = Z; - len++; - if (utcFlag) { - return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY - } else { - return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY - } - } - - public static short numberToString(short number, byte[] scratchPad, - short offset) { - byte zero = 0x30; - byte len = 2; - byte digit; - if (number > 999) { - len = 4; - } - byte index = len; - while (index > 0) { - digit = (byte) (number % 10); - number = (short) (number / 10); - scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero); - index--; - } - return len; - } - - // Use Euclid's formula: dividend = quotient*divisor + remainder - // i.e. dividend - quotient*divisor = remainder where remainder < divisor. - // so this is division by subtraction until remainder remains. - public static short divide(byte[] buf, short dividend, short divisor, - short remainder) { - short expCnt = 1; - short q = 0; - // first increase divisor so that it becomes greater then dividend. - while (compare(buf, divisor, dividend) < 0) { - shiftLeft(buf, divisor); - expCnt = (short) (expCnt << 1); - } - // Now subtract divisor from dividend if dividend is greater then divisor. - // Copy remainder in the dividend and repeat. - while (expCnt != 0) { - if (compare(buf, dividend, divisor) >= 0) { - subtract(buf, dividend, divisor, remainder); - copy(buf, remainder, dividend); - q = (short) (q + expCnt); - } - expCnt = (short) (expCnt >> 1); - shiftRight(buf, divisor); - } - return q; - } - - public static void copy(byte[] buf, short from, short to) { - Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8); - } - - public static byte compare(byte[] buf, short lhs, short rhs) { - return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); - } - - public static void shiftLeft(byte[] buf, short start, short count) { - short index = 0; - while (index < count) { - shiftLeft(buf, start); - index++; - } - } - - public static void shiftLeft(byte[] buf, short start) { - byte index = 7; - byte carry = 0; - byte tmp; - while (index >= 0) { - tmp = buf[(short) (start + index)]; - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] << 1); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] + carry); - if (tmp < 0) { - carry = 1; - } else { - carry = 0; - } - index--; - } - } - - public static void shiftRight(byte[] buf, short start) { - byte index = 0; - byte carry = 0; - byte tmp; - while (index < 8) { - tmp = (byte) (buf[(short) (start + index)] & 0x01); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] >> 1); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] & 0x7F); - buf[(short) (start + index)] = (byte) (buf[(short) (start + index)] | carry); - if (tmp == 1) { - carry = (byte) 0x80; - } else { - carry = 0; - } - index++; - } - } - - public static void add(byte[] buf, short op1, short op2, short result) { - byte index = 7; - byte carry = 0; - short tmp; - while (index >= 0) { - tmp = - (short) ((buf[(short) (op1 + index)] & 0xFF) + - (buf[(short) (op2 + index)] & 0xFF) + carry); - carry = 0; - if (tmp > 255) { - carry = 1; // max unsigned byte value is 255 - } - buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF); - index--; - } - } - - // subtraction by borrowing. - public static void subtract(byte[] buf, short op1, short op2, short result) { - byte borrow = 0; - byte index = 7; - short r; - short x; - short y; - while (index >= 0) { - x = (short) (buf[(short) (op1 + index)] & 0xFF); - y = (short) (buf[(short) (op2 + index)] & 0xFF); - r = (short) (x - y - borrow); - borrow = 0; - if (r < 0) { - borrow = 1; - r = (short) (r + 256); // max unsigned byte value is 255 - } - buf[(short) (result + index)] = (byte) (r & 0xFF); - index--; - } - } - - public static short countTemporalCount(byte[] bufTime, short timeOff, - short timeLen, byte[] scratchPad, short offset) { - Util.arrayFillNonAtomic(scratchPad, (short) offset, (short) 24, (byte) 0); - Util.arrayCopyNonAtomic( - bufTime, - timeOff, - scratchPad, - (short) (offset + 8 - timeLen), - timeLen); - Util.arrayCopyNonAtomic(oneMonthMsec, (short) 0, scratchPad, (short) (offset + 8), - (short) 8); - return divide(scratchPad, (short) 0, (short) 8, (short) 16); - } - - public static boolean isLeapYear(short year) { - if ((short) (year % 4) == (short) 0) { - if (((short) (year % 100) == (short) 0) && - ((short) (year % 400)) != (short) 0) { - return false; - } - return true; - } - return false; - } - - public static short getLeapYrIndex(boolean from2020, short yrsCount) { - short newBaseYr = (short) (from2020 ? (year2020 + yrsCount) : (year2051 + yrsCount)); - for (short i = 0; i < 4; i++) { - if (isLeapYear((short) (newBaseYr + i))) { - return i; - } - } - return -1; - } - - // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) - public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, - short scratchPadOff) { - byte[] shiftPos = {9, 8, 7, 6, 5, 3}; - short index = 0; - while (index < (short) (shiftPos.length)) { - Util.arrayCopyNonAtomic(buf, inputOff, buf, scratchPadOff, (short) 8); - shiftLeft(buf, scratchPadOff, shiftPos[index]); - Util.arrayCopyNonAtomic(buf, outputOff, buf, (short) (scratchPadOff + 8), (short) 8); - add(buf, scratchPadOff, (short) (8 + scratchPadOff), (short) (16 + scratchPadOff)); - Util.arrayCopyNonAtomic(buf, (short) (scratchPadOff + 16), buf, outputOff, (short) 8); - Util.arrayFillNonAtomic(buf, scratchPadOff, (short) 24, (byte) 0); - index++; - } - } - -} diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java deleted file mode 100644 index 42f102e9..00000000 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ /dev/null @@ -1,3928 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.test; - -import com.android.javacard.keymaster.KMArray; -import com.android.javacard.keymaster.KMBoolTag; -import com.android.javacard.keymaster.KMByteBlob; -import com.android.javacard.keymaster.KMByteTag; -import com.android.javacard.keymaster.KMComputedHmacKey; -import com.android.javacard.keymaster.KMConfigurations; -import com.android.javacard.keymaster.KMHmacKey; -import com.android.javacard.keymaster.KMJCardSimApplet; -import com.android.javacard.keymaster.KMJCardSimulator; -import com.android.javacard.keymaster.KMSEProvider; -import com.android.javacard.keymaster.KMDecoder; -import com.android.javacard.keymaster.KMEncoder; -import com.android.javacard.keymaster.KMEnum; -import com.android.javacard.keymaster.KMEnumArrayTag; -import com.android.javacard.keymaster.KMEnumTag; -import com.android.javacard.keymaster.KMError; -import com.android.javacard.keymaster.KMHardwareAuthToken; -import com.android.javacard.keymaster.KMHmacSharingParameters; -import com.android.javacard.keymaster.KMInteger; -import com.android.javacard.keymaster.KMIntegerTag; -import com.android.javacard.keymaster.KMKeyCharacteristics; -import com.android.javacard.keymaster.KMKeyParameters; -import com.android.javacard.keymaster.KMKeymasterApplet; -import com.android.javacard.keymaster.KMRepository; -import com.android.javacard.keymaster.KMType; -import com.android.javacard.keymaster.KMVerificationToken; -import com.licel.jcardsim.smartcardio.CardSimulator; -import com.licel.jcardsim.utils.AIDUtil; - -import javacard.framework.AID; -import javacard.framework.Util; -import javacard.security.ECPublicKey; -import javacard.security.KeyBuilder; -import javacard.security.KeyPair; -import javacard.security.RSAPublicKey; -import javacard.security.Signature; -import javacardx.crypto.Cipher; - -import java.math.BigInteger; -import java.security.AlgorithmParameters; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SignatureException; -import java.security.spec.ECGenParameterSpec; -import java.security.spec.ECParameterSpec; -import java.security.spec.ECPoint; -import java.security.spec.ECPublicKeySpec; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.InvalidParameterSpecException; -import java.security.spec.MGF1ParameterSpec; -import java.security.spec.RSAPublicKeySpec; -import java.util.Arrays; -import java.util.Random; - -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.OAEPParameterSpec; -import javax.crypto.spec.PSource; -import javax.smartcardio.CommandAPDU; -import javax.smartcardio.ResponseAPDU; - -import org.junit.Assert; -import org.junit.Test; - -public class KMFunctionalTest { - - private static final byte INS_BEGIN_KM_CMD = 0x00; - private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD + 1; //0x01 - private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = INS_BEGIN_KM_CMD + 2; //0x02 - private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 3; //0x04 - private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 4; //0x05 - private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 5; //0x06 - private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 6; //0x07 - private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 7; //0x08 - private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 8; //0x09 - // Top 32 commands are reserved for provisioning. - private static final byte INS_END_KM_PROVISION_CMD = 0x20; - - private static final byte INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD + 1; //0x21 - private static final byte INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 2; //0x22 - private static final byte INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD + 3; //0x23 - private static final byte INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 4; //0x24 - private static final byte INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD + 5; //0x25 - private static final byte INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD + 6; //0x26 - private static final byte INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD + 7; //0x27 - private static final byte INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD + 8; //0x28 - private static final byte INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD + 9; //0x29 - private static final byte INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD + 10; //0x2A - private static final byte INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD + 11; //0x2B - private static final byte INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD + 12; //0x2C - private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD + 13; //0x2D - private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD + 14; //0x2E - private static final byte INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD + 15; //0x2F - private static final byte INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 16; //0x30 - private static final byte INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 17; //0x31 - private static final byte INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 18; //0x32 - private static final byte INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 19; //0x33 - private static final byte INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD + 20;//0x34 - private static final byte INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD + 21; //0x35 - private static final byte INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD + 22; //0x36 - - private static final byte[] kEcPrivKey = { - (byte) 0x21, (byte) 0xe0, (byte) 0x86, (byte) 0x43, (byte) 0x2a, - (byte) 0x15, (byte) 0x19, (byte) 0x84, (byte) 0x59, (byte) 0xcf, - (byte) 0x36, (byte) 0x3a, (byte) 0x50, (byte) 0xfc, (byte) 0x14, - (byte) 0xc9, (byte) 0xda, (byte) 0xad, (byte) 0xf9, (byte) 0x35, - (byte) 0xf5, (byte) 0x27, (byte) 0xc2, (byte) 0xdf, (byte) 0xd7, - (byte) 0x1e, (byte) 0x4d, (byte) 0x6d, (byte) 0xbc, (byte) 0x42, - (byte) 0xe5, (byte) 0x44}; - private static final byte[] kEcPubKey = { - (byte) 0x04, (byte) 0xeb, (byte) 0x9e, (byte) 0x79, (byte) 0xf8, - (byte) 0x42, (byte) 0x63, (byte) 0x59, (byte) 0xac, (byte) 0xcb, - (byte) 0x2a, (byte) 0x91, (byte) 0x4c, (byte) 0x89, (byte) 0x86, - (byte) 0xcc, (byte) 0x70, (byte) 0xad, (byte) 0x90, (byte) 0x66, - (byte) 0x93, (byte) 0x82, (byte) 0xa9, (byte) 0x73, (byte) 0x26, - (byte) 0x13, (byte) 0xfe, (byte) 0xac, (byte) 0xcb, (byte) 0xf8, - (byte) 0x21, (byte) 0x27, (byte) 0x4c, (byte) 0x21, (byte) 0x74, - (byte) 0x97, (byte) 0x4a, (byte) 0x2a, (byte) 0xfe, (byte) 0xa5, - (byte) 0xb9, (byte) 0x4d, (byte) 0x7f, (byte) 0x66, (byte) 0xd4, - (byte) 0xe0, (byte) 0x65, (byte) 0x10, (byte) 0x66, (byte) 0x35, - (byte) 0xbc, (byte) 0x53, (byte) 0xb7, (byte) 0xa0, (byte) 0xa3, - (byte) 0xa6, (byte) 0x71, (byte) 0x58, (byte) 0x3e, (byte) 0xdb, - (byte) 0x3e, (byte) 0x11, (byte) 0xae, (byte) 0x10, (byte) 0x14}; - - private static final byte[] kEcAttestCert = { - 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x30, (byte) 0x82, - (byte) 0x02, (byte) 0x1e, (byte) 0xa0, (byte) 0x03, (byte) 0x02, - (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x02, (byte) 0x10, 0x01, - (byte) 0x30, (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, - (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x04, - (byte) 0x03, (byte) 0x02, (byte) 0x30, (byte) 0x81, (byte) 0x98, 0x31, - (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, - (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, - (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, 0x11, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, - (byte) 0x0c, (byte) 0x0a, (byte) 0x43, (byte) 0x61, (byte) 0x6c, - (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x6e, 0x69, - (byte) 0x61, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, - (byte) 0x0c, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f, (byte) 0x75, 0x6e, - (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x20, - (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31, - (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, 0x55, - (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x47, - (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, - (byte) 0x2c, (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x63, 0x2e, - (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, - (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, 0x6f, - (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x33, (byte) 0x30, - (byte) 0x31, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, - (byte) 0x03, (byte) 0x0c, (byte) 0x2a, (byte) 0x41, (byte) 0x6e, 0x64, - (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, - (byte) 0x4b, (byte) 0x65, (byte) 0x79, (byte) 0x73, (byte) 0x74, - (byte) 0x6f, (byte) 0x72, (byte) 0x65, (byte) 0x20, (byte) 0x53, 0x6f, - (byte) 0x66, (byte) 0x74, (byte) 0x77, (byte) 0x61, (byte) 0x72, - (byte) 0x65, (byte) 0x20, (byte) 0x41, (byte) 0x74, (byte) 0x74, - (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x61, (byte) 0x74, 0x69, - (byte) 0x6f, (byte) 0x6e, (byte) 0x20, (byte) 0x52, (byte) 0x6f, - (byte) 0x6f, (byte) 0x74, (byte) 0x30, (byte) 0x1e, (byte) 0x17, - (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30, (byte) 0x31, 0x31, - (byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x36, - (byte) 0x30, (byte) 0x39, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, - (byte) 0x32, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x30, 0x38, - (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x36, (byte) 0x30, - (byte) 0x39, (byte) 0x5a, (byte) 0x30, (byte) 0x81, (byte) 0x88, - (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, 0x03, - (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, - (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, - (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, 0x08, - (byte) 0x0c, (byte) 0x0a, (byte) 0x43, (byte) 0x61, (byte) 0x6c, - (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x6e, - (byte) 0x69, (byte) 0x61, (byte) 0x31, (byte) 0x15, (byte) 0x30, 0x13, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, - (byte) 0x0c, (byte) 0x0c, (byte) 0x47, (byte) 0x6f, (byte) 0x6f, - (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2c, (byte) 0x20, 0x49, - (byte) 0x6e, (byte) 0x63, (byte) 0x2e, (byte) 0x31, (byte) 0x10, - (byte) 0x30, (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, - (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x41, 0x6e, - (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, - (byte) 0x31, (byte) 0x3b, (byte) 0x30, (byte) 0x39, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, 0x32, - (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, - (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x4b, (byte) 0x65, - (byte) 0x79, (byte) 0x73, (byte) 0x74, (byte) 0x6f, (byte) 0x72, 0x65, - (byte) 0x20, (byte) 0x53, (byte) 0x6f, (byte) 0x66, (byte) 0x74, - (byte) 0x77, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x20, - (byte) 0x41, (byte) 0x74, (byte) 0x74, (byte) 0x65, (byte) 0x73, 0x74, - (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, - (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x74, (byte) 0x65, - (byte) 0x72, (byte) 0x6d, (byte) 0x65, (byte) 0x64, (byte) 0x69, 0x61, - (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x59, (byte) 0x30, - (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a, (byte) 0x86, - (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01, 0x06, - (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, - (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07, (byte) 0x03, - (byte) 0x42, (byte) 0x00, (byte) 0x04, (byte) 0xeb, (byte) 0x9e, 0x79, - (byte) 0xf8, (byte) 0x42, (byte) 0x63, (byte) 0x59, (byte) 0xac, - (byte) 0xcb, (byte) 0x2a, (byte) 0x91, (byte) 0x4c, (byte) 0x89, - (byte) 0x86, (byte) 0xcc, (byte) 0x70, (byte) 0xad, (byte) 0x90, 0x66, - (byte) 0x93, (byte) 0x82, (byte) 0xa9, (byte) 0x73, (byte) 0x26, - (byte) 0x13, (byte) 0xfe, (byte) 0xac, (byte) 0xcb, (byte) 0xf8, - (byte) 0x21, (byte) 0x27, (byte) 0x4c, (byte) 0x21, (byte) 0x74, - (byte) 0x97, (byte) 0x4a, (byte) 0x2a, (byte) 0xfe, (byte) 0xa5, - (byte) 0xb9, (byte) 0x4d, (byte) 0x7f, (byte) 0x66, (byte) 0xd4, - (byte) 0xe0, (byte) 0x65, (byte) 0x10, (byte) 0x66, (byte) 0x35, - (byte) 0xbc, 0x53, (byte) 0xb7, (byte) 0xa0, (byte) 0xa3, (byte) 0xa6, - (byte) 0x71, (byte) 0x58, (byte) 0x3e, (byte) 0xdb, (byte) 0x3e, - (byte) 0x11, (byte) 0xae, (byte) 0x10, (byte) 0x14, (byte) 0xa3, - (byte) 0x66, 0x30, (byte) 0x64, (byte) 0x30, (byte) 0x1d, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, - (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x3f, (byte) 0xfc, - (byte) 0xac, (byte) 0xd6, (byte) 0x1a, (byte) 0xb1, (byte) 0x3a, - (byte) 0x9e, (byte) 0x81, (byte) 0x20, (byte) 0xb8, (byte) 0xd5, - (byte) 0x25, (byte) 0x1c, (byte) 0xc5, (byte) 0x65, (byte) 0xbb, - (byte) 0x1e, (byte) 0x91, (byte) 0xa9, (byte) 0x30, (byte) 0x1f, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, - (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, - (byte) 0x14, (byte) 0xc8, (byte) 0xad, (byte) 0xe9, (byte) 0x77, - (byte) 0x4c, (byte) 0x45, (byte) 0xc3, (byte) 0xa3, (byte) 0xcf, - (byte) 0x0d, (byte) 0x16, (byte) 0x10, (byte) 0xe4, (byte) 0x79, - (byte) 0x43, (byte) 0x3a, (byte) 0x21, (byte) 0x5a, 0x30, (byte) 0xcf, - (byte) 0x30, (byte) 0x12, (byte) 0x06, (byte) 0x03, (byte) 0x55, - (byte) 0x1d, (byte) 0x13, (byte) 0x01, (byte) 0x01, (byte) 0xff, - (byte) 0x04, (byte) 0x08, (byte) 0x30, (byte) 0x06, 0x01, (byte) 0x01, - (byte) 0xff, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x30, - (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, - (byte) 0x0f, (byte) 0x01, (byte) 0x01, (byte) 0xff, 0x04, (byte) 0x04, - (byte) 0x03, (byte) 0x02, (byte) 0x02, (byte) 0x84, (byte) 0x30, - (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, - (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x04, 0x03, (byte) 0x02, - (byte) 0x03, (byte) 0x48, (byte) 0x00, (byte) 0x30, (byte) 0x45, - (byte) 0x02, (byte) 0x20, (byte) 0x4b, (byte) 0x8a, (byte) 0x9b, - (byte) 0x7b, (byte) 0xee, (byte) 0x82, (byte) 0xbc, (byte) 0xc0, - (byte) 0x33, (byte) 0x87, (byte) 0xae, (byte) 0x2f, (byte) 0xc0, - (byte) 0x89, (byte) 0x98, (byte) 0xb4, (byte) 0xdd, (byte) 0xc3, - (byte) 0x8d, (byte) 0xab, (byte) 0x27, (byte) 0x2a, (byte) 0x45, - (byte) 0x9f, (byte) 0x69, (byte) 0x0c, (byte) 0xc7, (byte) 0xc3, - (byte) 0x92, (byte) 0xd4, (byte) 0x0f, (byte) 0x8e, (byte) 0x02, - (byte) 0x21, (byte) 0x00, (byte) 0xee, (byte) 0xda, (byte) 0x01, - (byte) 0x5d, (byte) 0xb6, (byte) 0xf4, (byte) 0x32, (byte) 0xe9, - (byte) 0xd4, (byte) 0x84, (byte) 0x3b, (byte) 0x62, (byte) 0x4c, - (byte) 0x94, (byte) 0x04, (byte) 0xef, (byte) 0x3a, (byte) 0x7c, - (byte) 0xcc, (byte) 0xbd, 0x5e, (byte) 0xfb, (byte) 0x22, (byte) 0xbb, - (byte) 0xe7, (byte) 0xfe, (byte) 0xb9, (byte) 0x77, (byte) 0x3f, - (byte) 0x59, (byte) 0x3f, (byte) 0xfb,}; - - private static final byte[] kEcAttestRootCert = { - 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x8b, (byte) 0x30, - (byte) 0x82, (byte) 0x02, (byte) 0x32, (byte) 0xa0, (byte) 0x03, - (byte) 0x02, (byte) 0x01, (byte) 0x02, (byte) 0x02, (byte) 0x09, - (byte) 0x00, (byte) 0xa2, (byte) 0x05, (byte) 0x9e, (byte) 0xd1, - (byte) 0x0e, (byte) 0x43, (byte) 0x5b, (byte) 0x57, (byte) 0x30, - (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, - (byte) 0x48, (byte) 0xce, 0x3d, (byte) 0x04, (byte) 0x03, - (byte) 0x02, (byte) 0x30, (byte) 0x81, (byte) 0x98, (byte) 0x31, - (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, - (byte) 0x55, (byte) 0x04, (byte) 0x06, 0x13, (byte) 0x02, - (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, - (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, - (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x43, 0x61, - (byte) 0x6c, (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, - (byte) 0x6e, (byte) 0x69, (byte) 0x61, (byte) 0x31, (byte) 0x16, - (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55, - 0x04, (byte) 0x07, (byte) 0x0c, (byte) 0x0d, (byte) 0x4d, - (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, - (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, - (byte) 0x65, 0x77, (byte) 0x31, (byte) 0x15, (byte) 0x30, - (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, - (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x47, (byte) 0x6f, - (byte) 0x6f, (byte) 0x67, 0x6c, (byte) 0x65, (byte) 0x2c, - (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x63, (byte) 0x2e, - (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x04, 0x0b, (byte) 0x0c, - (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, - (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x33, - (byte) 0x30, (byte) 0x31, (byte) 0x06, (byte) 0x03, 0x55, - (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x2a, (byte) 0x41, - (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, - (byte) 0x64, (byte) 0x20, (byte) 0x4b, (byte) 0x65, (byte) 0x79, - 0x73, (byte) 0x74, (byte) 0x6f, (byte) 0x72, (byte) 0x65, - (byte) 0x20, (byte) 0x53, (byte) 0x6f, (byte) 0x66, (byte) 0x74, - (byte) 0x77, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x20, - (byte) 0x41, 0x74, (byte) 0x74, (byte) 0x65, (byte) 0x73, - (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6f, - (byte) 0x6e, (byte) 0x20, (byte) 0x52, (byte) 0x6f, (byte) 0x6f, - (byte) 0x74, (byte) 0x30, 0x1e, (byte) 0x17, (byte) 0x0d, - (byte) 0x31, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31, - (byte) 0x31, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x33, - (byte) 0x35, (byte) 0x30, (byte) 0x5a, 0x17, (byte) 0x0d, - (byte) 0x33, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x30, - (byte) 0x36, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x33, - (byte) 0x35, (byte) 0x30, (byte) 0x5a, (byte) 0x30, (byte) 0x81, - (byte) 0x98, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, - (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, - 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, - (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, - (byte) 0x43, (byte) 0x61, (byte) 0x6c, (byte) 0x69, (byte) 0x66, - (byte) 0x6f, 0x72, (byte) 0x6e, (byte) 0x69, (byte) 0x61, - (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c, - (byte) 0x0d, (byte) 0x4d, 0x6f, (byte) 0x75, (byte) 0x6e, - (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x20, - (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31, - (byte) 0x15, (byte) 0x30, (byte) 0x13, 0x06, (byte) 0x03, - (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, - (byte) 0x47, (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, - (byte) 0x65, (byte) 0x2c, (byte) 0x20, (byte) 0x49, 0x6e, - (byte) 0x63, (byte) 0x2e, (byte) 0x31, (byte) 0x10, (byte) 0x30, - (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, - (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x41, (byte) 0x6e, - 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, - (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x31, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, - (byte) 0x2a, 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, - (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x4b, - (byte) 0x65, (byte) 0x79, (byte) 0x73, (byte) 0x74, (byte) 0x6f, - (byte) 0x72, (byte) 0x65, 0x20, (byte) 0x53, (byte) 0x6f, - (byte) 0x66, (byte) 0x74, (byte) 0x77, (byte) 0x61, (byte) 0x72, - (byte) 0x65, (byte) 0x20, (byte) 0x41, (byte) 0x74, (byte) 0x74, - (byte) 0x65, (byte) 0x73, (byte) 0x74, 0x61, (byte) 0x74, - (byte) 0x69, (byte) 0x6f, (byte) 0x6e, 0x77, (byte) 0x1f, - (byte) 0x44, (byte) 0x22, (byte) 0x6d, (byte) 0xbd, (byte) 0xb1, - (byte) 0xaf, (byte) 0xfa, (byte) 0x16, (byte) 0xcb, (byte) 0xc7, - (byte) 0xad, (byte) 0xc5, (byte) 0x77, (byte) 0xd2, (byte) 0x20, - (byte) 0x52, (byte) 0x6f, (byte) 0x6f, (byte) 0x74, (byte) 0x30, - (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, - 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, - (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x08, (byte) 0x2a, - (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x03, - (byte) 0x01, 0x07, (byte) 0x03, (byte) 0x42, (byte) 0x00, - (byte) 0x04, (byte) 0xee, (byte) 0x5d, (byte) 0x5e, (byte) 0xc7, - (byte) 0xe1, (byte) 0xc0, (byte) 0xdb, (byte) 0x6d, (byte) 0x03, - (byte) 0xa6, (byte) 0x7e, (byte) 0xe6, (byte) 0xb6, (byte) 0x1b, - (byte) 0xec, (byte) 0x4d, (byte) 0x6a, (byte) 0x5d, (byte) 0x6a, - (byte) 0x68, (byte) 0x2e, (byte) 0x0f, (byte) 0xff, (byte) 0x7f, - (byte) 0x49, (byte) 0x0e, (byte) 0x7d, 0x56, (byte) 0x9c, - (byte) 0xaa, (byte) 0xb7, (byte) 0xb0, (byte) 0x2d, (byte) 0x54, - (byte) 0x01, (byte) 0x5d, (byte) 0x3e, (byte) 0x43, (byte) 0x2b, - (byte) 0x2a, (byte) 0x8e, (byte) 0xd7, (byte) 0x4e, (byte) 0xec, - (byte) 0x48, (byte) 0x75, (byte) 0x41, (byte) 0xa4, (byte) 0xa3, - (byte) 0x63, (byte) 0x30, (byte) 0x61, (byte) 0x30, (byte) 0x1d, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, - 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0xc8, - (byte) 0xad, (byte) 0xe9, (byte) 0x77, (byte) 0x4c, (byte) 0x45, - (byte) 0xc3, (byte) 0xa3, (byte) 0xcf, (byte) 0x0d, (byte) 0x16, - (byte) 0x10, (byte) 0xe4, (byte) 0x79, (byte) 0x43, (byte) 0x3a, - (byte) 0x21, (byte) 0x5a, (byte) 0x30, (byte) 0xcf, (byte) 0x30, - (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, - (byte) 0x23, (byte) 0x04, 0x18, (byte) 0x30, (byte) 0x16, - (byte) 0x80, (byte) 0x14, (byte) 0xc8, (byte) 0xad, (byte) 0xe9, - (byte) 0x77, (byte) 0x4c, (byte) 0x45, (byte) 0xc3, (byte) 0xa3, - (byte) 0xcf, (byte) 0x0d, (byte) 0x16, 0x10, (byte) 0xe4, - (byte) 0x79, (byte) 0x43, (byte) 0x3a, (byte) 0x21, (byte) 0x5a, - (byte) 0x30, (byte) 0xcf, (byte) 0x30, (byte) 0x0f, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, 0x01, - (byte) 0x01, (byte) 0xff, (byte) 0x04, (byte) 0x05, (byte) 0x30, - (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, - (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, - 0x0f, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x04, - (byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x02, (byte) 0x84, - (byte) 0x30, (byte) 0x0a, (byte) 0x06, (byte) 0x08, (byte) 0x2a, - (byte) 0x86, 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x04, - (byte) 0x03, (byte) 0x02, (byte) 0x03, (byte) 0x47, (byte) 0x00, - (byte) 0x30, (byte) 0x44, (byte) 0x02, (byte) 0x20, (byte) 0x35, - (byte) 0x21, (byte) 0xa3, (byte) 0xef, (byte) 0x8b, (byte) 0x34, - (byte) 0x46, (byte) 0x1e, (byte) 0x9c, (byte) 0xd5, (byte) 0x60, - (byte) 0xf3, (byte) 0x1d, (byte) 0x58, (byte) 0x89, (byte) 0x20, - (byte) 0x6a, (byte) 0xdc, (byte) 0xa3, 0x65, (byte) 0x41, - (byte) 0xf6, (byte) 0x0d, (byte) 0x9e, (byte) 0xce, (byte) 0x8a, - (byte) 0x19, (byte) 0x8c, (byte) 0x66, (byte) 0x48, (byte) 0x60, - (byte) 0x7b, (byte) 0x02, (byte) 0x20, (byte) 0x4d, 0x0b, - (byte) 0xf3, (byte) 0x51, (byte) 0xd9, (byte) 0x30, (byte) 0x7c, - (byte) 0x7d, (byte) 0x5b, (byte) 0xda, (byte) 0x35, (byte) 0x34, - (byte) 0x1d, (byte) 0xa8, (byte) 0x47, (byte) 0x1b, (byte) 0x63, - (byte) 0xa5, (byte) 0x85, (byte) 0x65, (byte) 0x3c, (byte) 0xad, - (byte) 0x4f, (byte) 0x24, (byte) 0xa7, (byte) 0xe7, (byte) 0x4d, - (byte) 0xaf, (byte) 0x41, (byte) 0x7d, (byte) 0xf1, - (byte) 0xbf,}; - - private static final byte[] X509Issuer = { - (byte) 0x30, (byte) 0x81, (byte) 0x88, (byte) 0x31, (byte) 0x0b, - (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, - (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, - (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, - (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, - (byte) 0x0c, (byte) 0x0a, (byte) 0x43, (byte) 0x61, (byte) 0x6c, - (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x6e, - (byte) 0x69, (byte) 0x61, (byte) 0x31, (byte) 0x15, (byte) 0x30, - (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, - (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x47, (byte) 0x6f, - (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2c, - (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x63, (byte) 0x2e, - (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, - (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, - (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, - (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x3b, - (byte) 0x30, (byte) 0x39, (byte) 0x06, (byte) 0x03, (byte) 0x55, - (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x32, (byte) 0x41, - (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, - (byte) 0x64, (byte) 0x20, (byte) 0x4b, (byte) 0x65, (byte) 0x79, - (byte) 0x73, (byte) 0x74, (byte) 0x6f, (byte) 0x72, (byte) 0x65, - (byte) 0x20, (byte) 0x53, (byte) 0x6f, (byte) 0x66, (byte) 0x74, - (byte) 0x77, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x20, - (byte) 0x41, (byte) 0x74, (byte) 0x74, (byte) 0x65, (byte) 0x73, - (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6f, - (byte) 0x6e, (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x74, - (byte) 0x65, (byte) 0x72, (byte) 0x6d, (byte) 0x65, (byte) 0x64, - (byte) 0x69, (byte) 0x61, (byte) 0x74, (byte) 0x65}; - - private static final byte[] rsa_key_pkcs8 = { - (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0xbc, (byte) 0x02, (byte) 0x01, (byte) 0x00, - (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, - (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, - (byte) 0x00, (byte) 0x04, (byte) 0x82, (byte) 0x04, (byte) 0xa6, (byte) 0x30, (byte) 0x82, - (byte) 0x04, (byte) 0xa2, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x82, - (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xc5, (byte) 0x28, (byte) 0x06, (byte) 0xb1, - (byte) 0x75, (byte) 0x6c, (byte) 0x84, (byte) 0x7a, (byte) 0x61, (byte) 0x6e, (byte) 0x49, - (byte) 0x66, (byte) 0xf8, (byte) 0x60, (byte) 0x4f, (byte) 0xec, (byte) 0x17, (byte) 0x8b, - (byte) 0x34, (byte) 0xfc, (byte) 0x3f, (byte) 0xce, (byte) 0x70, (byte) 0x6a, (byte) 0x02, - (byte) 0xf2, (byte) 0xf3, (byte) 0x6b, (byte) 0xb4, (byte) 0x78, (byte) 0xac, (byte) 0x8c, - (byte) 0x7e, (byte) 0xc5, (byte) 0xf2, (byte) 0xa8, (byte) 0xea, (byte) 0xc1, (byte) 0xe5, - (byte) 0xd3, (byte) 0xa8, (byte) 0xa9, (byte) 0x4b, (byte) 0x4b, (byte) 0x5a, (byte) 0x49, - (byte) 0xc2, (byte) 0xe7, (byte) 0x85, (byte) 0xdf, (byte) 0x56, (byte) 0xa5, (byte) 0x34, - (byte) 0xb2, (byte) 0xb6, (byte) 0xfd, (byte) 0xf2, (byte) 0xbc, (byte) 0xf1, (byte) 0xca, - (byte) 0x34, (byte) 0xba, (byte) 0x60, (byte) 0x50, (byte) 0x8d, (byte) 0x0b, (byte) 0x61, - (byte) 0xca, (byte) 0xd2, (byte) 0x76, (byte) 0x7d, (byte) 0xe4, (byte) 0xff, (byte) 0xdf, - (byte) 0x39, (byte) 0x10, (byte) 0x68, (byte) 0x9c, (byte) 0x45, (byte) 0x79, (byte) 0x8c, - (byte) 0x80, (byte) 0x0b, (byte) 0x58, (byte) 0xe4, (byte) 0x30, (byte) 0x9b, (byte) 0x74, - (byte) 0xc5, (byte) 0x09, (byte) 0x5e, (byte) 0x16, (byte) 0xa1, (byte) 0x63, (byte) 0x7f, - (byte) 0x03, (byte) 0xe9, (byte) 0xb0, (byte) 0x87, (byte) 0xf9, (byte) 0x81, (byte) 0x69, - (byte) 0x35, (byte) 0xca, (byte) 0x86, (byte) 0xe6, (byte) 0xa2, (byte) 0x1d, (byte) 0x3f, - (byte) 0xb8, (byte) 0x66, (byte) 0x39, (byte) 0x35, (byte) 0xf0, (byte) 0xef, (byte) 0xe3, - (byte) 0xde, (byte) 0x11, (byte) 0xa9, (byte) 0x9d, (byte) 0x54, (byte) 0x6f, (byte) 0xa8, - (byte) 0x04, (byte) 0x67, (byte) 0x75, (byte) 0x83, (byte) 0x67, (byte) 0xfb, (byte) 0xc2, - (byte) 0x71, (byte) 0x25, (byte) 0x43, (byte) 0xbe, (byte) 0x9c, (byte) 0x8b, (byte) 0x3e, - (byte) 0x94, (byte) 0x5e, (byte) 0xc1, (byte) 0x18, (byte) 0x83, (byte) 0x48, (byte) 0x9f, - (byte) 0x4d, (byte) 0x09, (byte) 0x1c, (byte) 0x0c, (byte) 0x61, (byte) 0xc5, (byte) 0x50, - (byte) 0x47, (byte) 0x34, (byte) 0x49, (byte) 0x17, (byte) 0x51, (byte) 0x16, (byte) 0xbc, - (byte) 0x09, (byte) 0x9b, (byte) 0x14, (byte) 0xc9, (byte) 0x44, (byte) 0x68, (byte) 0x58, - (byte) 0x19, (byte) 0xac, (byte) 0xf9, (byte) 0xd5, (byte) 0xa8, (byte) 0x52, (byte) 0x1f, - (byte) 0xb2, (byte) 0xcc, (byte) 0x9a, (byte) 0x22, (byte) 0xfe, (byte) 0xa7, (byte) 0x76, - (byte) 0x12, (byte) 0xe6, (byte) 0xfa, (byte) 0x3b, (byte) 0xc8, (byte) 0xe5, (byte) 0x26, - (byte) 0x6f, (byte) 0x62, (byte) 0xd8, (byte) 0xa4, (byte) 0x20, (byte) 0x0a, (byte) 0x6b, - (byte) 0x82, (byte) 0x6e, (byte) 0x43, (byte) 0x34, (byte) 0x34, (byte) 0x00, (byte) 0x59, - (byte) 0xbb, (byte) 0x3e, (byte) 0x54, (byte) 0xc9, (byte) 0x35, (byte) 0x77, (byte) 0x14, - (byte) 0xfd, (byte) 0x8b, (byte) 0xbd, (byte) 0x4e, (byte) 0xf0, (byte) 0x82, (byte) 0x6c, - (byte) 0xd1, (byte) 0x3d, (byte) 0xc0, (byte) 0x65, (byte) 0x98, (byte) 0xe4, (byte) 0x7e, - (byte) 0x4b, (byte) 0x69, (byte) 0xe0, (byte) 0x06, (byte) 0x92, (byte) 0x69, (byte) 0xb0, - (byte) 0x77, (byte) 0x90, (byte) 0x6b, (byte) 0xaa, (byte) 0x48, (byte) 0x2b, (byte) 0xd5, - (byte) 0x27, (byte) 0x95, (byte) 0xc2, (byte) 0xa6, (byte) 0x84, (byte) 0x45, (byte) 0xe2, - (byte) 0x84, (byte) 0x18, (byte) 0x0f, (byte) 0xfe, (byte) 0xc5, (byte) 0xf9, (byte) 0xab, - (byte) 0xbd, (byte) 0x28, (byte) 0x1d, (byte) 0x33, (byte) 0xcf, (byte) 0xb3, (byte) 0xb3, - (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x82, - (byte) 0x01, (byte) 0x00, (byte) 0x35, (byte) 0x96, (byte) 0x54, (byte) 0x83, (byte) 0x65, - (byte) 0x6c, (byte) 0x32, (byte) 0x71, (byte) 0xe5, (byte) 0x0b, (byte) 0x89, (byte) 0xed, - (byte) 0xef, (byte) 0xf2, (byte) 0x95, (byte) 0xa6, (byte) 0x91, (byte) 0x1b, (byte) 0xa8, - (byte) 0x32, (byte) 0x2b, (byte) 0xd1, (byte) 0x9b, (byte) 0xa2, (byte) 0x64, (byte) 0xdc, - (byte) 0xce, (byte) 0x26, (byte) 0xe7, (byte) 0x2d, (byte) 0xa9, (byte) 0x90, (byte) 0xa2, - (byte) 0x60, (byte) 0x81, (byte) 0x3d, (byte) 0x42, (byte) 0x59, (byte) 0xa3, (byte) 0x73, - (byte) 0x2d, (byte) 0x33, (byte) 0x9e, (byte) 0xa0, (byte) 0x83, (byte) 0x90, (byte) 0xea, - (byte) 0xe5, (byte) 0xec, (byte) 0xf0, (byte) 0x30, (byte) 0x67, (byte) 0xc4, (byte) 0xf4, - (byte) 0x12, (byte) 0x62, (byte) 0xe1, (byte) 0xd8, (byte) 0x53, (byte) 0x4b, (byte) 0xe7, - (byte) 0x9b, (byte) 0x04, (byte) 0xd4, (byte) 0xc0, (byte) 0x11, (byte) 0x68, (byte) 0xea, - (byte) 0x2c, (byte) 0xdc, (byte) 0x42, (byte) 0x09, (byte) 0xbd, (byte) 0x36, (byte) 0x5a, - (byte) 0x17, (byte) 0x48, (byte) 0xa7, (byte) 0xb9, (byte) 0x06, (byte) 0x79, (byte) 0x96, - (byte) 0xcf, (byte) 0xfe, (byte) 0xc0, (byte) 0x3f, (byte) 0x29, (byte) 0xf1, (byte) 0xca, - (byte) 0x20, (byte) 0x6a, (byte) 0xaf, (byte) 0x71, (byte) 0xfc, (byte) 0x4e, (byte) 0x28, - (byte) 0xad, (byte) 0x1a, (byte) 0xeb, (byte) 0x4a, (byte) 0x78, (byte) 0xcf, (byte) 0x34, - (byte) 0xec, (byte) 0xb0, (byte) 0x4f, (byte) 0xfd, (byte) 0x9e, (byte) 0x3f, (byte) 0x94, - (byte) 0x8a, (byte) 0x4c, (byte) 0x60, (byte) 0x89, (byte) 0xf5, (byte) 0x5a, (byte) 0x15, - (byte) 0x20, (byte) 0xed, (byte) 0xde, (byte) 0x32, (byte) 0x76, (byte) 0x0a, (byte) 0xcf, - (byte) 0xef, (byte) 0xa2, (byte) 0xf4, (byte) 0x2d, (byte) 0x13, (byte) 0xd9, (byte) 0xea, - (byte) 0x74, (byte) 0x89, (byte) 0xe5, (byte) 0x17, (byte) 0xae, (byte) 0xbf, (byte) 0x1d, - (byte) 0xbe, (byte) 0x0a, (byte) 0xc4, (byte) 0x4b, (byte) 0xf7, (byte) 0xbb, (byte) 0xc9, - (byte) 0x33, (byte) 0xd7, (byte) 0x5b, (byte) 0xa3, (byte) 0x45, (byte) 0xf4, (byte) 0xbe, - (byte) 0x02, (byte) 0xe6, (byte) 0x77, (byte) 0xd7, (byte) 0xfa, (byte) 0xa5, (byte) 0xda, - (byte) 0x13, (byte) 0x68, (byte) 0x94, (byte) 0x9f, (byte) 0x3e, (byte) 0xff, (byte) 0x15, - (byte) 0xf4, (byte) 0xd6, (byte) 0xa8, (byte) 0x28, (byte) 0xe1, (byte) 0x3f, (byte) 0x4e, - (byte) 0xa0, (byte) 0xce, (byte) 0x38, (byte) 0xa5, (byte) 0xb5, (byte) 0x17, (byte) 0x65, - (byte) 0x14, (byte) 0x06, (byte) 0x6c, (byte) 0xca, (byte) 0xb5, (byte) 0x8f, (byte) 0x70, - (byte) 0x98, (byte) 0x4d, (byte) 0x2a, (byte) 0xda, (byte) 0xeb, (byte) 0xe9, (byte) 0x07, - (byte) 0xb8, (byte) 0x09, (byte) 0xe7, (byte) 0x29, (byte) 0x31, (byte) 0x17, (byte) 0xf6, - (byte) 0x61, (byte) 0x96, (byte) 0xbf, (byte) 0x98, (byte) 0x76, (byte) 0x0d, (byte) 0x93, - (byte) 0xe1, (byte) 0xf8, (byte) 0xc7, (byte) 0xd1, (byte) 0xc4, (byte) 0xd8, (byte) 0x3a, - (byte) 0x33, (byte) 0x66, (byte) 0x4e, (byte) 0x84, (byte) 0xbd, (byte) 0x35, (byte) 0x29, - (byte) 0x51, (byte) 0x32, (byte) 0x34, (byte) 0x02, (byte) 0xcc, (byte) 0x16, (byte) 0xc6, - (byte) 0xce, (byte) 0xfa, (byte) 0x4f, (byte) 0x11, (byte) 0x9f, (byte) 0x61, (byte) 0x19, - (byte) 0xf6, (byte) 0xb6, (byte) 0xc1, (byte) 0xa4, (byte) 0xef, (byte) 0x83, (byte) 0x17, - (byte) 0xf1, (byte) 0x1e, (byte) 0xe6, (byte) 0x08, (byte) 0x76, (byte) 0x7a, (byte) 0xf0, - (byte) 0xf7, (byte) 0xa2, (byte) 0x9d, (byte) 0xa3, (byte) 0xa5, (byte) 0x69, (byte) 0x02, - (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xee, (byte) 0xb0, (byte) 0x63, (byte) 0x52, - (byte) 0x47, (byte) 0x7e, (byte) 0x94, (byte) 0x3b, (byte) 0xe5, (byte) 0x0c, (byte) 0x5c, - (byte) 0x0c, (byte) 0x5f, (byte) 0x9f, (byte) 0xec, (byte) 0xb8, (byte) 0xe6, (byte) 0x81, - (byte) 0x32, (byte) 0x7b, (byte) 0x2d, (byte) 0xf9, (byte) 0x2c, (byte) 0xa5, (byte) 0x30, - (byte) 0x86, (byte) 0x2b, (byte) 0xd0, (byte) 0x6f, (byte) 0x64, (byte) 0xfd, (byte) 0xb5, - (byte) 0xb7, (byte) 0x32, (byte) 0xe4, (byte) 0x02, (byte) 0x2f, (byte) 0x16, (byte) 0x94, - (byte) 0x95, (byte) 0xae, (byte) 0x7b, (byte) 0x57, (byte) 0xee, (byte) 0x4b, (byte) 0xf0, - (byte) 0xde, (byte) 0x9d, (byte) 0x54, (byte) 0x29, (byte) 0x99, (byte) 0xcc, (byte) 0xe0, - (byte) 0xf6, (byte) 0xb5, (byte) 0x17, (byte) 0x03, (byte) 0xfe, (byte) 0xfc, (byte) 0x56, - (byte) 0x91, (byte) 0x43, (byte) 0x22, (byte) 0xce, (byte) 0x0f, (byte) 0xfa, (byte) 0x08, - (byte) 0x88, (byte) 0x5e, (byte) 0xb6, (byte) 0x73, (byte) 0xaa, (byte) 0x82, (byte) 0xe7, - (byte) 0x4c, (byte) 0x2a, (byte) 0xaf, (byte) 0x80, (byte) 0xc6, (byte) 0x83, (byte) 0xab, - (byte) 0x2a, (byte) 0xdd, (byte) 0xd7, (byte) 0xc1, (byte) 0x15, (byte) 0xdb, (byte) 0x94, - (byte) 0x98, (byte) 0x0a, (byte) 0x97, (byte) 0x00, (byte) 0x26, (byte) 0x5b, (byte) 0x62, - (byte) 0x0b, (byte) 0x27, (byte) 0xc3, (byte) 0x64, (byte) 0x38, (byte) 0x98, (byte) 0xd7, - (byte) 0x26, (byte) 0xcf, (byte) 0x73, (byte) 0x98, (byte) 0xe4, (byte) 0x59, (byte) 0x0a, - (byte) 0xb1, (byte) 0x06, (byte) 0x1f, (byte) 0x80, (byte) 0x3c, (byte) 0x19, (byte) 0x20, - (byte) 0x1b, (byte) 0xc3, (byte) 0x47, (byte) 0xaf, (byte) 0x2b, (byte) 0x12, (byte) 0xdf, - (byte) 0xef, (byte) 0x1d, (byte) 0x4d, (byte) 0xfc, (byte) 0xbd, (byte) 0x02, (byte) 0x81, - (byte) 0x81, (byte) 0x00, (byte) 0xd3, (byte) 0x74, (byte) 0x85, (byte) 0xf6, (byte) 0xad, - (byte) 0xdf, (byte) 0x84, (byte) 0xf4, (byte) 0xde, (byte) 0x97, (byte) 0x19, (byte) 0x30, - (byte) 0xa8, (byte) 0x4a, (byte) 0xf6, (byte) 0x7f, (byte) 0x80, (byte) 0x55, (byte) 0x49, - (byte) 0xad, (byte) 0x55, (byte) 0x2c, (byte) 0x87, (byte) 0x5f, (byte) 0x29, (byte) 0xda, - (byte) 0x7a, (byte) 0x81, (byte) 0xd6, (byte) 0xe5, (byte) 0xd8, (byte) 0x8e, (byte) 0x9f, - (byte) 0xbd, (byte) 0x35, (byte) 0xfe, (byte) 0x82, (byte) 0x0b, (byte) 0x5c, (byte) 0x28, - (byte) 0x95, (byte) 0x44, (byte) 0xab, (byte) 0x8c, (byte) 0x9e, (byte) 0xa1, (byte) 0xf2, - (byte) 0x5f, (byte) 0x2a, (byte) 0x6a, (byte) 0x96, (byte) 0x35, (byte) 0xbc, (byte) 0x09, - (byte) 0x4a, (byte) 0xb1, (byte) 0x19, (byte) 0x2f, (byte) 0xc1, (byte) 0x00, (byte) 0xba, - (byte) 0x3f, (byte) 0x8b, (byte) 0x9e, (byte) 0x2b, (byte) 0xbd, (byte) 0x0a, (byte) 0x0f, - (byte) 0x2d, (byte) 0x75, (byte) 0x09, (byte) 0xb6, (byte) 0xea, (byte) 0x98, (byte) 0xb1, - (byte) 0xff, (byte) 0xd8, (byte) 0x21, (byte) 0x13, (byte) 0x5d, (byte) 0xee, (byte) 0x5b, - (byte) 0xf2, (byte) 0xad, (byte) 0x46, (byte) 0x81, (byte) 0x9d, (byte) 0x18, (byte) 0x2b, - (byte) 0x9e, (byte) 0x77, (byte) 0x78, (byte) 0x27, (byte) 0xf5, (byte) 0x3a, (byte) 0x5a, - (byte) 0xb5, (byte) 0x9b, (byte) 0x02, (byte) 0x66, (byte) 0x1b, (byte) 0xb8, (byte) 0x51, - (byte) 0x9a, (byte) 0x07, (byte) 0xb7, (byte) 0x3f, (byte) 0x41, (byte) 0x8b, (byte) 0xfe, - (byte) 0x1e, (byte) 0x85, (byte) 0xc7, (byte) 0xfe, (byte) 0x01, (byte) 0x7a, (byte) 0x7e, - (byte) 0x2e, (byte) 0xb6, (byte) 0x3b, (byte) 0x64, (byte) 0x6e, (byte) 0xdc, (byte) 0x9d, - (byte) 0x7a, (byte) 0x48, (byte) 0xd1, (byte) 0x2f, (byte) 0x02, (byte) 0x81, (byte) 0x80, - (byte) 0x36, (byte) 0x6a, (byte) 0x76, (byte) 0x2a, (byte) 0x42, (byte) 0xec, (byte) 0x63, - (byte) 0xa5, (byte) 0x08, (byte) 0x01, (byte) 0xfa, (byte) 0x56, (byte) 0x43, (byte) 0xd2, - (byte) 0xb4, (byte) 0xe8, (byte) 0x2e, (byte) 0x7c, (byte) 0xd3, (byte) 0xe2, (byte) 0x6b, - (byte) 0x47, (byte) 0xbc, (byte) 0x5a, (byte) 0xe8, (byte) 0xa6, (byte) 0x1e, (byte) 0x05, - (byte) 0x05, (byte) 0xf0, (byte) 0x53, (byte) 0x3b, (byte) 0x03, (byte) 0x4a, (byte) 0x11, - (byte) 0xdb, (byte) 0x41, (byte) 0x9a, (byte) 0xf7, (byte) 0x42, (byte) 0xec, (byte) 0xa5, - (byte) 0x68, (byte) 0x15, (byte) 0x86, (byte) 0xb0, (byte) 0xa2, (byte) 0x3f, (byte) 0xe1, - (byte) 0xf9, (byte) 0x1d, (byte) 0xfc, (byte) 0x2c, (byte) 0x69, (byte) 0x72, (byte) 0x3d, - (byte) 0x8e, (byte) 0x06, (byte) 0xaa, (byte) 0xc6, (byte) 0x9d, (byte) 0x95, (byte) 0x5d, - (byte) 0xb0, (byte) 0xf6, (byte) 0xc9, (byte) 0x7c, (byte) 0xfa, (byte) 0x82, (byte) 0x05, - (byte) 0x3c, (byte) 0x77, (byte) 0x6a, (byte) 0x22, (byte) 0x8b, (byte) 0x25, (byte) 0xcc, - (byte) 0x1f, (byte) 0x22, (byte) 0xa2, (byte) 0xcf, (byte) 0xfa, (byte) 0x14, (byte) 0xdb, - (byte) 0x64, (byte) 0x44, (byte) 0xb4, (byte) 0x6b, (byte) 0xbb, (byte) 0x01, (byte) 0xe7, - (byte) 0x0c, (byte) 0xfc, (byte) 0xb1, (byte) 0xa6, (byte) 0xb7, (byte) 0x7e, (byte) 0x58, - (byte) 0x38, (byte) 0x58, (byte) 0x02, (byte) 0xd8, (byte) 0x42, (byte) 0x1b, (byte) 0xd7, - (byte) 0x71, (byte) 0xca, (byte) 0xd5, (byte) 0x55, (byte) 0xef, (byte) 0xa7, (byte) 0xc2, - (byte) 0xb4, (byte) 0xbc, (byte) 0x7e, (byte) 0xc9, (byte) 0xe8, (byte) 0x2a, (byte) 0x6c, - (byte) 0x04, (byte) 0x4e, (byte) 0x60, (byte) 0x9e, (byte) 0x36, (byte) 0xe8, (byte) 0x4a, - (byte) 0x68, (byte) 0x4d, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x06, (byte) 0x73, - (byte) 0x24, (byte) 0x6e, (byte) 0xec, (byte) 0xc8, (byte) 0xc7, (byte) 0x96, (byte) 0x6c, - (byte) 0x7f, (byte) 0xb1, (byte) 0x5e, (byte) 0x01, (byte) 0x94, (byte) 0x1f, (byte) 0xc6, - (byte) 0xad, (byte) 0xd4, (byte) 0x6c, (byte) 0x25, (byte) 0xe4, (byte) 0x56, (byte) 0x32, - (byte) 0x5e, (byte) 0xdd, (byte) 0xb8, (byte) 0xf3, (byte) 0x49, (byte) 0xa8, (byte) 0x93, - (byte) 0x64, (byte) 0x32, (byte) 0x9d, (byte) 0x7e, (byte) 0xb8, (byte) 0xf9, (byte) 0xe5, - (byte) 0x5f, (byte) 0x91, (byte) 0x55, (byte) 0x0f, (byte) 0x90, (byte) 0x83, (byte) 0xa7, - (byte) 0x0b, (byte) 0x63, (byte) 0xa7, (byte) 0x2f, (byte) 0xed, (byte) 0xec, (byte) 0x48, - (byte) 0x5e, (byte) 0xa5, (byte) 0x38, (byte) 0xa7, (byte) 0x55, (byte) 0x95, (byte) 0x8e, - (byte) 0x16, (byte) 0x55, (byte) 0xfe, (byte) 0x58, (byte) 0x57, (byte) 0xda, (byte) 0xe0, - (byte) 0x3c, (byte) 0xa8, (byte) 0xe4, (byte) 0xe3, (byte) 0x9f, (byte) 0x11, (byte) 0x47, - (byte) 0xca, (byte) 0x0a, (byte) 0x14, (byte) 0x4b, (byte) 0xd8, (byte) 0x7c, (byte) 0xd1, - (byte) 0xc9, (byte) 0x68, (byte) 0xae, (byte) 0xd7, (byte) 0x4d, (byte) 0x1f, (byte) 0xbc, - (byte) 0x6e, (byte) 0x5d, (byte) 0x41, (byte) 0x5f, (byte) 0x59, (byte) 0x07, (byte) 0x8a, - (byte) 0x38, (byte) 0x79, (byte) 0xaa, (byte) 0x30, (byte) 0xa5, (byte) 0xe4, (byte) 0xc1, - (byte) 0xd6, (byte) 0x90, (byte) 0x9d, (byte) 0xb4, (byte) 0x94, (byte) 0x0d, (byte) 0xab, - (byte) 0xd9, (byte) 0x44, (byte) 0xfa, (byte) 0xe0, (byte) 0x55, (byte) 0x76, (byte) 0x4f, - (byte) 0x32, (byte) 0x1e, (byte) 0x59, (byte) 0x60, (byte) 0xf5, (byte) 0x60, (byte) 0x04, - (byte) 0x65, (byte) 0x39, (byte) 0x47, (byte) 0x78, (byte) 0x66, (byte) 0x66, (byte) 0x33, - (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x37, (byte) 0x90, (byte) 0x1c, (byte) 0x72, - (byte) 0x46, (byte) 0xc4, (byte) 0xda, (byte) 0x2c, (byte) 0x50, (byte) 0xb8, (byte) 0x4f, - (byte) 0xdc, (byte) 0x82, (byte) 0x98, (byte) 0xbc, (byte) 0xec, (byte) 0x1d, (byte) 0x84, - (byte) 0xc1, (byte) 0x33, (byte) 0xb7, (byte) 0x60, (byte) 0x1e, (byte) 0x58, (byte) 0x81, - (byte) 0x01, (byte) 0x24, (byte) 0x4c, (byte) 0x66, (byte) 0x17, (byte) 0xbc, (byte) 0xc3, - (byte) 0x83, (byte) 0x0b, (byte) 0x10, (byte) 0x38, (byte) 0x3c, (byte) 0x3c, (byte) 0xb4, - (byte) 0x36, (byte) 0x0e, (byte) 0x1b, (byte) 0xb5, (byte) 0x93, (byte) 0xd7, (byte) 0x47, - (byte) 0x14, (byte) 0x48, (byte) 0xf1, (byte) 0xf9, (byte) 0x53, (byte) 0xb5, (byte) 0xe1, - (byte) 0xe3, (byte) 0x0b, (byte) 0x51, (byte) 0x02, (byte) 0x14, (byte) 0x24, (byte) 0x0c, - (byte) 0x37, (byte) 0xf5, (byte) 0x78, (byte) 0xac, (byte) 0x00, (byte) 0x9f, (byte) 0xb2, - (byte) 0xfb, (byte) 0x32, (byte) 0x6c, (byte) 0xef, (byte) 0x2d, (byte) 0xa1, (byte) 0x7c, - (byte) 0xaf, (byte) 0xbb, (byte) 0x53, (byte) 0x9e, (byte) 0x7a, (byte) 0xc2, (byte) 0x5f, - (byte) 0x37, (byte) 0x74, (byte) 0xe9, (byte) 0x9b, (byte) 0x2b, (byte) 0xdb, (byte) 0x48, - (byte) 0xa0, (byte) 0x62, (byte) 0xcb, (byte) 0xee, (byte) 0x80, (byte) 0x07, (byte) 0xdc, - (byte) 0x0c, (byte) 0xc5, (byte) 0xe6, (byte) 0xc5, (byte) 0xbe, (byte) 0xd8, (byte) 0x82, - (byte) 0xd1, (byte) 0xd8, (byte) 0xd0, (byte) 0xd5, (byte) 0x8c, (byte) 0x55, (byte) 0xd4, - (byte) 0xfa, (byte) 0x50, (byte) 0x05, (byte) 0x7a, (byte) 0x02, (byte) 0x6d, (byte) 0xda, - (byte) 0x56, (byte) 0xec, (byte) 0xca, (byte) 0xf4, (byte) 0x27, (byte) 0xf0, (byte) 0x8f, - (byte) 0x8f, (byte) 0xc5, (byte) 0x3c, (byte) 0x28, (byte) 0x30 - }; - - private static final byte[] ec_key_pkcs8 = { - (byte)0x30, (byte)0x81, (byte)0x87, (byte)0x02, (byte)0x01, (byte)0x00, - (byte)0x30, (byte)0x13, (byte)0x06, (byte)0x07, (byte)0x2a, (byte)0x86, - (byte)0x48, (byte)0xce, (byte)0x3d, (byte)0x02, (byte)0x01, (byte)0x06, - (byte)0x08, (byte)0x2a, (byte)0x86, (byte)0x48, (byte)0xce, (byte)0x3d, - (byte)0x03, (byte)0x01, (byte)0x07, (byte)0x04, (byte)0x6d, (byte)0x30, - (byte)0x6b, (byte)0x02, (byte)0x01, (byte)0x01, (byte)0x04, (byte)0x20, - (byte)0xfc, (byte)0x06, (byte)0xed, (byte)0x57, (byte)0xe9, (byte)0x03, - (byte)0xd9, (byte)0xfe, (byte)0x3f, (byte)0x32, (byte)0x34, (byte)0x0f, - (byte)0xd3, (byte)0x69, (byte)0x0a, (byte)0x4d, (byte)0xe8, (byte)0x0b, - (byte)0x08, (byte)0xcd, (byte)0x17, (byte)0x1c, (byte)0x5f, (byte)0xe5, - (byte)0xd3, (byte)0xaa, (byte)0x34, (byte)0xd2, (byte)0x09, (byte)0x0b, - (byte)0xb2, (byte)0x1a, (byte)0xa1, (byte)0x44, (byte)0x03, (byte)0x42, - (byte)0x00, (byte)0x04, (byte)0xf7, (byte)0x84, (byte)0xf4, (byte)0xae, - (byte)0xf2, (byte)0x80, (byte)0xca, (byte)0xe0, (byte)0xe6, (byte)0x38, - (byte)0x63, (byte)0x83, (byte)0x39, (byte)0x65, (byte)0xd7, (byte)0x4c, - (byte)0x3d, (byte)0x75, (byte)0x13, (byte)0x7a, (byte)0x3b, (byte)0xcd, - (byte)0x1a, (byte)0xca, (byte)0xa1, (byte)0x4b, (byte)0x1d, (byte)0xa1, - (byte)0x6a, (byte)0xa2, (byte)0x13, (byte)0xf5, (byte)0xf5, (byte)0xee, - (byte)0x90, (byte)0x92, (byte)0xeb, (byte)0x8f, (byte)0x67, (byte)0xb1, - (byte)0xd0, (byte)0xa2, (byte)0x6e, (byte)0x02, (byte)0x1a, (byte)0x83, - (byte)0x12, (byte)0x5b, (byte)0x68, (byte)0x8e, (byte)0x50, (byte)0x65, - (byte)0x35, (byte)0x66, (byte)0xa1, (byte)0xee, (byte)0x86, (byte)0x62, - (byte)0x22, (byte)0xe6, (byte)0x00, (byte)0x61, (byte)0x54, (byte)0x86 - }; - // AttestationApplicationId ::= SEQUENCE { - // * packageInfoRecords SET OF PackageInfoRecord, - // * signatureDigests SET OF OCTET_STRING, - // * } - // * - // * PackageInfoRecord ::= SEQUENCE { - // * packageName OCTET_STRING, - // * version INTEGER, - // * } - private static final byte[] attAppId = {0x30, 0x10, 0x31, 0x0B, 0x30, 0x04, 0x05, 'A', 'B', 'C', - 'D', 'E', 0x02, 0x01, 0x01, 0x31, 0x02, 0x04, 0x00}; - private static final byte[] attChallenge = {'c', 'h', 'a', 'l', 'l', 'e', 'n', 'g', 'e'}; - private static final byte[] expiryTime = {(byte) 0x32, (byte) 0x36, (byte) 0x30, (byte) 0x31, - (byte) 0x30, (byte) 0x38, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x36, (byte) 0x30, - (byte) 0x39, (byte) 0x5a}; - private static final byte[] authKeyId = {(byte) 0x80, (byte) 0x14, (byte) 0xc8, (byte) 0xad, - (byte) 0xe9, (byte) 0x77, (byte) 0x4c, (byte) 0x45, (byte) 0xc3, (byte) 0xa3, (byte) 0xcf, - (byte) 0x0d, (byte) 0x16, (byte) 0x10, (byte) 0xe4, (byte) 0x79, (byte) 0x43, (byte) 0x3a, - (byte) 0x21, (byte) 0x5a, (byte) 0x30, (byte) 0xcf}; - private static final int OS_VERSION = 1; - private static final int OS_PATCH_LEVEL = 1; - private static final int VENDOR_PATCH_LEVEL = 1; - private static final int BOOT_PATCH_LEVEL = 1; - private static final short MAJOR_TYPE_MASK = 0xE0; - private static final byte CBOR_ARRAY_MAJOR_TYPE = (byte) 0x80; - private static final byte CBOR_UINT_MAJOR_TYPE = 0x00; - private static final short SE_POWER_RESET_FLAG = (short) 0x4000; - private static final boolean RESET = true; - private static final boolean NO_RESET = false; - - private CardSimulator simulator; - private KMEncoder encoder; - private KMDecoder decoder; - private KMSEProvider cryptoProvider; - - public KMFunctionalTest() { - cryptoProvider = new KMJCardSimulator(); - simulator = new CardSimulator(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - } - - private void init() { - // Create simulator - AID appletAID = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID, KMJCardSimApplet.class); - // Select applet - simulator.selectApplet(appletAID); - // provision attest key - provisionCmd(simulator); - } - - private void setAndroidOSSystemProperties(CardSimulator simulator, short osVersion, - short osPatchLevel, short vendorPatchLevel) { - // Argument 1 OS Version - short versionPtr = KMInteger.uint_16(osVersion); - // short versionTagPtr = KMIntegerTag.instance(KMType.UINT_TAG, - // KMType.OS_VERSION,versionPatchPtr); - // Argument 2 OS Patch level - short patchPtr = KMInteger.uint_16(osPatchLevel); - short vendorpatchPtr = KMInteger.uint_16((short) vendorPatchLevel); - // Arguments - short arrPtr = KMArray.instance((short) 3); - KMArray vals = KMArray.cast(arrPtr); - vals.add((short) 0, versionPtr); - vals.add((short) 1, patchPtr); - vals.add((short) 2, vendorpatchPtr); - CommandAPDU apdu = encodeApdu((byte) INS_SET_VERSION_PATCHLEVEL_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - - } - - private void setBootParams(CardSimulator simulator, short bootPatchLevel) { - // Argument 0 boot patch level - short bootpatchPtr = KMInteger.uint_16((short) bootPatchLevel); - // Argument 1 Verified Boot Key - byte[] bootKeyHash = "00011122233344455566677788899900".getBytes(); - short bootKeyPtr = KMByteBlob.instance(bootKeyHash, (short) 0, - (short) bootKeyHash.length); - // Argument 2 Verified Boot Hash - short bootHashPtr = KMByteBlob.instance(bootKeyHash, (short) 0, - (short) bootKeyHash.length); - // Argument 3 Verified Boot State - short bootStatePtr = KMEnum.instance(KMType.VERIFIED_BOOT_STATE, - KMType.VERIFIED_BOOT); - // Argument 4 Device Locked - short deviceLockedPtr = KMEnum.instance(KMType.DEVICE_LOCKED, - KMType.DEVICE_LOCKED_FALSE); - // Arguments - short arrPtr = KMArray.instance((short) 5); - KMArray vals = KMArray.cast(arrPtr); - vals.add((short) 0, bootpatchPtr); - vals.add((short) 1, bootKeyPtr); - vals.add((short) 2, bootHashPtr); - vals.add((short) 3, bootStatePtr); - vals.add((short) 4, deviceLockedPtr); - CommandAPDU apdu = encodeApdu((byte) INS_SET_BOOT_PARAMS_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - - } - - private void provisionSigningCertificate(CardSimulator simulator) { - short arrPtr = KMArray.instance((short) 3); - - short byteBlobPtr = KMByteBlob.instance( - (short) (kEcAttestCert.length + kEcAttestRootCert.length)); - Util.arrayCopyNonAtomic(kEcAttestCert, (short) 0, - KMByteBlob.cast(byteBlobPtr).getBuffer(), - KMByteBlob.cast(byteBlobPtr).getStartOff(), - (short) kEcAttestCert.length); - Util.arrayCopyNonAtomic(kEcAttestRootCert, (short) 0, - KMByteBlob.cast(byteBlobPtr).getBuffer(), - (short) (KMByteBlob.cast(byteBlobPtr).getStartOff() - + kEcAttestCert.length), - (short) kEcAttestRootCert.length); - KMArray.cast(arrPtr).add((short) 0, byteBlobPtr); - - short byteBlob1 = KMByteBlob.instance(X509Issuer, (short) 0, - (short) X509Issuer.length); - KMArray.cast(arrPtr).add((short) 1, byteBlob1); - short byteBlob2 = KMByteBlob.instance(expiryTime, (short) 0, - (short) expiryTime.length); - KMArray.cast(arrPtr).add((short) 2, byteBlob2); - - CommandAPDU apdu = encodeApdu( - (byte) INS_PROVISION_ATTESTATION_CERT_DATA_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void provisionSigningKey(CardSimulator simulator) { - // KeyParameters. - short arrPtr = KMArray.instance((short) 4); - short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - short byteBlob2 = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob2).add((short) 0, KMType.ATTEST_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob2); - KMArray.cast(arrPtr).add((short) 0, ecCurve); - KMArray.cast(arrPtr).add((short) 1, digest); - KMArray.cast(arrPtr).add((short) 2, - KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - KMArray.cast(arrPtr).add((short) 3, purpose); - short keyParams = KMKeyParameters.instance(arrPtr); - // Note: VTS uses PKCS8 KeyFormat RAW - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - - // Key - short signKeyPtr = KMArray.instance((short) 2); - KMArray.cast(signKeyPtr).add((short) 0, KMByteBlob.instance(kEcPrivKey, - (short) 0, (short) kEcPrivKey.length)); - KMArray.cast(signKeyPtr).add((short) 1, KMByteBlob.instance(kEcPubKey, - (short) 0, (short) kEcPubKey.length)); - byte[] keyBuf = new byte[120]; - short len = encoder.encode(signKeyPtr, keyBuf, (short) 0); - short signKeyBstr = KMByteBlob.instance(keyBuf, (short) 0, len); - - short finalArrayPtr = KMArray.instance((short) 3); - KMArray.cast(finalArrayPtr).add((short) 0, keyParams); - KMArray.cast(finalArrayPtr).add((short) 1, keyFormatPtr); - KMArray.cast(finalArrayPtr).add((short) 2, signKeyBstr); - - CommandAPDU apdu = encodeApdu((byte) INS_PROVISION_ATTESTATION_KEY_CMD, - finalArrayPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void provisionSharedSecret(CardSimulator simulator) { - byte[] sharedKeySecret = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0}; - short arrPtr = KMArray.instance((short) 1); - short byteBlob = KMByteBlob.instance(sharedKeySecret, (short) 0, - (short) sharedKeySecret.length); - KMArray.cast(arrPtr).add((short) 0, byteBlob); - - CommandAPDU apdu = encodeApdu((byte) INS_PROVISION_PRESHARED_SECRET_CMD, - arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void provisionAttestIds(CardSimulator simulator) { - short arrPtr = KMArray.instance((short) 8); - - byte[] buf = "Attestation Id".getBytes(); - - KMArray.cast(arrPtr).add((short) 0, - KMByteTag.instance(KMType.ATTESTATION_ID_BRAND, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - KMArray.cast(arrPtr).add((short) 1, - KMByteTag.instance(KMType.ATTESTATION_ID_PRODUCT, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - KMArray.cast(arrPtr).add((short) 2, - KMByteTag.instance(KMType.ATTESTATION_ID_DEVICE, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - KMArray.cast(arrPtr).add((short) 3, - KMByteTag.instance(KMType.ATTESTATION_ID_MODEL, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - KMArray.cast(arrPtr).add((short) 4, - KMByteTag.instance(KMType.ATTESTATION_ID_IMEI, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - KMArray.cast(arrPtr).add((short) 5, - KMByteTag.instance(KMType.ATTESTATION_ID_MEID, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - KMArray.cast(arrPtr).add((short) 6, - KMByteTag.instance(KMType.ATTESTATION_ID_MANUFACTURER, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - KMArray.cast(arrPtr).add((short) 7, - KMByteTag.instance(KMType.ATTESTATION_ID_SERIAL, - KMByteBlob.instance(buf, (short) 0, (short) buf.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - short outerArrPtr = KMArray.instance((short) 1); - KMArray.cast(outerArrPtr).add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_PROVISION_ATTEST_IDS_CMD, - outerArrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void provisionLocked(CardSimulator simulator) { - CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_LOCK_PROVISIONING_CMD, - 0x40, 0x00); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - Assert.assertEquals(0x9000, response.getSW()); - } - - private void provisionCmd(CardSimulator simulator) { - provisionSigningKey(simulator); - provisionSigningCertificate(simulator); - provisionSharedSecret(simulator); - provisionAttestIds(simulator); - // set bootup parameters - setBootParams(simulator, (short) BOOT_PATCH_LEVEL); - // set android system properties - setAndroidOSSystemProperties(simulator, (short) OS_VERSION, (short) OS_PATCH_LEVEL, - (short) VENDOR_PATCH_LEVEL); - provisionLocked(simulator); - } - - private void cleanUp() { - AID appletAID = AIDUtil.create("A000000062"); - // Delete i.e. uninstall applet - simulator.deleteApplet(appletAID); - } - - private void resetAndSelect() { - simulator.reset(); - AID appletAID = AIDUtil.create("A000000062"); - // Select applet - simulator.selectApplet(appletAID); - } - - - private CommandAPDU encodeApdu(byte ins, short cmd) { - byte[] buf = new byte[2500]; - buf[0] = (byte) 0x80; - buf[1] = ins; - buf[2] = (byte) 0x40; - buf[3] = (byte) 0x00; - buf[4] = 0; - short len = encoder.encode(cmd, buf, (short) 7); - Util.setShort(buf, (short) 5, len); - byte[] apdu = new byte[7 + len]; - Util.arrayCopyNonAtomic(buf, (short) 0, apdu, (short) 0, (short) (7 + len)); - //CommandAPDU commandAPDU = new CommandAPDU(0x80, 0x10, 0x40, 0x00, buf, 0, actualLen); - return new CommandAPDU(apdu); - } - - @Test - public void testAesImportKeySuccess() { - init(); - byte[] aesKeySecret = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - short arrPtr = KMArray.instance((short) 5); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ECB); - short blockMode = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.PKCS7); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - KMArray.cast(arrPtr).add((short) 0, boolTag); - KMArray.cast(arrPtr).add((short) 1, keySize); - KMArray.cast(arrPtr).add((short) 2, blockMode); - KMArray.cast(arrPtr).add((short) 3, paddingMode); - KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - short keyBlob = KMByteBlob.instance(aesKeySecret, (short) 0, (short) 16); - arrPtr = KMArray.instance((short) 3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short) 1, keyFormatPtr); - arg.add((short) 2, keyBlob); - CommandAPDU apdu = encodeApdu((byte) INS_IMPORT_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - @Test - public void testHmacImportKeySuccess() { - init(); - byte[] hmacKeySecret = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - short arrPtr = KMArray.instance((short) 5); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - short minMacLength = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short) 256)); - KMArray.cast(arrPtr).add((short) 0, boolTag); - KMArray.cast(arrPtr).add((short) 1, keySize); - KMArray.cast(arrPtr).add((short) 2, digest); - KMArray.cast(arrPtr).add((short) 3, minMacLength); - KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - short keyBlob = KMByteBlob.instance(hmacKeySecret, (short) 0, (short) 16); - arrPtr = KMArray.instance((short) 3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short) 1, keyFormatPtr); - arg.add((short) 2, keyBlob); - CommandAPDU apdu = encodeApdu((byte) INS_IMPORT_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - @Test - public void testRsaImportKeySuccess() { - init(); - byte[] pub = new byte[]{0x00, 0x01, 0x00, 0x01}; - short arrPtr = KMArray.instance((short) 6); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 2048)); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - short rsaPubExpTag = KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, - KMInteger.uint_32(pub, (short) 0)); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.RSA_PSS); - short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - KMArray.cast(arrPtr).add((short) 0, boolTag); - KMArray.cast(arrPtr).add((short) 1, keySize); - KMArray.cast(arrPtr).add((short) 2, digest); - KMArray.cast(arrPtr).add((short) 3, rsaPubExpTag); - KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - KMArray.cast(arrPtr).add((short) 5, padding); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.PKCS8); - short keyBlob = KMByteBlob.instance(rsa_key_pkcs8, (short) 0, (short) rsa_key_pkcs8.length); - arrPtr = KMArray.instance((short) 3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short) 1, keyFormatPtr); - arg.add((short) 2, keyBlob); - CommandAPDU apdu = encodeApdu((byte) INS_IMPORT_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PSS)); - tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), - 0x01); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - @Test - public void testDeviceLocked() { - init(); - byte[] hmacKey = new byte[32]; - cryptoProvider.newRandomNumber(hmacKey, (short) 0, (short) 32); - cryptoProvider.createComputedHmacKey(hmacKey, (short) 0, (short) 32); - // generate aes key with unlocked_device_required - short aesKey = generateAesDesKey(KMType.AES, (short) 128, null, null, true); - short keyBlobPtr = KMArray.cast(aesKey).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - // encrypt something - short inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, null); - byte[] plainData = "Hello World 123!".getBytes(); - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.ENCRYPT, - KMKeyParameters.instance(inParams), - (short) 0, null, false, false - ); - keyBlobPtr = KMArray.cast(ret).get((short) 2); - byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - cipherData, (short) 0, (short) cipherData.length); - // create verification token - short verToken = KMVerificationToken.instance(); - KMVerificationToken.cast(verToken).setTimestamp(KMInteger.uint_16((short) 1)); - verToken = signVerificationToken(verToken, KMConfigurations.TEE_MACHINE_TYPE); - // device locked request - deviceLock(verToken, KMError.VERIFICATION_FAILED); - cleanUp(); - } - - private short signHwToken(short hwToken) { - short len = 0; - byte[] scratchPad = new byte[256]; - // add 0 - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - len = 1; - // concatenate challenge - 8 bytes - short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate user id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator type - 4 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate timestamp -8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // hmac the data -/* HMACKey key = - cryptoProvider.createHMACKey( - KMRepository.instance().getComputedHmacKey(), - (short) 0, - (short) KMRepository.instance().getComputedHmacKey().length); - - */ - byte[] mac = new byte[32]; - short key = KMByteBlob.instance((short) 32); - KMHmacKey computedHmacKey = (KMHmacKey) cryptoProvider.getComputedHmacKey(); - computedHmacKey.getKey(KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff()); - cryptoProvider.hmacSign( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, (short) 0, len, - mac, - (short) 0); - KMHardwareAuthToken.cast(hwToken) - .setMac(KMByteBlob.instance(mac, (short) 0, (short) mac.length)); - return hwToken; - } - - private void deviceLock(short verToken, short expectedError) { - short req = KMArray.instance((short) 2); - KMArray.cast(req).add((short) 0, KMInteger.uint_8((byte) 1)); - KMArray.cast(req).add((short) 1, verToken); - CommandAPDU apdu = encodeApdu((byte) INS_DEVICE_LOCKED_CMD, req); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - byte majorType = readMajorType(respBuf); - short retError; - if (majorType == CBOR_ARRAY_MAJOR_TYPE) { - short ret = KMArray.instance((short) 1); - ret = decoder.decode(ret, respBuf, (short) 0, len); - retError = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - } else {//Major type UINT. - short ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, len); - retError = KMInteger.cast(ret).getShort(); - } - Assert.assertEquals(retError, expectedError); - } - - private short signVerificationToken(short verToken, byte machineType) { - byte[] scratchPad = new byte[256]; - byte[] authVer = "Auth Verification".getBytes(); - //print(authVer,(short)0,(short)authVer.length); - // concatenation length will be 37 + length of verified parameters list - which is typically empty - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - short params = KMVerificationToken.cast(verToken).getParametersVerified(); - // Add "Auth Verification" - 17 bytes. - Util.arrayCopy(authVer, (short) 0, scratchPad, (short) 0, (short) authVer.length); - short len = (short) authVer.length; - // concatenate challenge - 8 bytes - short ptr = KMVerificationToken.cast(verToken).getChallenge(); - if (machineType == KMConfigurations.LITTLE_ENDIAN) { - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - } else { - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - } - len += 8; - // concatenate timestamp -8 bytes - ptr = KMVerificationToken.cast(verToken).getTimestamp(); - if (machineType == KMConfigurations.LITTLE_ENDIAN) { - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - } else { - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - } - len += 8; - // concatenate security level - 4 bytes - ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - if (machineType == KMConfigurations.LITTLE_ENDIAN) { - scratchPad[len] = KMEnum.cast(ptr).getVal(); - } else { - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - } - len += 4; - // concatenate Parameters verified - blob of encoded data. - ptr = KMVerificationToken.cast(verToken).getParametersVerified(); - if (KMByteBlob.cast(ptr).length() != 0) { - len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); - } - // hmac the data - /* HMACKey key = - cryptoProvider.createHMACKey( - KMRepository.instance().getComputedHmacKey(), - (short) 0, - (short) KMRepository.instance().getComputedHmacKey().length); - - */ - ptr = KMVerificationToken.cast(verToken).getMac(); - byte[] mac = new byte[32]; - /*len = - cryptoProvider.hmacSign(key, scratchPad, (short) 0, len, - mac, - (short)0); - */ - short key = KMByteBlob.instance((short) 32); - KMHmacKey computedHmacKey = (KMHmacKey) cryptoProvider.getComputedHmacKey(); - computedHmacKey.getKey(KMByteBlob.cast(key).getBuffer(), KMByteBlob.cast(key).getStartOff()); - cryptoProvider.hmacSign(KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, (short) 0, len, - mac, - (short) 0); - KMVerificationToken.cast(verToken) - .setMac(KMByteBlob.instance(mac, (short) 0, (short) mac.length)); - return verToken; - } - - @Test - public void testEcImportKeySuccess() { - init(); - short arrPtr = KMArray.instance((short) 5); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); - KMArray.cast(arrPtr).add((short) 0, boolTag); - KMArray.cast(arrPtr).add((short) 1, keySize); - KMArray.cast(arrPtr).add((short) 2, digest); - KMArray.cast(arrPtr).add((short) 3, ecCurve); - KMArray.cast(arrPtr).add((short) 4, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - short keyParams = KMKeyParameters.instance(arrPtr); - short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.PKCS8); - short keyBlob = KMByteBlob.instance(ec_key_pkcs8, (short) 0, (short) ec_key_pkcs8.length); - arrPtr = KMArray.instance((short) 3); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - arg.add((short) 1, keyFormatPtr); - arg.add((short) 2, keyBlob); - CommandAPDU apdu = encodeApdu((byte) INS_IMPORT_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short blobArr = extractKeyBlobArray(KMArray.cast(ret).get((short) 1)); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ECCURVE, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.P_256); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.IMPORTED); - cleanUp(); - } - - private short extractKeyBlobArray(byte[] buf, short off, short buflen) { - short ret = KMArray.instance((short) 5); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); - short ptr = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, ptr); - KMArray.cast(ret).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); - ret = - decoder.decodeArray( - ret, - buf, off, buflen); - short len = KMArray.cast(ret).length(); - ptr = KMArray.cast(ret).get((short) 4); -// print(KMByteBlob.cast(ptr).getBuffer(),KMByteBlob.cast(ptr).getStartOff(),KMByteBlob.cast(ptr).length()); - return ret; - } - - private short extractKeyBlobArray(short keyBlob) { - return extractKeyBlobArray(KMByteBlob.cast(keyBlob).getBuffer(), KMByteBlob - .cast(keyBlob).getStartOff(), KMByteBlob.cast(keyBlob).length()); - } - - @Test - public void testRateLimitExceptsMaxOpsExceeded() { - init(); - short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 2)); - Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), - KMError.OK); - - // Cache keyblob - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); - inParams = KMKeyParameters.instance(inParams); - // Begin - begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); - - keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); - Util.arrayCopyNonAtomic(keyBlob, (short) 0, - KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - (short) keyBlob.length); - inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); - inParams = KMKeyParameters.instance(inParams); - begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); - - keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); - Util.arrayCopyNonAtomic(keyBlob, (short) 0, - KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - (short) keyBlob.length); - inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); - inParams = KMKeyParameters.instance(inParams); - short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); - Assert.assertEquals(KMError.KEY_MAX_OPS_EXCEEDED, beginResp); - cleanUp(); - } - - @Test - public void testRateLimitExceptsTooManyOperations() { - init(); - byte[] plainData = "Hello World 123!".getBytes(); - for (int i = 0; i <= 8; i++) { - short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 1)); - Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), - KMError.OK); - - // Cache keyblob - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); - short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); - inParams = KMKeyParameters.instance(inParams); - // Begin - short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); - if (i == 8) { - // Only 8 keys are allowed for MAX_USES_PER_BOOT - Assert.assertEquals(KMError.TOO_MANY_OPERATIONS, beginResp); - return; - } - short opHandle = KMArray.cast(beginResp).get((short) 2); - finish(opHandle, - KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK, false); - } - cleanUp(); - } - - @Test - public void testRateLimitClearBufferAfterReboot() { - init(); - byte[] plainData = "Hello World 123!".getBytes(); - for (int i = 0; i <= 32; i++) { - if (i % 8 == 0) { - // Simulate reboot using set boot parameters. - // Clear the rate limited keys from the flash memory - setBootParams(simulator, (short) BOOT_PATCH_LEVEL); - } - short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_8((byte) 1)); - Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), - KMError.OK); - - // Cache keyblob - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); - short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); - inParams = KMKeyParameters.instance(inParams); - // Begin - short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); - short opHandle = KMArray.cast(beginResp).get((short) 2); - // Finish - finish(opHandle, - KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK, false); - } - cleanUp(); - } - - @Test - public void testRateLimitWithHugeCount() { - init(); - short maxUsesPerBoot = 1000; - byte[] plainData = "Hello World 123!".getBytes(); - short rsaKeyArr = generateRsaKey(null, null, KMInteger.uint_16(maxUsesPerBoot)); - Assert.assertEquals(KMInteger.cast(KMArray.cast(rsaKeyArr).get((short) 0)).getShort(), - KMError.OK); - - // Cache keyblob - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - - for (int i = 0; i <= maxUsesPerBoot; i++) { - // Cache keyblob - keyBlobPtr = KMByteBlob.instance((short) keyBlob.length); - Util.arrayCopyNonAtomic(keyBlob, (short) 0, - KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - (short) keyBlob.length); - short inParams = getRsaParams(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN); - inParams = KMKeyParameters.instance(inParams); - // Begin - short beginResp = begin(KMType.SIGN, keyBlobPtr, inParams, (short) 0, false); - if (i == maxUsesPerBoot) { - Assert.assertEquals(KMError.KEY_MAX_OPS_EXCEEDED, beginResp); - return; - } - short opHandle = KMArray.cast(beginResp).get((short) 2); - // Finish - finish(opHandle, - KMByteBlob.instance(plainData, (short) 0, (short) plainData.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK, false); - } - cleanUp(); - } - - @Test - public void testRsaGenerateKeySuccess() { - init(); - short ret = generateRsaKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 2048); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.RSA_PKCS1_1_5_ENCRYPT)); - tag = KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getSignificantShort(), - 0x01); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 0x01); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.RSA); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); - cleanUp(); - } - - private short generateRsaKey(byte[] clientId, byte[] appData, short keyUsageLimitPtr) { - byte[] activeAndCreationDateTime = {0, 0, 0x01, 0x73, 0x51, 0x7C, (byte) 0xCC, 0x00}; - short tagCount = 11; - if (clientId != null) { - tagCount++; - } - if (appData != null) { - tagCount++; - } - if (keyUsageLimitPtr != KMType.INVALID_VALUE) { - tagCount++; - } - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 2048)); - short byteBlob = KMByteBlob.instance((short) 3); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.DIGEST_NONE); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.SHA2_256); - KMByteBlob.cast(byteBlob).add((short) 2, KMType.SHA1); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short) 5); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.RSA_PKCS1_1_5_ENCRYPT); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.RSA_PKCS1_1_5_SIGN); - KMByteBlob.cast(byteBlob).add((short) 2, KMType.RSA_OAEP); - KMByteBlob.cast(byteBlob).add((short) 3, KMType.RSA_PSS); - KMByteBlob.cast(byteBlob).add((short) 4, KMType.PADDING_NONE); - short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short) 5); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SIGN); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.VERIFY); - KMByteBlob.cast(byteBlob).add((short) 2, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short) 3, KMType.DECRYPT); - KMByteBlob.cast(byteBlob).add((short) 4, KMType.WRAP_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - byte[] pub = {0, 1, 0, 1}; - short rsaPubExpTag = KMIntegerTag - .instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short) 0)); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.INCLUDE_UNIQUE_ID)); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.RESET_SINCE_ID_ROTATION)); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - KMArray.cast(arrPtr).add(tagIndex++, padding); - short dateTag = KMInteger.uint_64(activeAndCreationDateTime, (short) 0); - KMArray.cast(arrPtr) - .add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG, KMType.ACTIVE_DATETIME, dateTag)); - KMArray.cast(arrPtr) - .add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG, KMType.CREATION_DATETIME, dateTag)); - - if (clientId != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, - KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); - } - if (appData != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, - KMByteBlob.instance(appData, (short) 0, (short) appData.length))); - } - if (keyUsageLimitPtr != KMType.INVALID_VALUE) { - KMArray.cast(arrPtr).add(tagIndex++, KMIntegerTag - .instance(KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, keyUsageLimitPtr)); - } - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short) 1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_GENERATE_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - private short generateRsaKey(byte[] clientId, byte[] appData) { - return generateRsaKey(clientId, appData, KMType.INVALID_VALUE); - } - - private short generateAttestationKey() { - // 15th July 2020 00.00.00 - byte[] activeAndCreationDateTime = {0, 0, 0x01, 0x73, 0x51, 0x7C, (byte) 0xCC, 0x00}; - short tagCount = 11; - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 2048)); - short byteBlob = KMByteBlob.instance((short) 3); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.DIGEST_NONE); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.SHA2_256); - KMByteBlob.cast(byteBlob).add((short) 2, KMType.SHA1); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.RSA_PKCS1_1_5_SIGN); - short padding = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - byte[] pub = {0, 1, 0, 1}; - short rsaPubExpTag = KMIntegerTag - .instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, KMInteger.uint_32(pub, (short) 0)); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.INCLUDE_UNIQUE_ID)); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.RESET_SINCE_ID_ROTATION)); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, rsaPubExpTag); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.RSA)); - KMArray.cast(arrPtr).add(tagIndex++, padding); - short dateTag = KMInteger.uint_64(activeAndCreationDateTime, (short) 0); - KMArray.cast(arrPtr) - .add(tagIndex++, KMIntegerTag.instance(KMType.ULONG_TAG, KMType.ACTIVE_DATETIME, dateTag)); - KMArray.cast(arrPtr).add(tagIndex++, - KMIntegerTag.instance(KMType.ULONG_TAG, KMType.CREATION_DATETIME, dateTag)); - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short) 1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_GENERATE_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - @Test - public void testEcGenerateKeySuccess() { - init(); - short ret = generateEcKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 256); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.DIGEST_NONE)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.EC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); - cleanUp(); - } - - public short generateEcKey(byte[] clientId, byte[] appData) { - byte[] activeAndCreationDateTime = {0, 0, 0x01, 0x73, 0x51, 0x7C, (byte) 0xCC, 0x00}; - short tagCount = 6; - if (clientId != null) { - tagCount++; - } - if (appData != null) { - tagCount++; - } - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); - short byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.DIGEST_NONE); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SIGN); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.VERIFY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - short dateTag = KMInteger.uint_64(activeAndCreationDateTime, (short) 0); - KMArray.cast(arrPtr) - .add(tagIndex++, KMIntegerTag.instance(KMType.DATE_TAG, KMType.CREATION_DATETIME, dateTag)); - if (clientId != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, - KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); - } - if (appData != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, - KMByteBlob.instance(appData, (short) 0, (short) appData.length))); - } - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short) 1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_GENERATE_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - @Test - public void testHmacGenerateKeySuccess() { - init(); - short ret = generateHmacKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.SHA2_256)); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 160); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.HMAC); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.GENERATED); - cleanUp(); - } - - public short generateHmacKey(byte[] clientId, byte[] appData) { - short tagCount = 6; - if (clientId != null) { - tagCount++; - } - if (appData != null) { - tagCount++; - } - short arrPtr = KMArray.instance(tagCount); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SIGN); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.VERIFY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short minMacLen = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short)/*256*/160)); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, minMacLen); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.HMAC)); - if (clientId != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, - KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); - } - if (appData != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, - KMByteBlob.instance(appData, (short) 0, (short) appData.length))); - } - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short) 1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_GENERATE_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - return ret; - } - - public short generateAesDesKey(byte alg, short keysize, byte[] clientId, byte[] appData, - boolean unlockReqd) { - short tagCount = 7; - if (clientId != null) { - tagCount++; - } - if (appData != null) { - tagCount++; - } - if (unlockReqd) { - tagCount++; - } - short arrPtr = KMArray.instance(tagCount); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); - short byteBlob = KMByteBlob.instance((short) 3); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ECB); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.CBC); - KMByteBlob.cast(byteBlob).add((short) 2, KMType.CTR); - short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.PKCS7); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.PADDING_NONE); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.DECRYPT); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); - KMArray.cast(arrPtr).add(tagIndex++, paddingMode); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, alg)); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); - if (unlockReqd) { - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.UNLOCKED_DEVICE_REQUIRED)); - } - if (clientId != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, - KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); - } - if (appData != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, - KMByteBlob.instance(appData, (short) 0, (short) appData.length))); - } - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short) 1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_GENERATE_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - return ret; - } - - public short generateAesGcmKey(short keysize, byte[] clientId, byte[] appData) { - short tagCount = 8; - if (clientId != null) { - tagCount++; - } - if (appData != null) { - tagCount++; - } - short arrPtr = KMArray.instance(tagCount); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16(keysize)); - short macLength = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, KMInteger.uint_16((short) 96)); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.GCM); - short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.PADDING_NONE); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.DECRYPT); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, macLength); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); - KMArray.cast(arrPtr).add(tagIndex++, paddingMode); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); - if (clientId != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, - KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); - } - if (appData != null) { - KMArray.cast(arrPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, - KMByteBlob.instance(appData, (short) 0, (short) appData.length))); - } - short keyParams = KMKeyParameters.instance(arrPtr); - arrPtr = KMArray.instance((short) 1); - KMArray arg = KMArray.cast(arrPtr); - arg.add((short) 0, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_GENERATE_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - return ret; - } - - @Test - public void testComputeHmacParams() { - init(); - // Get Hmac parameters - short ret = getHmacSharingParams(); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short) 1)); - short seed = params.getSeed(); - short nonce = params.getNonce(); - - short params1 = KMHmacSharingParameters.instance(); - KMHmacSharingParameters.cast(params1).setSeed(KMByteBlob.instance((short) 0)); - short num = KMByteBlob.instance((short) 32); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(nonce).getBuffer(), - KMByteBlob.cast(nonce).getStartOff(), - KMByteBlob.cast(num).getBuffer(), - KMByteBlob.cast(num).getStartOff(), - KMByteBlob.cast(num).length()); - - KMHmacSharingParameters.cast(params1).setNonce(num); - short params2 = KMHmacSharingParameters.instance(); - KMHmacSharingParameters.cast(params2).setSeed(KMByteBlob.instance((short) 0)); - num = KMByteBlob.instance((short) 32); - cryptoProvider.newRandomNumber( - KMByteBlob.cast(num).getBuffer(), - KMByteBlob.cast(num).getStartOff(), - KMByteBlob.cast(num).length()); - KMHmacSharingParameters.cast(params2).setNonce(num); - short arr = KMArray.instance((short) 2); - KMArray.cast(arr).add((short) 0, params1); - KMArray.cast(arr).add((short) 1, params2); - short arrPtr = KMArray.instance((short) 1); - KMArray.cast(arrPtr).add((short) 0, arr); - CommandAPDU apdu = encodeApdu((byte) INS_COMPUTE_SHARED_HMAC_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - Assert.assertEquals(0x9000, response.getSW()); - ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - - cleanUp(); - } - - @Test - public void testGetHmacSharingParams() { - init(); - CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_GET_HMAC_SHARING_PARAM_CMD, 0x40, 0x00); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - KMDecoder dec = new KMDecoder(); - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMHmacSharingParameters.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - KMHmacSharingParameters params = KMHmacSharingParameters.cast(KMArray.cast(ret).get((short) 1)); - short seed = params.getSeed(); - short nonce = params.getNonce(); - Assert.assertTrue(KMByteBlob.cast(seed).length() == 0); - Assert.assertTrue(KMByteBlob.cast(nonce).length() == 32); - //print(seed); - //print(nonce); - Assert.assertEquals(error, KMError.OK); - cleanUp(); - } - - public short getHmacSharingParams() { - CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_GET_HMAC_SHARING_PARAM_CMD, 0x40, 0x00); - //print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - KMDecoder dec = new KMDecoder(); - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMHmacSharingParameters.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - return ret; - } - - @Test - public void testImportWrappedKey() { - init(); - byte[] wrappedKey = new byte[16]; - cryptoProvider.newRandomNumber(wrappedKey, (short) 0, (short) 16); - byte[] encWrappedKey = new byte[16]; - byte[] transportKeyMaterial = new byte[32]; - cryptoProvider.newRandomNumber(transportKeyMaterial, (short) 0, (short) 32); - byte[] nonce = new byte[12]; - cryptoProvider.newRandomNumber(nonce, (short) 0, (short) 12); - byte[] authData = "Auth Data".getBytes(); - byte[] authTag = new byte[16]; - cryptoProvider.aesGCMEncrypt(transportKeyMaterial, (short) 0, (short) 32, wrappedKey, - (short) 0, (short) 16, encWrappedKey, (short) 0, - nonce, (short) 0, (short) 12, authData, (short) 0, (short) authData.length, - authTag, (short) 0, (short) 16); - byte[] maskingKey = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, - 0, 1, 0, 1, 0, 1, 0}; - byte[] maskedTransportKey = new byte[32]; - for (int i = 0; i < maskingKey.length; i++) { - maskedTransportKey[i] = (byte) (transportKeyMaterial[i] ^ maskingKey[i]); - } - short rsaKeyArr = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); - byte[] wrappingKeyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - wrappingKeyBlob, (short) 0, (short) wrappingKeyBlob.length); - - byte[] output = new byte[256]; - short outlen = rsaOaepEncryptMessage(wrappingKeyBlob, KMType.SHA2_256, - maskedTransportKey, (short) 0, (short) maskedTransportKey.length, - output, (short) 0); - Assert.assertTrue((outlen == 256)); - byte[] encTransportKey = new byte[outlen]; - Util.arrayCopyNonAtomic(output, (short) 0, encTransportKey, (short) 0, - outlen); - //Clean the heap. - KMRepository.instance().clean(); - short tagCount = 7; - short arrPtr = KMArray.instance(tagCount); - short boolTag = KMBoolTag.instance(KMType.NO_AUTH_REQUIRED); - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 128)); - short byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ECB); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.CBC); - short blockModeTag = KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob); - byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.PKCS7); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.PADDING_NONE); - short paddingMode = KMEnumArrayTag.instance(KMType.PADDING, byteBlob); - byteBlob = KMByteBlob.instance((short) 2); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ENCRYPT); - KMByteBlob.cast(byteBlob).add((short) 1, KMType.DECRYPT); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - short tagIndex = 0; - KMArray.cast(arrPtr).add(tagIndex++, boolTag); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, blockModeTag); - KMArray.cast(arrPtr).add(tagIndex++, paddingMode); - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.AES)); - KMArray.cast(arrPtr).add(tagIndex++, purpose); - KMArray.cast(arrPtr).add(tagIndex++, KMBoolTag.instance(KMType.CALLER_NONCE)); - short keyParams = KMKeyParameters.instance(arrPtr); - short nullParams = KMArray.instance((short) 0); - nullParams = KMKeyParameters.instance(nullParams); - short arr = KMArray.instance((short) 12); - KMArray.cast(arr).add((short) 0, keyParams); // Key Params of wrapped key - KMArray.cast(arr).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW)); // Key Format - KMArray.cast(arr).add((short) 2, KMByteBlob.instance(encWrappedKey, (short) 0, - (short) encWrappedKey.length)); // Wrapped Import Key Blob - KMArray.cast(arr).add((short) 3, - KMByteBlob.instance(authTag, (short) 0, (short) authTag.length)); // Auth Tag - KMArray.cast(arr) - .add((short) 4, KMByteBlob.instance(nonce, (short) 0, (short) nonce.length)); // IV - Nonce - KMArray.cast(arr).add((short) 5, KMByteBlob.instance(encTransportKey, (short) 0, - (short) encTransportKey.length)); // Encrypted Transport Key - KMArray.cast(arr).add((short) 6, KMByteBlob.instance(wrappingKeyBlob, (short) 0, - (short) wrappingKeyBlob.length)); // Wrapping Key KeyBlob - KMArray.cast(arr).add((short) 7, - KMByteBlob.instance(maskingKey, (short) 0, (short) maskingKey.length)); // Masking Key - KMArray.cast(arr).add((short) 8, nullParams); // Un-wrapping Params - KMArray.cast(arr).add((short) 9, KMByteBlob.instance(authData, (short) 0, - (short) authData.length)); // Wrapped Key ASSOCIATED AUTH DATA - KMArray.cast(arr).add((short) 10, KMInteger.uint_8((byte) 0)); // Password Sid - KMArray.cast(arr).add((short) 11, KMInteger.uint_8((byte) 0)); // Biometric Sid - CommandAPDU apdu = encodeApdu((byte) INS_IMPORT_WRAPPED_KEY_CMD, arr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short) 1)).length(); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - Assert.assertEquals(0x9000, response.getSW()); - Assert.assertEquals(error, KMError.OK); - short tag = KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, hwParams); - Assert.assertEquals(KMBoolTag.cast(tag).getVal(), 0x01); - tag = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, hwParams); - Assert.assertEquals(KMInteger.cast(KMIntegerTag.cast(tag).getValue()).getShort(), 128); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.PKCS7)); - tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, hwParams); - Assert.assertTrue(KMEnumArrayTag.cast(tag).contains(KMType.ECB)); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.AES); - tag = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ORIGIN, hwParams); - Assert.assertEquals(KMEnumTag.cast(tag).getValue(), KMType.SECURELY_IMPORTED); - cleanUp(); - } - - @Test - public void testGetKeyCharacteristicsWithIdDataSuccess() { - init(); - byte[] clientId = "clientId".getBytes(); - byte[] appData = "appData".getBytes(); - short ret = generateRsaKey(clientId, appData); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - short keyBlob = KMArray.cast(ret).get((short) 1); - - short arrPtr = KMArray.instance((short) 3); - KMArray.cast(arrPtr).add((short) 0, keyBlob); - KMArray.cast(arrPtr) - .add((short) 1, KMByteBlob.instance(clientId, (short) 0, (short) clientId.length)); - KMArray.cast(arrPtr) - .add((short) 2, KMByteBlob.instance(appData, (short) 0, (short) appData.length)); - CommandAPDU apdu = encodeApdu((byte) INS_GET_KEY_CHARACTERISTICS_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - cleanUp(); - } - - @Test - public void testGetKeyCharacteristicsSuccess() { - init(); - short ret = generateRsaKey(null, null); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - short keyBlob = KMArray.cast(ret).get((short) 1); - - short arrPtr = KMArray.instance((short) 3); - KMArray.cast(arrPtr).add((short) 0, keyBlob); - KMArray.cast(arrPtr).add((short) 1, KMByteBlob.instance((short) 0)); - KMArray.cast(arrPtr).add((short) 2, KMByteBlob.instance((short) 0)); - CommandAPDU apdu = encodeApdu((byte) INS_GET_KEY_CHARACTERISTICS_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - cleanUp(); - } - - @Test - public void testDeleteKeySuccess() { - init(); - short ret = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(ret).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob, (short) 0); - ret = getKeyCharacteristics(keyBlobPtr); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - ret = deleteKey(KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length)); - Assert.assertEquals(ret, KMError.OK); - cleanUp(); - } - - @Test - public void testDeleteAllKeySuccess() { - init(); - short ret1 = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(ret1).get((short) 1); - byte[] keyBlob1 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - short len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob1, (short) 0); - short ret2 = generateRsaKey(null, null); - keyBlobPtr = KMArray.cast(ret2).get((short) 1); - byte[] keyBlob2 = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - len = KMByteBlob.cast(keyBlobPtr).getValues(keyBlob2, (short) 0); - CommandAPDU apdu = new CommandAPDU(0x80, INS_DELETE_ALL_KEYS_CMD, 0x40, 0x00); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - Assert.assertEquals(respBuf[0], KMError.OK); - cleanUp(); - } - - private short deleteKey(short keyBlob) { - short arrPtr = KMArray.instance((short) 1); - KMArray.cast(arrPtr).add((short) 0, keyBlob); - CommandAPDU apdu = encodeApdu((byte) INS_DELETE_KEY_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - return respBuf[0]; - } - - private short abort(short opHandle, boolean triggerReset) { - short arrPtr = KMArray.instance((short) 1); - KMArray.cast(arrPtr).add((short) 0, opHandle); - CommandAPDU apdu = encodeApdu((byte) INS_ABORT_OPERATION_CMD, arrPtr); - // print(commandAPDU.getBytes()); - if (triggerReset) { - resetAndSelect(); - } - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - short ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, (short) respBuf.length); - if (triggerReset) { - short error = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - return ret; - } - - public short getKeyCharacteristics(short keyBlob) { - short arrPtr = KMArray.instance((short) 3); - KMArray.cast(arrPtr).add((short) 0, keyBlob); - KMArray.cast(arrPtr).add((short) 1, KMByteBlob.instance((short) 0)); - KMArray.cast(arrPtr).add((short) 2, KMByteBlob.instance((short) 0)); - CommandAPDU apdu = encodeApdu((byte) INS_GET_KEY_CHARACTERISTICS_CMD, arrPtr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 1, inst); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - if (len > 5) { - ret = decoder.decode(ret, respBuf, (short) 0, len); - } else { - ret = KMByteBlob.instance(respBuf, (short) 0, len); - } - return ret; - } - - @Test - public void testWithAesGcmWithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.GCM, KMType.PADDING_NONE, true); - cleanUp(); - } - - @Test - public void testWithAesEcbPkcs7WithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7, true); - cleanUp(); - } - - @Test - public void testWithAesCtrNoPadWithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE, true); - cleanUp(); - } - - @Test - public void testWithAesCtrNoPad() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CTR, KMType.PADDING_NONE, false); - cleanUp(); - } - - @Test - public void testWithAesEcbNoPadWithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE, true); - cleanUp(); - } - - @Test - public void testWithDesEcbPkcs7WithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7, true); - cleanUp(); - } - - @Test - public void testWithDesEcbNoPadWithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE, true); - cleanUp(); - } - - @Test - public void testWithAesCbcPkcs7WithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7, true); - cleanUp(); - } - - @Test - public void testWithAesCbcNoPadWithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE, true); - cleanUp(); - } - - @Test - public void testWithDesCbcPkcs7WithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7, true); - cleanUp(); - } - - @Test - public void testWithDesCbcNoPadWithUpdate() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE, true); - cleanUp(); - } - - @Test - public void testWithAesEcbPkcs7() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PKCS7, false); - cleanUp(); - } - - @Test - public void testWithAesCbcPkcs7() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PKCS7, false); - cleanUp(); - } - - @Test - public void testWithAesEcbNoPad() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.ECB, KMType.PADDING_NONE, false); - cleanUp(); - } - - @Test - public void testWithAesCbcNoPad() { - init(); - testEncryptDecryptWithAesDes(KMType.AES, KMType.CBC, KMType.PADDING_NONE, false); - cleanUp(); - } - - @Test - public void testWithDesCbcPkcs7() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PKCS7, false); - cleanUp(); - } - - @Test - public void testWithDesCbcNoPad() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.CBC, KMType.PADDING_NONE, false); - cleanUp(); - } - - @Test - public void testWithDesEcbNoPad() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PADDING_NONE, false); - cleanUp(); - } - - @Test - public void testWithDesEcbPkcs7() { - init(); - testEncryptDecryptWithAesDes(KMType.DES, KMType.ECB, KMType.PKCS7, false); - cleanUp(); - } - - @Test - public void testWithRsa256Oaep() { - init(); - testEncryptDecryptWithRsa(KMType.SHA2_256, KMType.RSA_OAEP); - cleanUp(); - } - - @Test - public void testWithRsaSha1Oaep() { - init(); - testEncryptDecryptWithRsa(KMType.SHA1, KMType.RSA_OAEP); - cleanUp(); - } - - @Test - public void testWithRsaNonePkcs1() { - init(); - testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_ENCRYPT); - cleanUp(); - } - - @Test - public void testWithRsaNoneNoPad() { - init(); - testEncryptDecryptWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE); - cleanUp(); - } - - // TODO Signing with no digest is not supported by crypto provider or javacard - @Test - public void testSignWithRsaNoneNoPad() { - init(); - testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.PADDING_NONE, false, false); - cleanUp(); - } - - @Test - public void testSignWithRsaNonePkcs1() { - init(); - testSignVerifyWithRsa(KMType.DIGEST_NONE, KMType.RSA_PKCS1_1_5_SIGN, false, false); - cleanUp(); - } - - public short getPublicKey(byte[] keyBlob, short off, short len, - byte[] pubKey, short pubKeyOff) { - short keyBlobPtr = extractKeyBlobArray(keyBlob, off, len); - short arrayLen = KMArray.cast(keyBlobPtr).length(); - if (arrayLen < 5) { - return 0; - } - short pubKeyPtr = KMArray.cast(keyBlobPtr).get( - KMKeymasterApplet.KEY_BLOB_PUB_KEY); - Util.arrayCopy(KMByteBlob.cast(pubKeyPtr).getBuffer(), - KMByteBlob.cast(pubKeyPtr).getStartOff(), pubKey, pubKeyOff, - KMByteBlob.cast(pubKeyPtr).length()); - return KMByteBlob.cast(pubKeyPtr).length(); - } - - private String toHexString(byte[] num) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < num.length; i++) { - sb.append(String.format("%02X", num[i])); - } - return sb.toString(); - } - - public short rsaEncryptMessage(byte[] keyBlob, short padding, short digest, byte[] input, - short inputOff, short inputlen, - byte[] output, short outputOff) { - byte alg = Cipher.ALG_RSA_PKCS1; - byte[] tmp = null; - short inLen = inputlen; - if (padding == KMType.PADDING_NONE) { - alg = Cipher.ALG_RSA_NOPAD; - // Length cannot be greater then key size according to JcardSim - if (inLen >= 256) { - return 0; - } - // make input equal to 255 bytes - tmp = new byte[255]; - Util.arrayFillNonAtomic(tmp, (short) 0, (short) 255, (byte) 0); - Util.arrayCopyNonAtomic( - input, - inputOff, - tmp, (short) (255 - inLen), inLen); - inLen = 255; - inputOff = 0; - } else if (padding == KMType.RSA_PKCS1_1_5_ENCRYPT) { - tmp = input; - } else { - /*Fail */ - Assert.assertTrue(false); - } - byte[] pubKey = new byte[256]; - KeyPair rsaKeyPair = new KeyPair(KeyPair.ALG_RSA, KeyBuilder.LENGTH_RSA_2048); - RSAPublicKey rsaPubKey = (RSAPublicKey) rsaKeyPair.getPublic(); - if (0 == getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, pubKey, (short) 0)) { - return 0; - } - - byte[] exponent = new byte[]{0x01, 0x00, 0x01}; - rsaPubKey.setModulus(pubKey, (short) 0, (short) pubKey.length); - rsaPubKey.setExponent(exponent, (short) 0, (short) exponent.length); - - Cipher rsaCipher = Cipher.getInstance(alg, false); - rsaCipher.init(rsaPubKey, Cipher.MODE_ENCRYPT); - return rsaCipher.doFinal(tmp, inputOff, inLen, output, outputOff); - } - - public short rsaOaepEncryptMessage(byte[] keyBlob, short digest, byte[] input, short inputOff, - short inputlen, - byte[] output, short outputOff) { - byte[] mod = new byte[256]; - if (0 == getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, mod, (short) 0)) { - return 0; - } - byte[] exponent = new byte[]{0x01, 0x00, 0x01}; - - // Convert byte arrays into keys - String modString = toHexString(mod); - String expString = toHexString(exponent); - BigInteger modInt = new BigInteger(modString, 16); - BigInteger expInt = new BigInteger(expString, 16); - javax.crypto.Cipher rsaCipher = null; - try { - KeyFactory kf = KeyFactory.getInstance("RSA"); - // Create cipher with oaep padding - OAEPParameterSpec oaepSpec = null; - if (digest == KMType.SHA2_256) { - oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } else { - oaepSpec = new OAEPParameterSpec("SHA1", "MGF1", - MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); - } - rsaCipher = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPPadding", "SunJCE"); - - RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(modInt, expInt); - java.security.interfaces.RSAPublicKey pubKey = (java.security.interfaces.RSAPublicKey) kf - .generatePublic(pubSpec); - rsaCipher.init(javax.crypto.Cipher.ENCRYPT_MODE, pubKey, oaepSpec); - byte[] cipherOut = rsaCipher.doFinal(input, inputOff, inputlen); - - if (cipherOut != null) { - Util.arrayCopyNonAtomic(cipherOut, (short) 0, output, outputOff, (short) cipherOut.length); - } - return (short) cipherOut.length; - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (InvalidKeySpecException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - } catch (NoSuchPaddingException e) { - e.printStackTrace(); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { - e.printStackTrace(); - } - return 0; - } - - public boolean ecNoDigestVerifyMessage(byte[] input, short inputOff, - short inputlen, byte[] sign, short signOff, short signLen, - byte[] keyBlob) { - KeyFactory kf; - byte[] pubKey = new byte[128]; - short keyStart = 0; - short keyLength = getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, - pubKey, (short) 0); - if (keyLength == 0) { - return false; - } - try { - java.security.Signature sunSigner = java.security.Signature.getInstance( - "NONEwithECDSA", "SunEC"); - kf = KeyFactory.getInstance("EC"); - AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC", - "SunEC"); - // Supported curve secp256r1 - parameters.init(new ECGenParameterSpec("secp256r1")); - ECParameterSpec ecParameters = parameters - .getParameterSpec(ECParameterSpec.class); - - // Check if the first byte is 04 and remove it. - if (pubKey[keyStart] == 0x04) { - // uncompressed format. - keyStart++; - keyLength--; - } - short i = 0; - byte[] pubx = new byte[keyLength / 2]; - for (; i < keyLength / 2; i++) { - pubx[i] = pubKey[keyStart + i]; - } - byte[] puby = new byte[keyLength / 2]; - for (i = 0; i < keyLength / 2; i++) { - puby[i] = pubKey[keyStart + keyLength / 2 + i]; - } - BigInteger bIX = new BigInteger(pubx); - BigInteger bIY = new BigInteger(puby); - ECPoint point = new ECPoint(bIX, bIY); - ECPublicKeySpec pubkeyspec = new ECPublicKeySpec(point, ecParameters); - java.security.interfaces.ECPublicKey ecPubkey = (java.security.interfaces.ECPublicKey) kf - .generatePublic(pubkeyspec); - sunSigner.initVerify(ecPubkey); - sunSigner.update(input, inputOff, inputlen); - return sunSigner.verify(sign, signOff, signLen); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (NoSuchProviderException e) { - e.printStackTrace(); - } catch (InvalidParameterSpecException e) { - e.printStackTrace(); - } catch (InvalidKeySpecException e) { - e.printStackTrace(); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (SignatureException e) { - e.printStackTrace(); - } - return false; - } - - public boolean ecVerifyMessage(byte[] input, short inputOff, short inputlen, - byte[] sign, short signOff, short signLen, byte[] keyBlob) { - Signature ecVerifier; - byte[] pubKey = new byte[128]; - short len = getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, - pubKey, (short) 0); - if (len == 0) { - return false; - } - ECPublicKey key = (ECPublicKey) KeyBuilder.buildKey( - KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false); - key.setW(pubKey, (short) 0, len); - ecVerifier = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); - ecVerifier.init(key, Signature.MODE_VERIFY); - return ecVerifier.verify(input, inputOff, inputlen, sign, signOff, signLen); - } - - public boolean rsaVerifyMessage(byte[] input, short inputOff, short inputlen, byte[] sign, - short signOff, short signLen, - short digest, short padding, byte[] keyBlob) { - if (digest == KMType.DIGEST_NONE || padding == KMType.PADDING_NONE) { - return false; - } - byte[] pubKey = new byte[256]; - if (0 == getPublicKey(keyBlob, (short) 0, (short) keyBlob.length, pubKey, (short) 0)) { - return false; - } - short alg = Signature.ALG_RSA_SHA_256_PKCS1_PSS; - - if (padding == KMType.RSA_PKCS1_1_5_SIGN) { - alg = Signature.ALG_RSA_SHA_256_PKCS1; - } - - Signature rsaVerifier = Signature.getInstance((byte) alg, false); - RSAPublicKey key = (RSAPublicKey) KeyBuilder - .buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false); - byte[] exponent = new byte[]{0x01, 0x00, 0x01}; - key.setExponent(exponent, (short) 0, (short) exponent.length); - key.setModulus(pubKey, (short) 0, (short) pubKey.length); - rsaVerifier.init(key, Signature.MODE_VERIFY); - return rsaVerifier.verify(input, inputOff, inputlen, sign, signOff, signLen); - } - - public byte[] EncryptMessage(byte[] input, short params, byte[] keyBlob) { - short ret = begin(KMType.ENCRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(params), (short) 0, false); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - ret = finish(opHandle, - KMByteBlob.instance(input, (short) 0, (short) input.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK, false); - short dataPtr = KMArray.cast(ret).get((short) 2); - byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; - if (KMByteBlob.cast(dataPtr).length() > 0) { - Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob - .cast(dataPtr).getStartOff(), output, (short) 0, - KMByteBlob.cast(dataPtr).length()); - } - return output; - } - - public byte[] DecryptMessage(byte[] input, short params, byte[] keyBlob) { - short ret = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(params), (short) 0, false); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - ret = finish(opHandle, - KMByteBlob.instance(input, (short) 0, (short) input.length), null, - (short) 0, (short) 0, (short) 0, KMError.OK, false); - short dataPtr = KMArray.cast(ret).get((short) 2); - byte[] output = new byte[KMByteBlob.cast(dataPtr).length()]; - if (KMByteBlob.cast(dataPtr).length() > 0) { - Util.arrayCopyNonAtomic(KMByteBlob.cast(dataPtr).getBuffer(), KMByteBlob - .cast(dataPtr).getStartOff(), output, (short) 0, - KMByteBlob.cast(dataPtr).length()); - } - return output; - } - - public short generateRandom(short upperBound) { - Random rand = new Random(); - short int_random = (short) rand.nextInt(upperBound); - return int_random; - } - - @Test - public void testUnsupportedBlockMode() { - init(); - short desKey = generateAesDesKey(KMType.DES, (short) 168, null, null, false); - short desKeyPtr = KMArray.cast(desKey).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(desKeyPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(desKeyPtr).getBuffer(), KMByteBlob - .cast(desKeyPtr).getStartOff(), keyBlob, (short) 0, - (short) keyBlob.length); - short desPkcs7Params = getAesDesParams(KMType.DES, (byte) KMType.CTR, - KMType.PKCS7, new byte[12]); - short ret = begin(KMType.ENCRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(desPkcs7Params), (short) 0, false); - Assert.assertTrue(ret == KMError.UNSUPPORTED_BLOCK_MODE); - cleanUp(); - } - - @Test - public void testDesEcbPkcs7PaddingCorrupted() { - init(); - short desKey = generateAesDesKey(KMType.DES, (short) 168, null, null, false); - short desKeyPtr = KMArray.cast(desKey).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(desKeyPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(desKeyPtr).getBuffer(), KMByteBlob - .cast(desKeyPtr).getStartOff(), keyBlob, (short) 0, - (short) keyBlob.length); - - byte[] message = { - 0x61}; - short desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, - KMType.PKCS7, null); - byte[] cipherText1 = EncryptMessage(message, desPkcs7Params, keyBlob); - Assert.assertEquals(8, cipherText1.length); - Assert.assertFalse(Arrays.equals(message, cipherText1)); - - // Corrupt the cipher text. - ++cipherText1[(cipherText1.length / 2)]; - - // Decrypt operation - // Begin - desPkcs7Params = getAesDesParams(KMType.DES, KMType.ECB, KMType.PKCS7, null); - - short ret = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(desPkcs7Params), (short) 0, false); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - // Finish - short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, - (short) cipherText1.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, - KMError.INVALID_ARGUMENT, false); - cleanUp(); - } - - @Test - public void testVtsRsaPkcs1Success() { - init(); - byte[] message = { - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, - 0x21}; // "Hello World!"; - for (int i = 0; i < 250; i++) { - short key = generateRsaKey(null, null); - short rsaKeyPtr = KMArray.cast(key).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(rsaKeyPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(rsaKeyPtr).getBuffer(), - KMByteBlob.cast(rsaKeyPtr).getStartOff(), keyBlob, (short) 0, - (short) keyBlob.length); - short pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - - byte[] cipherText1 = new byte[256]; - short cipherText1Len = rsaEncryptMessage(keyBlob, KMType.RSA_PKCS1_1_5_ENCRYPT, - KMType.DIGEST_NONE, - message, (short) 0, (short) message.length, - cipherText1, (short) 0); - Assert.assertEquals((2048 / 8), cipherText1Len); - - pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - byte[] cipherText2 = new byte[256]; - short cipherText2Len = rsaEncryptMessage(keyBlob, KMType.RSA_PKCS1_1_5_ENCRYPT, - KMType.DIGEST_NONE, - message, (short) 0, (short) message.length, - cipherText2, (short) 0); - Assert.assertEquals((2048 / 8), cipherText2Len); - - // PKCS1 v1.5 randomizes padding so every result should be different. - Assert.assertFalse(Arrays.equals(cipherText1, cipherText2)); - - pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - byte[] plainText = DecryptMessage(cipherText1, pkcs1Params, keyBlob); - Assert.assertTrue(Arrays.equals(message, plainText)); - - // Decrypting corrupted ciphertext should fail. - short offset_to_corrupt = generateRandom((short) cipherText1.length); - - byte corrupt_byte; - do { - corrupt_byte = (byte) generateRandom((short) 256); - } while (corrupt_byte == cipherText1[offset_to_corrupt]); - cipherText1[offset_to_corrupt] = corrupt_byte; - - pkcs1Params = getRsaParams(KMType.DIGEST_NONE, - KMType.RSA_PKCS1_1_5_ENCRYPT); - // Do Begin operation. - short ret = begin(KMType.DECRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(pkcs1Params), (short) 0, false); - - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, - (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - - short dataPtr = KMByteBlob.instance(cipherText1, (short) 0, - (short) cipherText1.length); - // Finish should return UNKNOWN_ERROR. - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, - KMError.UNKNOWN_ERROR, false); - } - cleanUp(); - } - - @Test - public void testSignVerifyWithHmacSHA256WithUpdate() { - init(); - testSignVerifyWithHmac(KMType.SHA2_256, true); - cleanUp(); - } - - @Test - public void testSignVerifyWithHmacSHA256() { - init(); - testSignVerifyWithHmac(KMType.SHA2_256, false); - cleanUp(); - } - - @Test - public void testSignVerifyWithEcdsaSHA256WithUpdate() { - init(); - testSignVerifyWithEcdsa(KMType.SHA2_256, true); - cleanUp(); - } - - @Test - public void testSignVerifyWithEcdsaSHA256() { - init(); - testSignVerifyWithEcdsa(KMType.SHA2_256, false); - cleanUp(); - } - - @Test - public void testSignVerifyWithRsaSHA256Pkcs1() { - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN, false, true); - cleanUp(); - } - - @Test - public void testSignVerifyWithRsaSHA256Pss() { - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS, false, true); - cleanUp(); - } - - @Test - public void testSignVerifyWithRsaSHA256Pkcs1WithUpdate() { - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PKCS1_1_5_SIGN, true, true); - cleanUp(); - } - - @Test - public void testProvisionSuccess() { - AID appletAID1 = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID1, KMJCardSimApplet.class); - // Select applet - simulator.selectApplet(appletAID1); - // provision attest key - provisionCmd(simulator); - cleanUp(); - } - - @Test - public void testAttestRsaKey() { - init(); - short key = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(key).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - testAttestKey(keyBlob); - cleanUp(); - } - - @Test - public void testAttestEcKey() { - init(); - short key = generateEcKey(null, null); - short keyBlobPtr = KMArray.cast(key).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - testAttestKey(keyBlob); - cleanUp(); - } - - public void testAttestKey(byte[] keyBlob) { - short arrPtr = KMArray.instance((short) 2); - KMArray.cast(arrPtr).add((short) 0, KMByteTag.instance(KMType.ATTESTATION_APPLICATION_ID, - KMByteBlob.instance(attAppId, (short) 0, (short) attAppId.length))); - KMArray.cast(arrPtr).add((short) 1, KMByteTag.instance(KMType.ATTESTATION_CHALLENGE, - KMByteBlob.instance(attChallenge, (short) 0, (short) attChallenge.length))); - short keyParams = KMKeyParameters.instance(arrPtr); - short args = KMArray.instance((short) 2); - KMArray.cast(args) - .add((short) 0, KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length)); - KMArray.cast(args).add((short) 1, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_ATTEST_KEY_CMD, args); - //print(apdu.getBytes(),(short)0,(short)apdu.getBytes().length); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 2); - short arrBlobs = KMArray.instance((short) 1); - KMArray.cast(arrBlobs).add((short) 0, KMByteBlob.exp()); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, arrBlobs); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - //(respBuf,(short)0,(short)respBuf.length); - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - arrBlobs = KMArray.cast(ret).get((short) 1); - short cert = KMArray.cast(arrBlobs).get((short) 0); - //printCert(KMByteBlob.cast(cert).getBuffer(),KMByteBlob.cast(cert).getStartOff(),KMByteBlob.cast(cert).length()); - } - - @Test - public void testUpgradeKey() { - init(); - short ret = generateHmacKey(null, null); - short keyBlobPtr = KMArray.cast(ret).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - short keyCharacteristics = KMArray.cast(ret).get((short) 2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - short osVersion = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, hwParams); - osVersion = KMIntegerTag.cast(osVersion).getValue(); - short osPatch = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, hwParams); - osPatch = KMIntegerTag.cast(osPatch).getValue(); - Assert.assertEquals(KMInteger.cast(osVersion).getShort(), 1); - Assert.assertEquals(KMInteger.cast(osPatch).getShort(), 1); - short NO_UPGRADE = 0x01; - short UPGRADE = 0x02; - short[][] test_data = { - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL+1, UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL+1, UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL+1, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION+1, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL-1, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - {OS_VERSION-1/*0*/, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL, BOOT_PATCH_LEVEL, UPGRADE, KMError.OK }, - {OS_VERSION, OS_PATCH_LEVEL, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - {OS_VERSION, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - {0, OS_PATCH_LEVEL+1, VENDOR_PATCH_LEVEL-1, BOOT_PATCH_LEVEL+1, NO_UPGRADE, KMError.INVALID_ARGUMENT }, - }; - for (int i = 0; i < test_data.length; i++) { - setBootParams(simulator, (short) test_data[i][3]); - setAndroidOSSystemProperties(simulator, (short) test_data[i][0], (short) test_data[i][1], - (short) test_data[i][2]); - ret = upgradeKey( - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - null, null, test_data[i][5]); - if (test_data[i][5] != KMError.OK) - continue; - keyBlobPtr = KMArray.cast(ret).get((short) 1); - if (test_data[i][4] == UPGRADE) - Assert.assertNotEquals(KMByteBlob.cast(keyBlobPtr).length(), 0); - else - Assert.assertEquals(KMByteBlob.cast(keyBlobPtr).length(), 0); - if (KMByteBlob.cast(keyBlobPtr).length() != 0) { - ret = getKeyCharacteristics(keyBlobPtr); - keyCharacteristics = KMArray.cast(ret).get((short) 1); - hwParams = KMKeyCharacteristics.cast(keyCharacteristics) - .getHardwareEnforced(); - osVersion = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.OS_VERSION, - hwParams); - osVersion = KMIntegerTag.cast(osVersion).getValue(); - osPatch = KMKeyParameters.findTag(KMType.UINT_TAG, - KMType.OS_PATCH_LEVEL, hwParams); - osPatch = KMIntegerTag.cast(osPatch).getValue(); - short ptr = KMKeyParameters.findTag(KMType.UINT_TAG, - KMType.VENDOR_PATCH_LEVEL, hwParams); - short vendorPatchLevel = KMIntegerTag.cast(ptr).getValue(); - ptr = KMKeyParameters.findTag(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, - hwParams); - short bootPatchLevel = KMIntegerTag.cast(ptr).getValue(); - Assert.assertEquals(KMInteger.cast(osVersion).getShort(), - test_data[i][0]); - Assert.assertEquals(KMInteger.cast(osPatch).getShort(), - test_data[i][1]); - Assert.assertEquals(KMInteger.cast(vendorPatchLevel).getShort(), - test_data[i][2]); - Assert.assertEquals(KMInteger.cast(bootPatchLevel).getShort(), - test_data[i][3]); - } - } - cleanUp(); - } - - public void testCardRest() { - byte[] input = new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; - // Test different combinations of reset events happening in the ordered flow of - // begin - begin1 - update - update1 - finish - finish1 - abort - boolean[][] resetEvents = { - //begin, begin1, update, update1, finish, finish1, abort - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET, NO_RESET}, - {NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET, RESET}, - {NO_RESET, NO_RESET, NO_RESET, RESET, RESET, NO_RESET, NO_RESET}, - {NO_RESET, RESET, RESET, NO_RESET, NO_RESET, NO_RESET, NO_RESET}, - {RESET, RESET, RESET, RESET, RESET, RESET, RESET}, - }; - for(int i = 0; i < resetEvents.length; i++) { - // Generate Key---------------- - short ret = generateHmacKey(null, null); - // Store the generated key in a new byte blob. - short keyBlobPtr = KMArray.cast(ret).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), keyBlob, - (short) 0, (short) keyBlob.length); - short inParams = getHmacParams(KMType.SHA2_256, true); - // Generate Key---------------- - - //Call begin operation---------------- - ret = begin(KMType.SIGN, keyBlobPtr, KMKeyParameters.instance(inParams), (short) 0, resetEvents[i][0]); - // Get the operation handle. - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); - //Get the keyblobptr again. - keyBlobPtr = KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length); - //Call begin end---------------- - - //Call begin1 operation---------------- - inParams = getHmacParams(KMType.SHA2_256, true); - ret = begin(KMType.SIGN, keyBlobPtr, KMKeyParameters.instance(inParams), (short) 0, resetEvents[i][1]); - // Get the operation handle. - short opHandle1 = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf1 = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle1).getValue(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); - //Get the keyblobptr again. - keyBlobPtr = KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length); - //Call begin1 end---------------- - - //Call update operation---------------- - // Call update operation and check if the secure element power reset flag is set or not. - short dataPtr = KMByteBlob.instance(input, (short) 0, (short) input.length); - opHandle = KMInteger.instance(opHandleBuf, (short) 0, (short) opHandleBuf.length); - // update with trigger reset. - ret = update(opHandle, dataPtr, (short) 0, (short) 0, (short) 0, resetEvents[i][2]); - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][1] || resetEvents[i][2]) { - short err = KMInteger.cast(ret).getShort(); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); - } - //Call update end---------------- - - //Call update1 operation---------------- - // Call update1 operation and check if the secure element power reset flag is set or not. - dataPtr = KMByteBlob.instance(input, (short) 0, (short) input.length); - opHandle1 = KMInteger.instance(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); - // update with trigger reset. - ret = update(opHandle1, dataPtr, (short) 0, (short) 0, (short) 0, resetEvents[i][3]); - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][2] || resetEvents[i][3]) { - short err = KMInteger.cast(ret).getShort(); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); - } - //Call update end---------------- - - //Call finish operation---------------- - // Call finish operation and check if the secure element power reset flag is set or not. - dataPtr = KMByteBlob.instance((short) 0); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - short expectedErr = KMError.OK; - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][1] | resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4]) - expectedErr = KMError.INVALID_OPERATION_HANDLE; - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, expectedErr, resetEvents[i][4]); - //Call finish end---------------- - - //Call finish1 operation---------------- - // Call finish1 operation and check if the secure element power reset flag is set or not. - dataPtr = KMByteBlob.instance((short) 0); - opHandle1 = KMInteger.instance(opHandleBuf1, (short) 0, (short) opHandleBuf1.length); - expectedErr = KMError.OK; - // If a reset event occurred then expect INVALID_OPERATION_HANDLE. - if (resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4] | resetEvents[i][5]) - expectedErr = KMError.INVALID_OPERATION_HANDLE; - ret = finish(opHandle1, dataPtr, null, (short) 0, (short) 0, (short) 0, expectedErr, resetEvents[i][5]); - //Call finish end---------------- - - //Call abort operation---------------- - // Call abort operation and check if the secure element power reset flag is set or not. - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = abort(opHandle, resetEvents[i][6]); - if (resetEvents[i][1] || resetEvents[i][2] | resetEvents[i][3] | resetEvents[i][4] | resetEvents[i][5] | resetEvents[i][6]) { - short err = KMInteger.cast(ret).getShort(); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, err); - } - //Call finish end---------------- - KMRepository.instance().clean(); - } - } - - @Test - public void testCardResetFunctionality() { - init(); - testCardRest(); - cleanUp(); - } - - @Test - public void testDestroyAttIds() { - init(); - CommandAPDU commandAPDU = new CommandAPDU(0x80, INS_DESTROY_ATT_IDS_CMD, 0x40, 0x00); - ResponseAPDU response = simulator.transmitCommand(commandAPDU); - byte[] respBuf = response.getBytes(); - Assert.assertEquals(respBuf[0], 0); - cleanUp(); - } - - private short upgradeKey(short keyBlobPtr, byte[] clientId, byte[] appData, short expectedErr) { - short tagCount = 0; - short clientIdTag = 0; - short appDataTag = 0; - if (clientId != null) { - tagCount++; - } - if (appData != null) { - tagCount++; - } - short keyParams = KMArray.instance(tagCount); - short tagIndex = 0; - if (clientId != null) { - KMArray.cast(keyBlobPtr).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_ID, - KMByteBlob.instance(clientId, (short) 0, (short) clientId.length))); - } - if (appData != null) { - KMArray.cast(keyParams).add(tagIndex++, - KMByteTag.instance(KMType.APPLICATION_DATA, - KMByteBlob.instance(appData, (short) 0, (short) appData.length))); - } - keyParams = KMKeyParameters.instance(keyParams); - short arr = KMArray.instance((short) 2); - KMArray.cast(arr).add((short) 0, keyBlobPtr); - KMArray.cast(arr).add((short) 1, keyParams); - CommandAPDU apdu = encodeApdu((byte) INS_UPGRADE_KEY_CMD, arr); - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - if (KMError.OK == expectedErr) { - short ret = KMArray.instance((short) 2); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMByteBlob.exp()); - ret = decoder.decode(ret, respBuf, (short) 0, len); - Assert.assertEquals(expectedErr, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); - return ret; - } else { - short ret = KMInteger.exp(); - ret = decoder.decode(ret, respBuf, (short) 0, len); - Assert.assertEquals(expectedErr, KMInteger.cast(ret).getShort()); - return ret; - } - } - - @Test - public void testSignVerifyWithRsaSHA256PssWithUpdate() { - init(); - testSignVerifyWithRsa(KMType.SHA2_256, KMType.RSA_PSS, true, true); - cleanUp(); - } - - @Test - public void testAbortOperation() { - init(); - short aesDesKeyArr = generateAesDesKey(KMType.AES, (short) 128, null, null, false); - ; - short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - byte[] nonce = new byte[16]; - cryptoProvider.newRandomNumber(nonce, (short) 0, (short) 16); - short inParams = getAesDesParams(KMType.AES, KMType.ECB, KMType.PKCS7, nonce); - byte[] plainData = "Hello World 123!".getBytes(); - short ret = begin(KMType.ENCRYPT, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMKeyParameters.instance(inParams), (short) 0, false); - short opHandle = KMArray.cast(ret).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = abort(opHandle, false); - Assert.assertEquals(KMError.OK, KMInteger.cast(ret).getShort()); - short dataPtr = KMByteBlob.instance(plainData, (short) 0, (short) plainData.length); - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = update(opHandle, dataPtr, (short) 0, (short) 0, (short) 0, false); - ret = KMInteger.cast(ret).getShort(); - Assert.assertEquals(KMError.INVALID_OPERATION_HANDLE, ret); - cleanUp(); - } - - public void testEncryptDecryptWithAesDes(byte alg, byte blockMode, byte padding, boolean update) { - short aesDesKeyArr; - boolean aesGcmFlag = false; - if (alg == KMType.AES) { - if (blockMode == KMType.GCM) { - aesDesKeyArr = generateAesGcmKey((short) 128, null, null); - aesGcmFlag = true; - } else { - aesDesKeyArr = generateAesDesKey(alg, (short) 128, null, null, false); - } - } else { - aesDesKeyArr = generateAesDesKey(alg, (short) 168, null, null, false); - } - short keyBlobPtr = KMArray.cast(aesDesKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - byte[] nonce = new byte[16]; - cryptoProvider.newRandomNumber(nonce, (short) 0, (short) 16); - short inParams = getAesDesParams(alg, blockMode, padding, nonce); - byte[] plainData = "Hello World 123!".getBytes(); - if (update) { - plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); - } - //Encrypt - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.ENCRYPT, - KMKeyParameters.instance(inParams), - (short) 0, null, update, aesGcmFlag - ); - inParams = getAesDesParams(alg, blockMode, padding, nonce); - keyBlobPtr = KMArray.cast(ret).get((short) 2); - //print(keyBlobPtr); - byte[] cipherData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - cipherData, (short) 0, (short) cipherData.length); - ret = processMessage(cipherData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.DECRYPT, - KMKeyParameters.instance(inParams), - (short) 0, null, update, aesGcmFlag - ); - keyBlobPtr = KMArray.cast(ret).get((short) 2); - //print(plainData,(short)0,(short)plainData.length); - //print(keyBlobPtr); - short equal = Util.arrayCompare(plainData, (short) 0, KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), (short) plainData.length); - Assert.assertTrue(equal == 0); - } - - public void testEncryptDecryptWithRsa(byte digest, byte padding) { - short rsaKeyArr = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - short inParams = getRsaParams(digest, padding); - byte[] plainData = "Hello World 123!".getBytes(); - byte[] cipherData = new byte[256]; - short cipherDataLen = 0; - //Encrypt - if (padding == KMType.RSA_OAEP) { - cipherDataLen = rsaOaepEncryptMessage(keyBlob, digest, plainData, - (short) 0, (short) plainData.length, cipherData, (short) 0); - } else { - cipherDataLen = rsaEncryptMessage(keyBlob, padding, digest, plainData, - (short) 0, (short) plainData.length, cipherData, (short) 0); - } - Assert.assertTrue((cipherDataLen == 256)); - inParams = getRsaParams(digest, padding); - short ret = processMessage(cipherData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.DECRYPT, - KMKeyParameters.instance(inParams), - (short) 0, null, false, false - ); - keyBlobPtr = KMArray.cast(ret).get((short) 2); - short len = KMByteBlob.cast(keyBlobPtr).length(); - short start = KMByteBlob.cast(keyBlobPtr).getStartOff(); - short equal = Util.arrayCompare(plainData, (short) 0, KMByteBlob.cast(keyBlobPtr).getBuffer(), - (short) (start + len - plainData.length), (short) plainData.length); - Assert.assertTrue(equal == 0); - } - - public void testSignVerifyWithRsa(byte digest, byte padding, boolean update, boolean verifyFlag) { - short rsaKeyArr = generateRsaKey(null, null); - short keyBlobPtr = KMArray.cast(rsaKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - short inParams = getRsaParams(digest, padding); - byte[] plainData = "Hello World 123!".getBytes(); - if (update) { - plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); - } - //Sign - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.SIGN, - KMKeyParameters.instance(inParams), - (short) 0, null, update, false - ); - inParams = getRsaParams(digest, padding); - keyBlobPtr = KMArray.cast(ret).get((short) 2); - byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - signatureData, (short) 0, (short) signatureData.length); - if (verifyFlag == false) { - Assert.assertEquals(signatureData.length, 256); - return; - } - boolean verify = rsaVerifyMessage(plainData, (short) 0, (short) plainData.length, - signatureData, (short) 0, (short) signatureData.length, - digest, padding, keyBlob); - Assert.assertTrue(verify); - } - - public void testSignVerifyWithEcdsa(byte digest, boolean update) { - short ecKeyArr = generateEcKey(null, null); - short keyBlobPtr = KMArray.cast(ecKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - short inParams = getEcParams(digest); - byte[] plainData = "Hello World 123!".getBytes(); - if (update) { - plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); - } - //Sign - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.SIGN, - KMKeyParameters.instance(inParams), - (short) 0, null, update, false - ); - inParams = getEcParams(digest); - keyBlobPtr = KMArray.cast(ret).get((short) 2); - byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - signatureData, (short) 0, (short) signatureData.length); - boolean verify = false; - if (digest == KMType.DIGEST_NONE) { - verify = ecNoDigestVerifyMessage(plainData, (short) 0, (short) plainData.length, - signatureData, (short) 0, (short) signatureData.length, - keyBlob); - } else { - verify = ecVerifyMessage(plainData, (short) 0, (short) plainData.length, - signatureData, (short) 0, (short) signatureData.length, - keyBlob); - } - Assert.assertTrue(verify); - } - - public void testSignVerifyWithHmac(byte digest, boolean update) { - short hmacKeyArr = generateHmacKey(null, null); - short keyBlobPtr = KMArray.cast(hmacKeyArr).get((short) 1); - byte[] keyBlob = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - keyBlob, (short) 0, (short) keyBlob.length); - short inParams = getHmacParams(digest, true); - byte[] plainData = "Hello World 123!".getBytes(); - if (update) { - plainData = "Hello World 123! Hip Hip Hoorah!".getBytes(); - } - //Sign - short ret = processMessage(plainData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.SIGN, - KMKeyParameters.instance(inParams), - (short) 0, null, update, false - ); - inParams = getHmacParams(digest, false); - keyBlobPtr = KMArray.cast(ret).get((short) 2); - byte[] signatureData = new byte[KMByteBlob.cast(keyBlobPtr).length()]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(keyBlobPtr).getBuffer(), - KMByteBlob.cast(keyBlobPtr).getStartOff(), - signatureData, (short) 0, (short) signatureData.length); - ret = processMessage(plainData, - KMByteBlob.instance(keyBlob, (short) 0, (short) keyBlob.length), - KMType.VERIFY, - KMKeyParameters.instance(inParams), - (short) 0, signatureData, update, false - ); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - } - - private short getAesDesParams(byte alg, byte blockMode, byte padding, byte[] nonce) { - short inParams; - if (blockMode == KMType.GCM) { - inParams = KMArray.instance((short) 5); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, blockMode); - KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, padding); - KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - short nonceLen = 12; - byteBlob = KMByteBlob.instance(nonce, (short) 0, nonceLen); - KMArray.cast(inParams).add((short) 2, KMByteTag.instance(KMType.NONCE, byteBlob)); - short macLen = KMInteger.uint_16((short) 128); - macLen = KMIntegerTag.instance(KMType.UINT_TAG, KMType.MAC_LENGTH, macLen); - KMArray.cast(inParams).add((short) 3, macLen); - byte[] authData = "AuthData".getBytes(); - short associatedData = KMByteBlob.instance(authData, (short) 0, (short) authData.length); - associatedData = KMByteTag.instance(KMType.ASSOCIATED_DATA, associatedData); - KMArray.cast(inParams).add((short) 4, associatedData); - } else if (blockMode == KMType.ECB) { - inParams = KMArray.instance((short) 2); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, blockMode); - KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, padding); - KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - } else { - inParams = KMArray.instance((short) 3); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, blockMode); - KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.BLOCK_MODE, byteBlob)); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, padding); - KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - short nonceLen = 16; - if (alg == KMType.DES) { - nonceLen = 8; - } - byteBlob = KMByteBlob.instance(nonce, (short) 0, nonceLen); - KMArray.cast(inParams).add((short) 2, KMByteTag.instance(KMType.NONCE, byteBlob)); - } - return inParams; - } - - private short getRsaParams(byte digest, byte padding) { - short inParams = KMArray.instance((short) 2); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, digest); - KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, padding); - KMArray.cast(inParams).add((short) 1, KMEnumArrayTag.instance(KMType.PADDING, byteBlob)); - return inParams; - } - - private short getEcParams(byte digest) { - short inParams = KMArray.instance((short) 1); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, digest); - KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); - return inParams; - } - - private short getHmacParams(byte digest, boolean sign) { - short paramsize = (short) (sign ? 2 : 1); - short inParams = KMArray.instance((short) paramsize); - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, digest); - KMArray.cast(inParams).add((short) 0, KMEnumArrayTag.instance(KMType.DIGEST, byteBlob)); - short macLength = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.MAC_LENGTH, KMInteger.uint_16((short)/*256*/160)); - if (sign) { - KMArray.cast(inParams).add((short) 1, macLength); - } - return inParams; - } - - public short processMessage( - byte[] data, - short keyBlob, - byte keyPurpose, - short inParams, - short hwToken, - byte[] signature, - boolean updateFlag, - boolean aesGcmFlag) { - short beginResp = begin(keyPurpose, keyBlob, inParams, hwToken, false); - short opHandle = KMArray.cast(beginResp).get((short) 2); - byte[] opHandleBuf = new byte[KMRepository.OPERATION_HANDLE_SIZE]; - KMInteger.cast(opHandle).getValue(opHandleBuf, (short) 0, (short) opHandleBuf.length); - short dataPtr = KMByteBlob.instance(data, (short) 0, (short) data.length); - short ret = KMType.INVALID_VALUE; - byte[] outputData = new byte[128]; - short len = 0; - inParams = 0; - //Test - short firstDataLen = 16; - if (keyPurpose == KMType.DECRYPT) { - firstDataLen = 32; - } - - //Test - - if (updateFlag) { - dataPtr = KMByteBlob.instance(data, (short) 0, (short) /*16*/firstDataLen); - if (aesGcmFlag) { - byte[] authData = "AuthData".getBytes(); - short associatedData = KMByteBlob.instance(authData, (short) 0, (short) authData.length); - associatedData = KMByteTag.instance(KMType.ASSOCIATED_DATA, associatedData); - inParams = KMArray.instance((short) 1); - KMArray.cast(inParams).add((short) 0, associatedData); - inParams = KMKeyParameters.instance(inParams); - } - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - ret = update(opHandle, dataPtr, inParams, (short) 0, (short) 0, false); - dataPtr = KMArray.cast(ret).get((short) 3); - if (KMByteBlob.cast(dataPtr).length() > 0) { - Util.arrayCopyNonAtomic( - KMByteBlob.cast(dataPtr).getBuffer(), - KMByteBlob.cast(dataPtr).getStartOff(), - outputData, - (short) 0, - KMByteBlob.cast(dataPtr).length()); - len = KMByteBlob.cast(dataPtr).length(); - dataPtr = KMByteBlob.instance(data, len, (short) (data.length - len)); - } else { - dataPtr = KMByteBlob - .instance(data, (short)/*16*/firstDataLen, (short) (data.length - /*16*/firstDataLen)); - } - } - - opHandle = KMInteger.uint_64(opHandleBuf, (short) 0); - if (keyPurpose == KMType.VERIFY) { - ret = finish(opHandle, dataPtr, signature, (short) 0, (short) 0, (short) 0, KMError.OK, false); - } else { - ret = finish(opHandle, dataPtr, null, (short) 0, (short) 0, (short) 0, KMError.OK, false); - } - if (len > 0) { - dataPtr = KMArray.cast(ret).get((short) 2); - if (KMByteBlob.cast(dataPtr).length() > 0) { - Util.arrayCopyNonAtomic( - KMByteBlob.cast(dataPtr).getBuffer(), - KMByteBlob.cast(dataPtr).getStartOff(), - outputData, - len, - KMByteBlob.cast(dataPtr).length()); - len = (short) (len + KMByteBlob.cast(dataPtr).length()); - } - KMArray.cast(ret).add((short) 2, KMByteBlob.instance(outputData, (short) 0, len)); - } - return ret; - } - - public short begin(byte keyPurpose, short keyBlob, short keyParmas, short hwToken, boolean triggerReset) { - short arrPtr = KMArray.instance((short) 4); - KMArray.cast(arrPtr).add((short) 0, KMEnum.instance(KMType.PURPOSE, keyPurpose)); - KMArray.cast(arrPtr).add((short) 1, keyBlob); - KMArray.cast(arrPtr).add((short) 2, keyParmas); - if (hwToken == 0) { - hwToken = KMHardwareAuthToken.instance(); - } - KMArray.cast(arrPtr).add((short) 3, hwToken); - CommandAPDU apdu = encodeApdu((byte) INS_BEGIN_OPERATION_CMD, arrPtr); - if (triggerReset) { - resetAndSelect(); - } - //print(apdu.getBytes(),(short)0,(short)apdu.getBytes().length); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 3); - short outParams = KMKeyParameters.exp(); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, outParams); - KMArray.cast(ret).add((short) 2, KMInteger.exp()); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - byte majorType = readMajorType(respBuf); - //if (len > 5) { - if (majorType == CBOR_ARRAY_MAJOR_TYPE) { - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - if (triggerReset) { - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - return ret; - } else {//Major type UINT. - ret = decoder.decode(KMInteger.exp(), respBuf, (short) 0, len); - if (triggerReset) { - short error = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - return KMInteger.cast(ret).getShort(); - /*if (len == 3) { - return respBuf[0]; - } - if (len == 4) { - return respBuf[1]; - } - return Util.getShort(respBuf, (short) 0);*/ - } - } - - public short translateExtendedErrorCodes(short err) { - switch (err) { - case KMError.SW_CONDITIONS_NOT_SATISFIED: - case KMError.UNSUPPORTED_CLA: - case KMError.INVALID_P1P2: - case KMError.INVALID_DATA: - case KMError.CRYPTO_ILLEGAL_USE: - case KMError.CRYPTO_ILLEGAL_VALUE: - case KMError.CRYPTO_INVALID_INIT: - case KMError.CRYPTO_UNINITIALIZED_KEY: - case KMError.GENERIC_UNKNOWN_ERROR: - err = KMError.UNKNOWN_ERROR; - break; - case KMError.CRYPTO_NO_SUCH_ALGORITHM: - err = KMError.UNSUPPORTED_ALGORITHM; - break; - case KMError.UNSUPPORTED_INSTRUCTION: - case KMError.CMD_NOT_ALLOWED: - case KMError.SW_WRONG_LENGTH: - err = KMError.UNIMPLEMENTED; - break; - default: - break; - } - return err; - } - - public short finish(short operationHandle, short data, byte[] signature, short inParams, - short hwToken, short verToken, short expectedErr, boolean triggerReset) { - if (hwToken == 0) { - hwToken = KMHardwareAuthToken.instance(); - } - if (verToken == 0) { - verToken = KMVerificationToken.instance(); - } - short signatureTag; - if (signature == null) { - signatureTag = KMByteBlob.instance((short) 0); - } else { - signatureTag = KMByteBlob.instance(signature, (short) 0, (short) signature.length); - } - if (inParams == 0) { - short arr = KMArray.instance((short) 0); - inParams = KMKeyParameters.instance(arr); - } - short arrPtr = KMArray.instance((short) 6); - KMArray.cast(arrPtr).add((short) 0, operationHandle); - KMArray.cast(arrPtr).add((short) 1, inParams); - KMArray.cast(arrPtr).add((short) 2, data); - KMArray.cast(arrPtr).add((short) 3, signatureTag); - KMArray.cast(arrPtr).add((short) 4, hwToken); - KMArray.cast(arrPtr).add((short) 5, verToken); - CommandAPDU apdu = encodeApdu((byte) INS_FINISH_OPERATION_CMD, arrPtr); - // print(commandAPDU.getBytes()); - if (triggerReset) { - resetAndSelect(); - } - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - short ret; - short error; - if (expectedErr == KMError.OK) { - ret = KMArray.instance((short) 3); - short outParams = KMKeyParameters.exp(); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, outParams); - KMArray.cast(ret).add((short) 2, KMByteBlob.exp()); - } else { - ret = KMInteger.exp(); - } - ret = decoder.decode(ret, respBuf, (short) 0, len); - if (expectedErr == KMError.OK) { - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - if (triggerReset) { - short powerResetStatus = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); - Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); - } - } else { - error = KMInteger.cast(ret).getShort(); - error = translateExtendedErrorCodes(error); - if (triggerReset) { - short powerResetStatus = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); - } - } - Assert.assertEquals(error, expectedErr); - return ret; - } - - public short update(short operationHandle, short data, short inParams, short hwToken, - short verToken, boolean triggerReset) { - if (hwToken == 0) { - hwToken = KMHardwareAuthToken.instance(); - } - if (verToken == 0) { - verToken = KMVerificationToken.instance(); - } - if (inParams == 0) { - short arr = KMArray.instance((short) 0); - inParams = KMKeyParameters.instance(arr); - } - short arrPtr = KMArray.instance((short) 5); - KMArray.cast(arrPtr).add((short) 0, operationHandle); - KMArray.cast(arrPtr).add((short) 1, inParams); - KMArray.cast(arrPtr).add((short) 2, data); - KMArray.cast(arrPtr).add((short) 3, hwToken); - KMArray.cast(arrPtr).add((short) 4, verToken); - CommandAPDU apdu = encodeApdu((byte) INS_UPDATE_OPERATION_CMD, arrPtr); - if (triggerReset) { - resetAndSelect(); - } - // print(commandAPDU.getBytes()); - ResponseAPDU response = simulator.transmitCommand(apdu); - short ret = KMArray.instance((short) 4); - short outParams = KMKeyParameters.exp(); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short) 1, KMInteger.exp()); - KMArray.cast(ret).add((short) 2, outParams); - KMArray.cast(ret).add((short) 3, KMByteBlob.exp()); - byte[] respBuf = response.getBytes(); - short len = (short) respBuf.length; - byte majorType = readMajorType(respBuf); - if (majorType == CBOR_ARRAY_MAJOR_TYPE) { - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort(); - Assert.assertEquals(error, KMError.OK); - if (triggerReset) { - error = KMInteger.cast(KMArray.cast(ret).get((short) 0)).getSignificantShort(); - Assert.assertEquals(error, SE_POWER_RESET_FLAG); - } - } else { - ret = decoder.decode(KMInteger.exp(), respBuf, (short)0, len); - if (triggerReset) { - short powerResetStatus = KMInteger.cast(ret).getSignificantShort(); - Assert.assertEquals(powerResetStatus, SE_POWER_RESET_FLAG); - } - } - return ret; - } - - private byte readMajorType(byte[] resp) { - byte val = resp[0]; - return (byte) (val & MAJOR_TYPE_MASK); - } - - private void print(short blob) { - print(KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff(), - KMByteBlob.cast(blob).length()); - } - - private void print(byte[] buf, short start, short length) { - StringBuilder sb = new StringBuilder(); - for (int i = start; i < (start + length); i++) { - sb.append(String.format(" 0x%02X", buf[i])); - } - System.out.println(sb.toString()); - } - - private void printCert(byte[] buf, short start, short length) { - StringBuilder sb = new StringBuilder(); - for (int i = start; i < (start + length); i++) { - sb.append(String.format("%02X", buf[i])); - } - System.out.println(sb.toString()); - } - - -/* - @Test - public void testApdu(){ - init(); - byte[] cmd = {(byte)0x80,0x11,0x40,0x00,0x00,0x00,0x4C,(byte)0x83,(byte)0xA5,0x1A,0x70,0x00,0x01,(byte)0xF7,0x01,0x1A,0x10, - 0x00,0x00,0x02,0x03,0x1A,0x30,0x00,0x00,0x03,0x19,0x01,0x00,0x1A,0x20,0x00,0x00,0x01,0x42,0x02, - 0x03,0x1A,0x20,0x00,0x00,0x05,0x41,0x04,0x03,0x58,0x24,(byte)0x82,0x58,0x20,0x73,0x7C,0x2E,(byte)0xCD, - 0x7B,(byte)0x8D,0x19,0x40,(byte)0xBF,0x29,0x30,(byte)0xAA,(byte)0x9B,0x4E, - (byte)0xD3,(byte)0xFF,(byte)0x94,0x1E,(byte)0xED,0x09,0x36,0x6B, - (byte)0xC0,0x32,(byte)0x99,(byte)0x98,0x64,(byte)0x81,(byte)0xF3,(byte)0xA4,(byte)0xD8,0x59,0x40}; - CommandAPDU cmdApdu = new CommandAPDU(cmd); - ResponseAPDU resp = simulator.transmitCommand(cmdApdu); - short ret = KMArray.instance((short) 3); - KMArray.cast(ret).add((short) 0, KMInteger.exp()); - KMArray.cast(ret).add((short)1, KMByteBlob.exp()); - short inst = KMKeyCharacteristics.exp(); - KMArray.cast(ret).add((short) 2, inst); - byte[] respBuf = resp.getBytes(); - short len = (short) respBuf.length; - ret = decoder.decode(ret, respBuf, (short) 0, len); - short error = KMInteger.cast(KMArray.cast(ret).get((short)0)).getShort(); - short keyBlobLength = KMByteBlob.cast(KMArray.cast(ret).get((short)1)).length(); - short blobArr = extractKeyBlobArray(KMArray.cast(ret).get((short)1)); - short keyCharacteristics = KMArray.cast(ret).get((short)2); - short hwParams = KMKeyCharacteristics.cast(keyCharacteristics).getHardwareEnforced(); - short swParams = KMKeyCharacteristics.cast(keyCharacteristics).getSoftwareEnforced(); - cleanUp(); - } - */ -} diff --git a/Applet/README.md b/Applet/README.md index 064fc9d9..ace69502 100644 --- a/Applet/README.md +++ b/Applet/README.md @@ -1,21 +1,15 @@ # JavaCardKeymaster Applet -This directory contains the implementation of the Keymaster 4.1 +This directory contains the implementation of the Keymint 1.0 interface, in the form of a JavaCard 3.0.5 applet which runs in a secure element. It must be deployed in conjuction with the associated HAL, -which serves to intermediate between Android Keystore and this applet. +which mediates between Android Keystore and this applet. # Supported Features! - - Support for AndroidSEProvider, which is compliant to JavaCard platform, Classic Edition 3.0.5. - - Keymaster 4.1 supported functions for required VTS compliance. - - Support for SE Provisioning and bootup - - Support for Global platoform Amendment H in AndroidSEProvider. - - Unit test using JCardSim. - -#### Building for source -- Install Javacard 3.0.5 classic sdk. -- set JC_HOME_SIMULATOR environment variable to the installed sdk. -- Give ant build from Applet folder. -- Download [gpapi-upgrade.jar](https://globalplatform.wpengine.com/specs-library/globalplatform-card-api-org-globalplatform-upgrade-v1/) and copy inside lib folder of both AndroidSEProvider and JCardSimProvider to resolve the compilation errors. + - Keymint 1.0 supported functions for required VTS compliance. + - SharedSecret 1.0 supported functions for required VTS compliance. +# Not supported features + - Factory provisioned attestation key will not be supported in this applet. + - Limited usage keys will not be supported in this applet. diff --git a/Applet/build.xml b/Applet/build.xml deleted file mode 100644 index 4a14664e..00000000 --- a/Applet/build.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java deleted file mode 100644 index 1ae67595..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; - -public class KMEncoder { - - // major types - private static final byte UINT_TYPE = 0x00; - private static final byte BYTES_TYPE = 0x40; - private static final byte ARRAY_TYPE = (byte) 0x80; - private static final byte MAP_TYPE = (byte) 0xA0; - - // masks - private static final byte ADDITIONAL_MASK = 0x1F; - - // value length - private static final byte UINT8_LENGTH = (byte) 0x18; - private static final byte UINT16_LENGTH = (byte) 0x19; - private static final byte UINT32_LENGTH = (byte) 0x1A; - private static final byte UINT64_LENGTH = (byte) 0x1B; - private static final short TINY_PAYLOAD = 0x17; - private static final short SHORT_PAYLOAD = 0x100; - private static final short STACK_SIZE = (short) 50; - private static final short SCRATCH_BUF_SIZE = (short) 6; - private static final short START_OFFSET = (short) 0; - private static final short LEN_OFFSET = (short) 2; - private static final short STACK_PTR_OFFSET = (short) 4; - - private Object[] bufferRef; - private short[] scratchBuf; - private short[] stack; - - public KMEncoder() { - bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - scratchBuf = JCSystem.makeTransientShortArray((short) SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); - stack = JCSystem.makeTransientShortArray(STACK_SIZE, JCSystem.CLEAR_ON_RESET); - bufferRef[0] = null; - scratchBuf[START_OFFSET] = (short) 0; - scratchBuf[LEN_OFFSET] = (short) 0; - scratchBuf[STACK_PTR_OFFSET] = (short) 0; - } - - private void push(short objPtr) { - stack[scratchBuf[STACK_PTR_OFFSET]] = objPtr; - scratchBuf[STACK_PTR_OFFSET]++; - } - - private short pop() { - scratchBuf[STACK_PTR_OFFSET]--; - return stack[scratchBuf[STACK_PTR_OFFSET]]; - } - - private void encode(short obj) { - push(obj); - } - - public short encode(short object, byte[] buffer, short startOff) { - scratchBuf[STACK_PTR_OFFSET] = 0; - bufferRef[0] = buffer; - scratchBuf[START_OFFSET] = startOff; - short len = (short) buffer.length; - if ((len < 0) || (len > KMKeymasterApplet.MAX_LENGTH)) { - scratchBuf[LEN_OFFSET] = KMKeymasterApplet.MAX_LENGTH; - } else { - scratchBuf[LEN_OFFSET] = (short) buffer.length; - } - //this.length = (short)(startOff + length); - push(object); - encode(); - return (short) (scratchBuf[START_OFFSET] - startOff); - } - - // array{KMError.OK, KMByteBlob} - public void encodeCertChain(byte[] buffer, short offset, short length, short errInt32Ptr, short certChainOff, short certChainLen) { - bufferRef[0] = buffer; - scratchBuf[START_OFFSET] = offset; - scratchBuf[LEN_OFFSET] = (short) (offset + length + 1); - - writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - encodeInteger(errInt32Ptr); - writeMajorTypeWithLength(BYTES_TYPE, certChainLen); - writeBytes(buffer, certChainOff, certChainLen); - } - - //array{KMError.OK,Array{KMByteBlobs}} - public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength, short errInt32Ptr) { - bufferRef[0] = certBuffer; - scratchBuf[START_OFFSET] = certStart; - scratchBuf[LEN_OFFSET] = (short) (certStart + 1); - //Array header - 2 elements i.e. 1 byte - scratchBuf[START_OFFSET]--; - // errInt32Ptr - PowerResetStatus + ErrorCode - 4 bytes - // Integer header - 1 byte - scratchBuf[START_OFFSET] -= getEncodedIntegerLength(errInt32Ptr); - //Array header - 2 elements i.e. 1 byte - scratchBuf[START_OFFSET]--; - // Cert Byte blob - typically 2 bytes length i.e. 3 bytes header - scratchBuf[START_OFFSET] -= 2; - if (certLength >= SHORT_PAYLOAD) { - scratchBuf[START_OFFSET]--; - } - if (scratchBuf[START_OFFSET] < bufferStart) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - bufferStart = scratchBuf[START_OFFSET]; - writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - encodeInteger(errInt32Ptr); //PowerResetStatus + ErrorCode - writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 element - writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length - return bufferStart; - } - - public short encodeError(short errInt32Ptr, byte[] buffer, short startOff, short length) { - bufferRef[0] = buffer; - scratchBuf[START_OFFSET] = startOff; - scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); - encodeInteger(errInt32Ptr); - return (short) (scratchBuf[START_OFFSET] - startOff); - } - - private void encode() { - while (scratchBuf[STACK_PTR_OFFSET] > 0) { - short exp = pop(); - byte type = KMType.getType(exp); - switch (type) { - case KMType.BYTE_BLOB_TYPE: - encodeByteBlob(exp); - break; - case KMType.INTEGER_TYPE: - encodeInteger(exp); - break; - case KMType.ARRAY_TYPE: - encodeArray(exp); - break; - case KMType.ENUM_TYPE: - encodeEnum(exp); - break; - case KMType.KEY_PARAM_TYPE: - encodeKeyParam(exp); - break; - case KMType.KEY_CHAR_TYPE: - encodeKeyChar(exp); - break; - case KMType.VERIFICATION_TOKEN_TYPE: - encodeVeriToken(exp); - break; - case KMType.HMAC_SHARING_PARAM_TYPE: - encodeHmacSharingParam(exp); - break; - case KMType.HW_AUTH_TOKEN_TYPE: - encodeHwAuthToken(exp); - break; - case KMType.TAG_TYPE: - short tagType = KMTag.getTagType(exp); - encodeTag(tagType, exp); - break; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - } - } - - private void encodeTag(short tagType, short exp) { - switch (tagType) { - case KMType.BYTES_TAG: - encodeBytesTag(exp); - return; - case KMType.BOOL_TAG: - encodeBoolTag(exp); - return; - case KMType.UINT_TAG: - case KMType.ULONG_TAG: - case KMType.DATE_TAG: - encodeIntegerTag(exp); - return; - case KMType.ULONG_ARRAY_TAG: - case KMType.UINT_ARRAY_TAG: - encodeIntegerArrayTag(exp); - return; - case KMType.ENUM_TAG: - encodeEnumTag(exp); - return; - case KMType.ENUM_ARRAY_TAG: - encodeEnumArrayTag(exp); - return; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - } - - private void encodeKeyParam(short obj) { - encodeAsMap(KMKeyParameters.cast(obj).getVals()); - } - - private void encodeKeyChar(short obj) { - encode(KMKeyCharacteristics.cast(obj).getVals()); - } - - private void encodeVeriToken(short obj) { - encode(KMVerificationToken.cast(obj).getVals()); - } - - private void encodeHwAuthToken(short obj) { - encode(KMHardwareAuthToken.cast(obj).getVals()); - } - - private void encodeHmacSharingParam(short obj) { - encode(KMHmacSharingParameters.cast(obj).getVals()); - } - - private void encodeArray(short obj) { - writeMajorTypeWithLength(ARRAY_TYPE, KMArray.cast(obj).length()); - short len = KMArray.cast(obj).length(); - short index = (short) (len - 1); - while (index >= 0) { - encode(KMArray.cast(obj).get(index)); - index--; - } - } - - private void encodeAsMap(short obj) { - writeMajorTypeWithLength(MAP_TYPE, KMArray.cast(obj).length()); - short len = KMArray.cast(obj).length(); - short index = (short) (len - 1); - short inst; - while (index >= 0) { - inst = KMArray.cast(obj).get(index); - encode(inst); - index--; - } - } - - private void encodeIntegerArrayTag(short obj) { - writeTag(KMIntegerArrayTag.cast(obj).getTagType(), KMIntegerArrayTag.cast(obj).getKey()); - encode(KMIntegerArrayTag.cast(obj).getValues()); - } - - private void encodeEnumArrayTag(short obj) { - writeTag(KMEnumArrayTag.cast(obj).getTagType(), KMEnumArrayTag.cast(obj).getKey()); - encode(KMEnumArrayTag.cast(obj).getValues()); - } - - private void encodeIntegerTag(short obj) { - writeTag(KMIntegerTag.cast(obj).getTagType(), KMIntegerTag.cast(obj).getKey()); - encode(KMIntegerTag.cast(obj).getValue()); - } - - private void encodeBytesTag(short obj) { - writeTag(KMByteTag.cast(obj).getTagType(), KMByteTag.cast(obj).getKey()); - encode(KMByteTag.cast(obj).getValue()); - } - - private void encodeBoolTag(short obj) { - writeTag(KMBoolTag.cast(obj).getTagType(), KMBoolTag.cast(obj).getKey()); - writeByteValue(KMBoolTag.cast(obj).getVal()); - } - - private void encodeEnumTag(short obj) { - writeTag(KMEnumTag.cast(obj).getTagType(), KMEnumTag.cast(obj).getKey()); - writeByteValue(KMEnumTag.cast(obj).getValue()); - } - - private void encodeEnum(short obj) { - writeByteValue(KMEnum.cast(obj).getVal()); - } - - /* The total length of UINT Major type along with actual length of - * integer is returned. - */ - public short getEncodedIntegerLength(short obj) { - byte[] val = KMInteger.cast(obj).getBuffer(); - short len = KMInteger.cast(obj).length(); - short startOff = KMInteger.cast(obj).getStartOff(); - byte index = 0; - // find out the most significant byte - while (index < len) { - if (val[(short) (startOff + index)] > 0) { - break; - } else if (val[(short) (startOff + index)] < 0) { - break; - } - index++; // index will be equal to len if value is 0. - } - // find the difference between most significant byte and len - short diff = (short) (len - index); - switch (diff) { - case 0: case 1: //Byte | Short - if ((val[(short) (startOff + index)] < UINT8_LENGTH) && - (val[(short) (startOff + index)] >= 0)) { - return (short) 1; - } else { - return (short) 2; - } - case 2: //Short - return (short) 3; - case 3: case 4: //Uint32 - return (short) 5; - case 5: case 6: case 7: case 8: //Uint64 - return (short) 9; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return 0; - } - - private void encodeInteger(short obj) { - byte[] val = KMInteger.cast(obj).getBuffer(); - short len = KMInteger.cast(obj).length(); - short startOff = KMInteger.cast(obj).getStartOff(); - byte index = 0; - // find out the most significant byte - while (index < len) { - if (val[(short) (startOff + index)] > 0) { - break; - } else if (val[(short) (startOff + index)] < 0) { - break; - } - index++; // index will be equal to len if value is 0. - } - // find the difference between most significant byte and len - short diff = (short) (len - index); - if (diff == 0) { - writeByte((byte) (UINT_TYPE | 0)); - } else if ((diff == 1) && (val[(short) (startOff + index)] < UINT8_LENGTH) - && (val[(short) (startOff + index)] >= 0)) { - writeByte((byte) (UINT_TYPE | val[(short) (startOff + index)])); - } else if (diff == 1) { - writeByte((byte) (UINT_TYPE | UINT8_LENGTH)); - writeByte(val[(short) (startOff + index)]); - } else if (diff == 2) { - writeByte((byte) (UINT_TYPE | UINT16_LENGTH)); - writeBytes(val, (short) (startOff + index), (short) 2); - } else if (diff <= 4) { - writeByte((byte) (UINT_TYPE | UINT32_LENGTH)); - writeBytes(val, (short) (startOff + len - 4), (short) 4); - } else { - writeByte((byte) (UINT_TYPE | UINT64_LENGTH)); - writeBytes(val, startOff, (short) 8); - } - } - - private void encodeByteBlob(short obj) { - writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.cast(obj).length()); - writeBytes(KMByteBlob.cast(obj).getBuffer(), KMByteBlob.cast(obj).getStartOff(), - KMByteBlob.cast(obj).length()); - } - - private void writeByteValue(byte val) { - if ((val < UINT8_LENGTH) && (val >= 0)) { - writeByte((byte) (UINT_TYPE | val)); - } else { - writeByte((byte) (UINT_TYPE | UINT8_LENGTH)); - writeByte((byte) val); - } - } - - private void writeTag(short tagType, short tagKey) { - writeByte((byte) (UINT_TYPE | UINT32_LENGTH)); - writeShort(tagType); - writeShort(tagKey); - } - - private void writeMajorTypeWithLength(byte majorType, short len) { - if (len <= TINY_PAYLOAD) { - writeByte((byte) (majorType | (byte) (len & ADDITIONAL_MASK))); - } else if (len < SHORT_PAYLOAD) { - writeByte((byte) (majorType | UINT8_LENGTH)); - writeByte((byte) (len & 0xFF)); - } else { - writeByte((byte) (majorType | UINT16_LENGTH)); - writeShort(len); - } - } - - private void writeBytes(byte[] buf, short start, short len) { - byte[] buffer = (byte[]) bufferRef[0]; - Util.arrayCopyNonAtomic(buf, start, buffer, scratchBuf[START_OFFSET], len); - incrementStartOff(len); - } - - private void writeShort(short val) { - byte[] buffer = (byte[]) bufferRef[0]; - buffer[scratchBuf[START_OFFSET]] = (byte) ((val >> 8) & 0xFF); - incrementStartOff((short) 1); - buffer[scratchBuf[START_OFFSET]] = (byte) ((val & 0xFF)); - incrementStartOff((short) 1); - } - - private void writeByte(byte val) { - byte[] buffer = (byte[]) bufferRef[0]; - buffer[scratchBuf[START_OFFSET]] = val; - incrementStartOff((short) 1); - } - - private void incrementStartOff(short inc) { - scratchBuf[START_OFFSET] += inc; - if (scratchBuf[START_OFFSET] >= scratchBuf[LEN_OFFSET]) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - } -} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java deleted file mode 100644 index 7913f6ff..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMKeyCharacteristics represents KeyCharacteristics structure from android keymaster hal - * specifications. It corresponds to CBOR array type. struct{byte KEY_CHAR_TYPE; short length=2; - * short arrayPtr} where arrayPtr is a pointer to ordered array with following elements: - * {KMKeyParameters sofEnf; KMKeyParameters hwEnf} - */ -public class KMKeyCharacteristics extends KMType { - - public static final byte SOFTWARE_ENFORCED = 0x00; - public static final byte HARDWARE_ENFORCED = 0x01; - private static KMKeyCharacteristics prototype; - - private KMKeyCharacteristics() { - } - - public static short exp() { - short softEnf = KMKeyParameters.exp(); - short hwEnf = KMKeyParameters.exp(); - short arrPtr = KMArray.instance((short) 2); - KMArray arr = KMArray.cast(arrPtr); - arr.add(SOFTWARE_ENFORCED, softEnf); - arr.add(HARDWARE_ENFORCED, hwEnf); - return instance(arrPtr); - } - - private static KMKeyCharacteristics proto(short ptr) { - if (prototype == null) { - prototype = new KMKeyCharacteristics(); - } - instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] = ptr; - return prototype; - } - - public static short instance() { - short arrPtr = KMArray.instance((short) 2); - return instance(arrPtr); - } - - public static short instance(short vals) { - short ptr = KMType.instance(KEY_CHAR_TYPE, (short) 2); - if (KMArray.cast(vals).length() != 2) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); - return ptr; - } - - public static KMKeyCharacteristics cast(short ptr) { - if (heap[ptr] != KEY_CHAR_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - if (heap[arrPtr] != ARRAY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] + TLV_HEADER_SIZE)); - } - - public short length() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); - } - - public short getSoftwareEnforced() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(SOFTWARE_ENFORCED); - } - - public short getHardwareEnforced() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(HARDWARE_ENFORCED); - } - - public void setSoftwareEnforced(short ptr) { - KMKeyParameters.cast(ptr); - short arrPtr = getVals(); - KMArray.cast(arrPtr).add(SOFTWARE_ENFORCED, ptr); - } - - public void setHardwareEnforced(short ptr) { - KMKeyParameters.cast(ptr); - short arrPtr = getVals(); - KMArray.cast(arrPtr).add(HARDWARE_ENFORCED, ptr); - } -} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java deleted file mode 100644 index 709b604d..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMKeyParameters represents KeyParameters structure from android keymaster hal specifications. It - * corresponds to CBOR map type. struct{byte KEY_PARAM_TYPE; short length=2; short arrayPtr} where - * arrayPtr is a pointer to array with any KMTag subtype instances. - */ -public class KMKeyParameters extends KMType { - - private static final short[] unsupportedTags = { - // 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_TAG, KMType.ORIGIN, - 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.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, - KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, - KMType.UINT_TAG, KMType.AUTH_TIMEOUT, - 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.ENUM_TAG, KMType.USER_AUTH_TYPE, - KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, - KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION, - KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, - KMType.BOOL_TAG, KMType.EARLY_BOOT_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.BOOL_TAG, KMType.ALLOW_WHILE_ON_BODY - }; - - private static final short[] invalidTagsArr = { - KMType.BYTES_TAG, KMType.NONCE, - KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, - KMType.BYTES_TAG, KMType.UNIQUE_ID, - KMType.UINT_TAG, KMType.MAC_LENGTH, - }; - - private static final short[] customTags = { - KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, - }; - - private static KMKeyParameters prototype; - - private KMKeyParameters() { - } - - private static KMKeyParameters proto(short ptr) { - if (prototype == null) { - prototype = new KMKeyParameters(); - } - instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr; - return prototype; - } - - public static short exp() { - short arrPtr = KMArray.instance((short) 9); - KMArray arr = KMArray.cast(arrPtr); - arr.add((short) 0, KMIntegerTag.exp(UINT_TAG)); - arr.add((short) 1, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); - arr.add((short) 2, KMIntegerTag.exp(ULONG_TAG)); - arr.add((short) 3, KMIntegerTag.exp(DATE_TAG)); - arr.add((short) 4, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); - arr.add((short) 5, KMEnumTag.exp()); - arr.add((short) 6, KMEnumArrayTag.exp()); - arr.add((short) 7, KMByteTag.exp()); - arr.add((short) 8, KMBoolTag.exp()); - return instance(arrPtr); - } - - public static short instance(short vals) { - short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); - return ptr; - } - - public static KMKeyParameters cast(short ptr) { - if (heap[ptr] != KEY_PARAM_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - if (heap[arrPtr] != ARRAY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); - } - - public short length() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); - } - - public static short findTag(short tagType, short tagKey, short keyParam) { - KMKeyParameters instParam = KMKeyParameters.cast(keyParam); - return instParam.findTag(tagType, tagKey); - } - - public short findTag(short tagType, short tagKey) { - KMArray vals = KMArray.cast(getVals()); - short index = 0; - short length = vals.length(); - short key; - short type; - short ret = KMType.INVALID_VALUE; - short obj; - while (index < length) { - obj = vals.get(index); - key = KMTag.getKey(obj); - type = KMTag.getTagType(obj); - if ((tagKey == key) && (tagType == type)) { - ret = obj; - break; - } - index++; - } - return ret; - } - - public static boolean hasUnsupportedTags(short keyParamsPtr) { - byte index = 0; - short tagInd; - short tagPtr; - short tagKey; - short tagType; - short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); - short len = KMArray.cast(arrPtr).length(); - while (index < len) { - tagInd = 0; - tagPtr = KMArray.cast(arrPtr).get(index); - tagKey = KMTag.getKey(tagPtr); - tagType = KMTag.getTagType(tagPtr); - while (tagInd < (short) unsupportedTags.length) { - if ((unsupportedTags[tagInd] == tagType) - && (unsupportedTags[(short) (tagInd + 1)] == tagKey)) { - return true; - } - tagInd += 2; - } - index++; - } - return false; - } - - // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal - public static short makeHwEnforced(short keyParamsPtr, byte origin, - short osVersionObjPtr, short osPatchObjPtr, short vendorPatchObjPtr, - short bootPatchObjPtr, byte[] scratchPad) { - byte index = 0; - short tagInd; - short arrInd = 0; - short tagPtr; - short tagKey; - short tagType; - short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); - short len = KMArray.cast(arrPtr).length(); - while (index < len) { - tagInd = 0; - tagPtr = KMArray.cast(arrPtr).get(index); - tagKey = KMTag.getKey(tagPtr); - tagType = KMTag.getTagType(tagPtr); - if (!isValidTag(tagType, tagKey)) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - while (tagInd < (short) hwEnforcedTagArr.length) { - if ((hwEnforcedTagArr[tagInd] == tagType) - && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { - Util.setShort(scratchPad, arrInd, tagPtr); - arrInd += 2; - break; - } - tagInd += 2; - } - index++; - } - short originTag = KMEnumTag.instance(KMType.ORIGIN, origin); - Util.setShort(scratchPad, arrInd, originTag); - arrInd += 2; - short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr); - Util.setShort(scratchPad, arrInd, osVersionTag); - arrInd += 2; - short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr); - Util.setShort(scratchPad, arrInd, osPatchTag); - arrInd += 2; - short vendorPatchTag = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, vendorPatchObjPtr); - Util.setShort(scratchPad, arrInd, vendorPatchTag); - arrInd += 2; - short bootPatchTag = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr); - Util.setShort(scratchPad, arrInd, bootPatchTag); - arrInd += 2; - // Add custom tags at the end of the array. So it becomes easy to - // delete them when sending key characteristics back to HAL. - arrInd = addCustomTags(keyParamsPtr, scratchPad, arrInd); - return createKeyParameters(scratchPad, (short) (arrInd / 2)); - } - - // ALL_USERS, EXPORTABLE missing from types.hal - public static short makeSwEnforced(short keyParamsPtr, byte[] scratchPad) { - byte index = 0; - short tagInd; - short arrInd = 0; - short tagPtr; - short tagKey; - short tagType; - short arrPtr = KMKeyParameters.cast(keyParamsPtr).getVals(); - short len = KMArray.cast(arrPtr).length(); - while (index < len) { - tagInd = 0; - tagPtr = KMArray.cast(arrPtr).get(index); - tagKey = KMTag.getKey(tagPtr); - tagType = KMTag.getTagType(tagPtr); - if (!isValidTag(tagType, tagKey)) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - while (tagInd < (short) swEnforcedTagsArr.length) { - if ((swEnforcedTagsArr[tagInd] == tagType) - && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { - Util.setShort(scratchPad, arrInd, tagPtr); - arrInd += 2; - break; - } - tagInd += 2; - } - index++; - } - return createKeyParameters(scratchPad, (short) (arrInd / 2)); - } - - public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) { - short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); - if (appId != KMTag.INVALID_VALUE) { - appId = KMByteTag.cast(appId).getValue(); - } - short appData = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr); - if (appData != KMTag.INVALID_VALUE) { - appData = KMByteTag.cast(appData).getValue(); - } - return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); - } - - public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfTrustBlob, - byte[] scratchPad) { - // Order in which the hidden array is created should not change. - short index = 0; - KMByteBlob.cast(rootOfTrustBlob); - Util.setShort(scratchPad, index, rootOfTrustBlob); - index += 2; - if (appIdBlob != KMTag.INVALID_VALUE) { - KMByteBlob.cast(appIdBlob); - Util.setShort(scratchPad, index, appIdBlob); - index += 2; - } - if (appDataBlob != KMTag.INVALID_VALUE) { - Util.setShort(scratchPad, index, appDataBlob); - index += 2; - } - return createKeyParameters(scratchPad, (short) (index / 2)); - - } - - public static boolean isValidTag(short tagType, short tagKey) { - short index = 0; - if (tagKey == KMType.INVALID_TAG) { - return false; - } - while (index < invalidTagsArr.length) { - if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) { - return false; - } - index += 2; - } - return true; - } - - public static short createKeyParameters(byte[] ptrArr, short len) { - short arrPtr = KMArray.instance(len); - short index = 0; - short ptr = 0; - while (index < len) { - KMArray.cast(arrPtr).add(index, Util.getShort(ptrArr, ptr)); - index++; - ptr += 2; - } - return KMKeyParameters.instance(arrPtr); - } - - public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { - short index = 0; - short tagPtr; - short len = (short) customTags.length; - short tagType; - while (index < len) { - tagType = customTags[(short) (index + 1)]; - switch(tagType) { - case KMType.AUTH_TIMEOUT_MILLIS: - short authTimeOutTag = - KMKeyParameters.cast(keyParams).findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT); - if (authTimeOutTag != KMType.INVALID_VALUE) { - tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset); - Util.setShort(scratchPad, offset, tagPtr); - offset += 2; - } - break; - default: - break; - } - index += 2; - } - return offset; - } - - public void deleteCustomTags() { - short arrPtr = getVals(); - short index = (short) (customTags.length - 1); - short obj; - while (index >= 0) { - obj = findTag(customTags[(short) (index - 1)], customTags[index]); - if (obj != KMType.INVALID_VALUE) { - KMArray.cast(arrPtr).deleteLastEntry(); - } - index -= 2; - } - } - - public static short createAuthTimeOutMillisTag(short authTimeOutTag, byte[] scratchPad, short offset) { - short authTime = KMIntegerTag.cast(authTimeOutTag).getValue(); - Util.arrayFillNonAtomic(scratchPad, offset, (short) 40, (byte) 0); - Util.arrayCopyNonAtomic( - KMInteger.cast(authTime).getBuffer(), - KMInteger.cast(authTime).getStartOff(), - scratchPad, - (short) (offset + 8 - KMInteger.cast(authTime).length()), - KMInteger.cast(authTime).length()); - KMUtils.convertToMilliseconds(scratchPad, offset, (short) (offset + 8), (short) (offset + 16)); - return KMIntegerTag.instance(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, - KMInteger.uint_64(scratchPad, (short) (offset + 8))); - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java deleted file mode 100644 index 0eebb569..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ /dev/null @@ -1,4154 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.APDU; -import javacard.framework.Applet; -import javacard.framework.AppletEvent; -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; -import javacard.security.CryptoException; -import javacardx.apdu.ExtendedLength; - -/** - * KMKeymasterApplet implements the javacard applet. It creates repository and other install time - * objects. It also implements the keymaster state machine and handles javacard applet life cycle - * events. - */ -public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLength { - - // Constants. - public static final byte[] F4 = {0x01, 0x00, 0x01}; - public static final byte AES_BLOCK_SIZE = 16; - public static final byte DES_BLOCK_SIZE = 8; - public static final short MAX_LENGTH = (short) 0x2000; - private static final byte CLA_ISO7816_NO_SM_NO_CHAN = (byte) 0x80; - private static final short KM_HAL_VERSION = (short) 0x4000; - private static final short MAX_AUTH_DATA_SIZE = (short) 256; - private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; - private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; - // Magic number version - public static final byte KM_MAGIC_NUMBER = (byte) 0x81; - // MSB byte is for Major version and LSB byte is for Minor version. - public static final short CURRENT_PACKAGE_VERSION = 0x0200; // 2.0 - - // "Keymaster HMAC Verification" - used for HMAC key verification. - public static final byte[] sharingCheck = { - 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, - 0x56, - 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E - }; - // "KeymasterSharedMac" - public static final byte[] ckdfLable = { - 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, - 0x4D, - 0x61, 0x63 - }; - // "Auth Verification" - public static final byte[] authVerification = { - 0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6F, - 0x6E - }; - // "confirmation token" - public static final byte[] confirmationToken = { - 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, - 0x6B, - 0x65, 0x6E - }; - - // getHardwareInfo constants. - private static final byte[] JAVACARD_KEYMASTER_DEVICE = { - 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, - 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, - }; - private static final byte[] GOOGLE = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - - - // Possible states of the applet. - private static final byte KM_BEGIN_STATE = 0x00; - private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; - private static final byte INIT_STATE = KM_BEGIN_STATE + 2; - private static final byte IN_PROVISION_STATE = KM_BEGIN_STATE + 3; - private static final byte ACTIVE_STATE = KM_BEGIN_STATE + 4; - - // Commands - private static final byte INS_BEGIN_KM_CMD = 0x00; - // Instructions for Provision Commands. - private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_BEGIN_KM_CMD + 1; //0x01 - private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = INS_BEGIN_KM_CMD + 2; //0x02 - private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_BEGIN_KM_CMD + 3; //0x03 - private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_BEGIN_KM_CMD + 4; //0x04 - private static final byte INS_SET_BOOT_PARAMS_CMD = INS_BEGIN_KM_CMD + 5; //0x05 - private static final byte INS_LOCK_PROVISIONING_CMD = INS_BEGIN_KM_CMD + 6; //0x06 - private static final byte INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD + 7; //0x07 - private static final byte INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD + 8; //0x08 - private static final byte INS_SET_BOOT_ENDED_CMD = INS_BEGIN_KM_CMD + 9; //0x09 - - // Top 32 commands are reserved for provisioning. - private static final byte INS_END_KM_PROVISION_CMD = 0x20; - - private static final byte INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD + 1; //0x21 - private static final byte INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 2; //0x22 - private static final byte INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD + 3; //0x23 - private static final byte INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD + 4; //0x24 - private static final byte INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD + 5; //0x25 - private static final byte INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD + 6; //0x26 - private static final byte INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD + 7; //0x27 - private static final byte INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD + 8; //0x28 - private static final byte INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD + 9; //0x29 - private static final byte INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD + 10; //0x2A - private static final byte INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD + 11; //0x2B - private static final byte INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD + 12; //0x2C - private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD + 13; //0x2D - private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD + 14; //0x2E - private static final byte INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD + 15; //0x2F - private static final byte INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 16; //0x30 - private static final byte INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 17; //0x31 - private static final byte INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 18; //0x32 - private static final byte INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD + 19; //0x33 - private static final byte INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD + 20;//0x34 - private static final byte INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD + 21; //0x35 - private static final byte INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD + 22; //0x36 - - private static final byte INS_END_KM_CMD = 0x7F; - - // Provision reporting status - protected static final byte NOT_PROVISIONED = 0x00; - protected 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; - protected static final byte PROVISION_STATUS_ATTEST_IDS = 0x08; - protected static final byte PROVISION_STATUS_PRESHARED_SECRET = 0x10; - protected static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x20; - - // Data Dictionary items - public static final byte DATA_ARRAY_SIZE = 30; - public static final byte TMP_VARIABLE_ARRAY_SIZE = 20; - public static final byte UPDATE_PARAM_ARRAY_SIZE = 40; - public static final byte KEY_PARAMETERS = 0; - public static final byte KEY_CHARACTERISTICS = 1; - public static final byte HIDDEN_PARAMETERS = 2; - public static final byte HW_PARAMETERS = 3; - public static final byte SW_PARAMETERS = 4; - public static final byte AUTH_DATA = 5; - public static final byte AUTH_TAG = 6; - public static final byte NONCE = 7; - public static final byte KEY_BLOB = 8; - public static final byte AUTH_DATA_LENGTH = 9; - public static final byte SECRET = 10; - public static final byte ROT = 11; - public static final byte DERIVED_KEY = 12; - public static final byte RSA_PUB_EXPONENT = 13; - public static final byte APP_ID = 14; - public static final byte APP_DATA = 15; - public static final byte PUB_KEY = 16; - public static final byte IMPORTED_KEY_BLOB = 17; - public static final byte ORIGIN = 18; - public static final byte ENC_TRANSPORT_KEY = 19; - public static final byte MASKING_KEY = 20; - public static final byte HMAC_SHARING_PARAMS = 21; - public static final byte OP_HANDLE = 22; - public static final byte IV = 23; - public static final byte INPUT_DATA = 24; - public static final byte OUTPUT_DATA = 25; - public static final byte HW_TOKEN = 26; - public static final byte VERIFICATION_TOKEN = 27; - public static final byte SIGNATURE = 28; - - // AddRngEntropy - protected static final short MAX_SEED_SIZE = 2048; - // Keyblob constants - public static final byte KEY_BLOB_SECRET = 0; - public static final byte KEY_BLOB_NONCE = 1; - public static final byte KEY_BLOB_AUTH_TAG = 2; - public static final byte KEY_BLOB_KEYCHAR = 3; - public static final byte KEY_BLOB_PUB_KEY = 4; - // AES GCM constants - private static final byte AES_GCM_AUTH_TAG_LENGTH = 16; - private static final byte AES_GCM_NONCE_LENGTH = 12; - // ComputeHMAC constants - private static final short HMAC_SHARED_PARAM_MAX_SIZE = 64; - // Maximum certificate size. - private static final short MAX_CERT_SIZE = 2048; - // Buffer constants. - private static final short BUF_START_OFFSET = 0; - private static final short BUF_LEN_OFFSET = 2; - - // Keymaster Applet attributes - protected static byte keymasterState = ILLEGAL_STATE; - protected static KMEncoder encoder; - protected static KMDecoder decoder; - protected static KMRepository repository; - protected static KMSEProvider seProvider; - protected static Object[] bufferRef; - protected static short[] bufferProp; - protected static short[] tmpVariables; - protected static short[] data; - protected static byte provisionStatus = NOT_PROVISIONED; - // First two bytes are Major version and second bytes are minor version. - protected short packageVersion; - - - /** - * Registers this applet. - */ - protected KMKeymasterApplet(KMSEProvider seImpl) { - seProvider = seImpl; - boolean isUpgrading = seImpl.isUpgrading(); - repository = new KMRepository(isUpgrading); - initializeTransientArrays(); - if (!isUpgrading) { - keymasterState = KMKeymasterApplet.INIT_STATE; - seProvider.createMasterKey((short) (KMRepository.MASTER_KEY_SIZE * 8)); - packageVersion = CURRENT_PACKAGE_VERSION; - } - KMType.initialize(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - } - - private void initializeTransientArrays() { - data = JCSystem.makeTransientShortArray((short) DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - bufferProp = JCSystem.makeTransientShortArray((short) 4, JCSystem.CLEAR_ON_RESET); - tmpVariables = - JCSystem.makeTransientShortArray((short) TMP_VARIABLE_ARRAY_SIZE, JCSystem.CLEAR_ON_RESET); - bufferProp[BUF_START_OFFSET] = 0; - bufferProp[BUF_LEN_OFFSET] = 0; - } - - /** - * Selects this applet. - * - * @return Returns true if the keymaster is in correct state - */ - @Override - public boolean select() { - repository.onSelect(); - if (keymasterState == KMKeymasterApplet.INIT_STATE) { - keymasterState = KMKeymasterApplet.IN_PROVISION_STATE; - } - return true; - } - - /** - * De-selects this applet. - */ - @Override - public void deselect() { - repository.onDeselect(); - } - - /** - * Uninstalls the applet after cleaning the repository. - */ - @Override - public void uninstall() { - repository.onUninstall(); - } - - private short mapISOErrorToKMError(short reason) { - switch (reason) { - case ISO7816.SW_CLA_NOT_SUPPORTED: - return KMError.UNSUPPORTED_CLA; - case ISO7816.SW_CONDITIONS_NOT_SATISFIED: - return KMError.SW_CONDITIONS_NOT_SATISFIED; - case ISO7816.SW_COMMAND_NOT_ALLOWED: - return KMError.CMD_NOT_ALLOWED; - case ISO7816.SW_DATA_INVALID: - return KMError.INVALID_DATA; - case ISO7816.SW_INCORRECT_P1P2: - return KMError.INVALID_P1P2; - case ISO7816.SW_INS_NOT_SUPPORTED: - return KMError.UNSUPPORTED_INSTRUCTION; - case ISO7816.SW_WRONG_LENGTH: - return KMError.SW_WRONG_LENGTH; - case ISO7816.SW_UNKNOWN: - default: - return KMError.UNKNOWN_ERROR; - } - } - - private short mapCryptoErrorToKMError(short reason) { - switch (reason) { - case CryptoException.ILLEGAL_USE: - return KMError.CRYPTO_ILLEGAL_USE; - case CryptoException.ILLEGAL_VALUE: - return KMError.CRYPTO_ILLEGAL_VALUE; - case CryptoException.INVALID_INIT: - return KMError.CRYPTO_INVALID_INIT; - case CryptoException.NO_SUCH_ALGORITHM: - return KMError.CRYPTO_NO_SUCH_ALGORITHM; - case CryptoException.UNINITIALIZED_KEY: - return KMError.CRYPTO_UNINITIALIZED_KEY; - default: - return KMError.UNKNOWN_ERROR; - } - } - - protected void validateApduHeader(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)) { - ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); - } - - // Validate P1P2. - if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { - ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); - } - } - - /** - * Processes an incoming APDU and handles it using command objects. - * - * @param apdu the incoming APDU - */ - @Override - public void process(APDU apdu) { - try { - // Handle the card reset status before processing apdu. - if (repository.isPowerResetEventOccurred()) { - // Release all the operation instances. - seProvider.releaseAllOperations(); - } - repository.onProcess(); - // Verify whether applet is in correct state. - if ((keymasterState == KMKeymasterApplet.INIT_STATE) - || (keymasterState == KMKeymasterApplet.ILLEGAL_STATE)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // If this is select applet apdu which is selecting this applet then - // return - if (apdu.isISOInterindustryCLA()) { - if (selectingApplet()) { - return; - } - } - // Validate APDU Header. - validateApduHeader(apdu); - - byte[] apduBuffer = apdu.getBuffer(); - byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; - - // Validate whether INS can be supported - if (!(apduIns > INS_BEGIN_KM_CMD && apduIns < INS_END_KM_CMD)) { - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - bufferRef[0] = repository.getHeap(); - // Process the apdu - if (keymasterState == KMKeymasterApplet.IN_PROVISION_STATE) { - switch (apduIns) { - case INS_PROVISION_ATTESTATION_KEY_CMD: - processProvisionAttestationKey(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_KEY; - sendError(apdu, KMError.OK); - return; - - case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: - processProvisionAttestationCertDataCmd(apdu); - provisionStatus |= (KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_CHAIN | - KMKeymasterApplet.PROVISION_STATUS_ATTESTATION_CERT_PARAMS); - sendError(apdu, KMError.OK); - return; - - case INS_PROVISION_ATTEST_IDS_CMD: - processProvisionAttestIdsCmd(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_ATTEST_IDS; - sendError(apdu, KMError.OK); - return; - - case INS_PROVISION_PRESHARED_SECRET_CMD: - processProvisionSharedSecretCmd(apdu); - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_PRESHARED_SECRET; - sendError(apdu, KMError.OK); - return; - - case INS_LOCK_PROVISIONING_CMD: - if (isProvisioningComplete()) { - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED; - keymasterState = KMKeymasterApplet.ACTIVE_STATE; - sendError(apdu, KMError.OK); - } else { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - return; - } - } - - if ((keymasterState == KMKeymasterApplet.ACTIVE_STATE) - || (keymasterState == KMKeymasterApplet.IN_PROVISION_STATE)) { - switch (apduIns) { - case INS_SET_BOOT_PARAMS_CMD: - if (seProvider.isBootSignalEventSupported() - && (keymasterState == KMKeymasterApplet.ACTIVE_STATE) - && (!seProvider.isDeviceRebooted())) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - processSetBootParamsCmd(apdu); - - sendError(apdu, KMError.OK); - return; - - case INS_SET_BOOT_ENDED_CMD: - if (seProvider.isBootSignalEventSupported() - && (keymasterState == KMKeymasterApplet.ACTIVE_STATE) - && (!seProvider.isDeviceRebooted())) { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - //set the flag to mark boot ended - repository.setBootEndedStatus(true); - - seProvider.clearDeviceBooted(false); - sendError(apdu, KMError.OK); - return; - - case INS_GET_PROVISION_STATUS_CMD: - processGetProvisionStatusCmd(apdu); - return; - } - } - - if ((keymasterState == KMKeymasterApplet.ACTIVE_STATE) - || ((keymasterState == KMKeymasterApplet.IN_PROVISION_STATE) - && isProvisioningComplete())) { - switch (apduIns) { - case INS_GENERATE_KEY_CMD: - processGenerateKey(apdu); - break; - case INS_IMPORT_KEY_CMD: - processImportKeyCmd(apdu); - break; - case INS_IMPORT_WRAPPED_KEY_CMD: - processImportWrappedKeyCmd(apdu); - break; - case INS_EXPORT_KEY_CMD: - processExportKeyCmd(apdu); - break; - case INS_ATTEST_KEY_CMD: - processAttestKeyCmd(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_GET_CERT_CHAIN_CMD: - processGetCertChainCmd(apdu); - break; - case INS_SET_VERSION_PATCHLEVEL_CMD: - processSetVersionAndPatchLevels(apdu); - break; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - } else { - ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); - } - } catch (KMException exception) { - freeOperations(); - sendError(apdu, KMException.getReason()); - exception.clear(); - } catch (ISOException exp) { - freeOperations(); - sendError(apdu, mapISOErrorToKMError(exp.getReason())); - } catch (CryptoException e) { - freeOperations(); - sendError(apdu, mapCryptoErrorToKMError(e.getReason())); - } catch (Exception e) { - freeOperations(); - sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); - } finally { - resetData(); - repository.clean(); - } - } - - private void generateUniqueOperationHandle(byte[] buf, short offset, short len) { - do { - seProvider.newRandomNumber(buf, offset, len); - } while (null != repository.findOperation(buf, offset, len)); - } - - private boolean isProvisioningComplete() { - if ((0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_KEY)) - && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) - && (0 != (provisionStatus & PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) - && (0 != (provisionStatus & PROVISION_STATUS_PRESHARED_SECRET))) { - return true; - } else { - return false; - } - } - - private void freeOperations() { - if (data[OP_HANDLE] != KMType.INVALID_VALUE) { - KMOperationState op = repository.findOperation(data[OP_HANDLE]); - if (op != null) { - repository.releaseOperation(op); - } - } - } - - private void processEarlyBootEndedCmd(APDU apdu) { - repository.setEarlyBootEndedStatus(true); - } - - private void processDeviceLockedCmd(APDU apdu) { - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMInteger.exp()); - tmpVariables[1] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - // Decode the arguments - tmpVariables[0] = decoder.decode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - tmpVariables[1] = KMArray.cast(tmpVariables[0]).get((short) 0); - tmpVariables[1] = KMInteger.cast(tmpVariables[1]).getByte(); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[0]).get((short) 1); - validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad); - short verTime = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - short lastDeviceLockedTime = repository.getDeviceTimeStamp(); - if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) { - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); - KMInteger.cast(verTime).getValue(scratchPad, (short) 0, (short) 8); - repository.setDeviceLock(true); - repository.setDeviceLockPasswordOnly(tmpVariables[1] == 0x01); - repository.setDeviceLockTimestamp(scratchPad, (short) 0, (short) 8); - } - sendError(apdu, KMError.OK); - } - - private void resetData() { - short index = 0; - while (index < data.length) { - data[index] = KMType.INVALID_VALUE; - index++; - } - index = 0; - while (index < tmpVariables.length) { - tmpVariables[index] = KMType.INVALID_VALUE; - index++; - } - } - - /** - * Sends a response, may be extended response, as requested by the command. - */ - public static void sendOutgoing(APDU apdu) { - if (((short) (bufferProp[BUF_LEN_OFFSET] + bufferProp[BUF_START_OFFSET])) > ((short) repository - .getHeap().length)) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - // Send data - apdu.setOutgoing(); - apdu.setOutgoingLength(bufferProp[BUF_LEN_OFFSET]); - apdu.sendBytesLong((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - } - - /** - * Receives data, which can be extended data, as requested by the command instance. - */ - public static void receiveIncoming(APDU apdu) { - byte[] srcBuffer = apdu.getBuffer(); - short recvLen = apdu.setIncomingAndReceive(); - short srcOffset = apdu.getOffsetCdata(); - bufferProp[BUF_LEN_OFFSET] = apdu.getIncomingLength(); - bufferProp[BUF_START_OFFSET] = repository.allocReclaimableMemory(bufferProp[BUF_LEN_OFFSET]); - short index = bufferProp[BUF_START_OFFSET]; - - while (recvLen > 0 && ((short) (index - bufferProp[BUF_START_OFFSET]) < bufferProp[BUF_LEN_OFFSET])) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, (byte[]) bufferRef[0], index, recvLen); - index += recvLen; - recvLen = apdu.receiveBytes(srcOffset); - } - } - - private void processGetHwInfoCmd(APDU apdu) { - // No arguments expected - // Make the response - short respPtr = KMArray.instance((short) 3); - KMArray resp = KMArray.cast(respPtr); - resp.add((short) 0, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); - resp.add( - (short) 1, - KMByteBlob.instance( - JAVACARD_KEYMASTER_DEVICE, (short) 0, (short) JAVACARD_KEYMASTER_DEVICE.length)); - resp.add((short) 2, KMByteBlob.instance(GOOGLE, (short) 0, (short) GOOGLE.length)); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - actual bufferProp[BUF_LEN_OFFSET] is 86 - bufferProp[BUF_LEN_OFFSET] = encoder.encode(respPtr, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - // send buffer to master - sendOutgoing(apdu); - } - - private void processAddRngEntropyCmd(APDU apdu) { - // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Argument 1 - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // Process - KMByteBlob blob = KMByteBlob.cast(KMArray.cast(args).get((short) 0)); - // Maximum 2KiB of seed is allowed. - if (blob.length() > MAX_SEED_SIZE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - seProvider.addRngEntropy(blob.getBuffer(), blob.getStartOff(), blob.length()); - sendError(apdu, KMError.OK); - } - - private void processSetVersionAndPatchLevels(APDU apdu) { - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - // Argument 1 OS Version - tmpVariables[0] = KMInteger.exp(); - // Argument 2 OS Patch level - tmpVariables[1] = KMInteger.exp(); - // Argument 3 Vendor Patch level - tmpVariables[2] = KMInteger.exp(); - // Array of expected arguments - short argsProto = KMArray.instance((short) 3); - KMArray.cast(argsProto).add((short) 0, tmpVariables[0]); - KMArray.cast(argsProto).add((short) 1, tmpVariables[1]); - KMArray.cast(argsProto).add((short) 2, tmpVariables[2]); - // Decode the arguments - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[1] = KMArray.cast(args).get((short) 1); - tmpVariables[2] = KMArray.cast(args).get((short) 2); - - repository.setOsVersion( - KMInteger.cast(tmpVariables[0]).getBuffer(), - KMInteger.cast(tmpVariables[0]).getStartOff(), - KMInteger.cast(tmpVariables[0]).length()); - - repository.setOsPatch( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), - KMInteger.cast(tmpVariables[1]).length()); - - repository.setVendorPatchLevel( - KMInteger.cast(tmpVariables[2]).getBuffer(), - KMInteger.cast(tmpVariables[2]).getStartOff(), - KMInteger.cast(tmpVariables[2]).length()); - - sendError(apdu, KMError.OK); - } - - private short getProvisionedCertificateData(byte dataType) { - short len = seProvider.getProvisionedDataLength(dataType); - if (len == 0) { - KMException.throwIt(KMError.INVALID_DATA); - } - short ptr = KMByteBlob.instance(len); - seProvider.readProvisionedData( - dataType, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff()); - return ptr; - } - - private void processGetCertChainCmd(APDU apdu) { - // Make the response - short certChainLen = seProvider.getProvisionedDataLength(KMSEProvider.CERTIFICATE_CHAIN); - short int32Ptr = buildErrorStatus(KMError.OK); - short maxByteHeaderLen = 3; // Maximum possible ByteBlob header len. - short arrayHeaderLen = 1; - // Allocate maximum possible buffer. - // Add arrayHeader + (PowerResetStatus + KMError.OK) + Byte Header - short totalLen = (short) (arrayHeaderLen + encoder.getEncodedIntegerLength(int32Ptr) + maxByteHeaderLen + certChainLen); - tmpVariables[1] = KMByteBlob.instance(totalLen); - bufferRef[0] = KMByteBlob.cast(tmpVariables[1]).getBuffer(); - bufferProp[BUF_START_OFFSET] = KMByteBlob.cast(tmpVariables[1]).getStartOff(); - bufferProp[BUF_LEN_OFFSET] = KMByteBlob.cast(tmpVariables[1]).length(); - // copy the certificate chain to the end of the buffer. - seProvider.readProvisionedData( - KMSEProvider.CERTIFICATE_CHAIN, - (byte[]) bufferRef[0], - (short) (bufferProp[BUF_START_OFFSET] + totalLen - certChainLen)); - // Encode cert chain. - encoder.encodeCertChain((byte[]) bufferRef[0], - bufferProp[BUF_START_OFFSET], - bufferProp[BUF_LEN_OFFSET], - int32Ptr, // uint32 ptr - (short) (bufferProp[BUF_START_OFFSET] + totalLen - certChainLen), // start pos of cert chain. - certChainLen); - sendOutgoing(apdu); - } - - private void processProvisionAttestationCertDataCmd(APDU apdu) { - receiveIncoming(apdu); - // Buffer holds the corresponding offsets and lengths of the certChain, certIssuer and certExpiry - // in the bufferRef[0] buffer. - short var = KMByteBlob.instance((short) 12); - // These variables point to the appropriate positions in the var buffer. - short certChainPos = KMByteBlob.cast(var).getStartOff(); - short certIssuerPos = (short) (KMByteBlob.cast(var).getStartOff() + 4); - short certExpiryPos = (short) (KMByteBlob.cast(var).getStartOff() + 8); - decoder.decodeCertificateData((short) 3, - (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET], - KMByteBlob.cast(var).getBuffer(), KMByteBlob.cast(var).getStartOff()); - // persist data - seProvider.persistProvisionData( - (byte[]) bufferRef[0], - Util.getShort(KMByteBlob.cast(var).getBuffer(), certChainPos), // offset - Util.getShort(KMByteBlob.cast(var).getBuffer(), (short) (certChainPos + 2)), // length - Util.getShort(KMByteBlob.cast(var).getBuffer(), certIssuerPos), // offset - Util.getShort(KMByteBlob.cast(var).getBuffer(), (short) (certIssuerPos + 2)), // length - Util.getShort(KMByteBlob.cast(var).getBuffer(), certExpiryPos), // offset - Util.getShort(KMByteBlob.cast(var).getBuffer(), (short) (certExpiryPos + 2))); // length - - // reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - } - - private void processProvisionAttestationKey(APDU apdu) { - receiveIncoming(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); - - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // 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); - data[IMPORTED_KEY_BLOB] = KMArray.cast(args).get((short) 2); - // Key format must be RAW format - byte keyFormat = KMEnum.cast(tmpVariables[0]).getVal(); - if (keyFormat != KMType.RAW) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - data[ORIGIN] = KMType.IMPORTED; - - // 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 ATTEST_KEY - 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.ATTEST_KEY) { - KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); - } - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Import EC Key - initializes data[SECRET] data[PUB_KEY] - importECKeys(scratchPad, keyFormat); - - // persist key - seProvider.createAttestationKey( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - } - - private void processProvisionAttestIdsCmd(APDU apdu) { - receiveIncoming(apdu); - // Arguments - short keyparams = KMKeyParameters.exp(); - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, keyparams); - // Decode the argument. - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); - // persist attestation Ids - if any is missing then exception occurs - saveAttId(); - } - - private void processProvisionSharedSecretCmd(APDU apdu) { - receiveIncoming(apdu); - // Arguments - short blob = KMByteBlob.exp(); - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, blob); - // Decode the argument. - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - tmpVariables[0] = KMArray.cast(args).get((short) 0); - if (tmpVariables[0] != KMType.INVALID_VALUE - && KMByteBlob.cast(tmpVariables[0]).length() != KMRepository.SHARED_SECRET_KEY_SIZE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Persist shared Hmac. - seProvider.createPresharedKey( - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - } - - private void processGetProvisionStatusCmd(APDU apdu) { - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, KMInteger.uint_16(provisionStatus)); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void saveAttId() { - // clear the attestation ids. - repository.deleteAttIds(); - - short attTag = KMType.ATTESTATION_ID_BRAND; - while (attTag <= KMType.ATTESTATION_ID_MODEL) { - tmpVariables[0] = KMKeyParameters.findTag(KMType.BYTES_TAG, attTag, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); - repository.persistAttId( - mapToAttId(attTag), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - } else { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - attTag++; - } - } - - private byte mapToAttId(short attTag) { - switch (attTag) { - case KMType.ATTESTATION_ID_BRAND: - return KMRepository.ATT_ID_BRAND; - case KMType.ATTESTATION_ID_DEVICE: - return KMRepository.ATT_ID_DEVICE; - case KMType.ATTESTATION_ID_IMEI: - return KMRepository.ATT_ID_IMEI; - case KMType.ATTESTATION_ID_MANUFACTURER: - return KMRepository.ATT_ID_MANUFACTURER; - case KMType.ATTESTATION_ID_MEID: - return KMRepository.ATT_ID_MEID; - case KMType.ATTESTATION_ID_MODEL: - return KMRepository.ATT_ID_MODEL; - case KMType.ATTESTATION_ID_PRODUCT: - return KMRepository.ATT_ID_PRODUCT; - case KMType.ATTESTATION_ID_SERIAL: - return KMRepository.ATT_ID_SERIAL; - } - KMException.throwIt(KMError.INVALID_TAG); - return (byte) 0xFF; // should never happen - } - - private void processGetKeyCharacteristicsCmd(APDU apdu) { - // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - // Arguments - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); - KMArray.cast(tmpVariables[0]).add((short) 2, KMByteBlob.exp()); - // Decode the arguments - tmpVariables[0] = decoder.decode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_BLOB] = KMArray.cast(tmpVariables[0]).get((short) 0); - data[APP_ID] = KMArray.cast(tmpVariables[0]).get((short) 1); - data[APP_DATA] = KMArray.cast(tmpVariables[0]).get((short) 2); - if (!KMByteBlob.cast(data[APP_ID]).isValid()) { - data[APP_ID] = KMType.INVALID_VALUE; - } - if (!KMByteBlob.cast(data[APP_DATA]).isValid()) { - data[APP_DATA] = KMType.INVALID_VALUE; - } - // Parse Key Blob - parseEncryptedKeyBlob(scratchPad); - // Check Version and Patch Level - checkVersionAndPatchLevel(scratchPad); - // Remove custom tags from key characteristics - short hwParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); - KMKeyParameters.cast(hwParams).deleteCustomTags(); - // make response. - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_CHARACTERISTICS]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void processGetHmacSharingParamCmd(APDU apdu) { - // No Arguments - // Create HMAC Sharing Parameters - tmpVariables[2] = KMHmacSharingParameters.instance(); - KMHmacSharingParameters.cast(tmpVariables[2]).setNonce(repository.getHmacNonce()); - KMHmacSharingParameters.cast(tmpVariables[2]).setSeed(KMByteBlob.instance((short) 0)); - // prepare the response - tmpVariables[3] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[3]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[3]).add((short) 1, tmpVariables[2]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[3], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void processDeleteAllKeysCmd(APDU apdu) { - - // No arguments - // Send ok - sendError(apdu, KMError.OK); - } - - private void processDeleteKeyCmd(APDU apdu) { - - // Receive the incoming request fully from the master. - receiveIncoming(apdu); - // Arguments - short argsProto = KMArray.instance((short) 1); - KMArray.cast(argsProto).add((short) 0, KMByteBlob.exp()); - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // Process - data[KEY_BLOB] = KMArray.cast(args).get((short) 0); - tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); - tmpVariables[1] = KMArray.instance((short) 5); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, KMByteBlob.exp()); - tmpVariables[2] = KMKeyCharacteristics.exp(); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); - try { - data[KEY_BLOB] = decoder.decodeArray(tmpVariables[1], - KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[KEY_BLOB]).length()); - } catch (ISOException e) { - // As per VTS, deleteKey should return KMError.OK but in case if - // input is empty then VTS accepts UNIMPLEMENTED errorCode as well. - KMException.throwIt(KMError.UNIMPLEMENTED); - } - tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); - if (tmpVariables[0] < 4) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - // Send ok - sendError(apdu, KMError.OK); - } - - private void processComputeSharedHmacCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMHmacSharingParameters.exp(); - tmpVariables[0] = KMArray.exp(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[2]).add((short) 0, tmpVariables[0]); // Vector of hmac params - // Decode the arguments - tmpVariables[0] = decoder.decode(tmpVariables[2], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[HMAC_SHARING_PARAMS] = KMArray.cast(tmpVariables[0]).get((short) 0); - // Concatenate HMAC Params - tmpVariables[0] = KMArray.cast(data[HMAC_SHARING_PARAMS]).length(); // total number of params - tmpVariables[1] = repository.alloc((short) (tmpVariables[0] * HMAC_SHARED_PARAM_MAX_SIZE)); - tmpVariables[2] = 0; // index for params - tmpVariables[3] = 0; // index for concatenation buffer - // To check if nonce created by Strongbox is found. This value becomes 1 if both - // seed and nonce created here are found in hmac sharing parameters received. - tmpVariables[7] = 0; - tmpVariables[9] = repository.getHmacNonce(); - - while (tmpVariables[2] < tmpVariables[0]) { - // read HmacSharingParam - tmpVariables[4] = KMArray.cast(data[HMAC_SHARING_PARAMS]).get(tmpVariables[2]); - // get seed - 32 bytes max - tmpVariables[5] = KMHmacSharingParameters.cast(tmpVariables[4]).getSeed(); - tmpVariables[6] = KMByteBlob.cast(tmpVariables[5]).length(); - // if seed is present - if (tmpVariables[6] != 0) { - // then copy that to concatenation buffer - Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[5]).getBuffer(), - KMByteBlob.cast(tmpVariables[5]).getStartOff(), - repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[3]), // concat index - tmpVariables[6]); - tmpVariables[3] += tmpVariables[6]; // increment the concat index - } else if (tmpVariables[7] == 0) { - tmpVariables[7] = 1; - } - // if nonce is present get nonce - 32 bytes - tmpVariables[5] = KMHmacSharingParameters.cast(tmpVariables[4]).getNonce(); - tmpVariables[6] = KMByteBlob.cast(tmpVariables[5]).length(); - // if nonce is not present - it is an error - if (tmpVariables[6] == 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // copy nonce to concatenation buffer - Util.arrayCopyNonAtomic( - KMByteBlob.cast(tmpVariables[5]).getBuffer(), - KMByteBlob.cast(tmpVariables[5]).getStartOff(), - repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[3]), // index - tmpVariables[6]); - - // Check if the nonce generated here is present in the hmacSharingParameters array. - // Otherwise throw INVALID_ARGUMENT error. - if (tmpVariables[7] == 1) { - if (0 - == Util.arrayCompare( - repository.getHeap(), - (short) (tmpVariables[1] + tmpVariables[3]), - KMByteBlob.cast(tmpVariables[9]).getBuffer(), - KMByteBlob.cast(tmpVariables[9]).getStartOff(), - tmpVariables[6])) { - tmpVariables[7] = 2; // hmac nonce for this keymaster found. - } else { - tmpVariables[7] = 0; - } - } - tmpVariables[3] += tmpVariables[6]; // increment by nonce length - tmpVariables[2]++; // go to next hmac param in the vector - } - if (tmpVariables[7] != 2) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - // generate the key and store it in scratch pad - 32 bytes - tmpVariables[6] = - seProvider.cmacKDF( - seProvider.getPresharedKey(), - ckdfLable, - (short) 0, - (short) ckdfLable.length, - repository.getHeap(), - tmpVariables[1], - tmpVariables[3], - scratchPad, - (short) 0); - // persist the computed hmac key. - seProvider.createComputedHmacKey(scratchPad, (short) 0, tmpVariables[6]); - - // Generate sharingKey verification signature and store that in scratch pad. - tmpVariables[5] = - seProvider.hmacSign( - scratchPad, - (short) 0, - tmpVariables[6], - sharingCheck, - (short) 0, - (short) sharingCheck.length, - scratchPad, - tmpVariables[6]); - // verification signature blob - 32 bytes - tmpVariables[1] = KMByteBlob.instance(scratchPad, tmpVariables[6], tmpVariables[5]); - // prepare the response - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private boolean isKeyUpgradeRequired(short tag, short systemParam) { - // validate the tag and check if key needs upgrade. - tmpVariables[0] = KMKeyParameters.findTag(KMType.UINT_TAG, tag, data[HW_PARAMETERS]); - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - tmpVariables[1] = KMInteger.uint_8((byte) 0); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - // OS version in key characteristics must be less the OS version stored in Javacard or the - // stored version must be zero. Then only upgrade is allowed else it is invalid argument. - if ((tag == KMType.OS_VERSION - && KMInteger.compare(tmpVariables[0], systemParam) == 1 - && KMInteger.compare(systemParam, tmpVariables[1]) == 0)) { - // Key needs upgrade. - return true; - } else if ((KMInteger.compare(tmpVariables[0], 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; - } else if (KMInteger.compare(tmpVariables[0], systemParam) == 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - return false; - } - - private void processUpgradeKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 2); - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMByteBlob.exp()); // Key Blob - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); // Key Params - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[0]).getValue(); - } - // parse existing key blob - parseEncryptedKeyBlob(scratchPad); - boolean isKeyUpgradeRequired = false; - // Check if key requires upgrade. - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, repository.getOsVersion()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, repository.getOsPatch()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, repository.getVendorPatchLevel()); - isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, repository.getBootPatchLevel()); - - if (isKeyUpgradeRequired) { - // copy origin - data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); - // create new key blob with current os version etc. - createEncryptedKeyBlob(scratchPad); - } else { - data[KEY_BLOB] = KMByteBlob.instance((short) 0); - } - // prepare the response - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void processExportKeyCmd(APDU apdu) { - sendError(apdu, KMError.UNIMPLEMENTED); - } - - private void processImportWrappedKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 12); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); // Key Params of wrapped key - KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); // Auth Tag - KMArray.cast(tmpVariables[1]).add((short) 4, KMByteBlob.exp()); // IV - Nonce - KMArray.cast(tmpVariables[1]).add((short) 5, KMByteBlob.exp()); // Encrypted Transport Key - KMArray.cast(tmpVariables[1]).add((short) 6, KMByteBlob.exp()); // Wrapping Key KeyBlob - KMArray.cast(tmpVariables[1]).add((short) 7, KMByteBlob.exp()); // Masking Key - KMArray.cast(tmpVariables[1]).add((short) 8, tmpVariables[2]); // Un-wrapping Params - KMArray.cast(tmpVariables[1]) - .add((short) 9, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA - KMArray.cast(tmpVariables[1]).add((short) 10, KMInteger.exp()); // Password Sid - KMArray.cast(tmpVariables[1]).add((short) 11, KMInteger.exp()); // Biometric Sid - // Decode the arguments - short args = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - // Step -0 - check whether the key format and algorithm supported - // read algorithm - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[1] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[0]); - // read key format - tmpVariables[2] = KMArray.cast(args).get((short) 1); - byte keyFormat = KMEnum.cast(tmpVariables[2]).getVal(); - if ((tmpVariables[1] == KMType.RSA || tmpVariables[1] == KMType.EC) - && (keyFormat != KMType.PKCS8)) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - - // Step -1 parse the wrapping key blob - // read wrapping key blob - data[KEY_BLOB] = KMArray.cast(args).get((short) 6); - // read un wrapping key params - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 8); - // Read App Id and App Data if any from un wrapping key params - data[APP_ID] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - data[APP_DATA] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (data[APP_ID] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - if (data[APP_DATA] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(tmpVariables[3]).getValue(); - } - // parse the wrapping key blob - parseEncryptedKeyBlob(scratchPad); - // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest - // SHA2_256. - if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); - } - if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - if (!KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, data[HW_PARAMETERS])) { - KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE)); - } - - // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key - // create rsa decipher - // read encrypted transport key from args - tmpVariables[0] = KMArray.cast(args).get((short) 5); - // Decrypt the transport key - tmpVariables[1] = - seProvider.rsaDecipherOAEP256( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length(), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length(), - scratchPad, - (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); - - // Step 3 - XOR the decrypted AES-GCM key with with masking key - // read masking key - tmpVariables[0] = KMArray.cast(args).get((short) 7); - tmpVariables[1] = KMByteBlob.cast(tmpVariables[0]).length(); - // Length of masking key and transport key must be same. - if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - tmpVariables[2] = 0; // index - // Xor every byte of masking and key and store the result in data[SECRET] - while (tmpVariables[2] < tmpVariables[1]) { - tmpVariables[3] = - (short) (((short) KMByteBlob.cast(tmpVariables[0]).get(tmpVariables[2])) & 0x00FF); - tmpVariables[4] = - (short) (((short) KMByteBlob.cast(data[SECRET]).get(tmpVariables[2])) & 0x00FF); - KMByteBlob.cast(data[SECRET]) - .add(tmpVariables[2], (byte) (tmpVariables[3] ^ tmpVariables[4])); - tmpVariables[2]++; - } - - // Step 4 - AES-GCM decrypt the wrapped key - data[INPUT_DATA] = KMArray.cast(args).get((short) 2); - data[AUTH_DATA] = KMArray.cast(args).get((short) 9); - data[AUTH_TAG] = KMArray.cast(args).get((short) 3); - data[NONCE] = KMArray.cast(args).get((short) 4); - Util.arrayFillNonAtomic( - scratchPad, (short) 0, KMByteBlob.cast(data[INPUT_DATA]).length(), (byte) 0); - - if (!seProvider.aesGCMDecrypt( - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length(), - KMByteBlob.cast(data[AUTH_DATA]).getBuffer(), - KMByteBlob.cast(data[AUTH_DATA]).getStartOff(), - KMByteBlob.cast(data[AUTH_DATA]).length(), - KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), - KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length())) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - - // Step 5 - Import decrypted key - data[ORIGIN] = KMType.SECURELY_IMPORTED; - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); - // create key blob array - data[IMPORTED_KEY_BLOB] = KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[INPUT_DATA]).length()); - importKey(apdu, scratchPad, keyFormat); - } - - private void processAttestKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - - // Arguments - short keyParams = KMKeyParameters.exp(); - short keyBlob = KMByteBlob.exp(); - short argsProto = KMArray.instance((short) 2); - KMArray.cast(argsProto).add((short) 0, keyBlob); - KMArray.cast(argsProto).add((short) 1, keyParams); - - // Decode the argument - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_BLOB] = KMArray.cast(args).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 1); - - // parse key blob - parseEncryptedKeyBlob(scratchPad); - // This below code is added to pass one of the VTS 4.1 tests. - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BOOL_TAG, KMType.DEVICE_UNIQUE_ATTESTATION, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.CANNOT_ATTEST_IDS); - } - // The key which is being attested should be asymmetric i.e. RSA or EC - tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.RSA && tmpVariables[0] != KMType.EC) { - KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); - } - boolean rsaCert = true; - if (tmpVariables[0] == KMType.EC) { - rsaCert = false; - } - KMAttestationCert cert = seProvider.getAttestationCert(rsaCert); - // Validate and add attestation ids. - addAttestationIds(cert); - // Save attestation application id - must be present. - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); - } - cert.extensionTag(tmpVariables[0], false); - // Save attestation challenge - tmpVariables[0] = - KMKeyParameters.findTag( - KMType.BYTES_TAG, KMType.ATTESTATION_CHALLENGE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - cert.attestationChallenge(KMByteTag.cast(tmpVariables[0]).getValue()); - // unique id byte blob - uses application id and temporal month count of creation time. - setUniqueId(cert, scratchPad); - - // validity period - // active time or creation time - byte blob - // current assumption is that if active and creation time are missing from characteristics - // then - // then it is an error. - tmpVariables[1] = - KMKeyParameters.findTag(KMType.DATE_TAG, KMType.ACTIVE_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[1] != KMType.INVALID_VALUE) { - tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - } else { - tmpVariables[1] = - KMKeyParameters.findTag(KMType.DATE_TAG, KMType.CREATION_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - tmpVariables[1] = KMIntegerTag.cast(tmpVariables[1]).getValue(); - } - // convert milliseconds to UTC date. Start of validity period has to be UTC. - cert.notBefore(tmpVariables[1], scratchPad); - // expiry time - byte blob - tmpVariables[2] = - KMKeyParameters.findTag(KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME, data[SW_PARAMETERS]); - cert.notAfter(tmpVariables[2], - getProvisionedCertificateData(KMSEProvider.CERTIFICATE_EXPIRY), - scratchPad, - (short) 0); - - addTags(KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(), true, cert); - addTags( - KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(), false, cert); - - cert.deviceLocked(repository.getBootLoaderLock()); - cert.issuer(getProvisionedCertificateData(KMSEProvider.CERTIFICATE_ISSUER)); - cert.publicKey(data[PUB_KEY]); - cert.verifiedBootHash(repository.getVerifiedBootHash()); - - cert.verifiedBootKey(repository.getVerifiedBootKey()); - cert.verifiedBootState(repository.getBootState()); - // buffer for cert - we allocate 2KBytes buffer - // make this buffer size configurable - tmpVariables[3] = KMByteBlob.instance(MAX_CERT_SIZE); - bufferRef[0] = KMByteBlob.cast(tmpVariables[3]).getBuffer(); - bufferProp[BUF_START_OFFSET] = KMByteBlob.cast(tmpVariables[3]).getStartOff(); - bufferProp[BUF_LEN_OFFSET] = KMByteBlob.cast(tmpVariables[3]).length(); - cert.buffer((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - cert.build(); - bufferProp[BUF_START_OFFSET] = - encoder.encodeCert((byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], cert.getCertStart(), cert.getCertLength(), - buildErrorStatus(KMError.OK)); - bufferProp[BUF_LEN_OFFSET] = (short) (cert.getCertLength() + (cert.getCertStart() - bufferProp[BUF_START_OFFSET])); - sendOutgoing(apdu); - } - - private boolean isEmpty(byte[] buf, short offset, short len) { - boolean empty = true; - short index = 0; - while (index < len) { - if (buf[(short) (index + offset)] != 0) { - empty = false; - break; - } - index++; - } - return empty; - } - - // -------------------------------- - // Only add the Attestation ids which are requested in the attestation parameters. - // If the requested attestation ids are not provisioned or deleted then - // throw CANNOT_ATTEST_IDS error. If there is mismatch in the attestation - // id values of both the requested parameters and the provisioned parameters - // then throw INVALID_TAG error. - private void addAttestationIds(KMAttestationCert cert) { - 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; - short storedAttId; - while (index < (short) attTags.length) { - attIdTag = KMKeyParameters.findTag(KMType.BYTES_TAG, attTags[index], data[KEY_PARAMETERS]); - if (attIdTag != KMType.INVALID_VALUE) { - attIdTagValue = KMByteTag.cast(attIdTag).getValue(); - storedAttId = repository.getAttId(mapToAttId(attTags[index])); - // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or - // Attestation IDs are deleted. - if (storedAttId == KMType.INVALID_VALUE || - isEmpty(KMByteBlob.cast(storedAttId).getBuffer(), - KMByteBlob.cast(storedAttId).getStartOff(), - KMByteBlob.cast(storedAttId).length())) { - KMException.throwIt(KMError.CANNOT_ATTEST_IDS); - } - // Return INVALID_TAG if Attestation IDs does not match. - if ((KMByteBlob.cast(storedAttId).length() != KMByteBlob.cast(attIdTagValue).length()) || - (0 != Util.arrayCompare(KMByteBlob.cast(storedAttId).getBuffer(), - KMByteBlob.cast(storedAttId).getStartOff(), - KMByteBlob.cast(attIdTagValue).getBuffer(), - KMByteBlob.cast(attIdTagValue).getStartOff(), - KMByteBlob.cast(storedAttId).length()))) { - KMException.throwIt(KMError.INVALID_TAG); - } - cert.extensionTag(attIdTag, true); - } - index++; - } - } - - private void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { - short index = 0; - short arr = KMKeyParameters.cast(params).getVals(); - short len = KMArray.cast(arr).length(); - short tag; - while (index < len) { - tag = KMArray.cast(arr).get(index); - cert.extensionTag(tag, hwEnforced); - index++; - } - } - - private void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { - tmpVariables[0] = KMKeyParameters.findTag(KMType.BOOL_TAG, - KMType.INCLUDE_UNIQUE_ID, data[HW_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - return; - } - - // temporal count T - tmpVariables[0] = KMKeyParameters.findTag(KMType.DATE_TAG, - KMType.CREATION_DATETIME, data[SW_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_TAG); - } - tmpVariables[0] = KMIntegerTag.cast(tmpVariables[0]).getValue(); - - // Application Id C - tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, - KMType.ATTESTATION_APPLICATION_ID, data[KEY_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); - } - tmpVariables[1] = KMByteTag.cast(tmpVariables[1]).getValue(); - - // Reset After Rotation R - it will be part of HW Enforced key - // characteristics - byte resetAfterRotation = 0; - tmpVariables[2] = KMKeyParameters.findTag(KMType.BOOL_TAG, - KMType.RESET_SINCE_ID_ROTATION, data[HW_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - resetAfterRotation = 0x01; - } - - cert.makeUniqueId( - scratchPad, - (short) 0, - KMInteger.cast(tmpVariables[0]).getBuffer(), - KMInteger.cast(tmpVariables[0]).getStartOff(), - KMInteger.cast(tmpVariables[0]).length(), - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).length(), resetAfterRotation, - seProvider.getMasterKey()); - } - - private void processDestroyAttIdsCmd(APDU apdu) { - repository.deleteAttIds(); - sendError(apdu, KMError.OK); - } - - private void processVerifyAuthorizationCmd(APDU apdu) { - sendError(apdu, KMError.UNIMPLEMENTED); - } - - private void processAbortOperationCmd(APDU apdu) { - receiveIncoming(apdu); - tmpVariables[1] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - KMOperationState op = repository.findOperation(data[OP_HANDLE]); - if (op == null) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - repository.releaseOperation(op); - sendError(apdu, KMError.OK); - } - - private void processFinishOperationCmd(APDU apdu) { - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 6); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 3, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 5, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[SIGNATURE] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 5); - // Check Operation Handle - KMOperationState op = repository.findOperation(data[OP_HANDLE]); - if (op == null) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - // Authorize the finish operation - authorizeUpdateFinishOperation(op, scratchPad); - switch (op.getPurpose()) { - case KMType.SIGN: - finishTrustedConfirmationOperation(op); - case KMType.VERIFY: - finishSigningVerifyingOperation(op, scratchPad); - break; - case KMType.ENCRYPT: - finishEncryptOperation(op); - break; - case KMType.DECRYPT: - finishDecryptOperation(op, scratchPad); - break; - } - // Remove the operation handle - repository.releaseOperation(op); - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 3); - if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { - data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); - } - KMArray.cast(tmpVariables[2]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, tmpVariables[1]); - KMArray.cast(tmpVariables[2]).add((short) 2, data[OUTPUT_DATA]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[2], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void finishEncryptOperation(KMOperationState op) { - if(op.getAlgorithm() != KMType.AES && op.getAlgorithm() != KMType.DES){ - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - finishAesDesOperation(op); - } - - private void finishAesDesOperation(KMOperationState op) { - short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - short blockSize = DES_BLOCK_SIZE; - if (op.getAlgorithm() == KMType.AES) { - blockSize = AES_BLOCK_SIZE; - } - - if((op.getPurpose() == KMType.DECRYPT) && (op.getPadding() == KMType.PKCS7) - && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) - && ((short) (len % blockSize) != 0)){ - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - - if (op.getBlockMode() == KMType.GCM) { - if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // update aad if there is any - updateAAD(op, (byte) 0x01); - // Get the output size - len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); - } - // If padding i.e. pkcs7 then add padding to right - // Output data can at most one block size more the input data in case of pkcs7 - // encryption - data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize)); - try { - len = op.getOperation().finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); - } catch (CryptoException e) { - if (e.getReason() == CryptoException.ILLEGAL_USE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } - - // Update the length of the output - KMByteBlob.cast(data[OUTPUT_DATA]).setLength(len); - } - - private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { - short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - switch (op.getAlgorithm()) { - case KMType.RSA: - // Fill the scratch pad with zero - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - if (op.getPadding() == KMType.PADDING_NONE && len != 256) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - len = - op.getOperation() - .finish( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - len, - scratchPad, - (short) 0); - - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len); - break; - case KMType.AES: - case KMType.DES: - finishAesDesOperation(op); - break; - } - } - - // update operation should send 0x00 for finish variable, where as finish operation - // should send 0x01 for finish variable. - private void updateAAD(KMOperationState op, byte finish) { - // Is input data absent - if (data[INPUT_DATA] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Update can be called either to update auth data, update input data or both. - // But if it is called for neither then return error. - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - tmpVariables[1] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, data[KEY_PARAMETERS]); - // For Finish operation the input data can be zero length and associated data can be - // INVALID_VALUE - // For update operation either input data or associated data should be present. - if (tmpVariables[1] == KMType.INVALID_VALUE && tmpVariables[0] <= 0 && finish == 0x00) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - // Check if associated data is present and update aad still allowed by the operation. - if (tmpVariables[1] != KMType.INVALID_VALUE) { - if (!op.isAesGcmUpdateAllowed()) { - KMException.throwIt(KMError.INVALID_TAG); - } - // If allowed the update the aad - tmpVariables[1] = KMByteTag.cast(tmpVariables[1]).getValue(); - - op.getOperation() - .updateAAD( - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).length()); - } - } - - private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratchPad) { - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - switch (op.getAlgorithm()) { - case KMType.RSA: - // If there is no padding we can treat signing as a RSA decryption operation. - try { - if (op.getPurpose() == KMType.SIGN) { - // len of signature will be 256 bytes - short len = op.getOperation().sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), scratchPad, - (short) 0); - // Maximum output size of signature is 256 bytes. - data[OUTPUT_DATA] = KMByteBlob.instance((short) 256); - Util.arrayCopyNonAtomic( - scratchPad, - (short) 0, - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - (short) (KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff() + 256 - len), - len); - } else { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - } catch (CryptoException e) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - break; - case KMType.EC: - short len = KMByteBlob.cast(data[INPUT_DATA]).length(); - // If DIGEST NONE then truncate the input data to 32 bytes. - if (op.getDigest() == KMType.DIGEST_NONE && len > 32) { - len = 32; - } - if (op.getPurpose() == KMType.SIGN) { - // len of signature will be 512 bits i.e. 64 bytes - len = - op.getOperation() - .sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - len, - scratchPad, - (short) 0); - data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len); - } else { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - break; - case KMType.HMAC: - // As per Keymaster HAL documentation, the length of the Hmac output can - // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no - // such provision to control the length of the Hmac output using JavaCard - // crypto APIs and the current implementation always returns 32 bytes - // length of Hmac output. So to provide support to TAG_MAC_LENGTH - // feature, we truncate the output signature to TAG_MAC_LENGTH and return - // the truncated signature back to the caller. At the time of verfication - // we again compute the signature of the plain text input, truncate it to - // TAG_MAC_LENGTH and compare it with the input signature for - // verification. So this is the reason we are using KMType.SIGN directly - // instead of using op.getPurpose(). - op.getOperation() - .sign( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - scratchPad, - (short) 0); - - // Copy only signature of mac length size. - data[OUTPUT_DATA] = - KMByteBlob.instance(scratchPad, (short) 0, (short) (op.getMacLength() / 8)); - if (op.getPurpose() == KMType.VERIFY) { - if (0 - != Util.arrayCompare( - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[SIGNATURE]).getBuffer(), - KMByteBlob.cast(data[SIGNATURE]).getStartOff(), - (short) (op.getMacLength() / 8))) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - } - break; - default: // This is should never happen - KMException.throwIt(KMError.OPERATION_CANCELLED); - break; - } - } - - private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) { - // If one time user Authentication is required - if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { - // Validate Verification Token. - validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad); - // validate operation handle. - short ptr = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getChallenge(); - if (KMInteger.compare(ptr, op.getHandle()) != 0) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - tmpVariables[0] = op.getAuthTime(); - tmpVariables[2] = KMVerificationToken.cast(data[VERIFICATION_TOKEN]).getTimestamp(); - if (tmpVariables[2] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - op.setAuthTimeoutValidated(true); - } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required - tmpVariables[0] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getChallenge(); - if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - } - } - - private void authorizeKeyUsageForCount(byte[] scratchPad) { - short scratchPadOff = 0; - Util.arrayFillNonAtomic(scratchPad, scratchPadOff, (short) 12, (byte) 0); - - short usageLimitBufLen = KMIntegerTag.getValue(scratchPad, scratchPadOff, - KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); - - if (usageLimitBufLen == KMType.INVALID_VALUE) { - return; - } - - if (usageLimitBufLen > 4) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - if (repository.isAuthTagPersisted(data[AUTH_TAG])) { - // Get current counter, update and increment it. - short len = repository - .getRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + 4)); - if (len != 4) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - if (0 >= KMInteger.unsignedByteArrayCompare(scratchPad, scratchPadOff, scratchPad, - (short) (scratchPadOff + 4), (short) 4)) { - KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); - } - // Increment the counter. - Util.arrayFillNonAtomic(scratchPad, scratchPadOff, len, (byte) 0); - Util.setShort(scratchPad, (short) (scratchPadOff + 2), (short) 1); - KMUtils.add(scratchPad, scratchPadOff, (short) (scratchPadOff + len), - (short) (scratchPadOff + len * 2)); - - repository - .setRateLimitedKeyCount(data[AUTH_TAG], scratchPad, (short) (scratchPadOff + len * 2), - len); - - } else { - // Persist auth tag. - if (!repository.persistAuthTag(data[AUTH_TAG])) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - } - } - - - private void authorizeDeviceUnlock(byte[] scratchPad) { - // If device is locked and key characteristics requires unlocked device then check whether - // HW auth token has correct timestamp. - short ptr = - KMKeyParameters.findTag( - KMType.BOOL_TAG, KMType.UNLOCKED_DEVICE_REQUIRED, data[HW_PARAMETERS]); - - if (ptr != KMType.INVALID_VALUE && repository.getDeviceLock()) { - if (!validateHwToken(data[HW_TOKEN], scratchPad)) { - KMException.throwIt(KMError.DEVICE_LOCKED); - } - ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(); - // Check if the current auth time stamp is greater then device locked time stamp - short ts = repository.getDeviceTimeStamp(); - if (KMInteger.compare(ptr, ts) <= 0) { - KMException.throwIt(KMError.DEVICE_LOCKED); - } - // Now check if the device unlock requires password only authentication and whether - // auth token is generated through password authentication or not. - if (repository.getDeviceLockPasswordOnly()) { - ptr = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); - ptr = KMEnum.cast(ptr).getVal(); - if (((byte) ptr & KMType.PASSWORD) == 0) { - KMException.throwIt(KMError.DEVICE_LOCKED); - } - } - // Unlock the device - // repository.deviceLockedFlag = false; - repository.setDeviceLock(false); - repository.clearDeviceLockTimeStamp(); - } - } - - private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) { - // concatenation length will be 37 + length of verified parameters list - which - // is typically - // empty - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - // Add "Auth Verification" - 17 bytes. - Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); - short len = (short) authVerification.length; - // concatenate challenge - 8 bytes - short ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate timestamp -8 bytes - ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr).value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate security level - 4 bytes - ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate Parameters verified - blob of encoded data. - ptr = KMVerificationToken.cast(verToken).getParametersVerified(); - if (KMByteBlob.cast(ptr).length() != 0) { - len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); - } - // hmac the data - ptr = KMVerificationToken.cast(verToken).getMac(); - - return seProvider.hmacVerify( - seProvider.getComputedHmacKey(), - scratchPad, - (short) 0, - len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - } - - private boolean verifyVerificationTokenMacInLittleEndian(short verToken, byte[] scratchPad) { - // concatenation length will be 37 + length of verified parameters list - which - // is typically - // empty - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - // Add "Auth Verification" - 17 bytes. - Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, (short) authVerification.length); - short len = (short) authVerification.length; - // concatenate challenge - 8 bytes - short ptr = KMVerificationToken.cast(verToken).getChallenge(); - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; - // concatenate timestamp -8 bytes - ptr = KMVerificationToken.cast(verToken).getTimestamp(); - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; - // concatenate security level - 4 bytes - ptr = KMVerificationToken.cast(verToken).getSecurityLevel(); - scratchPad[len] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate Parameters verified - blob of encoded data. - ptr = KMVerificationToken.cast(verToken).getParametersVerified(); - if (KMByteBlob.cast(ptr).length() != 0) { - len += KMByteBlob.cast(ptr).getValues(scratchPad, (short) 0); - } - // hmac the data - ptr = KMVerificationToken.cast(verToken).getMac(); - - return seProvider.hmacVerify( - seProvider.getComputedHmacKey(), - scratchPad, - (short) 0, - len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - } - - private void validateVerificationToken(short verToken, byte[] scratchPad) { - short ptr = KMVerificationToken.cast(verToken).getMac(); - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - boolean verify; - if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { - verify = verifyVerificationTokenMacInLittleEndian(verToken, scratchPad); - } else { - verify = verifyVerificationTokenMacInBigEndian(verToken, scratchPad); - } - if (!verify) { - // Throw Exception if none of the combination works. - KMException.throwIt(KMError.VERIFICATION_FAILED); - } - } - - private void processUpdateOperationCmd(APDU apdu) { - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 5); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMInteger.exp()); - KMArray.cast(tmpVariables[1]).add((short) 1, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - tmpVariables[4] = KMVerificationToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 4, tmpVariables[4]); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[OP_HANDLE] = KMArray.cast(tmpVariables[2]).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[INPUT_DATA] = KMArray.cast(tmpVariables[2]).get((short) 2); - data[HW_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 3); - data[VERIFICATION_TOKEN] = KMArray.cast(tmpVariables[2]).get((short) 4); - // Input data must be present even if it is zero length. - if (data[INPUT_DATA] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Check Operation Handle and get op state - // Check Operation Handle - KMOperationState op = repository.findOperation(data[OP_HANDLE]); - if (op == null) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - // authorize the update operation - authorizeUpdateFinishOperation(op, scratchPad); - short inputConsumed = 0; - // If signing without digest then do length validation checks - if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { - tmpVariables[0] = KMByteBlob.cast(data[INPUT_DATA]).length(); - // update the data. - op.getOperation() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); - // update trusted confirmation operation - updateTrustedConfirmationOperation(op); - - data[OUTPUT_DATA] = KMType.INVALID_VALUE; - } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { - // Update for encrypt/decrypt using RSA will not be supported because to do this op state - // will have to buffer the data - so reject the update if it is rsa algorithm. - if (op.getAlgorithm() == KMType.RSA) { - KMException.throwIt(KMError.OPERATION_CANCELLED); - } - short inputLen = KMByteBlob.cast(data[INPUT_DATA]).length(); - short blockSize = DES_BLOCK_SIZE; - if (op.getAlgorithm() == KMType.AES) { - blockSize = AES_BLOCK_SIZE; - if (op.getBlockMode() == KMType.GCM) { - updateAAD(op, (byte) 0x00); - // if input data present - if (inputLen > 0) { - // no more future updateAAD allowed if input data present. - if (op.isAesGcmUpdateAllowed()) { - op.setAesGcmUpdateComplete(); - } - } - } - } - // Allocate output buffer as input data is already block aligned - data[OUTPUT_DATA] = KMByteBlob.instance((short) (inputLen + 2 * blockSize)); - // Otherwise just update the data. - // HAL consumes all the input and maintains a buffered data inside it. So the - // applet sends the inputConsumed length as same as the input length. - inputConsumed = inputLen; - try { - tmpVariables[0] = - op.getOperation() - .update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(data[OUTPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[OUTPUT_DATA]).getStartOff()); - } catch (CryptoException e) { - KMException.throwIt(KMError.INVALID_TAG); - } - // Adjust the Output data if it is not equal to input data. - // This happens in case of JCardSim provider. - KMByteBlob.cast(data[OUTPUT_DATA]).setLength(tmpVariables[0]); - } - // Persist if there are any updates. - op.persist(); - // make response - tmpVariables[1] = KMArray.instance((short) 0); - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[1]); - tmpVariables[2] = KMArray.instance((short) 4); - if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { - data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); - } - KMArray.cast(tmpVariables[2]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[2]).add((short) 1, KMInteger.uint_16(inputConsumed)); - KMArray.cast(tmpVariables[2]).add((short) 2, tmpVariables[1]); - KMArray.cast(tmpVariables[2]).add((short) 3, data[OUTPUT_DATA]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[2], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void processBeginOperationCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - short args; - tmpVariables[1] = KMArray.instance((short) 4); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, KMEnum.instance(KMType.PURPOSE)); - KMArray.cast(tmpVariables[1]).add((short) 1, KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add((short) 2, tmpVariables[2]); - tmpVariables[3] = KMHardwareAuthToken.exp(); - KMArray.cast(tmpVariables[1]).add((short) 3, tmpVariables[3]); - // Decode the arguments - args = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 2); - data[KEY_BLOB] = KMArray.cast(args).get((short) 1); - // Check for app id and app data. - data[APP_ID] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); - data[APP_DATA] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); - if (data[APP_ID] != KMTag.INVALID_VALUE) { - data[APP_ID] = KMByteTag.cast(data[APP_ID]).getValue(); - } - if (data[APP_DATA] != KMTag.INVALID_VALUE) { - data[APP_DATA] = KMByteTag.cast(data[APP_DATA]).getValue(); - } - // Parse the encrypted blob and decrypt it. - parseEncryptedKeyBlob(scratchPad); - // Authorize the begin operation and reserve op - data[OP_HANDLE] will have the handle. - // It will also set data[IV] field if required. - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[0] = KMEnum.cast(tmpVariables[0]).getVal(); - data[HW_TOKEN] = KMArray.cast(args).get((short) 3); - /*Generate a random number for operation handle */ - short buf = KMByteBlob.instance(KMRepository.OPERATION_HANDLE_SIZE); - generateUniqueOperationHandle( - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - /* opHandle is a KMInteger and is encoded as KMInteger when it is returned back. */ - short opHandle = KMInteger.instance( - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - KMOperationState op = repository.reserveOperation(opHandle); - if (op == null) { - KMException.throwIt(KMError.TOO_MANY_OPERATIONS); - } - data[OP_HANDLE] = op.getHandle(); - op.setPurpose((byte) tmpVariables[0]); - op.setKeySize(KMByteBlob.cast(data[SECRET]).length()); - authorizeAndBeginOperation(op, scratchPad); - switch (op.getPurpose()) { - case KMType.SIGN: - beginTrustedConfirmationOperation(op); - case KMType.VERIFY: - beginSignVerifyOperation(op); - break; - case KMType.ENCRYPT: - case KMType.DECRYPT: - beginCipherOperation(op); - break; - default: - KMException.throwIt(KMError.UNIMPLEMENTED); - break; - } - // If the data[IV] is required to be returned. - // As per VTS, for the decryption operation don't send the iv back. - if (data[IV] != KMType.INVALID_VALUE - && op.getPurpose() != KMType.DECRYPT - && op.getBlockMode() != KMType.ECB) { - tmpVariables[2] = KMArray.instance((short) 1); - if (op.getAlgorithm() == KMType.DES && op.getBlockMode() == KMType.CBC) { - // For AES/DES we are generate an random iv of length 16 bytes. - // While sending the iv back for DES/CBC mode of opeation only send - // 8 bytes back. - tmpVariables[1] = KMByteBlob.instance((short) 8); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), - (short) 8); - data[IV] = tmpVariables[1]; - } - KMArray.cast(tmpVariables[2]).add((short) 0, KMByteTag.instance(KMType.NONCE, data[IV])); - } else { - tmpVariables[2] = KMArray.instance((short) 0); - } - tmpVariables[1] = KMKeyParameters.instance(tmpVariables[2]); - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, tmpVariables[1]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[OP_HANDLE]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void authorizeAlgorithm(KMOperationState op) { - short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); - if (alg == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - op.setAlgorithm((byte) alg); - } - - private void authorizePurpose(KMOperationState op) { - switch (op.getAlgorithm()) { - case KMType.AES: - case KMType.DES: - if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - break; - case KMType.EC: - case KMType.HMAC: - if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - break; - default: - break; - } - if (!KMEnumArrayTag.contains(KMType.PURPOSE, op.getPurpose(), data[HW_PARAMETERS])) { - KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); - } - } - - private void authorizeDigest(KMOperationState op) { - short digests = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[HW_PARAMETERS]); - op.setDigest(KMType.DIGEST_NONE); - short param = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); - if (param != KMType.INVALID_VALUE) { - if (KMEnumArrayTag.cast(param).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - param = KMEnumArrayTag.cast(param).get((short) 0); - if (!KMEnumArrayTag.cast(digests).contains(param)) { - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - op.setDigest((byte) param); - } - short paramPadding = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); - if (paramPadding != KMType.INVALID_VALUE) { - if (KMEnumArrayTag.cast(paramPadding).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - paramPadding = KMEnumArrayTag.cast(paramPadding).get((short) 0); - } - switch (op.getAlgorithm()) { - case KMType.RSA: - if ((paramPadding == KMType.RSA_OAEP || paramPadding == KMType.RSA_PSS) - && param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - break; - case KMType.EC: - case KMType.HMAC: - if (param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - break; - default: - break; - } - } - - private void authorizePadding(KMOperationState op) { - short paddings = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[HW_PARAMETERS]); - op.setPadding(KMType.PADDING_NONE); - short param = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PADDING, data[KEY_PARAMETERS]); - if (param != KMType.INVALID_VALUE) { - if (KMEnumArrayTag.cast(param).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - param = KMEnumArrayTag.cast(param).get((short) 0); - if (!KMEnumArrayTag.cast(paddings).contains(param)) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - } - switch (op.getAlgorithm()) { - case KMType.RSA: - if (param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); - } - if ((op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) - && param != KMType.PADDING_NONE - && param != KMType.RSA_PSS - && param != KMType.RSA_PKCS1_1_5_SIGN) { - KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); - } - if ((op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) - && param != KMType.PADDING_NONE - && param != KMType.RSA_OAEP - && param != KMType.RSA_PKCS1_1_5_ENCRYPT) { - KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); - } - if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) { - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - if ((param == KMType.RSA_OAEP || param == KMType.RSA_PSS) - && op.getDigest() == KMType.DIGEST_NONE) { - KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); - } - op.setPadding((byte) param); - break; - case KMType.DES: - case KMType.AES: - if (param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); - } - op.setPadding((byte) param); - break; - default: - break; - } - } - - private void authorizeBlockModeAndMacLength(KMOperationState op) { - short param = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); - if (param != KMType.INVALID_VALUE) { - if (KMEnumArrayTag.cast(param).length() != 1) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - param = KMEnumArrayTag.cast(param).get((short) 0); - } - if (KMType.AES == op.getAlgorithm() || KMType.DES == op.getAlgorithm()) { - if (!KMEnumArrayTag.contains(KMType.BLOCK_MODE, param, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE); - } - } - short macLen = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); - switch (op.getAlgorithm()) { - case KMType.AES: - //Validate the block mode. - switch (param) { - case KMType.ECB: - case KMType.CBC: - case KMType.CTR: - case KMType.GCM: - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - if (param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (param == KMType.GCM) { - if (op.getPadding() != KMType.PADDING_NONE) { - KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); - } - if (macLen == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - if (macLen % 8 != 0 - || macLen > 128) { - KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); - } - if(macLen - < KMIntegerTag.getShortValue( - KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - op.setMacLength(macLen); - } - break; - case KMType.DES: - //Validate the block mode. - switch (param) { - case KMType.ECB: - case KMType.CBC: - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); - } - if (param == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - break; - case KMType.HMAC: - if (macLen == KMType.INVALID_VALUE) { - if (op.getPurpose() == KMType.SIGN) { - KMException.throwIt(KMError.MISSING_MAC_LENGTH); - } - } else { - // MAC length may not be specified for verify. - if (op.getPurpose() == KMType.VERIFY) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (macLen - < KMIntegerTag.getShortValue( - KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { - KMException.throwIt(KMError.INVALID_MAC_LENGTH); - } - if (macLen % 8 != 0 - || macLen > 256) { - KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); - } - op.setMacLength(macLen); - } - break; - default: - break; - } - op.setBlockMode((byte) param); - } - - private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) { - authorizeAlgorithm(op); - authorizePurpose(op); - authorizeDigest(op); - authorizePadding(op); - authorizeBlockModeAndMacLength(op); - authorizeUserSecureIdAuthTimeout(op, scratchPad); - authorizeDeviceUnlock(scratchPad); - authorizeKeyUsageForCount(scratchPad); - - //Validate bootloader only - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getBootEndedStatus()) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - - //Validate early boot - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getEarlyBootEndedStatus()) { - KMException.throwIt(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; - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.CALLER_NONCE, data[HW_PARAMETERS]); - tmpVariables[1] = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.NONCE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - if (tmpVariables[1] != KMType.INVALID_VALUE && op.getPurpose() != KMType.DECRYPT) { - KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); - } - } - // If Nonce is present then check whether the size of nonce is correct. - if (tmpVariables[1] != KMType.INVALID_VALUE) { - data[IV] = KMByteTag.cast(tmpVariables[1]).getValue(); - // For CBC mode - iv must be 8 bytes - if (op.getBlockMode() == KMType.CBC - && op.getAlgorithm() == KMType.DES - && KMByteBlob.cast(data[IV]).length() != 8) { - KMException.throwIt(KMError.INVALID_NONCE); - } - // For GCM mode - IV must be 12 bytes - if (KMByteBlob.cast(data[IV]).length() != 12 && op.getBlockMode() == KMType.GCM) { - KMException.throwIt(KMError.INVALID_NONCE); - } - // For AES CBC and CTR modes IV must be 16 bytes - if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR) - && op.getAlgorithm() == KMType.AES - && KMByteBlob.cast(data[IV]).length() != 16) { - KMException.throwIt(KMError.INVALID_NONCE); - } - } else if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) { - // For symmetric decryption iv is required - if (op.getPurpose() == KMType.DECRYPT - && (op.getBlockMode() == KMType.CBC - || op.getBlockMode() == KMType.GCM - || op.getBlockMode() == KMType.CTR)) { - KMException.throwIt(KMError.MISSING_NONCE); - } else if (op.getBlockMode() == KMType.ECB) { - // For ECB we create zero length nonce - data[IV] = KMByteBlob.instance((short) 0); - } else if (op.getPurpose() == KMType.ENCRYPT) { - // For encrypt mode if nonce is absent then create random nonce of correct length - byte ivLen = 16; - if (op.getBlockMode() == KMType.GCM) { - ivLen = 12; - } else if (op.getAlgorithm() == KMType.DES) { - ivLen = 8; - } - data[IV] = KMByteBlob.instance(ivLen); - seProvider.newRandomNumber( - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length()); - } - } - } - - private void beginCipherOperation(KMOperationState op) { - switch (op.getAlgorithm()) { - case KMType.RSA: - try { - if (op.getPurpose() == KMType.DECRYPT) { - op.setOperation( - seProvider.initAsymmetricOperation( - (byte) op.getPurpose(), - op.getAlgorithm(), - op.getPadding(), - op.getDigest(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); - } else { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - } catch (CryptoException exp) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - break; - case KMType.AES: - case KMType.DES: - if (op.getBlockMode() == KMType.GCM) { - op.setAesGcmUpdateStart(); - } - try { - op.setOperation( - seProvider.initSymmetricOperation( - (byte) op.getPurpose(), - op.getAlgorithm(), - op.getDigest(), - op.getPadding(), - op.getBlockMode(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[IV]).getBuffer(), - KMByteBlob.cast(data[IV]).getStartOff(), - KMByteBlob.cast(data[IV]).length(), - op.getMacLength())); - } catch (CryptoException exception) { - if (exception.getReason() == CryptoException.ILLEGAL_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } else if (exception.getReason() == CryptoException.NO_SUCH_ALGORITHM) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - } - } - } - - private void beginTrustedConfirmationOperation(KMOperationState op) { - // Check for trusted confirmation - if required then set the signer in op state. - if (KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.TRUSTED_CONFIRMATION_REQUIRED, - data[HW_PARAMETERS]) != KMType.INVALID_VALUE) { - - op.setTrustedConfirmationSigner( - seProvider.initTrustedConfirmationSymmetricOperation(seProvider.getComputedHmacKey())); - - op.getTrustedConfirmationSigner().update( - confirmationToken, - (short) 0, - (short) confirmationToken.length); - } - } - - private void beginSignVerifyOperation(KMOperationState op) { - switch (op.getAlgorithm()) { - case KMType.RSA: - try { - if (op.getPurpose() == KMType.SIGN) { - op.setOperation( - seProvider.initAsymmetricOperation( - (byte) op.getPurpose(), - op.getAlgorithm(), - op.getPadding(), - op.getDigest(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length())); - } else { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - } catch (CryptoException exp) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - break; - case KMType.EC: - try { - if (op.getPurpose() == KMType.SIGN) { - op.setOperation( - seProvider.initAsymmetricOperation( - (byte) op.getPurpose(), - op.getAlgorithm(), - op.getPadding(), - op.getDigest(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - null, - (short) 0, - (short) 0)); - } else { - KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); - } - } catch (CryptoException exp) { - // Javacard does not support NO digest based signing. - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - break; - case KMType.HMAC: - // As per Keymaster HAL documentation, the length of the Hmac output can - // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no - // such provision to control the length of the Hmac output using JavaCard - // crypto APIs and the current implementation always returns 32 bytes - // length of Hmac output. So to provide support to TAG_MAC_LENGTH - // feature, we truncate the output signature to TAG_MAC_LENGTH and return - // the truncated signature back to the caller. At the time of verfication - // we again compute the signature of the plain text input, truncate it to - // TAG_MAC_LENGTH and compare it with the input signature for - // verification. So this is the reason we are using KMType.SIGN directly - // instead of using op.getPurpose(). - try { - op.setOperation( - seProvider.initSymmetricOperation( - (byte) KMType.SIGN, - op.getAlgorithm(), - op.getDigest(), - op.getPadding(), - op.getBlockMode(), - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - null, - (short) 0, - (short) 0, - (short) 0)); - } catch (CryptoException exp) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - break; - } - } - - private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) { - short authTime; - short authType; - // Authorize User Secure Id and Auth timeout - short userSecureIdPtr = - KMKeyParameters.findTag(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, data[HW_PARAMETERS]); - if (userSecureIdPtr != KMType.INVALID_VALUE) { - // Authentication required. - if (KMType.INVALID_VALUE != - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED, data[HW_PARAMETERS])) { - // Key has both USER_SECURE_ID and NO_AUTH_REQUIRED - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - // store authenticator type - if(KMType.INVALID_VALUE == - (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) { - // Authentication required, but no auth type found. - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - - short authTimeoutTagPtr = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.AUTH_TIMEOUT, data[HW_PARAMETERS]); - if (authTimeoutTagPtr != KMType.INVALID_VALUE) { - // authenticate user - if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) { - KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); - } - - authTimeoutTagPtr = - KMKeyParameters.findTag(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, data[HW_PARAMETERS]); - if (authTimeoutTagPtr == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - authTime = KMIntegerTag.cast(authTimeoutTagPtr).getValue(); - // set the one time auth - op.setOneTimeAuthReqd(true); - // set the authentication time stamp in operation state - authTime = - addIntegers(authTime, - KMHardwareAuthToken.cast(data[HW_TOKEN]).getTimestamp(), scratchPad); - op.setAuthTime( - KMInteger.cast(authTime).getBuffer(), KMInteger.cast(authTime).getStartOff()); - // auth time validation will happen in update or finish - op.setAuthTimeoutValidated(false); - } else { - // auth per operation required - // store user secure id and authType in OperationState. - op.setUserSecureId(userSecureIdPtr); - op.setAuthType((byte) authType); - // set flags - op.setOneTimeAuthReqd(false); - op.setAuthPerOperationReqd(true); - } - } - } - - private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, - short secureUserIdsObj) { - short secureUserId = KMHardwareAuthToken.cast(hwAuthToken).getUserId(); - if (!KMInteger.cast(secureUserId).isZero()) { - if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(secureUserId)) - return true; - } - - short authenticatorId = KMHardwareAuthToken.cast(hwAuthToken).getAuthenticatorId(); - if (!KMInteger.cast(authenticatorId).isZero()) { - if (KMIntegerArrayTag.cast(secureUserIdsObj).contains(authenticatorId)) - return true; - } - return false; - } - - private boolean authTokenMatches(short userSecureIdsPtr, short authType, - byte[] scratchPad) { - if (!validateHwToken(data[HW_TOKEN], scratchPad)) { - return false; - } - if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) { - return false; - } - // check auth type - tmpVariables[2] = KMHardwareAuthToken.cast(data[HW_TOKEN]).getHwAuthenticatorType(); - tmpVariables[2] = KMEnum.cast(tmpVariables[2]).getVal(); - if (((byte) tmpVariables[2] & (byte) authType) == 0) { - return false; - } - return true; - } - - private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { - // The challenge, userId and authenticatorId, authenticatorType and timestamp - // are in network order (big-endian). - short len = 0; - // add 0 - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - len = 1; - // concatenate challenge - 8 bytes - short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate user id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - // concatenate authenticator type - 4 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate timestamp -8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - - ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - - return seProvider.hmacVerify( - seProvider.getComputedHmacKey(), - scratchPad, - (short) 0, - len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - - } - - private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) { - // The challenge, userId and authenticatorId values are in little endian order, - // but authenticatorType and timestamp are in network order (big-endian). - short len = 0; - // add 0 - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - len = 1; - // concatenate challenge - 8 bytes - short ptr = KMHardwareAuthToken.cast(hwToken).getChallenge(); - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; - // concatenate user id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getUserId(); - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; - // concatenate authenticator id - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getAuthenticatorId(); - KMInteger.cast(ptr).toLittleEndian(scratchPad, len); - len += 8; - // concatenate authenticator type - 4 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getHwAuthenticatorType(); - scratchPad[(short) (len + 3)] = KMEnum.cast(ptr).getVal(); - len += 4; - // concatenate timestamp - 8 bytes - ptr = KMHardwareAuthToken.cast(hwToken).getTimestamp(); - KMInteger.cast(ptr) - .value(scratchPad, (short) (len + (short) (8 - KMInteger.cast(ptr).length()))); - len += 8; - - ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - - return seProvider.hmacVerify( - seProvider.getComputedHmacKey(), - scratchPad, - (short) 0, - len, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()); - } - - private boolean validateHwToken(short hwToken, byte[] scratchPad) { - // CBOR Encoding is always big endian - short ptr = KMHardwareAuthToken.cast(hwToken).getMac(); - // If mac length is zero then token is empty. - if (KMByteBlob.cast(ptr).length() == 0) { - return false; - } - if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { - return verifyHwTokenMacInLittleEndian(hwToken, scratchPad); - } else { - return verifyHwTokenMacInBigEndian(hwToken, scratchPad); - } - } - - private void processImportKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - tmpVariables[1] = KMArray.instance((short) 3); - // Arguments - tmpVariables[2] = KMKeyParameters.exp(); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); - KMArray.cast(tmpVariables[1]).add((short) 2, KMByteBlob.exp()); - // Decode the arguments - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); - tmpVariables[3] = KMArray.cast(tmpVariables[2]).get((short) 1); - data[IMPORTED_KEY_BLOB] = KMArray.cast(tmpVariables[2]).get((short) 2); - - byte keyFormat = KMEnum.cast(tmpVariables[3]).getVal(); - - short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && keyFormat != KMType.RAW ) { - KMException.throwIt(KMError.UNIMPLEMENTED); - } - if((alg == KMType.RSA || alg == KMType.EC) && keyFormat != KMType.PKCS8){ - KMException.throwIt(KMError.UNIMPLEMENTED); - } - - data[ORIGIN] = KMType.IMPORTED; - importKey(apdu, scratchPad, keyFormat); - } - - private void importKey(APDU apdu, byte[] scratchPad, byte keyFormat) { - - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getEarlyBootEndedStatus()) { - KMException.throwIt(KMError.EARLY_BOOT_ENDED); - } - - // Rollback protection not supported - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - } - - // get algorithm - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - //Check if the tags are supported. - if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_TAG); - } - // Check algorithm and dispatch to appropriate handler. - switch (tmpVariables[3]) { - case KMType.RSA: - importRSAKey(scratchPad); - break; - case KMType.AES: - importAESKey(scratchPad); - break; - case KMType.DES: - importTDESKey(scratchPad); - break; - case KMType.HMAC: - importHmacKey(scratchPad); - break; - case KMType.EC: - importECKeys(scratchPad, keyFormat); - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - break; - } - // create key blob - createEncryptedKeyBlob(scratchPad); - - // prepare the response - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - sendOutgoing(apdu); - } - - private void decodeRawECKey() { - // Decode key material - tmpVariables[0] = KMArray.instance((short) 2); - KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret - KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key - tmpVariables[0] = - decoder.decode( - tmpVariables[0], - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); - data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); - data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); - } - - private void decodePKCS8ECKeys() { - // Decode key material - short keyBlob = seProvider.getPKCS8DecoderInstance().decodeEc(data[IMPORTED_KEY_BLOB]); - data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0); - data[SECRET] = KMArray.cast(keyBlob).get((short) 1); - } - - private void importECKeys(byte[] scratchPad, byte keyFormat) { - if (keyFormat == KMType.RAW) { - decodeRawECKey(); - } else { - decodePKCS8ECKeys(); - } - // initialize 256 bit p256 key for given private key and public key. - tmpVariables[4] = 0; // index for update list in scratchPad - - // check whether the keysize tag is present in key parameters. - tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - // As per NIST.SP.800-186 page 9, secret for 256 curve should be between - // 256-383 - if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) - && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ tmpVariables[2] == 256) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - if (tmpVariables[2] != 256) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } else { - // add the key size to scratchPad - if (!(256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) - && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))){ - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - tmpVariables[5] = KMInteger.uint_16((short) 256); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; - } - // check the curve if present in key parameters. - tmpVariables[3] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); - if (tmpVariables[3] != KMType.INVALID_VALUE) { - // As per NIST.SP.800-186 page 9, secret length for 256 curve should be between - // 256-383 - if (((256 <= (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) - && (383 >= (short) (KMByteBlob.cast(data[SECRET]).length() * 8))) - ^ tmpVariables[3] == KMType.P_256) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - if (tmpVariables[3] != KMType.P_256) { - KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); - } - } else { - // add the curve to scratchPad - tmpVariables[5] = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[5]); - tmpVariables[4] += 2; - } - // Check whether key can be created - seProvider.importAsymmetricKey( - KMType.EC, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length()); - - // add scratch pad to key parameters - updateKeyParameters(scratchPad, tmpVariables[4]); - // validate updated key parameters. - validateECKeys(); - data[KEY_BLOB] = KMArray.instance((short) 5); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); - } - - private void importHmacKey(byte[] scratchPad) { - // Get Key - data[SECRET] = data[IMPORTED_KEY_BLOB]; - tmpVariables[4] = 0; // index in scratchPad for update params - // check the keysize tag if present in key parameters. - tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (!(tmpVariables[2] >= 64 && tmpVariables[2] <= 512 && tmpVariables[2] % 8 == 0)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - if (tmpVariables[2] != (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - } else { - // add the key size to scratchPad - tmpVariables[6] = (short) (KMByteBlob.cast(data[SECRET]).length() * 8); - if (!(tmpVariables[6] >= 64 && tmpVariables[6] <= 512 && tmpVariables[6] % 8 == 0)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - tmpVariables[5] = KMInteger.uint_16(tmpVariables[6]); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; - } - // Check whether key can be created - seProvider.importSymmetricKey( - KMType.HMAC, - tmpVariables[2], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - - // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); - // validate HMAC Key parameters - validateHmacKey(); - - data[KEY_BLOB] = KMArray.instance((short) 4); - } - - private void importTDESKey(byte[] scratchPad) { - // Decode Key Material - data[SECRET] = data[IMPORTED_KEY_BLOB]; - tmpVariables[4] = 0; // index in scratchPad for update params - // check the keysize tag if present in key parameters. - tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 168 || - 192 != (short)( 8 * KMByteBlob.cast(data[SECRET]).length())) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - } else { - // add the key size to scratchPad - tmpVariables[6] = (short)( 8 * KMByteBlob.cast(data[SECRET]).length()); - if(tmpVariables[6] != 192) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - tmpVariables[5] = KMInteger.uint_16(tmpVariables[6]); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; - } - // Check whether key can be created - seProvider.importSymmetricKey( - KMType.DES, - tmpVariables[2], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - - // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); - // Read Minimum Mac length - it must not be present - // Added this error check based on default reference implementation. - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_TAG); - } - data[KEY_BLOB] = KMArray.instance((short) 4); - } - - private void validateAesKeySize(short keySizeBits) { - if (keySizeBits != 128 && keySizeBits != 256) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } - - private void importAESKey(byte[] scratchPad) { - // Get Key - data[SECRET] = data[IMPORTED_KEY_BLOB]; - // create 128 or 256 bit AES key - tmpVariables[4] = 0; // index in scratchPad for update params - // check the keysize tag if present in key parameters. - short keysize = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - - if (keysize != KMType.INVALID_VALUE) { - if(keysize != (short)( 8 * KMByteBlob.cast(data[SECRET]).length())) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - validateAesKeySize(keysize); - } else { - // add the key size to scratchPad - keysize = (short) ( 8 * KMByteBlob.cast(data[SECRET]).length()); - validateAesKeySize(keysize); - keysize = KMInteger.uint_16(keysize); - short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); - Util.setShort(scratchPad, tmpVariables[4], keysizeTag); - tmpVariables[4] += 2; - } - // Check whether key can be created - seProvider.importSymmetricKey( - KMType.AES, - tmpVariables[2], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length()); - - // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); - // validate AES Key parameters - validateAESKey(); - data[KEY_BLOB] = KMArray.instance((short) 4); - } - - private void importRSAKey(byte[] scratchPad) { - // Decode key material - short keyblob = seProvider.getPKCS8DecoderInstance().decodeRsa(data[IMPORTED_KEY_BLOB]); - data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0); - short pubKeyExp = KMArray.cast(keyblob).get((short)1); - data[SECRET] = KMArray.cast(keyblob).get((short) 2); - - if(F4.length != KMByteBlob.cast(pubKeyExp).length()) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - if(Util.arrayCompare(F4, (short)0, KMByteBlob.cast(pubKeyExp).getBuffer(), - KMByteBlob.cast(pubKeyExp).getStartOff(), (short)F4.length) != 0) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - tmpVariables[4] = 0; // index in scratchPad for update parameters. - // validate public exponent if present in key params - it must be 0x010001 - tmpVariables[2] = - KMIntegerTag.getValue( - scratchPad, - (short) 10, // using offset 10 as first 10 bytes reserved for update params - KMType.ULONG_TAG, - KMType.RSA_PUBLIC_EXPONENT, - data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMTag.INVALID_VALUE) { - if (tmpVariables[2] != 4 - || Util.getShort(scratchPad, (short) 10) != 0x01 - || Util.getShort(scratchPad, (short) 12) != 0x01) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - } else { - // add public exponent to scratchPad - Util.setShort(scratchPad, (short) 10, (short) 0x01); - Util.setShort(scratchPad, (short) 12, (short) 0x01); - tmpVariables[5] = KMInteger.uint_32(scratchPad, (short) 10); - tmpVariables[6] = - KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; - } - - // check the keysize tag if present in key parameters. - tmpVariables[2] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[2] != KMType.INVALID_VALUE) { - if (tmpVariables[2] != 2048 - || tmpVariables[2] != (short) (KMByteBlob.cast(data[SECRET]).length() * 8)) { - KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); - } - } else { - // add the key size to scratchPad - tmpVariables[6] = (short) (KMByteBlob.cast(data[SECRET]).length() * 8); - if(tmpVariables[6] != 2048) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - tmpVariables[5] = KMInteger.uint_16((short) tmpVariables[6]); - tmpVariables[6] = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, tmpVariables[5]); - Util.setShort(scratchPad, tmpVariables[4], tmpVariables[6]); - tmpVariables[4] += 2; - } - - // Check whether key can be created - seProvider.importAsymmetricKey( - KMType.RSA, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length()); - - // update the key parameters list - updateKeyParameters(scratchPad, tmpVariables[4]); - // validate RSA Key parameters - validateRSAKey(scratchPad); - data[KEY_BLOB] = KMArray.instance((short) 5); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); - } - - private void updateKeyParameters(byte[] ptrArr, short len) { - if (len == 0) { - return; // nothing to update - } - // Create Update Param array and copy current params - tmpVariables[0] = KMKeyParameters.cast(data[KEY_PARAMETERS]).getVals(); - tmpVariables[1] = (short) (KMArray.cast(tmpVariables[0]).length() + (short) (len / 2)); - tmpVariables[1] = KMArray.instance(tmpVariables[1]); // update params - tmpVariables[2] = KMArray.cast(tmpVariables[0]).length(); - tmpVariables[3] = 0; - // copy the existing key parameters to updated array - while (tmpVariables[3] < tmpVariables[2]) { - tmpVariables[4] = KMArray.cast(tmpVariables[0]).get(tmpVariables[3]); - KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); - tmpVariables[3]++; - } - // copy new parameters to updated array - tmpVariables[2] = KMArray.cast(tmpVariables[1]).length(); - tmpVariables[5] = 0; // index in ptrArr - while (tmpVariables[3] < tmpVariables[2]) { - tmpVariables[4] = Util.getShort(ptrArr, tmpVariables[5]); - KMArray.cast(tmpVariables[1]).add(tmpVariables[3], tmpVariables[4]); - tmpVariables[3]++; - tmpVariables[5] += 2; - } - // replace with updated key parameters. - data[KEY_PARAMETERS] = KMKeyParameters.instance(tmpVariables[1]); - } - - // This command is executed to set the boot parameters. - // releaseAllOperations has to be called on every boot, so - // it is called from inside setBootParams. Later in future if - // setBootParams is removed, then make sure that releaseAllOperations - // is moved to a place where it is called on every boot. - private void processSetBootParamsCmd(APDU apdu) { - receiveIncoming(apdu); - byte[] scratchPad = apdu.getBuffer(); - // Argument 0 Boot Patch level - tmpVariables[0] = KMInteger.exp(); - // Argument 1 Verified Boot Key - tmpVariables[1] = KMByteBlob.exp(); - // Argument 2 Verified Boot Hash - tmpVariables[2] = KMByteBlob.exp(); - // Argument 3 Verified Boot State - tmpVariables[3] = KMEnum.instance(KMType.VERIFIED_BOOT_STATE); - // Argument 4 Device Locked - tmpVariables[4] = KMEnum.instance(KMType.DEVICE_LOCKED); - // Array of e4pected arguments - short argsProto = KMArray.instance((short) 5); - KMArray.cast(argsProto).add((short) 0, tmpVariables[0]); - KMArray.cast(argsProto).add((short) 1, tmpVariables[1]); - KMArray.cast(argsProto).add((short) 2, tmpVariables[2]); - KMArray.cast(argsProto).add((short) 3, tmpVariables[3]); - KMArray.cast(argsProto).add((short) 4, tmpVariables[4]); - // Decode the arguments - short args = decoder.decode(argsProto, (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - tmpVariables[0] = KMArray.cast(args).get((short) 0); - tmpVariables[1] = KMArray.cast(args).get((short) 1); - tmpVariables[2] = KMArray.cast(args).get((short) 2); - tmpVariables[3] = KMArray.cast(args).get((short) 3); - tmpVariables[4] = KMArray.cast(args).get((short) 4); - if (KMByteBlob.cast(tmpVariables[1]).length() > KMRepository.BOOT_KEY_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - if (KMByteBlob.cast(tmpVariables[2]).length() > KMRepository.BOOT_HASH_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - - repository.setBootPatchLevel( - KMInteger.cast(tmpVariables[0]).getBuffer(), - KMInteger.cast(tmpVariables[0]).getStartOff(), - KMInteger.cast(tmpVariables[0]).length()); - - repository.setVerifiedBootKey( - KMByteBlob.cast(tmpVariables[1]).getBuffer(), - KMByteBlob.cast(tmpVariables[1]).getStartOff(), - KMByteBlob.cast(tmpVariables[1]).length()); - - repository.setVerifiedBootHash( - KMByteBlob.cast(tmpVariables[2]).getBuffer(), - KMByteBlob.cast(tmpVariables[2]).getStartOff(), - KMByteBlob.cast(tmpVariables[2]).length()); - - byte enumVal = KMEnum.cast(tmpVariables[3]).getVal(); - repository.setBootState(enumVal); - - enumVal = KMEnum.cast(tmpVariables[4]).getVal(); - repository.setBootloaderLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); - - // Clear Android system properties expect boot patch level as it is - // already set. - repository.clearAndroidSystemProperties(); - - // Clear the Computed SharedHmac and Hmac nonce from persistent memory. - Util.arrayFillNonAtomic(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE, (byte) 0); - seProvider.createComputedHmacKey(scratchPad, (short) 0, KMRepository.COMPUTED_HMAC_KEY_SIZE); - repository.clearHmacNonce(); - - //Clear all the operation state. - repository.releaseAllOperations(); - - // Hmac is cleared, so generate a new Hmac nonce. - seProvider.newRandomNumber(scratchPad, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); - repository.initHmacNonce(scratchPad, (short) 0, KMRepository.HMAC_SEED_NONCE_SIZE); - - //flag to maintain the boot state - repository.setBootEndedStatus(false); - - //flag to maintain early boot ended state - repository.setEarlyBootEndedStatus(false); - - // Clear all the auth tags - repository.removeAllAuthTags(); - } - - private static void processGenerateKey(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - receiveIncoming(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - // Argument - tmpVariables[0] = KMKeyParameters.exp(); - // Array of expected arguments - tmpVariables[1] = KMArray.instance((short) 1); - KMArray.cast(tmpVariables[1]).add((short) 0, tmpVariables[0]); - // Decode the argument - tmpVariables[2] = decoder.decode(tmpVariables[1], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET], bufferProp[BUF_LEN_OFFSET]); - //reclaim memory - repository.reclaimMemory(bufferProp[BUF_LEN_OFFSET]); - - data[KEY_PARAMETERS] = KMArray.cast(tmpVariables[2]).get((short) 0); - // Check if EarlyBootEnded tag is present. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE && repository.getEarlyBootEndedStatus()) { - KMException.throwIt(KMError.EARLY_BOOT_ENDED); - } - // Check if rollback resistance tag is present - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); - } - - // get algorithm - tmpVariables[3] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); - if (tmpVariables[3] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - tmpVariables[4] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[4] != KMType.INVALID_VALUE) { - if (!KMIntegerTag.cast(tmpVariables[4]).isValidKeySize((byte) tmpVariables[3])) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } - // Only STANDALONE is supported for BLOB_USAGE_REQ tag. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[0] = KMEnumTag.getValue(KMType.BLOB_USAGE_REQ, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.STANDALONE) { - KMException.throwIt(KMError.UNSUPPORTED_TAG); - } - } - //Check if the tags are supported. - if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_TAG); - } - - // Check algorithm and dispatch to appropriate handler. - switch (tmpVariables[3]) { - case KMType.RSA: - generateRSAKey(scratchPad); - break; - case KMType.AES: - generateAESKey(scratchPad); - break; - case KMType.DES: - generateTDESKey(scratchPad); - break; - case KMType.HMAC: - generateHmacKey(scratchPad); - break; - case KMType.EC: - generateECKeys(scratchPad); - break; - default: - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - break; - } - // create key blob - data[ORIGIN] = KMType.GENERATED; - createEncryptedKeyBlob(scratchPad); - - // prepare the response - tmpVariables[0] = KMArray.instance((short) 3); - KMArray.cast(tmpVariables[0]).add((short) 0, buildErrorStatus(KMError.OK)); - KMArray.cast(tmpVariables[0]).add((short) 1, data[KEY_BLOB]); - KMArray.cast(tmpVariables[0]).add((short) 2, data[KEY_CHARACTERISTICS]); - - bufferProp[BUF_START_OFFSET] = repository.allocAvailableMemory(); - // Encode the response - bufferProp[BUF_LEN_OFFSET] = encoder.encode(tmpVariables[0], (byte[]) bufferRef[0], bufferProp[BUF_START_OFFSET]); - - sendOutgoing(apdu); - } - - private static void validateRSAKey(byte[] scratchPad) { - // Read key size - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - if (tmpVariables[0] != 2048) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - // Read public exponent into scratch pad - tmpVariables[1] = - KMIntegerTag.getValue( - scratchPad, - (short) 0, - KMType.ULONG_TAG, - KMType.RSA_PUBLIC_EXPONENT, - data[KEY_PARAMETERS]); - if ((tmpVariables[1] == KMTag.INVALID_VALUE) || (tmpVariables[1] != 4)) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Only exponent support is F4 - 65537 which is 0x00010001. - if (Util.getShort(scratchPad, (short) 0) != 0x01 - || Util.getShort(scratchPad, (short) 2) != 0x01) { - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - } - - // Generate key handlers - private static void generateRSAKey(byte[] scratchPad) { - // Validate RSA Key - validateRSAKey(scratchPad); - // Now generate 2048 bit RSA keypair for the given exponent - short[] lengths = tmpVariables; - data[PUB_KEY] = KMByteBlob.instance((short) 256); - data[SECRET] = KMByteBlob.instance((short) 256); - seProvider.createAsymmetricKey( - KMType.RSA, - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - KMByteBlob.cast(data[PUB_KEY]).getBuffer(), - KMByteBlob.cast(data[PUB_KEY]).getStartOff(), - KMByteBlob.cast(data[PUB_KEY]).length(), - lengths); - - data[KEY_BLOB] = KMArray.instance((short) 5); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); - } - - private static void validateAESKey() { - // Read key size - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - if ((tmpVariables[0] != 256) && (tmpVariables[0] != 128)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - // Read Block mode - array of byte values - tmpVariables[1] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE, data[KEY_PARAMETERS]); - if (tmpVariables[1] != KMTag.INVALID_VALUE) { // block mode specified - // Find Minimum Mac length - tmpVariables[2] = - KMKeyParameters.findTag(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - // If block modes contain GCM mode - if (KMEnumArrayTag.cast(tmpVariables[1]).contains(KMType.GCM)) { - // minimum mac length must be specified - if (tmpVariables[2] == KMTag.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); - } - tmpVariables[3] = KMIntegerTag.cast(tmpVariables[2]).getValue(); - // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits - // and greater the 96 bits - if (KMInteger.cast(tmpVariables[3]).getSignificantShort() != 0 - || KMInteger.cast(tmpVariables[3]).getShort() > 128 - || KMInteger.cast(tmpVariables[3]).getShort() < 96 - || (KMInteger.cast(tmpVariables[3]).getShort() % 8) != 0) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } - } - } - - private static void generateAESKey(byte[] scratchPad) { - validateAESKey(); - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - tmpVariables[0] = - seProvider.createSymmetricKey(KMType.AES, tmpVariables[0], scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); - data[KEY_BLOB] = KMArray.instance((short) 4); - } - - private static void validateECKeys() { - // Read key size - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - tmpVariables[1] = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); - if ((tmpVariables[0] == KMTag.INVALID_VALUE) && (tmpVariables[1] == KMTag.INVALID_VALUE)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } else if ((tmpVariables[0] != KMTag.INVALID_VALUE) && (tmpVariables[0] != (short) 256)) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } else if ((tmpVariables[1] != KMType.INVALID_VALUE) && (tmpVariables[1] != KMType.P_256)) { - KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); - } - } - - private static void generateECKeys(byte[] scratchPad) { - validateECKeys(); - short[] lengths = tmpVariables; - seProvider.createAsymmetricKey( - KMType.EC, - scratchPad, - (short) 0, - (short) 128, - scratchPad, - (short) 128, - (short) 128, - lengths); - data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); - data[KEY_BLOB] = KMArray.instance((short) 5); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PUB_KEY, data[PUB_KEY]); - } - - private static void validateTDESKey() { - // Read Minimum Mac length - it must not be present - // This below check is done based on the reference implementation. - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_TAG); - } - // Read keysize - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - if (tmpVariables[1] != 168) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } - - private static void generateTDESKey(byte[] scratchPad) { - validateTDESKey(); - tmpVariables[0] = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); - data[KEY_BLOB] = KMArray.instance((short) 4); - } - - private static void validateHmacKey() { - // If params does not contain any digest throw unsupported digest error. - tmpVariables[0] = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); - if (KMType.INVALID_VALUE == tmpVariables[0]) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - - if (KMEnumArrayTag.contains(KMType.DIGEST, KMType.DIGEST_NONE, data[KEY_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - // Strongbox supports only SHA256. - if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[KEY_PARAMETERS])) { - KMException.throwIt(KMError.UNSUPPORTED_DIGEST); - } - // Read Minimum Mac length - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.MISSING_MIN_MAC_LENGTH); - } - // Check whether digest size is greater than or equal to min mac length. - // This below check is done based on the reference implementation. - if (((short) (tmpVariables[0] % 8) != 0) - || (tmpVariables[0] < (short) 64) - || tmpVariables[0] > (short) 256) { - KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); - } - // Read keysize - tmpVariables[1] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - if (tmpVariables[1] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - if (((short) (tmpVariables[1] % 8) != 0) - || (tmpVariables[1] < (short) 64) - || tmpVariables[1] > (short) 512) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } - } - - private static void generateHmacKey(byte[] scratchPad) { - validateHmacKey(); - tmpVariables[0] = - KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); - // generate HMAC Key - tmpVariables[0] = - seProvider.createSymmetricKey(KMType.HMAC, tmpVariables[0], scratchPad, (short) 0); - data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[0]); - data[KEY_BLOB] = KMArray.instance((short) 4); - } - - private void checkVersionAndPatchLevel(byte[] scratchPad) { - tmpVariables[0] = - KMIntegerTag.getValue( - scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[1] = repository.getOsVersion(); - tmpVariables[1] = - KMInteger.unsignedByteArrayCompare( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), - scratchPad, - (short) 0, - tmpVariables[0]); - if (tmpVariables[1] == -1) { - // If the key characteristics has os version > current os version - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (tmpVariables[1] == 1) { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - } - tmpVariables[0] = - KMIntegerTag.getValue( - scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); - if (tmpVariables[0] != KMType.INVALID_VALUE) { - tmpVariables[1] = repository.getOsPatch(); - tmpVariables[1] = - KMInteger.unsignedByteArrayCompare( - KMInteger.cast(tmpVariables[1]).getBuffer(), - KMInteger.cast(tmpVariables[1]).getStartOff(), - scratchPad, - (short) 0, - tmpVariables[0]); - if (tmpVariables[1] == -1) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } else if (tmpVariables[1] == 1) { - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - } - } - - private static void makeKeyCharacteristics(byte[] scratchPad) { - tmpVariables[0] = repository.getOsPatch(); - tmpVariables[1] = repository.getOsVersion(); - tmpVariables[2] = repository.getVendorPatchLevel(); - tmpVariables[3] = repository.getBootPatchLevel(); - data[HW_PARAMETERS] = - KMKeyParameters.makeHwEnforced( - data[KEY_PARAMETERS], - (byte) data[ORIGIN], - tmpVariables[1], - tmpVariables[0], - tmpVariables[2], - tmpVariables[3], - scratchPad); - data[SW_PARAMETERS] = KMKeyParameters.makeSwEnforced(data[KEY_PARAMETERS], scratchPad); - data[KEY_CHARACTERISTICS] = KMKeyCharacteristics.instance(); - KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setHardwareEnforced(data[HW_PARAMETERS]); - KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setSoftwareEnforced(data[SW_PARAMETERS]); - } - - private static void createEncryptedKeyBlob(byte[] scratchPad) { - // make key characteristics - returns key characteristics in data[KEY_CHARACTERISTICS] - makeKeyCharacteristics(scratchPad); - // make root of trust blob - data[ROT] = repository.readROT(); - if (data[ROT] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - - // make hidden key params list - data[HIDDEN_PARAMETERS] = - KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); - // make authorization data - makeAuthData(scratchPad); - // encrypt the secret and cryptographically attach that to authorization data - encryptSecret(scratchPad); - - // create key blob array - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_SECRET, data[SECRET]); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_NONCE, data[NONCE]); - KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_KEYCHAR, data[KEY_CHARACTERISTICS]); - - // allocate reclaimable memory. - tmpVariables[0] = repository.alloc((short) 1024); - tmpVariables[1] = encoder.encode(data[KEY_BLOB], repository.getHeap(), tmpVariables[0]); - data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), tmpVariables[0], tmpVariables[1]); - } - - private static void parseEncryptedKeyBlob(byte[] scratchPad) { - data[ROT] = repository.readROT(); - if (data[ROT] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - try { - tmpVariables[0] = KMByteBlob.cast(data[KEY_BLOB]).getStartOff(); - tmpVariables[1] = KMArray.instance((short) 5); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_SECRET, - KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_AUTH_TAG, - KMByteBlob.exp()); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_NONCE, - KMByteBlob.exp()); - tmpVariables[2] = KMKeyCharacteristics.exp(); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_KEYCHAR, - tmpVariables[2]); - KMArray.cast(tmpVariables[1]).add(KMKeymasterApplet.KEY_BLOB_PUB_KEY, - KMByteBlob.exp()); - data[KEY_BLOB] = decoder.decodeArray(tmpVariables[1], - KMByteBlob.cast(data[KEY_BLOB]).getBuffer(), - KMByteBlob.cast(data[KEY_BLOB]).getStartOff(), - KMByteBlob.cast(data[KEY_BLOB]).length()); - tmpVariables[0] = KMArray.cast(data[KEY_BLOB]).length(); - if (tmpVariables[0] < 4) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - data[AUTH_TAG] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_AUTH_TAG); - - // initialize data - data[NONCE] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_NONCE); - data[SECRET] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); - data[KEY_CHARACTERISTICS] = KMArray.cast(data[KEY_BLOB]).get( - KEY_BLOB_KEYCHAR); - data[PUB_KEY] = KMType.INVALID_VALUE; - if (tmpVariables[0] == 5) { - data[PUB_KEY] = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PUB_KEY); - } - data[HW_PARAMETERS] = KMKeyCharacteristics - .cast(data[KEY_CHARACTERISTICS]).getHardwareEnforced(); - data[SW_PARAMETERS] = KMKeyCharacteristics - .cast(data[KEY_CHARACTERISTICS]).getSoftwareEnforced(); - - data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(data[APP_ID], - data[APP_DATA], data[ROT], scratchPad); - // make auth data - makeAuthData(scratchPad); - // Decrypt Secret and verify auth tag - decryptSecret(scratchPad); - } catch (Exception e) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - } - - private static void decryptSecret(byte[] scratchPad) { - // derive master key - stored in derivedKey - tmpVariables[0] = deriveKey(scratchPad); - if (!seProvider.aesGCMDecrypt( - repository.getHeap(), - data[DERIVED_KEY], - tmpVariables[0], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length(), - repository.getHeap(), - data[AUTH_DATA], - data[AUTH_DATA_LENGTH], - KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), - KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length())) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - // Copy the decrypted secret - data[SECRET] = - KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.cast(data[SECRET]).length()); - } - - private static void encryptSecret(byte[] scratchPad) { - // make nonce - data[NONCE] = KMByteBlob.instance((short) AES_GCM_NONCE_LENGTH); - data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).length()); - seProvider.newRandomNumber( - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length()); - // derive master key - stored in derivedKey - tmpVariables[0] = deriveKey(scratchPad); - tmpVariables[1] = - seProvider.aesGCMEncrypt( - repository.getHeap(), - data[DERIVED_KEY], - tmpVariables[0], - KMByteBlob.cast(data[SECRET]).getBuffer(), - KMByteBlob.cast(data[SECRET]).getStartOff(), - KMByteBlob.cast(data[SECRET]).length(), - scratchPad, - (short) 0, - KMByteBlob.cast(data[NONCE]).getBuffer(), - KMByteBlob.cast(data[NONCE]).getStartOff(), - KMByteBlob.cast(data[NONCE]).length(), - repository.getHeap(), - data[AUTH_DATA], - data[AUTH_DATA_LENGTH], - KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), - KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length()); - if (tmpVariables[1] > 0) { - if (tmpVariables[1] != KMByteBlob.cast(data[SECRET]).length()) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - KMByteBlob.cast(data[SECRET]).setValue(scratchPad, (short) 0, tmpVariables[1]); - } - } - - private static void makeAuthData(byte[] scratchPad) { - - short arrayLen = 3; - if (KMArray.cast(data[KEY_BLOB]).length() == 5) { - arrayLen = 4; - } - short params = KMArray.instance((short) arrayLen); - KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); - KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); - KMArray.cast(params).add((short) 2, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); - if (4 == arrayLen) { - KMArray.cast(params).add((short) 3, data[PUB_KEY]); - } - - short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); - short index = 0; - short len = 0; - short paramsLen = KMArray.cast(params).length(); - Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); - while (index < paramsLen) { - short tag = KMArray.cast(params).get(index); - len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); - Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), - (short) (authIndex + len + 32), (short) 32); - len = seProvider.messageDigest256(repository.getHeap(), - (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); - if (len != 32) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - index++; - } - data[AUTH_DATA] = authIndex; - data[AUTH_DATA_LENGTH] = len; - } - - private static short deriveKey(byte[] scratchPad) { - // KeyDerivation: - // 1. Do HMAC Sign, Auth data. - // 2. HMAC Sign generates an output of 32 bytes length. - // Consume only first 16 bytes as derived key. - // Hmac sign. - short len = seProvider.hmacKDF( - seProvider.getMasterKey(), - repository.getHeap(), - data[AUTH_DATA], - data[AUTH_DATA_LENGTH], - scratchPad, - (short) 0); - if (len < 16) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - len = 16; - data[DERIVED_KEY] = repository.alloc(len); - // store the derived secret in data dictionary - Util.arrayCopyNonAtomic( - scratchPad, (short) 0, repository.getHeap(), data[DERIVED_KEY], len); - return len; - } - - // 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 (repository.isPowerResetEventOccurred()) { - 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(); - return int32Ptr; - } - - private static void sendError(APDU apdu, short err) { - bufferProp[BUF_START_OFFSET] = repository.alloc((short) 5); - short int32Ptr = buildErrorStatus(err); - bufferProp[BUF_LEN_OFFSET] = encoder.encodeError(int32Ptr, (byte[]) bufferRef[0], - bufferProp[BUF_START_OFFSET], (short) 5); - sendOutgoing(apdu); - } - - private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) { - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0); - Util.arrayCopyNonAtomic( - KMInteger.cast(authTime).getBuffer(), - KMInteger.cast(authTime).getStartOff(), - scratchPad, - (short) (8 - KMInteger.cast(timeStamp).length()), - KMInteger.cast(timeStamp).length()); - - // Copy timestamp to scratchpad - Util.arrayCopyNonAtomic( - KMInteger.cast(timeStamp).getBuffer(), - KMInteger.cast(timeStamp).getStartOff(), - scratchPad, - (short) (16 - KMInteger.cast(timeStamp).length()), - KMInteger.cast(timeStamp).length()); - - // add authTime in millis to timestamp. - KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16); - return KMInteger.uint_64(scratchPad, (short) 16); - } - - private void updateTrustedConfirmationOperation(KMOperationState op) { - if (op.isTrustedConfirmationRequired()) { - op.getTrustedConfirmationSigner().update( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length()); - } - } - - private void finishTrustedConfirmationOperation(KMOperationState op) { - // Perform trusted confirmation if required - if (op.isTrustedConfirmationRequired()) { - tmpVariables[0] = - KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN, data[KEY_PARAMETERS]); - if (tmpVariables[0] == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.NO_USER_CONFIRMATION); - } - tmpVariables[0] = KMByteTag.cast(tmpVariables[0]).getValue(); - boolean verified = - op.getTrustedConfirmationSigner().verify( - KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), - KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(tmpVariables[0]).getBuffer(), - KMByteBlob.cast(tmpVariables[0]).getStartOff(), - KMByteBlob.cast(tmpVariables[0]).length()); - if (!verified) { - KMException.throwIt(KMError.NO_USER_CONFIRMATION); - } - } - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java deleted file mode 100644 index bfd67ceb..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; - -/** - * KMOperationState is the container of an active operation started by beginOperation function. This - * operation state is persisted by the applet in non volatile memory. However, this state is not - * retained if applet is upgraded. There will be four operation state records maintained i.e. only - * four active operations are supported at any given time. - */ -public class KMOperationState { - - public static final byte MAX_DATA = 63; - private static final byte OPERATION = 0; - private static final byte HMAC_SIGNER_OPERATION = 1; - private static final byte TRUE = 1; - private static final byte FALSE = 0; - // byte type - private static final byte ALG = 0; - private static final byte PURPOSE = 1; - private static final byte PADDING = 2; - private static final byte BLOCKMODE = 3; - private static final byte DIGEST = 4; - private static final byte FLAGS = 5; - private static final byte AUTH_TYPE = 6; - // short type - private static final byte KEY_SIZE = 7; - private static final byte MAC_LENGTH = 9; - // Handle - currently this is short - private static final byte OP_HANDLE = 11; - // Auth time 64 bits - private static final byte AUTH_TIME = 13; - // Secure user ids 5 * 8 = 40 bytes ( Considering Maximum 5 SECURE USER IDs) - // First two bytes are reserved to store number of secure ids. SO total 42 bytes. - private static final byte USER_SECURE_ID = 21; - // Flag masks - private static final byte AUTH_PER_OP_REQD = 1; - private static final byte SECURE_USER_ID_REQD = 2; - private static final byte AUTH_TIMEOUT_VALIDATED = 4; - private static final byte AES_GCM_UPDATE_ALLOWED = 8; - private static final byte MAX_SECURE_USER_IDS = 5; - - // Object References - private byte[] data; - private Object[] objRefs; - private static KMOperationState prototype; - private byte[] isDataUpdated; - - private KMOperationState() { - data = JCSystem.makeTransientByteArray(MAX_DATA, JCSystem.CLEAR_ON_RESET); - objRefs = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); - isDataUpdated = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); - } - - private static KMOperationState proto() { - if (prototype == null) { - prototype = new KMOperationState(); - } - return prototype; - } - - public static KMOperationState instance(short opHandle) { - KMOperationState opState = proto(); - opState.reset(); - Util.setShort(prototype.data, OP_HANDLE, opHandle); - return opState; - } - - public static KMOperationState read(byte[] oprHandle, short off, byte[] data, short dataOff, Object opr, Object hmacSignerOpr) { - KMOperationState opState = proto(); - opState.reset(); - Util.arrayCopyNonAtomic(data, dataOff, prototype.data, (short) 0, (short) prototype.data.length); - prototype.objRefs[OPERATION] = opr; - prototype.objRefs[HMAC_SIGNER_OPERATION] = hmacSignerOpr; - Util.setShort(prototype.data, OP_HANDLE, KMInteger.uint_64(oprHandle, off)); - return opState; - } - - public void persist() { - if (FALSE == isDataUpdated[0]) { - return; - } - KMRepository.instance().persistOperation(data, - Util.getShort(data, OP_HANDLE), - (KMOperation) objRefs[OPERATION], - (KMOperation) objRefs[HMAC_SIGNER_OPERATION]); - isDataUpdated[0] = FALSE; - } - - public void setKeySize(short keySize) { - Util.setShort(data, KEY_SIZE, keySize); - } - - public short getKeySize() { - return Util.getShort(data, KEY_SIZE); - } - - public void reset() { - isDataUpdated[0] = FALSE; - objRefs[OPERATION] = null; - objRefs[HMAC_SIGNER_OPERATION] = null; - Util.arrayFillNonAtomic( - data, (short) 0, (short) data.length, (byte) 0); - } - - private void dataUpdated() { - isDataUpdated[0] = TRUE; - } - - public void release() { - if (objRefs[OPERATION] != null) { - ((KMOperation) objRefs[OPERATION]).abort(); - } - if (objRefs[HMAC_SIGNER_OPERATION] != null) { - ((KMOperation) objRefs[HMAC_SIGNER_OPERATION]).abort(); - } - reset(); - } - - public short getHandle() { - return Util.getShort(data, OP_HANDLE); - } - - public short getPurpose() { - return data[PURPOSE]; - } - - public void setPurpose(byte purpose) { - data[PURPOSE] = purpose; - dataUpdated(); - } - - public void setOperation(KMOperation opr) { - objRefs[OPERATION] = opr; - dataUpdated(); - persist(); - } - - public KMOperation getOperation() { - return (KMOperation) objRefs[OPERATION]; - } - - public boolean isAuthPerOperationReqd() { - return (data[FLAGS] & AUTH_PER_OP_REQD) != 0; - } - - public boolean isAuthTimeoutValidated() { - return (data[FLAGS] & AUTH_TIMEOUT_VALIDATED) != 0; - } - - public boolean isSecureUserIdReqd() { - return (data[FLAGS] & SECURE_USER_ID_REQD) != 0; - } - - public short getAuthTime() { - return KMInteger.uint_64(data, (short) AUTH_TIME); - } - - public void setAuthTime(byte[] timeBuf, short start) { - Util.arrayCopyNonAtomic(timeBuf, start, data, (short) AUTH_TIME, (short) 8); - dataUpdated(); - } - - public void setAuthType(byte authType) { - data[AUTH_TYPE] = authType; - dataUpdated(); - } - - public short getAuthType() { - return data[AUTH_TYPE]; - } - - public short getUserSecureId() { - short offset = USER_SECURE_ID; - short length = Util.getShort(data, USER_SECURE_ID); - if (length == 0) { - return KMType.INVALID_VALUE; - } - short arrObj = KMArray.instance(length); - short index = 0; - short obj; - offset = (short) (2 + USER_SECURE_ID); - while (index < length) { - obj = KMInteger.instance(data, (short) (offset + index * 8), (short) 8); - KMArray.cast(arrObj).add(index, obj); - index++; - } - return KMIntegerArrayTag.instance(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, arrObj); - } - - public void setUserSecureId(short integerArrayPtr) { - short length = KMIntegerArrayTag.cast(integerArrayPtr).length(); - if (length > MAX_SECURE_USER_IDS) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - Util.arrayFillNonAtomic(data, USER_SECURE_ID, (short) (MAX_SECURE_USER_IDS * 8) , (byte) 0); - short index = 0; - short obj; - short offset = USER_SECURE_ID; - Util.setShort(data, offset, length); - offset += 2; - while (index < length) { - obj = KMIntegerArrayTag.cast(integerArrayPtr).get(index); - Util.arrayCopyNonAtomic( - KMInteger.cast(obj).getBuffer(), - KMInteger.cast(obj).getStartOff(), - data, - (short) (8 - KMInteger.cast(obj).length() + offset + 8 * index), - KMInteger.cast(obj).length() - ); - index++; - } - dataUpdated(); - } - - public void setOneTimeAuthReqd(boolean flag) { - if (flag) { - data[FLAGS] = (byte) (data[FLAGS] | SECURE_USER_ID_REQD); - } else { - data[FLAGS] = (byte) (data[FLAGS] & (~SECURE_USER_ID_REQD)); - } - dataUpdated(); - } - - public void setAuthTimeoutValidated(boolean flag) { - if (flag) { - data[FLAGS] = (byte) (data[FLAGS] | AUTH_TIMEOUT_VALIDATED); - } else { - data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED)); - } - dataUpdated(); - } - - public void setAuthPerOperationReqd(boolean flag) { - if (flag) { - data[FLAGS] = (byte) (data[FLAGS] | AUTH_PER_OP_REQD); - } else { - data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_PER_OP_REQD)); - } - dataUpdated(); - } - - public byte getAlgorithm() { - return data[ALG]; - } - - public void setAlgorithm(byte algorithm) { - data[ALG] = algorithm; - dataUpdated(); - } - - public byte getPadding() { - return data[PADDING]; - } - - public void setPadding(byte padding) { - data[PADDING] = padding; - dataUpdated(); - } - - public byte getBlockMode() { - return data[BLOCKMODE]; - } - - public void setBlockMode(byte blockMode) { - data[BLOCKMODE] = blockMode; - dataUpdated(); - } - - public byte getDigest() { - return data[DIGEST]; - } - - public void setDigest(byte digest) { - data[DIGEST] = digest; - dataUpdated(); - } - - public boolean isAesGcmUpdateAllowed() { - return (data[FLAGS] & AES_GCM_UPDATE_ALLOWED) != 0; - } - - public void setAesGcmUpdateComplete() { - data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED)); - dataUpdated(); - } - - public void setAesGcmUpdateStart() { - data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED); - dataUpdated(); - } - - public void setMacLength(short length) { - Util.setShort(data, MAC_LENGTH, length); - dataUpdated(); - } - - public short getMacLength() { - return Util.getShort(data, MAC_LENGTH); - } - - public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) { - objRefs[HMAC_SIGNER_OPERATION] = hmacSignerOp; - dataUpdated(); - } - - public KMOperation getTrustedConfirmationSigner() { - return (KMOperation)objRefs[HMAC_SIGNER_OPERATION]; - } - - public boolean isTrustedConfirmationRequired() { - return objRefs[HMAC_SIGNER_OPERATION] != null; - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java deleted file mode 100644 index 7bf5bb4b..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -public interface KMPKCS8Decoder { - - /** - * Decodes the PKCS8 encoded RSA Key and extracts the private and public key - * - * @param Instance of the PKCS8 encoded data - * @return Instance of KMArray holding RSA public key, RSA private key and modulus. - */ - short decodeRsa(short blob); - - /** - * Decodes the PKCS8 encoded EC Key and extracts the private and public key - * - * @param Instance of the PKCS8 encoded data. - * @return Instance of KMArray holding EC public key and EC private key. - */ - short decodeEc(short blob); - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java deleted file mode 100644 index 1cfe8ef9..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ /dev/null @@ -1,1022 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import org.globalplatform.upgrade.Element; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; - -/** - * KMRepository class manages persistent and volatile memory usage by the applet. Note the - * repository is only used by applet and it is not intended to be used by seProvider. - */ -public class KMRepository implements KMUpgradable { - - // Data table configuration - public static final short DATA_INDEX_SIZE = 33; - public static final short DATA_INDEX_ENTRY_SIZE = 4; - public static final short DATA_MEM_SIZE = 2048; - public static final short HEAP_SIZE = 10000; - public static final short DATA_INDEX_ENTRY_LENGTH = 0; - public static final short DATA_INDEX_ENTRY_OFFSET = 2; - public static final short OPERATION_HANDLE_SIZE = 8; /* 8 bytes */ - private static final short OPERATION_HANDLE_STATUS_OFFSET = 0; - private static final short OPERATION_HANDLE_STATUS_SIZE = 1; - private static final short OPERATION_HANDLE_OFFSET = 1; - private static final short OPERATION_HANDLE_ENTRY_SIZE = - OPERATION_HANDLE_SIZE + OPERATION_HANDLE_STATUS_SIZE; - private static final byte POWER_RESET_STATUS_FLAG = (byte) 0xEF; - - // Data table offsets - public static final byte ATT_ID_BRAND = 0; - public static final byte ATT_ID_DEVICE = 1; - public static final byte ATT_ID_PRODUCT = 2; - public static final byte ATT_ID_SERIAL = 3; - public static final byte ATT_ID_IMEI = 4; - public static final byte ATT_ID_MEID = 5; - public static final byte ATT_ID_MANUFACTURER = 6; - public static final byte ATT_ID_MODEL = 7; - public static final byte COMPUTED_HMAC_KEY = 8; - public static final byte HMAC_NONCE = 9; - public static final byte CERT_ISSUER = 10; - public static final byte CERT_EXPIRY_TIME = 11; - public static final byte BOOT_OS_VERSION = 12; - public static final byte BOOT_OS_PATCH_LEVEL = 13; - public static final byte VENDOR_PATCH_LEVEL = 14; - public static final byte BOOT_PATCH_LEVEL = 15; - public static final byte BOOT_VERIFIED_BOOT_KEY = 16; - public static final byte BOOT_VERIFIED_BOOT_HASH = 17; - public static final byte BOOT_VERIFIED_BOOT_STATE = 18; - public static final byte BOOT_DEVICE_LOCKED_STATUS = 19; - public static final byte DEVICE_LOCKED_TIME = 20; - public static final byte DEVICE_LOCKED = 21; - public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 22; - // Total 8 auth tags, so the next offset is AUTH_TAG_1 + 8 - public static final byte AUTH_TAG_1 = 23; - public static final byte BOOT_ENDED_STATUS = 31; - public static final byte EARLY_BOOT_ENDED_STATUS = 32; - - // Data Item sizes - public static final short MASTER_KEY_SIZE = 16; - public static final short SHARED_SECRET_KEY_SIZE = 32; - public static final short HMAC_SEED_NONCE_SIZE = 32; - public static final short COMPUTED_HMAC_KEY_SIZE = 32; - public static final short OS_VERSION_SIZE = 4; - public static final short OS_PATCH_SIZE = 4; - public static final short VENDOR_PATCH_SIZE = 4; - public static final short BOOT_PATCH_SIZE = 4; - public static final short DEVICE_LOCK_TS_SIZE = 8; - public static final short BOOT_DEVICE_LOCK_FLAG_SIZE = 1; - public static final short DEVICE_LOCKED_FLAG_SIZE = 1; - public static final short DEVICE_LOCKED_PASSWORD_ONLY_SIZE = 1; - public static final short BOOT_STATE_SIZE = 1; - public static final short MAX_OPS = 4; - public static final byte BOOT_KEY_MAX_SIZE = 32; - public static final byte BOOT_HASH_MAX_SIZE = 32; - public static final short MAX_BLOB_STORAGE = 8; - public static final short AUTH_TAG_LENGTH = 16; - public static final short AUTH_TAG_COUNTER_SIZE = 4; - public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); - public static final short BOOT_ENDED_FLAG_SIZE = 1; - public static final short EARLY_BOOT_ENDED_FLAG_SIZE = 1; - private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; - - // Buffer type - public static final byte DEFAULT_BUF_TYPE = 0; - public static final byte ATTEST_IDS_BUF_TYPE = 1; - - // Class Attributes - private Object[] operationStateTable; - private byte[] heap; - private short[] heapIndex; - private byte[] dataTable; - private short dataIndex; - private short[] reclaimIndex; - private short attestIdsIndex; - // This variable is used to monitor the power reset status as the Applet does not get - // any power reset event. Initially the value of this variable is set to POWER_RESET_STATUS_FLAG. - // If the power reset happens then this value becomes 0. - private byte[] powerResetStatus; - - // Operation table. - private static final short OPER_TABLE_DATA_OFFSET = 0; - private static final short OPER_TABLE_OPR_OFFSET = 1; - private static final short OPER_TABLE_HMAC_SIGNER_OPR_OFFSET = 2; - private static final short OPER_DATA_LEN = OPERATION_HANDLE_ENTRY_SIZE + KMOperationState.MAX_DATA; - private static final short DATA_ARRAY_LENGTH = MAX_OPS * OPER_DATA_LEN; - - - // Singleton instance - private static KMRepository repository; - - public static KMRepository instance() { - return repository; - } - - public KMRepository(boolean isUpgrading) { - heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); - heapIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); - reclaimIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); - powerResetStatus = JCSystem.makeTransientByteArray((short) 1, JCSystem.CLEAR_ON_RESET); - heapIndex[0] = (short) 0; - reclaimIndex[0] = HEAP_SIZE; - powerResetStatus[0] = POWER_RESET_STATUS_FLAG; - newDataTable(isUpgrading); - - operationStateTable = new Object[3]; - operationStateTable[0] = JCSystem.makeTransientByteArray(DATA_ARRAY_LENGTH, JCSystem.CLEAR_ON_RESET); - operationStateTable[1] = JCSystem.makeTransientObjectArray(MAX_OPS, JCSystem.CLEAR_ON_RESET); - operationStateTable[2] = JCSystem.makeTransientObjectArray(MAX_OPS, JCSystem.CLEAR_ON_RESET); - - //Initialize the device locked status - if (!isUpgrading) { - setDeviceLock(false); - setDeviceLockPasswordOnly(false); - } else { - // In case of upgrade, the applet is deleted and installed again so all - // volatile memory is erased. so it is necessary to force the power reset flag - // to 0 so that the HAL can clear its operation state. - powerResetStatus[0] = (byte) 0; - } - repository = this; - } - - // This function checks if card reset event occurred and this function - // should only be called before processing any of the APUs. - // Transient memory is cleared in two cases: - // 1. Card reset event - // 2. Applet upgrade. - public boolean isPowerResetEventOccurred() { - if (powerResetStatus[0] == POWER_RESET_STATUS_FLAG) { - return false; - } - return true; - } - - /** - * This function sets the power reset status flag to its - * default value. - */ - public void restorePowerResetStatus() { - powerResetStatus[0] = POWER_RESET_STATUS_FLAG; - } - - public void getOperationHandle(short oprHandle, byte[] buf, short off, short len) { - if (KMInteger.cast(oprHandle).length() != OPERATION_HANDLE_SIZE) { - KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); - } - KMInteger.cast(oprHandle).getValue(buf, off, len); - } - - public KMOperationState findOperation(byte[] buf, short off, short len) { - short index = 0; - byte[] oprTableData; - short offset = 0; - oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if (0 == Util.arrayCompare(buf, off, oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), len)) { - return KMOperationState.read(oprTableData, (short) (offset + OPERATION_HANDLE_OFFSET), oprTableData, - (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), - operations[index], hmacSignerOprs[index]); - } - index++; - } - return null; - } - - /* operationHandle is a KMInteger */ - public KMOperationState findOperation(short operationHandle) { - short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); - getOperationHandle( - operationHandle, - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - return findOperation( - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - } - - /* opHandle is a KMInteger */ - public KMOperationState reserveOperation(short opHandle) { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - short offset = 0; - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - /* Check for unreserved operation state */ - if (oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 0) { - return KMOperationState.instance(opHandle); - } - index++; - } - return null; - } - - public void persistOperation(byte[] data, short opHandle, KMOperation op, KMOperation hmacSignerOp) { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; - short offset = 0; - short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); - getOperationHandle( - opHandle, - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - //Update an existing operation state. - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if ((1 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) - && (0 == Util.arrayCompare( - oprTableData, - (short) (offset + OPERATION_HANDLE_OFFSET), - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()))) { - Util.arrayCopyNonAtomic(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), - KMOperationState.MAX_DATA); - operations[index] = op; - hmacSignerOprs[index] = hmacSignerOp; - return; - } - index++; - } - - index = 0; - //Persist a new operation. - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if (0 == oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)]) { - oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] = 1;/*reserved */ - Util.arrayCopyNonAtomic( - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - oprTableData, - (short) (offset + OPERATION_HANDLE_OFFSET), - OPERATION_HANDLE_SIZE); - Util.arrayCopyNonAtomic(data, (short) 0, oprTableData, (short) (offset + OPERATION_HANDLE_ENTRY_SIZE), - KMOperationState.MAX_DATA); - operations[index] = op; - hmacSignerOprs[index] = hmacSignerOp; - break; - } - index++; - } - } - - public void releaseOperation(KMOperationState op) { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; - short offset = 0; - short buf = KMByteBlob.instance(OPERATION_HANDLE_SIZE); - getOperationHandle( - op.getHandle(), - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()); - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if ((oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 1) && - (0 == Util.arrayCompare(oprTableData, - (short) (offset + OPERATION_HANDLE_OFFSET), - KMByteBlob.cast(buf).getBuffer(), - KMByteBlob.cast(buf).getStartOff(), - KMByteBlob.cast(buf).length()))) { - Util.arrayFillNonAtomic(oprTableData, offset, OPER_DATA_LEN, (byte) 0); - op.release(); - operations[index] = null; - hmacSignerOprs[index] = null; - break; - } - index++; - } - } - - public void releaseAllOperations() { - short index = 0; - byte[] oprTableData = (byte[]) operationStateTable[OPER_TABLE_DATA_OFFSET]; - Object[] operations = (Object[]) operationStateTable[OPER_TABLE_OPR_OFFSET]; - Object[] hmacSignerOprs = (Object[]) operationStateTable[OPER_TABLE_HMAC_SIGNER_OPR_OFFSET]; - - short offset = 0; - while (index < MAX_OPS) { - offset = (short) (index * OPER_DATA_LEN); - if (oprTableData[(short) (offset + OPERATION_HANDLE_STATUS_OFFSET)] == 1) { - Util.arrayFillNonAtomic(oprTableData, offset, OPER_DATA_LEN, (byte) 0); - if (operations[index] != null) { - ((KMOperation) operations[index]).abort(); - operations[index] = null; - } - if (hmacSignerOprs[index] != null) { - ((KMOperation) hmacSignerOprs[index]).abort(); - hmacSignerOprs[index] = null; - } - } - index++; - } - } - - public void initHmacNonce(byte[] nonce, short offset, short len) { - if (len != HMAC_SEED_NONCE_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(HMAC_NONCE, nonce, offset, len); - } - - public void clearHmacNonce() { - clearDataEntry(HMAC_NONCE); - } - - public void onUninstall() { - // Javacard Runtime environment cleans up the data. - - } - - public void onProcess() { - } - - public void clean() { - Util.arrayFillNonAtomic(heap, (short) 0, heapIndex[0], (byte) 0); - heapIndex[0] = (short) 0; - reclaimIndex[0] = HEAP_SIZE; - } - - public void onDeselect() { - } - - public void onSelect() { - // If write through caching is implemented then this method will restore the data into cache - } - - // This function uses memory from the back of the heap(transient memory). Call - // reclaimMemory function immediately after the use. - public short allocReclaimableMemory(short length) { - if ((((short) (reclaimIndex[0] - length)) <= heapIndex[0]) - || (length >= HEAP_SIZE / 2)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - reclaimIndex[0] -= length; - return reclaimIndex[0]; - } - - // Reclaims the memory back. - public void reclaimMemory(short length) { - if (reclaimIndex[0] < heapIndex[0]) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - reclaimIndex[0] += length; - } - - public short allocAvailableMemory() { - if (heapIndex[0] >= heap.length) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short index = heapIndex[0]; - heapIndex[0] = (short) heap.length; - return index; - } - - public short alloc(short length) { - if (length < 0) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if ((((short) (heapIndex[0] + length)) > heap.length) || - (((short) (heapIndex[0] + length)) > reclaimIndex[0])) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - heapIndex[0] += length; - return (short) (heapIndex[0] - length); - } - - private short dataAlloc(byte bufType, short length) { - short maxSize = getMaxLimitSize(bufType); - short dataIndex = getDataTableIndex(bufType); - if (length < 0) { - ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); - } - if (((short) (dataIndex + length)) > maxSize) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - dataIndex += length; - setDataTableIndex(bufType, dataIndex); - return (short) (dataIndex - length); - } - - private short getDataTableIndex(byte bufType) { - if (bufType == ATTEST_IDS_BUF_TYPE) { - return this.attestIdsIndex; - } else { - return this.dataIndex; - } - } - - private void setDataTableIndex(byte bufType, short index) { - if (bufType == ATTEST_IDS_BUF_TYPE) { - JCSystem.beginTransaction(); - this.attestIdsIndex = index; - JCSystem.commitTransaction(); - } else { - JCSystem.beginTransaction(); - this.dataIndex = index; - JCSystem.commitTransaction(); - } - } - - private short getMaxLimitSize(byte bufType) { - if (bufType == ATTEST_IDS_BUF_TYPE) { - return (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); - } else { // Default buf type. - return (short) dataTable.length; - } - } - - private void newDataTable(boolean isUpgrading) { - if (!isUpgrading) { - if (dataTable == null) { - dataTable = new byte[DATA_MEM_SIZE]; - attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); - } - } - } - - public byte[] getDataTable() { - return dataTable; - } - - private void clearDataEntry(short id) { - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (dataLen != 0) { - short dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); - JCSystem.beginTransaction(); - Util.arrayFillNonAtomic(dataTable, dataPtr, dataLen, (byte) 0); - JCSystem.commitTransaction(); - } - } - - private void writeDataEntry(short id, byte[] buf, short offset, short len) { - writeDataEntry(DEFAULT_BUF_TYPE, id, buf, offset, len); - } - - private short readDataEntry(short id, byte[] buf, short offset) { - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (len != 0) { - Util.arrayCopyNonAtomic( - dataTable, - Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), - buf, - offset, - len); - } - return len; - } - - private void writeDataEntry(byte bufType, short id, byte[] buf, short offset, short len) { - short dataPtr; - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short dataLen = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (dataLen == 0) { - dataPtr = dataAlloc(bufType, len); - // Begin Transaction - JCSystem.beginTransaction(); - Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET), dataPtr); - Util.setShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH), len); - Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); - JCSystem.commitTransaction(); - // End Transaction - } else { - if (len != dataLen) { - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - dataPtr = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)); - // Begin Transaction - JCSystem.beginTransaction(); - Util.arrayCopyNonAtomic(buf, offset, dataTable, dataPtr, len); - JCSystem.commitTransaction(); - // End Transaction - } - } - - private short dataLength(short id) { - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - return Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - } - - public byte[] getHeap() { - return heap; - } - - public short getHmacNonce() { - return readData(HMAC_NONCE); - } - - public short getComputedHmacKey() { - return readData(COMPUTED_HMAC_KEY); - } - - public void persistAttId(byte id, byte[] buf, short start, short len) { - writeDataEntry(ATTEST_IDS_BUF_TYPE, id, buf, start, len); - } - - public short getAttId(byte id) { - return readData(id); - } - - public void deleteAttIds() { - JCSystem.beginTransaction(); - attestIdsIndex = (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - Util.arrayFillNonAtomic(dataTable, attestIdsIndex, KMConfigurations.TOTAL_ATTEST_IDS_SIZE, (byte) 0); - JCSystem.commitTransaction(); - } - - public short getIssuer() { - return readData(CERT_ISSUER); - } - - public short readData(short id) { - short len = dataLength(id); - if (len != 0) { - short blob = KMByteBlob.instance(len); - readDataEntry(id, KMByteBlob.cast(blob).getBuffer(), KMByteBlob.cast(blob).getStartOff()); - return blob; - } - return KMType.INVALID_VALUE; - } - - public short readData(byte[] dataTable, short id, byte[] buf, short startOff, short bufLen) { - id = (short) (id * DATA_INDEX_ENTRY_SIZE); - short len = Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_LENGTH)); - if (len > bufLen) { - return KMType.INVALID_VALUE; - } - if (len != 0) { - Util.arrayCopyNonAtomic( - dataTable, - Util.getShort(dataTable, (short) (id + DATA_INDEX_ENTRY_OFFSET)), - buf, - startOff, - len); - } - return len; - } - - public short getCertExpiryTime() { - return readData(CERT_EXPIRY_TIME); - } - - 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); - } - } - - 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); - } - } - - public short getBootPatchLevel() { - short blob = readData(BOOT_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); - } - } - - 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); - } - } - - public short readROT() { - short totalLength = 0; - short length = dataLength(BOOT_VERIFIED_BOOT_KEY); - if (length == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - if ((length = dataLength(BOOT_VERIFIED_BOOT_HASH)) == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - if ((length = dataLength(BOOT_VERIFIED_BOOT_STATE)) == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - if ((length = dataLength(BOOT_DEVICE_LOCKED_STATUS)) == 0) { - return KMType.INVALID_VALUE; - } - totalLength += length; - - short blob = KMByteBlob.instance(totalLength); - length = readDataEntry(BOOT_VERIFIED_BOOT_KEY, KMByteBlob.cast(blob) - .getBuffer(), KMByteBlob.cast(blob).getStartOff()); - - length += readDataEntry(BOOT_VERIFIED_BOOT_HASH, KMByteBlob.cast(blob) - .getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + length)); - - length += readDataEntry(BOOT_VERIFIED_BOOT_STATE, KMByteBlob.cast(blob) - .getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + length)); - - readDataEntry(BOOT_DEVICE_LOCKED_STATUS, KMByteBlob.cast(blob) - .getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + length)); - return blob; - } - - public short getVerifiedBootKey() { - return readData(BOOT_VERIFIED_BOOT_KEY); - } - - public short getVerifiedBootHash() { - return readData(BOOT_VERIFIED_BOOT_HASH); - } - - public boolean getBootLoaderLock() { - short blob = readData(BOOT_DEVICE_LOCKED_STATUS); - if (blob == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_DATA); - } - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; - } - - public byte getBootState() { - short blob = readData(BOOT_VERIFIED_BOOT_STATE); - if (blob == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_DATA); - } - return (getHeap())[KMByteBlob.cast(blob).getStartOff()]; - } - - public boolean getDeviceLock() { - short blob = readData(DEVICE_LOCKED); - if (blob == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_DATA); - } - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; - } - - public boolean getDeviceLockPasswordOnly() { - short blob = readData(DEVICE_LOCKED_PASSWORD_ONLY); - if (blob == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_DATA); - } - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; - } - - 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); - } - } - - public void setOsVersion(byte[] buf, short start, short len) { - if (len != OS_VERSION_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_OS_VERSION, buf, start, len); - } - - public void setVendorPatchLevel(byte[] buf, short start, short len) { - if (len != VENDOR_PATCH_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(VENDOR_PATCH_LEVEL, buf, start, len); - } - - public void setBootPatchLevel(byte[] buf, short start, short len) { - if (len != BOOT_PATCH_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_PATCH_LEVEL, buf, start, len); - } - - public void clearAndroidSystemProperties() { - clearDataEntry(BOOT_OS_VERSION); - clearDataEntry(BOOT_OS_PATCH_LEVEL); - clearDataEntry(VENDOR_PATCH_LEVEL); - // Don't clear BOOT_PATCH_LEVEL as it is part of - // boot parameters. - } - - public void setBootloaderLocked(boolean flag) { - short start = alloc(BOOT_DEVICE_LOCK_FLAG_SIZE); - if (flag) { - (getHeap())[start] = (byte) 0x01; - } else { - (getHeap())[start] = (byte) 0x00; - } - writeDataEntry(BOOT_DEVICE_LOCKED_STATUS, getHeap(), start, BOOT_DEVICE_LOCK_FLAG_SIZE); - } - - public void setDeviceLock(boolean flag) { - short start = alloc(DEVICE_LOCKED_FLAG_SIZE); - if (flag) { - (getHeap())[start] = (byte) 0x01; - } else { - (getHeap())[start] = (byte) 0x00; - } - writeDataEntry(DEVICE_LOCKED, getHeap(), start, DEVICE_LOCKED_FLAG_SIZE); - } - - public void setDeviceLockPasswordOnly(boolean flag) { - short start = alloc(DEVICE_LOCKED_PASSWORD_ONLY_SIZE); - if (flag) { - (getHeap())[start] = (byte) 0x01; - } else { - (getHeap())[start] = (byte) 0x00; - } - writeDataEntry(DEVICE_LOCKED_PASSWORD_ONLY, getHeap(), start, DEVICE_LOCKED_PASSWORD_ONLY_SIZE); - } - - public void setDeviceLockTimestamp(byte[] buf, short start, short len) { - if (len != DEVICE_LOCK_TS_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(DEVICE_LOCKED_TIME, buf, start, len); - } - - public void clearDeviceLockTimeStamp() { - clearDataEntry(DEVICE_LOCKED_TIME); - } - - public void setOsPatch(byte[] buf, short start, short len) { - if (len != OS_PATCH_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_OS_PATCH_LEVEL, buf, start, len); - } - - public void setVerifiedBootKey(byte[] buf, short start, short len) { - if (len > BOOT_KEY_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_VERIFIED_BOOT_KEY, buf, start, len); - } - - - public void setVerifiedBootHash(byte[] buf, short start, short len) { - if (len > BOOT_HASH_MAX_SIZE) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - writeDataEntry(BOOT_VERIFIED_BOOT_HASH, buf, start, len); - } - - public void setBootState(byte state) { - short start = alloc(BOOT_STATE_SIZE); - (getHeap())[start] = state; - writeDataEntry(BOOT_VERIFIED_BOOT_STATE, getHeap(), start, BOOT_STATE_SIZE); - } - - private boolean isAuthTagSlotAvailable(short tagId, byte[] buf, short offset) { - readDataEntry(tagId, buf, offset); - return (0 == buf[offset]); - } - - private void writeAuthTagState(byte[] buf, short offset, byte state) { - buf[offset] = state; - } - - public boolean persistAuthTag(short authTag) { - - if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - - short authTagEntry = alloc(AUTH_TAG_ENTRY_SIZE); - short scratchPadOff = alloc(AUTH_TAG_ENTRY_SIZE); - byte[] scratchPad = getHeap(); - writeAuthTagState(getHeap(), authTagEntry, (byte) 1); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - getHeap(), (short) (authTagEntry + 1), AUTH_TAG_LENGTH); - Util.setShort(getHeap(), (short) (authTagEntry + AUTH_TAG_LENGTH + 1 + 2), - (short) 1); - short index = 0; - while (index < MAX_BLOB_STORAGE) { - if ((dataLength((short) (index + AUTH_TAG_1)) == 0) || - isAuthTagSlotAvailable((short) (index + AUTH_TAG_1), scratchPad, scratchPadOff)) { - - writeDataEntry((short) (index + AUTH_TAG_1), getHeap(), authTagEntry, AUTH_TAG_ENTRY_SIZE); - return true; - } - index++; - } - return false; - } - - public void removeAllAuthTags() { - short index = 0; - while (index < MAX_BLOB_STORAGE) { - clearDataEntry((short) (index + AUTH_TAG_1)); - index++; - } - } - - public boolean isAuthTagPersisted(short authTag) { - return (KMType.INVALID_VALUE != findTag(authTag)); - } - - private short findTag(short authTag) { - if (KMByteBlob.cast(authTag).length() != AUTH_TAG_LENGTH) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - short index = 0; - short found; - short offset = alloc(AUTH_TAG_ENTRY_SIZE); - while (index < MAX_BLOB_STORAGE) { - if (dataLength((short) (index + AUTH_TAG_1)) != 0) { - readDataEntry((short) (index + AUTH_TAG_1), - getHeap(), offset); - found = - Util.arrayCompare( - getHeap(), - (short) (offset + 1), - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - AUTH_TAG_LENGTH); - if (found == 0) { - return (short) (index + AUTH_TAG_1); - } - } - index++; - } - return KMType.INVALID_VALUE; - } - - public short getRateLimitedKeyCount(short authTag, byte[] out, short outOff) { - short tag = findTag(authTag); - short blob; - if (tag != KMType.INVALID_VALUE) { - blob = readData(tag); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(blob).getBuffer(), - (short) (KMByteBlob.cast(blob).getStartOff() + AUTH_TAG_LENGTH + 1), - out, - outOff, - AUTH_TAG_COUNTER_SIZE); - return AUTH_TAG_COUNTER_SIZE; - } - return (short) 0; - } - - public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short len) { - short tag = findTag(authTag); - if (tag != KMType.INVALID_VALUE) { - short dataPtr = readData(tag); - Util.arrayCopyNonAtomic( - buf, - off, - KMByteBlob.cast(dataPtr).getBuffer(), - (short) (KMByteBlob.cast(dataPtr).getStartOff() + AUTH_TAG_LENGTH + 1), - len); - writeDataEntry(tag, - KMByteBlob.cast(dataPtr).getBuffer(), - KMByteBlob.cast(dataPtr).getStartOff(), - KMByteBlob.cast(dataPtr).length()); - } - } - - @Override - public void onSave(Element ele) { - ele.write(dataIndex); - ele.write(dataTable); - ele.write(attestIdsIndex); - } - - @Override - public void onRestore(Element ele, short oldVersion, short currentVersion) { - dataIndex = ele.readShort(); - dataTable = (byte[]) ele.readObject(); - if (oldVersion == 0) { - // Previous versions does not contain version information. - handleDataUpgradeToVersion2_0(); - } else { - attestIdsIndex = ele.readShort(); - } - } - - @Override - public short getBackupPrimitiveByteCount() { - // dataIndex - return (short) 4; - } - - @Override - public short getBackupObjectCount() { - // dataTable - return (short) 1; - } - - public boolean getBootEndedStatus() { - short blob = readData(BOOT_ENDED_STATUS); - if (blob == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_DATA); - } - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; - } - - public void setBootEndedStatus(boolean flag) { - short start = alloc(BOOT_ENDED_STATUS); - if (flag) { - (getHeap())[start] = (byte) 0x01; - } else { - (getHeap())[start] = (byte) 0x00; - } - writeDataEntry(BOOT_ENDED_STATUS, getHeap(), start, BOOT_ENDED_FLAG_SIZE); - } - - public boolean getEarlyBootEndedStatus() { - short blob = readData(EARLY_BOOT_ENDED_STATUS); - if (blob == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.INVALID_DATA); - } - return (byte) ((getHeap())[KMByteBlob.cast(blob).getStartOff()]) == 0x01; - } - - public void setEarlyBootEndedStatus(boolean flag) { - short start = alloc(EARLY_BOOT_ENDED_STATUS); - if (flag) { - (getHeap())[start] = (byte) 0x01; - } else { - (getHeap())[start] = (byte) 0x00; - } - writeDataEntry(EARLY_BOOT_ENDED_STATUS, getHeap(), start, EARLY_BOOT_ENDED_FLAG_SIZE); - } - - public void handleDataUpgradeToVersion2_0() { - byte[] oldDataTable = dataTable; - dataTable = new byte[2048]; - attestIdsIndex = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - dataIndex = (short) (attestIdsIndex + KMConfigurations.TOTAL_ATTEST_IDS_SIZE); - // temp buffer. - short startOffset = alloc((short) 256); - - short index = ATT_ID_BRAND; - short len = 0; - while (index <= DEVICE_LOCKED) { - len = readData(oldDataTable, index, heap, startOffset, (short) 256); - writeDataEntry(index, heap, startOffset, len); - index++; - } - // set default values for the new IDS. - setDeviceLockPasswordOnly(false); - setBootEndedStatus(false); - setEarlyBootEndedStatus(false); - - // Request object deletion - oldDataTable = null; - JCSystem.requestObjectDeletion(); - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/src/com/android/javacard/keymaster/KMTag.java deleted file mode 100644 index fa9bb38e..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMTag.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.Util; - -/** - * This class represents a tag as defined by keymaster hal specifications. It is composed of key - * value pair. The key consists of short tag type e.g. KMType.ENUM and short tag key e.g. - * KMType.ALGORITHM. The key is encoded as uint CBOR type with 4 bytes. This is followed by value - * which can be any CBOR type based on key. struct{byte tag=KMType.TAG_TYPE, short length, value) - * where value is subtype of KMTag i.e. struct{short tagType=one of tag types declared in KMType , - * short tagKey=one of the tag keys declared in KMType, value} where value is one of the sub-types - * of KMType. - */ -public class KMTag extends KMType { - - public static short getTagType(short ptr) { - return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - } - - public static short getKey(short ptr) { - return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2)); - } -} diff --git a/Applet/src/com/android/javacard/keymaster/KMArray.java b/Applet/src/com/android/javacard/kmdevice/KMArray.java similarity index 53% rename from Applet/src/com/android/javacard/keymaster/KMArray.java rename to Applet/src/com/android/javacard/kmdevice/KMArray.java index adf61723..dc7513a7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMArray.java +++ b/Applet/src/com/android/javacard/kmdevice/KMArray.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -44,7 +44,7 @@ private static KMArray proto(short ptr) { if (prototype == null) { prototype = new KMArray(); } - instanceTable[KM_ARRAY_OFFSET] = ptr; + KMType.instanceTable[KM_ARRAY_OFFSET] = ptr; return prototype; } @@ -75,52 +75,122 @@ public static short instance(short length, byte type) { return ptr; } - public static KMArray cast(short ptr) { + private static KMArray cast(short ptr) { if (heap[ptr] != ARRAY_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } return proto(ptr); } - public void add(short index, short objPtr) { + private void add(short index, short objPtr) { short len = length(); if (index >= len) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } Util.setShort( - heap, - (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index * 2)), - objPtr); + heap, + (short) (getStartOff() + (short) (index * 2)), + objPtr); } - - public void deleteLastEntry() { - short len = length(); - Util.setShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), (short) (len -1)); - } - - public short get(short index) { + private short get(short index) { short len = length(); if (index >= len) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } return Util.getShort( - heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) (index * 2))); + heap, (short) (getStartOff() + (short) (index * 2))); } - public short containedType() { - return Util.getShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE)); + private void swap(short index1, short index2) { + short len = length(); + if (index1 >= len || index2 >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short indexPtr1 = + Util.getShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + + (short) (index1 * 2))); + short indexPtr2 = + Util.getShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + + (short) (index2 * 2))); + Util.setShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) ( + index1 * 2)), + indexPtr2); + Util.setShort( + heap, + (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE + (short) ( + index2 * 2)), + indexPtr1); } - public short getStartOff() { - return (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); + private short containedType() { + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE)); } - public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2)); + private short getStartOff() { + return (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + ARRAY_HEADER_SIZE); } - public byte[] getBuffer() { + private short length() { + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + private short setLength(short len) { + return Util.setShort(heap, + (short) (KMType.instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), len); + } + + private byte[] getBuffer() { return heap; } + + private void deleteLastEntry() { + short len = length(); + Util.setShort(heap, (short) (instanceTable[KM_ARRAY_OFFSET] + TLV_HEADER_SIZE + 2), + (short) (len - 1)); + } + + public static void add(short bPtr, short index, short objPtr) { + KMArray.cast(bPtr).add(index, objPtr); + } + + public static short get(short bPtr, short index) { + return KMArray.cast(bPtr).get(index); + } + + public static void swap(short bPtr, short index1, short index2) { + KMArray.cast(bPtr).swap(index1, index2); + } + + public static short containedType(short bPtr) { + return KMArray.cast(bPtr).containedType(); + } + + public static short getStartOff(short bPtr) { + return KMArray.cast(bPtr).getStartOff(); + } + + public static short length(short bPtr) { + return KMArray.cast(bPtr).length(); + } + + public static short setLength(short bPtr, short len) { + return KMArray.cast(bPtr).setLength(len); + } + + public static byte[] getBuffer(short bPtr) { + return KMArray.cast(bPtr).getBuffer(); + } + + public static void deleteLastEntry(short bPtr) { + KMArray.cast(bPtr).deleteLastEntry(); + } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java b/Applet/src/com/android/javacard/kmdevice/KMAttestationCert.java similarity index 79% rename from Applet/src/com/android/javacard/keymaster/KMAttestationCert.java rename to Applet/src/com/android/javacard/kmdevice/KMAttestationCert.java index 487dccc7..67abb60b 100644 --- a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java +++ b/Applet/src/com/android/javacard/kmdevice/KMAttestationCert.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; /** * The KMAttestationCert interface represents a X509 compliant attestation certificate required to @@ -60,7 +60,7 @@ public interface KMAttestationCert { * @param attestAppIdOff Start offset of the attestAppId buffer. * @param attestAppIdLen Length of the attestAppId buffer. * @param resetSinceIdRotation This holds the information of RESET_SINCE_ID_ROTATION. - * @param instance of the master key. + * @param masterKey instance of the KMMasterKey. * @return instance of KMAttestationCert. */ KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] creationTime, @@ -76,22 +76,18 @@ KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] cr * @param scratchpad Buffer to store intermediate results. * @return instance of KMAttestationCert. */ - KMAttestationCert notBefore(short obj, byte[] scratchpad); + KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad); /** * Set expiry time received from expiry time tag or ca certificates expiry time. Used for * certificate's valid period. * - * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. - * @param certExpirtyTimeObj This is a KMByteblob containing expirty time extracted from - * certificate. - * @param scratchpad Buffer to store intermediate results. - * @param offset Variable used to store intermediate results. + * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. certificate. + * @param scratchPad Buffer to store intermediate results. * @return instance of KMAttestationCert */ - KMAttestationCert notAfter(short usageExpiryTimeObj, - short certExpirtyTimeObj, byte[] scratchPad, short offset); + KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad); /** * Set device lock status received during booting time or due to device lock command. @@ -153,13 +149,6 @@ KMAttestationCert notAfter(short usageExpiryTimeObj, */ short getCertStart(); - /** - * Get the end of the certificate - * - * @return end of the attestation cert. - */ - short getCertEnd(); - /** * Get the length of the certificate * @@ -167,8 +156,43 @@ KMAttestationCert notAfter(short usageExpiryTimeObj, */ short getCertLength(); + /** - * Build the certificate. After this method the certificate is ready. + * Build a fake signed certificate. After this method executes the certificate is ready with the + * signature equal to 1 byte which is 0 and with rsa signature algorithm. */ void build(); + + /** + * Set the Serial number in the certificate. If no serial number is set then serial number is 1. + * + * @param serialNumber is serial number represented as KMByteBlob. + */ + boolean serialNumber(short serialNumber); + + /** + * Set the Subject Name in the certificate. + * + * @param subject is serial number represented as KMByteBlob. + */ + boolean subjectName(short subject); + + /** + * Set attestation key and mode. + * + * @param attestKey KMByteBlob of the key + * @param mode is the attestation mode. + */ + KMAttestationCert ecAttestKey(short attestKey, byte mode); + + /** + * Set attestation key and mode. + * + * @param attestKey KMByteBlob of the key + * @param mode is the attestation mode + */ + KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode); + + KMAttestationCert factoryAttestKey(KMAttestationKey key, byte mode); + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/src/com/android/javacard/kmdevice/KMAttestationCertImpl.java similarity index 56% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java rename to Applet/src/com/android/javacard/kmdevice/KMAttestationCertImpl.java index 6ca72904..9e817c9e 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/src/com/android/javacard/kmdevice/KMAttestationCertImpl.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -29,74 +29,59 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static final byte MAX_PARAMS = 30; // DER encoded object identifiers required by the cert. // rsaEncryption - 1.2.840.113549.1.1.1 - private static final byte[] rsaEncryption = { - 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01 - }; + private static byte[] rsaEncryption; // ecPublicKey - 1.2.840.10045.2.1 - private static final byte[] eccPubKey = { - 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01 - }; + private static byte[] eccPubKey; // prime256v1 curve - 1.2.840.10045.3.1.7 - private static final byte[] prime256v1 = { - 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07 - }; + private static byte[] prime256v1; // Key Usage Extn - 2.5.29.15 - private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F}; + private static byte[] keyUsageExtn; // Android Extn - 1.3.6.1.4.1.11129.2.1.17 - private static final byte[] androidExtn = { - 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11 - }; - + private static byte[] androidExtn; + private static final short RSA_SIG_LEN = 256; private static final short ECDSA_MAX_SIG_LEN = 72; - //Signature algorithm identifier - always ecdsaWithSha256 - 1.2.840.10045.4.3.2 + //Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2 //SEQUENCE of alg OBJ ID and parameters = NULL. - private static final byte[] X509SignAlgIdentifier = { - 0x30, - 0x0A, - 0x06, - 0x08, - 0x2A, - (byte) 0x86, - 0x48, - (byte) 0xCE, - (byte) 0x3D, - 0x04, - 0x03, - 0x02 - }; + private static byte[] X509EcdsaSignAlgIdentifier; + // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11 + // SEQUENCE of alg OBJ ID and parameters = NULL. + private static byte[] X509RsaSignAlgIdentifier; + + // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. + private static short[] swTagIds; + + // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. + private static short[] hwTagIds; + // Validity is not fixed field // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys - private static final byte[] X509Subject = { - 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, - 0x64, - 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, - 0x65, - 0x79 - }; + private static byte[] X509Subject; private static final byte keyUsageSign = (byte) 0x80; // 0 bit private static final byte keyUsageKeyEncipher = (byte) 0x20; // 2nd- bit private static final byte keyUsageDataEncipher = (byte) 0x10; // 3rd- bit + private static final byte keyUsageKeyAgreement = (byte) 0x08; // 4th- bit + private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit - private static final byte KEYMASTER_VERSION = 41; - private static final byte ATTESTATION_VERSION = 4; - private static final byte[] pubExponent = {0x01, 0x00, 0x01}; + private static final byte KEYMASTER_VERSION = 100; + private static final byte ATTESTATION_VERSION = 100; + private static byte[] pubExponent; private static final byte SERIAL_NUM = (byte) 0x01; private static final byte X509_VERSION = (byte) 0x02; private static short certStart; - private static short signatureOffset; - private static short tbsOffset; + private static short certLength; + private static short tbsStart; private static short tbsLength; - - private static short stackPtr; private static byte[] stack; - private static short start; - private static short length; - // private static KMRepository repo; + private static short stackPtr; + private static short bufStart; + private static short bufLength; + private static short uniqueId; private static short attChallenge; private static short notBefore; + private static short notAfter; private static short pubKey; private static short[] swParams; @@ -106,20 +91,107 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static byte keyUsage; private static byte unusedBits; private static KMAttestationCert inst; + private static KMSEProvider seProvider; private static boolean rsaCert; private static byte deviceLocked; private static short verifiedBootKey; private static byte verifiedState; private static short verifiedHash; private static short issuer; + private static short subjectName; private static short signPriv; + private static short serialNum; + + private static byte certMode; + private static short certAttestKeySecret; + private static short certAttestKeyRsaPubModulus; + private static KMAttestationKey factoryAttestKey; + private static boolean certRsaSign; + private static final byte SERIAL_NUM_MAX_LEN = 20; + private static final byte SUBJECT_NAME_MAX_LEN = 32; + + public static void initStatics() { + rsaEncryption = new byte[]{ + 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01 + }; + eccPubKey = new byte[]{ + 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01 + }; + prime256v1 = new byte[]{ + 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07 + }; + keyUsageExtn = new byte[]{0x06, 0x03, 0x55, 0x1D, 0x0F}; + androidExtn = new byte[]{ + 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11 + }; + X509EcdsaSignAlgIdentifier = new byte[]{ + 0x30, + 0x0A, + 0x06, + 0x08, + 0x2A, + (byte) 0x86, + 0x48, + (byte) 0xCE, + (byte) 0x3D, + 0x04, + 0x03, + 0x02 + }; + X509RsaSignAlgIdentifier = new byte[]{ + 0x30, + 0x0D, + 0x06, + 0x09, + 0x2A, + (byte) 0x86, + 0x48, + (byte) 0x86, + (byte) 0xF7, + 0x0D, + 0x01, + 0x01, + 0x0B, + 0x05, + 0x00 + }; + swTagIds = new short[]{ + KMType.ATTESTATION_APPLICATION_ID, + KMType.CREATION_DATETIME, + KMType.USAGE_EXPIRE_DATETIME, + KMType.ORIGINATION_EXPIRE_DATETIME, + KMType.ACTIVE_DATETIME, + KMType.UNLOCKED_DEVICE_REQUIRED + }; + hwTagIds = new short[]{ + KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, + KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER, + KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI, + KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT, + KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND, + KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, + KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, + KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID, + KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH, + KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE, + KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE}; + X509Subject = new byte[]{ + 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, + 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, + 0x65, + 0x79 + }; + pubExponent = new byte[]{0x01, 0x00, 0x01}; + } private KMAttestationCertImpl() { } - public static KMAttestationCert instance(boolean rsaCert) { + public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) { if (inst == null) { inst = new KMAttestationCertImpl(); + seProvider = provider; } init(); KMAttestationCertImpl.rsaCert = rsaCert; @@ -130,9 +202,9 @@ private static void init() { stack = null; stackPtr = 0; certStart = 0; - signatureOffset = 0; - start = 0; - length = 0; + certLength = 0; + bufStart = 0; + bufLength = 0; tbsLength = 0; if (swParams == null) { swParams = JCSystem.makeTransientShortArray((short) MAX_PARAMS, JCSystem.CLEAR_ON_RESET); @@ -156,20 +228,23 @@ private static void init() { rsaCert = true; deviceLocked = 0; signPriv = 0; + certMode = KMType.NO_CERT; + certAttestKeySecret = KMType.INVALID_VALUE; + certRsaSign = true; + issuer = KMType.INVALID_VALUE; + subjectName = KMType.INVALID_VALUE; + serialNum = KMType.INVALID_VALUE; + factoryAttestKey = null; } @Override public KMAttestationCert verifiedBootHash(short obj) { - if (obj == KMType.INVALID_VALUE) - KMException.throwIt(KMError.INVALID_DATA); verifiedHash = obj; return this; } @Override public KMAttestationCert verifiedBootKey(short obj) { - if (obj == KMType.INVALID_VALUE) - KMException.throwIt(KMError.INVALID_DATA); verifiedBootKey = obj; return this; } @@ -186,30 +261,41 @@ private KMAttestationCert uniqueId(short obj) { } @Override - public KMAttestationCert notBefore(short obj, byte[] scratchpad) { - // convert milliseconds to UTC date - notBefore = KMUtils.convertToDate(obj, scratchpad, true); + public KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad) { + if (!derEncoded) { + // convert milliseconds to UTC date + notBefore = KMUtils.convertToDate(obj, scratchpad, true); + } else { + notBefore = KMByteBlob.instance(KMByteBlob.getBuffer(obj), + KMByteBlob.getStartOff(obj), KMByteBlob.length(obj)); + } return this; } @Override - public KMAttestationCert notAfter(short usageExpiryTimeObj, - short certExpirtyTimeObj, byte[] scratchPad, short tmpVar) { - if (usageExpiryTimeObj != KMType.INVALID_VALUE) { - // compare if the expiry time is greater then 2051 then use generalized - // time format else use utc time format. - usageExpiryTimeObj = KMIntegerTag.cast(usageExpiryTimeObj).getValue(); - tmpVar = KMInteger.uint_64(KMUtils.firstJan2051, (short) 0); - if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) { - usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, - false); + public KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, + byte[] scratchPad) { + if (!derEncoded) { + if (usageExpiryTimeObj != KMType.INVALID_VALUE) { + // compare if the expiry time is greater then 2051 then use generalized + // time format else use utc time format. + short tmpVar = KMInteger.uint_64(KMUtils.firstJan2051, (short) 0); + if (KMInteger.compare(usageExpiryTimeObj, tmpVar) >= 0) { + usageExpiryTimeObj = KMUtils.convertToDate(usageExpiryTimeObj, scratchPad, + false); + } else { + usageExpiryTimeObj = KMUtils + .convertToDate(usageExpiryTimeObj, scratchPad, true); + } + notAfter = usageExpiryTimeObj; } else { - usageExpiryTimeObj = KMUtils - .convertToDate(usageExpiryTimeObj, scratchPad, true); + //notAfter = certExpirtyTimeObj; } - notAfter = usageExpiryTimeObj; } else { - notAfter = certExpirtyTimeObj; + // notAfter = KMKeymasterApplet.instance(KMKeymasterApplet.cast(usageExpiryTimeObj).getBuffer(), + // KMKeymasterApplet.cast(usageExpiryTimeObj).getStartOff(), + // KMKeymasterApplet.cast(usageExpiryTimeObj).length()); + notAfter = usageExpiryTimeObj; } return this; } @@ -245,7 +331,7 @@ public KMAttestationCert extensionTag(short tag, boolean hwEnforced) { swParams[swParamsIndex] = tag; swParamsIndex++; } - if (KMTag.getKey(tag) == KMType.PURPOSE) { + if (KMTag.getKMTagKey(tag) == KMType.PURPOSE) { createKeyUsage(tag); } return this; @@ -253,22 +339,24 @@ public KMAttestationCert extensionTag(short tag, boolean hwEnforced) { @Override public KMAttestationCert issuer(short obj) { - if (obj == KMType.INVALID_VALUE) - KMException.throwIt(KMError.INVALID_DATA); issuer = obj; return this; } private void createKeyUsage(short tag) { - short len = KMEnumArrayTag.cast(tag).length(); + short len = KMEnumArrayTag.length(tag); byte index = 0; while (index < len) { - if (KMEnumArrayTag.cast(tag).get(index) == KMType.SIGN) { + if (KMEnumArrayTag.get(tag, index) == KMType.SIGN) { keyUsage = (byte) (keyUsage | keyUsageSign); - } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.WRAP_KEY) { + } else if (KMEnumArrayTag.get(tag, index) == KMType.WRAP_KEY) { keyUsage = (byte) (keyUsage | keyUsageKeyEncipher); - } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.DECRYPT) { + } else if (KMEnumArrayTag.get(tag, index) == KMType.DECRYPT) { keyUsage = (byte) (keyUsage | keyUsageDataEncipher); + } else if (KMEnumArrayTag.get(tag, index) == KMType.AGREE_KEY) { + keyUsage = (byte) (keyUsage | keyUsageKeyAgreement); + } else if (KMEnumArrayTag.get(tag, index) == KMType.ATTEST_KEY) { + keyUsage = (byte) (keyUsage | keyUsageCertSign); } index++; } @@ -279,9 +367,12 @@ private void createKeyUsage(short tag) { } } - private static void pushTbsCert(boolean rsaCert) { + //TODO Serial number, X509Version needa to be passed as parameter + private void pushTbsCert(boolean rsaCert, boolean rsa) { short last = stackPtr; - pushExtensions(); + if (certMode == KMType.ATTESTATION_CERT || certMode == KMType.FACTORY_PROVISIONED_ATTEST_CERT) { + pushExtensions(); + } // subject public key info if (rsaCert) { pushRsaSubjectKeyInfo(); @@ -289,18 +380,24 @@ private static void pushTbsCert(boolean rsaCert) { pushEccSubjectKeyInfo(); } // subject - pushBytes(X509Subject, (short) 0, (short) X509Subject.length); + pushBytes(KMByteBlob.getBuffer(subjectName), KMByteBlob.getStartOff(subjectName), + KMByteBlob.length(subjectName)); pushValidity(); // issuer - der encoded pushBytes( - KMByteBlob.cast(issuer).getBuffer(), - KMByteBlob.cast(issuer).getStartOff(), - KMByteBlob.cast(issuer).length()); + KMByteBlob.getBuffer(issuer), + KMByteBlob.getStartOff(issuer), + KMByteBlob.length(issuer)); // Algorithm Id - pushAlgorithmId(X509SignAlgIdentifier); + if (rsa) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + } else { + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } // Serial Number - pushByte(SERIAL_NUM); - pushIntegerHeader((short) 1); + pushBytes(KMByteBlob.getBuffer(serialNum), KMByteBlob.getStartOff(serialNum), + KMByteBlob.length(serialNum)); + pushIntegerHeader(KMByteBlob.length(serialNum)); // Version pushByte(X509_VERSION); pushIntegerHeader((short) 1); @@ -310,7 +407,7 @@ private static void pushTbsCert(boolean rsaCert) { pushSequenceHeader((short) (last - stackPtr)); } - private static void pushExtensions() { + private void pushExtensions() { short last = stackPtr; if (keyUsage != 0) { pushKeyUsage(keyUsage, unusedBits); @@ -323,22 +420,22 @@ private static void pushExtensions() { } // Time SEQUENCE{UTCTime, UTC or Generalized Time) - private static void pushValidity() { + private void pushValidity() { short last = stackPtr; - if (notAfter != KMType.INVALID_VALUE) { + if (notAfter != 0) { pushBytes( - KMByteBlob.cast(notAfter).getBuffer(), - KMByteBlob.cast(notAfter).getStartOff(), - KMByteBlob.cast(notAfter).length()); + KMByteBlob.getBuffer(notAfter), + KMByteBlob.getStartOff(notAfter), + KMByteBlob.length(notAfter)); } else { KMException.throwIt(KMError.INVALID_DATA); } - pushTimeHeader(KMByteBlob.cast(notAfter).length()); + pushTimeHeader(KMByteBlob.length(notAfter)); pushBytes( - KMByteBlob.cast(notBefore).getBuffer(), - KMByteBlob.cast(notBefore).getStartOff(), - KMByteBlob.cast(notBefore).length()); - pushTimeHeader(KMByteBlob.cast(notBefore).length()); + KMByteBlob.getBuffer(notBefore), + KMByteBlob.getStartOff(notBefore), + KMByteBlob.length(notBefore)); + pushTimeHeader(KMByteBlob.length(notBefore)); pushSequenceHeader((short) (last - stackPtr)); } @@ -357,21 +454,21 @@ private static void pushTimeHeader(short len) { // SEQUENCE{SEQUENCE{algId, NULL}, bitString{SEQUENCE{ modulus as positive integer, public // exponent // as positive integer} - private static void pushRsaSubjectKeyInfo() { + private void pushRsaSubjectKeyInfo() { short last = stackPtr; pushBytes(pubExponent, (short) 0, (short) pubExponent.length); pushIntegerHeader((short) pubExponent.length); pushBytes( - KMByteBlob.cast(pubKey).getBuffer(), - KMByteBlob.cast(pubKey).getStartOff(), - KMByteBlob.cast(pubKey).length()); + KMByteBlob.getBuffer(pubKey), + KMByteBlob.getStartOff(pubKey), + KMByteBlob.length(pubKey)); // encode modulus as positive if the MSB is 1. - if (KMByteBlob.cast(pubKey).get((short) 0) < 0) { + if (KMByteBlob.get(pubKey, (short) 0) < 0) { pushByte((byte) 0x00); - pushIntegerHeader((short) (KMByteBlob.cast(pubKey).length() + 1)); + pushIntegerHeader((short) (KMByteBlob.length(pubKey) + 1)); } else { - pushIntegerHeader(KMByteBlob.cast(pubKey).length()); + pushIntegerHeader(KMByteBlob.length(pubKey)); } pushSequenceHeader((short) (last - stackPtr)); pushBitStringHeader((byte) 0x00, (short) (last - stackPtr)); @@ -380,13 +477,13 @@ private static void pushRsaSubjectKeyInfo() { } // SEQUENCE{SEQUENCE{ecPubKey, prime256v1}, bitString{pubKey}} - private static void pushEccSubjectKeyInfo() { + private void pushEccSubjectKeyInfo() { short last = stackPtr; pushBytes( - KMByteBlob.cast(pubKey).getBuffer(), - KMByteBlob.cast(pubKey).getStartOff(), - KMByteBlob.cast(pubKey).length()); - pushBitStringHeader((byte) 0x00, KMByteBlob.cast(pubKey).length()); + KMByteBlob.getBuffer(pubKey), + KMByteBlob.getStartOff(pubKey), + KMByteBlob.length(pubKey)); + pushBitStringHeader((byte) 0x00, KMByteBlob.length(pubKey)); pushEcDsa(); pushSequenceHeader((short) (last - stackPtr)); } @@ -415,22 +512,22 @@ private static void pushRsaEncryption() { // softwareEnforced AuthorizationList, # See below // hardwareEnforced AuthorizationList, # See below // } - private static void pushKeyDescription() { + private void pushKeyDescription() { short last = stackPtr; pushHWParams(); pushSWParams(); if (uniqueId != 0) { pushOctetString( - KMByteBlob.cast(uniqueId).getBuffer(), - KMByteBlob.cast(uniqueId).getStartOff(), - KMByteBlob.cast(uniqueId).length()); + KMByteBlob.getBuffer(uniqueId), + KMByteBlob.getStartOff(uniqueId), + KMByteBlob.length(uniqueId)); } else { pushOctetStringHeader((short) 0); } pushOctetString( - KMByteBlob.cast(attChallenge).getBuffer(), - KMByteBlob.cast(attChallenge).getStartOff(), - KMByteBlob.cast(attChallenge).length()); + KMByteBlob.getBuffer(attChallenge), + KMByteBlob.getStartOff(attChallenge), + KMByteBlob.length(attChallenge)); pushEnumerated(KMType.STRONGBOX); pushByte(KEYMASTER_VERSION); pushIntegerHeader((short) 1); @@ -443,53 +540,36 @@ private static void pushKeyDescription() { pushSequenceHeader((short) (last - stackPtr)); } - private static void pushSWParams() { + private void pushSWParams() { short last = stackPtr; - // Below are the allowed softwareEnforced Authorization tags inside the attestation certificate's extension. - short[] tagIds = { - KMType.ATTESTATION_APPLICATION_ID, KMType.CREATION_DATETIME, - KMType.USAGE_EXPIRE_DATETIME, KMType.ORIGINATION_EXPIRE_DATETIME, - KMType.ACTIVE_DATETIME, KMType.UNLOCKED_DEVICE_REQUIRED}; byte index = 0; + short length = (short) swTagIds.length; do { - pushParams(swParams, swParamsIndex, tagIds[index]); - } while (++index < tagIds.length); + pushParams(swParams, swParamsIndex, swTagIds[index]); + } while (++index < length); pushSequenceHeader((short) (last - stackPtr)); } - private static void pushHWParams() { + private void pushHWParams() { short last = stackPtr; - // Below are the allowed hardwareEnforced Authorization tags inside the attestation certificate's extension. - short[] tagIds = { - KMType.BOOT_PATCH_LEVEL, KMType.VENDOR_PATCH_LEVEL, - KMType.ATTESTATION_ID_MODEL, KMType.ATTESTATION_ID_MANUFACTURER, - KMType.ATTESTATION_ID_MEID, KMType.ATTESTATION_ID_IMEI, - KMType.ATTESTATION_ID_SERIAL, KMType.ATTESTATION_ID_PRODUCT, - KMType.ATTESTATION_ID_DEVICE, KMType.ATTESTATION_ID_BRAND, - KMType.OS_PATCH_LEVEL, KMType.OS_VERSION, KMType.ROOT_OF_TRUST, - KMType.ORIGIN, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, - KMType.NO_AUTH_REQUIRED, KMType.USER_SECURE_ID, - KMType.RSA_PUBLIC_EXPONENT, KMType.ECCURVE, KMType.MIN_MAC_LENGTH, - KMType.CALLER_NONCE, KMType.PADDING, KMType.DIGEST, KMType.BLOCK_MODE, - KMType.KEYSIZE, KMType.ALGORITHM, KMType.PURPOSE}; - byte index = 0; + short length = (short) hwTagIds.length; do { - if (tagIds[index] == KMType.ROOT_OF_TRUST) { + if (hwTagIds[index] == KMType.ROOT_OF_TRUST) { pushRoT(); continue; } - if (pushParams(hwParams, hwParamsIndex, tagIds[index])) { + if (pushParams(hwParams, hwParamsIndex, hwTagIds[index])) { continue; } - } while (++index < tagIds.length); + } while (++index < length); pushSequenceHeader((short) (last - stackPtr)); } - private static boolean pushParams(short[] params, short len, short tagId) { + private boolean pushParams(short[] params, short len, short tagId) { short index = 0; while (index < len) { - if (tagId == KMTag.getKey(params[index])) { + if (tagId == KMTag.getKMTagKey(params[index])) { pushTag(params[index]); return true; } @@ -498,50 +578,50 @@ private static boolean pushParams(short[] params, short len, short tagId) { return false; } - private static void pushTag(short tag) { - short type = KMTag.getTagType(tag); - short tagId = KMTag.getKey(tag); + private void pushTag(short tag) { + short type = KMTag.getKMTagType(tag); + short tagId = KMTag.getKMTagKey(tag); short val; switch (type) { case KMType.BYTES_TAG: - val = KMByteTag.cast(tag).getValue(); + val = KMByteTag.getValue(tag); pushBytesTag( tagId, - KMByteBlob.cast(val).getBuffer(), - KMByteBlob.cast(val).getStartOff(), - KMByteBlob.cast(val).length()); + KMByteBlob.getBuffer(val), + KMByteBlob.getStartOff(val), + KMByteBlob.length(val)); break; case KMType.ENUM_TAG: - val = KMEnumTag.cast(tag).getValue(); + val = KMEnumTag.getValue(tag); pushEnumTag(tagId, (byte) val); break; case KMType.ENUM_ARRAY_TAG: - val = KMEnumArrayTag.cast(tag).getValues(); + val = KMEnumArrayTag.getValues(tag); pushEnumArrayTag( tagId, - KMByteBlob.cast(val).getBuffer(), - KMByteBlob.cast(val).getStartOff(), - KMByteBlob.cast(val).length()); + KMByteBlob.getBuffer(val), + KMByteBlob.getStartOff(val), + KMByteBlob.length(val)); break; case KMType.UINT_TAG: case KMType.ULONG_TAG: case KMType.DATE_TAG: - val = KMIntegerTag.cast(tag).getValue(); + val = KMIntegerTag.getValue(tag); pushIntegerTag( tagId, - KMInteger.cast(val).getBuffer(), - KMInteger.cast(val).getStartOff(), - KMInteger.cast(val).length()); + KMInteger.getBuffer(val), + KMInteger.getStartOff(val), + KMInteger.length(val)); break; case KMType.UINT_ARRAY_TAG: case KMType.ULONG_ARRAY_TAG: // According to keymaster hal only one user secure id is used but this conflicts with // tag type which is ULONG-REP. Currently this is encoded as SET OF INTEGERS - val = KMIntegerArrayTag.cast(tag).getValues(); + val = KMIntegerArrayTag.getValues(tag); pushIntegerArrayTag(tagId, val); break; case KMType.BOOL_TAG: - val = KMBoolTag.cast(tag).getVal(); + KMBoolTag.validate(tag); pushBoolTag(tagId); break; default: @@ -562,22 +642,22 @@ private static void pushTag(short tag) { // Unverified (2), // Failed (3), // } - private static void pushRoT() { + private void pushRoT() { short last = stackPtr; // verified boot hash pushOctetString( - KMByteBlob.cast(verifiedHash).getBuffer(), - KMByteBlob.cast(verifiedHash).getStartOff(), - KMByteBlob.cast(verifiedHash).length()); + KMByteBlob.getBuffer(verifiedHash), + KMByteBlob.getStartOff(verifiedHash), + KMByteBlob.length(verifiedHash)); pushEnumerated(verifiedState); pushBoolean(deviceLocked); // verified boot Key pushOctetString( - KMByteBlob.cast(verifiedBootKey).getBuffer(), - KMByteBlob.cast(verifiedBootKey).getStartOff(), - KMByteBlob.cast(verifiedBootKey).length()); + KMByteBlob.getBuffer(verifiedBootKey), + KMByteBlob.getStartOff(verifiedBootKey), + KMByteBlob.length(verifiedBootKey)); // Finally sequence header pushSequenceHeader((short) (last - stackPtr)); @@ -616,17 +696,17 @@ private static void pushEnumArrayTag(short tagId, byte[] buf, short start, short // Only SET of INTEGERS supported are padding, digest, purpose and blockmode // All of these are enum array tags i.e. byte long values - private static void pushIntegerArrayTag(short tagId, short arr) { + private void pushIntegerArrayTag(short tagId, short arr) { short last = stackPtr; short index = 0; - short len = KMArray.cast(arr).length(); + short len = KMArray.length(arr); short ptr; while (index < len) { - ptr = KMArray.cast(arr).get(index); + ptr = KMArray.get(arr, index); pushInteger( - KMInteger.cast(ptr).getBuffer(), - KMInteger.cast(ptr).getStartOff(), - KMInteger.cast(ptr).length()); + KMInteger.getBuffer(ptr), + KMInteger.getStartOff(ptr), + KMInteger.length(ptr)); index++; } pushSetHeader((short) (last - stackPtr)); @@ -670,6 +750,7 @@ private static void pushEnumTag(short tagId, byte val) { private static void pushIntegerTag(short tagId, byte[] buf, short start, short len) { short last = stackPtr; pushInteger(buf, start, len); + // pushIntegerHeader((short) (last - stackPtr)); pushTagIdHeader(tagId, (short) (last - stackPtr)); } @@ -787,17 +868,17 @@ private static void pushBytes(byte[] buf, short start, short len) { private static void decrementStackPtr(short cnt) { stackPtr = (short) (stackPtr - cnt); - if (start > stackPtr) { + if (bufStart > stackPtr) { KMException.throwIt(KMError.UNKNOWN_ERROR); } } @Override - public KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen) { + public KMAttestationCert buffer(byte[] buf, short start, short maxLen) { stack = buf; - start = bufStart; - length = maxLen; - stackPtr = (short) (start + length); + bufStart = start; + bufLength = maxLen; + stackPtr = (short) (bufStart + bufLength); return this; } @@ -806,46 +887,110 @@ public short getCertStart() { return certStart; } - @Override - public short getCertEnd() { - return (short) (start + length - 1); - } - @Override public short getCertLength() { - return (short) (getCertEnd() - getCertStart() + 1); + return certLength; } - @Override - public void build() { + + public void build(KMAttestationKey factoryAttestKey, short attSecret, short attMod, + boolean rsaSign, boolean fakeCert) { + stackPtr = (short) (bufStart + bufLength); short last = stackPtr; - decrementStackPtr((short) ECDSA_MAX_SIG_LEN); - signatureOffset = stackPtr; + short sigLen = 0; + if (fakeCert) { + rsaSign = true; + pushByte((byte) 0); + sigLen = 1; + } + // Push placeholder signature Bit string header + // This will potentially change at the end + else if (rsaSign) { + decrementStackPtr(RSA_SIG_LEN); + } else { + decrementStackPtr(ECDSA_MAX_SIG_LEN); + } + short signatureOffset = stackPtr; pushBitStringHeader((byte) 0, (short) (last - stackPtr)); - pushAlgorithmId(X509SignAlgIdentifier); + if (rsaSign) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + } else { + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } tbsLength = stackPtr; - pushTbsCert(rsaCert); - tbsOffset = stackPtr; - tbsLength = (short) (tbsLength - tbsOffset); + pushTbsCert(rsaCert, rsaSign); + tbsStart = stackPtr; + tbsLength = (short) (tbsLength - tbsStart); + if (attSecret != KMType.INVALID_VALUE || factoryAttestKey != null) { + // Sign with the attestation key + // The pubKey is the modulus. + if (rsaSign) { + sigLen = seProvider + .rsaSign256Pkcs1( + KMByteBlob.getBuffer(attSecret), + KMByteBlob.getStartOff(attSecret), + KMByteBlob.length(attSecret), + KMByteBlob.getBuffer(attMod), + KMByteBlob.getStartOff(attMod), + KMByteBlob.length(attMod), + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if (sigLen > RSA_SIG_LEN) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } else if (factoryAttestKey != null) { + sigLen = seProvider + .ecSign256( + factoryAttestKey, + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if (sigLen > ECDSA_MAX_SIG_LEN) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } else { + sigLen = seProvider + .ecSign256( + KMByteBlob.getBuffer(attSecret), + KMByteBlob.getStartOff(attSecret), + KMByteBlob.length(attSecret), + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if (sigLen > ECDSA_MAX_SIG_LEN) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + // Adjust signature length + stackPtr = signatureOffset; + pushBitStringHeader((byte) 0, sigLen); + } else if (!fakeCert) { // no attestation key provisioned in the factory + KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } + last = (short) (signatureOffset + sigLen); + // Add certificate sequence header + stackPtr = tbsStart; pushSequenceHeader((short) (last - stackPtr)); certStart = stackPtr; - KMJCardSimulator provider = KMJCardSimulator.getInstance(); - short sigLen = provider - .ecSign256( - provider.getAttestationKey(), - stack, - tbsOffset, - tbsLength, - stack, - signatureOffset); - if (sigLen != ECDSA_MAX_SIG_LEN) { - // Update the lengths appropriately. - stackPtr = (short) (signatureOffset - 1); - pushLength((short) (sigLen + 1)); - stackPtr = tbsOffset; - last -= (short) (ECDSA_MAX_SIG_LEN - sigLen); - pushLength((short) (last - stackPtr)); - length -= (short) (ECDSA_MAX_SIG_LEN - sigLen); + certLength = (short) (last - certStart); + } + + + @Override + public void build() { + if (certMode == KMType.FAKE_CERT) { + build(null, KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true); + } else if (certMode == KMType.FACTORY_PROVISIONED_ATTEST_CERT) { + build(factoryAttestKey, KMType.INVALID_VALUE, KMType.INVALID_VALUE, false, false); + } else { + build(null, certAttestKeySecret, certAttestKeyRsaPubModulus, certRsaSign, false); } } @@ -872,24 +1017,88 @@ public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, scratchPadOff++; //Get the key data from the master key - KMAESKey aesKey = (KMAESKey) masterKey; + KMMasterKey aesKey = masterKey; short mKeyData = KMByteBlob.instance((short) (aesKey.getKeySizeBits() / 8)); aesKey.getKey( - KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ - KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/ + KMByteBlob.getBuffer(mKeyData), /* Key */ + KMByteBlob.getStartOff(mKeyData)); /* Key start*/ timeOffset = KMByteBlob.instance((short) 32); - appIdOff = KMJCardSimulator.getInstance().hmacSign( - KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ - KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/ - KMByteBlob.cast(mKeyData).length(), /* Key length*/ + appIdOff = seProvider.hmacSign( + KMByteBlob.getBuffer(mKeyData), /* Key */ + KMByteBlob.getStartOff(mKeyData), /* Key start*/ + KMByteBlob.length(mKeyData), /* Key length*/ scratchPad, /* data */ temp, /* data start */ scratchPadOff, /* data length */ - KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */ - KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */ + KMByteBlob.getBuffer(timeOffset), /* signature buffer */ + KMByteBlob.getStartOff(timeOffset)); /* signature start */ if (appIdOff != 32) { KMException.throwIt(KMError.UNKNOWN_ERROR); } return uniqueId(timeOffset); } + + @Override + public boolean serialNumber(short number) { + short length = KMByteBlob.length(number); + if (length > SERIAL_NUM_MAX_LEN) { + return false; + } + byte msb = KMByteBlob.get(number, (short) 0); + if (msb < 0 && length > (SERIAL_NUM_MAX_LEN - 1)) { + return false; + } + serialNum = number; + return true; + } + + @Override + public boolean subjectName(short sub) { + /* + short length = KMKeymasterApplet.cast(sub).length(); + if(length > SUBJECT_NAME_MAX_LEN){ + return false; + } + Util.arrayCopyNonAtomic(KMKeymasterApplet.cast(sub).getBuffer(), KMKeymasterApplet.cast(sub).getStartOff(), + subjectName,(short)0,length); + subjectLen = length; + */ + if (sub == KMType.INVALID_VALUE || KMByteBlob.length(sub) == 0) { + return false; + } + subjectName = sub; + return true; + } + + @Override + public KMAttestationCert ecAttestKey(short attestKey, byte mode) { + certMode = mode; + certAttestKeySecret = attestKey; + certAttestKeyRsaPubModulus = KMType.INVALID_VALUE; + certRsaSign = false; + return this; + } + + @Override + public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode) { + certMode = mode; + certAttestKeySecret = attestPrivExp; + certAttestKeyRsaPubModulus = attestMod; + certRsaSign = true; + return this; + } + + public KMAttestationCert factoryAttestKey(KMAttestationKey key, byte mode) { + certMode = mode; + factoryAttestKey = key; + return this; + } + + //Check + /* + * private void print(byte[] buf, short start, short length){ StringBuilder sb = + * new StringBuilder(length * 2); for(short i = start; i < (start+length); i + * ++){ sb.append(String.format("%02x", buf[i])); } System.out.println( + * sb.toString()); } + */ } diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java b/Applet/src/com/android/javacard/kmdevice/KMAttestationKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMAttestationKey.java rename to Applet/src/com/android/javacard/kmdevice/KMAttestationKey.java index 3d626bbf..1dd4667f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java +++ b/Applet/src/com/android/javacard/kmdevice/KMAttestationKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; /** * KMAttestationKey is a marker interface and the SE Provider has to implement this interface. @@ -23,3 +23,4 @@ public interface KMAttestationKey { } + diff --git a/Applet/src/com/android/javacard/kmdevice/KMBignumTag.java b/Applet/src/com/android/javacard/kmdevice/KMBignumTag.java new file mode 100644 index 00000000..bdafc67d --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMBignumTag.java @@ -0,0 +1,139 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMBignumTag represents BIGNUM Tag Type from android keymaster hal specifications. The tag value + * of this tag is the KMByteBlob pointer i.e. offset of KMByteBlob in memory heap. struct{byte + * TAG_TYPE; short length; struct{short BIGNUM_TAG; short tagKey; short blobPtr}} + */ + +public class KMBignumTag extends KMTag { + + private static KMBignumTag prototype; + + // The allowed tag keys of type bool tag + private static short[] tags; + + public static void initStatics() { + tags = new short[]{ + CERTIFICATE_SERIAL_NUM, + }; + } + + private KMBignumTag() { + } + + private static KMBignumTag proto(short ptr) { + if (prototype == null) { + prototype = new KMBignumTag(); + } + KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short blobPtr = KMByteBlob.exp(); + short ptr = instance(TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), INVALID_TAG); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), blobPtr); + return ptr; + } + + public static short instance(short key) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return instance(key, KMByteBlob.exp()); + } + + public static short instance(short key, short byteBlob) { + if (!validateKey(key)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + if (heap[byteBlob] != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BIGNUM_TAG); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), key); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), byteBlob); + return ptr; + } + + private static KMBignumTag cast(short ptr) { + if (heap[ptr] != TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BIGNUM_TAG) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getKey() { + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + public short getTagType() { + return KMType.BIGNUM_TAG; + } + + public short getValue() { + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + public short length() { + short blobPtr = Util.getShort(heap, + (short) (KMType.instanceTable[KM_BIGNUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return KMByteBlob.length(blobPtr); + } + + public static short getKey(short bPtr) { + return KMBignumTag.cast(bPtr).getKey(); + } + + public static short getTagType(short bPtr) { + return KMBignumTag.cast(bPtr).getTagType(); + } + + public static short getValue(short bPtr) { + return KMBignumTag.cast(bPtr).getValue(); + } + + public static short length(short bPtr) { + return KMBignumTag.cast(bPtr).length(); + } + + private static boolean validateKey(short key) { + short index = (short) tags.length; + while (--index >= 0) { + if (tags[index] == key) { + return true; + } + } + return false; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/src/com/android/javacard/kmdevice/KMBoolTag.java similarity index 68% rename from Applet/src/com/android/javacard/keymaster/KMBoolTag.java rename to Applet/src/com/android/javacard/kmdevice/KMBoolTag.java index 69619c0d..453eccb2 100644 --- a/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/src/com/android/javacard/kmdevice/KMBoolTag.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -32,29 +32,34 @@ public class KMBoolTag extends KMTag { private static KMBoolTag prototype; // The allowed tag keys of type bool tag. - private static final short[] tags = { - CALLER_NONCE, - INCLUDE_UNIQUE_ID, - BOOTLOADER_ONLY, - ROLLBACK_RESISTANCE, - NO_AUTH_REQUIRED, - ALLOW_WHILE_ON_BODY, - TRUSTED_USER_PRESENCE_REQUIRED, - TRUSTED_CONFIRMATION_REQUIRED, - UNLOCKED_DEVICE_REQUIRED, - RESET_SINCE_ID_ROTATION, - EARLY_BOOT_ONLY, - DEVICE_UNIQUE_ATTESTATION - }; + private static short[] tags; private KMBoolTag() { } + public static void initStatics() { + // The allowed tag keys of type bool tag. + tags = new short[]{ + CALLER_NONCE, + INCLUDE_UNIQUE_ID, + BOOTLOADER_ONLY, + ROLLBACK_RESISTANCE, + NO_AUTH_REQUIRED, + ALLOW_WHILE_ON_BODY, + TRUSTED_USER_PRESENCE_REQUIRED, + TRUSTED_CONFIRMATION_REQUIRED, + UNLOCKED_DEVICE_REQUIRED, + RESET_SINCE_ID_ROTATION, + EARLY_BOOT_ONLY, + DEVICE_UNIQUE_ATTESTATION + }; + } + private static KMBoolTag proto(short ptr) { if (prototype == null) { prototype = new KMBoolTag(); } - instanceTable[KM_BOOL_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_BOOL_TAG_OFFSET] = ptr; return prototype; } @@ -67,7 +72,7 @@ public static short exp() { public static short instance(short key) { if (!validateKey(key)) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMException.throwIt(KMError.INVALID_TAG); } short ptr = KMType.instance(TAG_TYPE, (short) 5); Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), BOOL_TAG); @@ -77,18 +82,23 @@ public static short instance(short key) { return ptr; } - public static KMBoolTag cast(short ptr) { + private static KMBoolTag cast(short ptr) { + validate(ptr); + return proto(ptr); + } + + public static void validate(short ptr) { if (heap[ptr] != TAG_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } if (Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)) != BOOL_TAG) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - return proto(ptr); } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -96,7 +106,19 @@ public short getTagType() { } public byte getVal() { - return heap[(short) (instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; + return heap[(short) (KMType.instanceTable[KM_BOOL_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; + } + + public static short getKey(short bPtr) { + return KMBoolTag.cast(bPtr).getKey(); + } + + public static short getTagType(short bPtr) { + return KMBoolTag.cast(bPtr).getTagType(); + } + + public static byte getVal(short bPtr) { + return KMBoolTag.cast(bPtr).getVal(); } // validate the tag key. @@ -113,4 +135,5 @@ private static boolean validateKey(short key) { public static short[] getTags() { return tags; } + } diff --git a/Applet/src/com/android/javacard/kmdevice/KMBootDataStore.java b/Applet/src/com/android/javacard/kmdevice/KMBootDataStore.java new file mode 100644 index 00000000..ca875b96 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMBootDataStore.java @@ -0,0 +1,45 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +public interface KMBootDataStore { + + /** + * Get Verified Boot hash. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getVerifiedBootHash(byte[] buffer, short start); + + /** + * Get Boot Key. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getBootKey(byte[] buffer, short start); + + /** + * Get Boot state. Part of RoT. Part of data sent by the aosp bootloader. + */ + short getBootState(); + + /** + * Returns true if device bootloader is locked. Part of RoT. Part of data sent by the aosp + * bootloader. + */ + boolean isDeviceBootLocked(); + + /** + * Get Boot patch level. Part of data sent by the aosp bootloader. + */ + short getBootPatchLevel(byte[] buffer, short start); +} diff --git a/Applet/src/com/android/javacard/keymaster/KMByteBlob.java b/Applet/src/com/android/javacard/kmdevice/KMByteBlob.java similarity index 57% rename from Applet/src/com/android/javacard/keymaster/KMByteBlob.java rename to Applet/src/com/android/javacard/kmdevice/KMByteBlob.java index d980bd9a..97b73bec 100644 --- a/Applet/src/com/android/javacard/keymaster/KMByteBlob.java +++ b/Applet/src/com/android/javacard/kmdevice/KMByteBlob.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -27,16 +27,17 @@ */ public class KMByteBlob extends KMType { + private static short OFFSET_SIZE = 2; private static KMByteBlob prototype; - private KMByteBlob() { + protected KMByteBlob() { } private static KMByteBlob proto(short ptr) { if (prototype == null) { prototype = new KMByteBlob(); } - instanceTable[KM_BYTE_BLOB_OFFSET] = ptr; + KMType.instanceTable[KM_BYTE_BLOB_OFFSET] = ptr; return prototype; } @@ -47,25 +48,34 @@ public static short exp() { // return an empty byte blob instance public static short instance(short length) { - return KMType.instance(BYTE_BLOB_TYPE, length); + short ptr = KMType.instance(BYTE_BLOB_TYPE, (short) (length + OFFSET_SIZE)); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), + (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE)); + Util.setShort(heap, (short) (ptr + 1), length); + return ptr; } // byte blob from existing buf public static short instance(byte[] buf, short startOff, short length) { short ptr = instance(length); - Util.arrayCopyNonAtomic(buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE), length); + Util.arrayCopyNonAtomic(buf, startOff, heap, (short) (ptr + TLV_HEADER_SIZE + OFFSET_SIZE), + length); return ptr; } // cast the ptr to KMByteBlob - public static KMByteBlob cast(short ptr) { + private static KMByteBlob cast(short ptr) { + validate(ptr); + return proto(ptr); + } + + public static void validate(short ptr) { if (heap[ptr] != BYTE_BLOB_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - return proto(ptr); } // Add the byte @@ -74,7 +84,7 @@ public void add(short index, byte val) { if (index >= len) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - heap[(short) (instanceTable[KM_BYTE_BLOB_OFFSET] + TLV_HEADER_SIZE + index)] = val; + heap[(short) (getStartOff() + index)] = val; } // Get the byte @@ -83,17 +93,17 @@ public byte get(short index) { if (index >= len) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - return heap[(short) (instanceTable[KM_BYTE_BLOB_OFFSET] + TLV_HEADER_SIZE + index)]; + return heap[(short) (getStartOff() + index)]; } // Get the start of blob public short getStartOff() { - return (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + TLV_HEADER_SIZE); + return Util.getShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE)); } // Get the length of the blob public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + 1)); + return Util.getShort(heap, (short) (getBaseOffset() + 1)); } // Get the buffer pointer in which blob is contained. @@ -112,26 +122,71 @@ public short getValues(byte[] destBuf, short destStart) { } public void setValue(byte[] srcBuf, short srcStart, short srcLength) { - if (length() > srcLength) { + if (length() < srcLength) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), length()); + Util.arrayCopyNonAtomic(srcBuf, srcStart, heap, getStartOff(), srcLength); + setLength(srcLength); } public boolean isValid() { - if (length() == 0) { - return false; - } - return true; + return (length() != 0); + } + + public void setStartOff(short offset) { + Util.setShort(heap, (short) (getBaseOffset() + TLV_HEADER_SIZE), offset); } - public void decrementLength(short len) { - short length = Util.getShort(heap, (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + 1)); - length = (short) (length - len); - Util.setShort(heap, (short) (instanceTable[KM_BYTE_BLOB_OFFSET] + 1), length); + protected short getBaseOffset() { + return instanceTable[KM_BYTE_BLOB_OFFSET]; } public void setLength(short len) { - Util.setShort(heap, (short)(instanceTable[KM_BYTE_BLOB_OFFSET] + 1), len); + Util.setShort(heap, (short) (getBaseOffset() + 1), len); + } + + + public static void add(short bPtr, short index, byte val) { + cast(bPtr).add(index, val); + } + + public static byte get(short bPtr, short index) { + return cast(bPtr).get(index); + } + + public static short getStartOff(short bPtr) { + return cast(bPtr).getStartOff(); + } + + public static short length(short bPtr) { + return cast(bPtr).length(); + } + + public static byte[] getBuffer(short bPtr) { + return cast(bPtr).getBuffer(); + } + + public static void getValue(short bPtr, byte[] destBuf, short destStart, short destLength) { + cast(bPtr).getValue(destBuf, destStart, destLength); + } + + public static short getValues(short bPtr, byte[] destBuf, short destStart) { + return cast(bPtr).getValues(destBuf, destStart); + } + + public static void setValue(short bPtr, byte[] srcBuf, short srcStart, short srcLength) { + cast(bPtr).setValue(srcBuf, srcStart, srcLength); + } + + public static boolean isValid(short bPtr) { + return cast(bPtr).isValid(); + } + + public static void setStartOff(short bPtr, short offset) { + cast(bPtr).setStartOff(offset); + } + + public static void setLength(short bPtr, short len) { + cast(bPtr).setLength(len); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/src/com/android/javacard/kmdevice/KMByteTag.java similarity index 65% rename from Applet/src/com/android/javacard/keymaster/KMByteTag.java rename to Applet/src/com/android/javacard/kmdevice/KMByteTag.java index 89401e4f..a5507515 100644 --- a/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/src/com/android/javacard/kmdevice/KMByteTag.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -31,36 +31,42 @@ public class KMByteTag extends KMTag { private static KMByteTag prototype; // The allowed tag keys of type bool tag - private static final short[] tags = { - APPLICATION_ID, - APPLICATION_DATA, - ROOT_OF_TRUST, - UNIQUE_ID, - ATTESTATION_CHALLENGE, - ATTESTATION_APPLICATION_ID, - ATTESTATION_ID_BRAND, - ATTESTATION_ID_DEVICE, - ATTESTATION_ID_PRODUCT, - ATTESTATION_ID_SERIAL, - ATTESTATION_ID_IMEI, - ATTESTATION_ID_MEID, - ATTESTATION_ID_MANUFACTURER, - ATTESTATION_ID_MODEL, - ASSOCIATED_DATA, - NONCE, - CONFIRMATION_TOKEN, - VERIFIED_BOOT_KEY, - VERIFIED_BOOT_HASH - }; + private static short[] tags; private KMByteTag() { } + public static void initStatics() { + tags = new short[]{ + APPLICATION_ID, + APPLICATION_DATA, + ROOT_OF_TRUST, + UNIQUE_ID, + ATTESTATION_CHALLENGE, + ATTESTATION_APPLICATION_ID, + ATTESTATION_ID_BRAND, + ATTESTATION_ID_DEVICE, + ATTESTATION_ID_PRODUCT, + ATTESTATION_ID_SERIAL, + ATTESTATION_ID_IMEI, + ATTESTATION_ID_MEID, + ATTESTATION_ID_MANUFACTURER, + ATTESTATION_ID_MODEL, + ASSOCIATED_DATA, + NONCE, + CONFIRMATION_TOKEN, + VERIFIED_BOOT_KEY, + VERIFIED_BOOT_HASH, + CERTIFICATE_SERIAL_NUM, + CERTIFICATE_SUBJECT_NAME, + }; + } + private static KMByteTag proto(short ptr) { if (prototype == null) { prototype = new KMByteTag(); } - instanceTable[KM_BYTE_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_BYTE_TAG_OFFSET] = ptr; return prototype; } @@ -95,7 +101,7 @@ public static short instance(short key, short byteBlob) { return ptr; } - public static KMByteTag cast(short ptr) { + private static KMByteTag cast(short ptr) { if (heap[ptr] != TAG_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -106,7 +112,8 @@ public static KMByteTag cast(short ptr) { } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -114,12 +121,30 @@ public short getTagType() { } public short getValue() { - return Util.getShort(heap, (short) (instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { - short blobPtr = Util.getShort(heap, (short) (instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); - return KMByteBlob.cast(blobPtr).length(); + short blobPtr = Util.getShort(heap, + (short) (KMType.instanceTable[KM_BYTE_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return KMByteBlob.length(blobPtr); + } + + public static short getKey(short bPtr) { + return KMByteTag.cast(bPtr).getKey(); + } + + public static short getTagType(short bPtr) { + return KMByteTag.cast(bPtr).getTagType(); + } + + public static short getValue(short bPtr) { + return KMByteTag.cast(bPtr).getValue(); + } + + public static short length(short bPtr) { + return KMByteTag.cast(bPtr).length(); } private static boolean validateKey(short key) { diff --git a/Applet/src/com/android/javacard/keymaster/KMComputedHmacKey.java b/Applet/src/com/android/javacard/kmdevice/KMComputedHmacKey.java similarity index 50% rename from Applet/src/com/android/javacard/keymaster/KMComputedHmacKey.java rename to Applet/src/com/android/javacard/kmdevice/KMComputedHmacKey.java index 9621b417..82c55785 100644 --- a/Applet/src/com/android/javacard/keymaster/KMComputedHmacKey.java +++ b/Applet/src/com/android/javacard/kmdevice/KMComputedHmacKey.java @@ -1,5 +1,6 @@ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; public interface KMComputedHmacKey { + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/src/com/android/javacard/kmdevice/KMConfigurations.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java rename to Applet/src/com/android/javacard/kmdevice/KMConfigurations.java index 6e5090a1..85c7aaf5 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/src/com/android/javacard/kmdevice/KMConfigurations.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; public class KMConfigurations { + // Machine types public static final byte LITTLE_ENDIAN = 0x00; public static final byte BIG_ENDIAN = 0x01; diff --git a/Applet/src/com/android/javacard/kmdevice/KMCose.java b/Applet/src/com/android/javacard/kmdevice/KMCose.java new file mode 100644 index 00000000..e9e05d0e --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCose.java @@ -0,0 +1,591 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; + +/** + * This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, CoseSign1, + * SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures. + */ +public class KMCose { + + //COSE SIGN1 + public static final byte COSE_SIGN1_ENTRY_COUNT = 4; + public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0; + public static final short COSE_SIGN1_UNPROTECTED_PARAMS_OFFSET = 1; + public static final short COSE_SIGN1_PAYLOAD_OFFSET = 2; + public static final short COSE_SIGN1_SIGNATURE_OFFSET = 3; + //COSE MAC0 + public static final short COSE_MAC0_ENTRY_COUNT = 4; + public static final short COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0; + public static final short COSE_MAC0_UNPROTECTED_PARAMS_OFFSET = 1; + public static final short COSE_MAC0_PAYLOAD_OFFSET = 2; + public static final short COSE_MAC0_TAG_OFFSET = 3; + //COSE ENCRYPT + public static final short COSE_ENCRYPT_ENTRY_COUNT = 4; + public static final short COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3; + public static final short COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3; + public static final short COSE_ENCRYPT_PROTECTED_PARAMS_OFFSET = 0; + public static final short COSE_ENCRYPT_UNPROTECTED_PARAMS_OFFSET = 1; + public static final short COSE_ENCRYPT_PAYLOAD_OFFSET = 2; + public static final short COSE_ENCRYPT_RECIPIENTS_OFFSET = 3; + + //COSE Labels + public static final byte COSE_LABEL_ALGORITHM = 1; + public static final byte COSE_LABEL_KEYID = 4; + public static final byte COSE_LABEL_IV = 5; + public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1 + + //COSE Algorithms + public static final byte COSE_ALG_AES_GCM_256 = 3; //AES-GCM mode w/ 256-bit key, 128-bit tag. + public static final byte COSE_ALG_HMAC_256 = 5; //HMAC w/ SHA-256 + public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7 + public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25 + + //COSE P256 EC Curve + public static final byte COSE_ECCURVE_256 = 1; + + //COSE key types + public static final byte COSE_KEY_TYPE_EC2 = 2; + public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4; + + //COSE Key Operations + public static final byte COSE_KEY_OP_SIGN = 1; + public static final byte COSE_KEY_OP_VERIFY = 2; + public static final byte COSE_KEY_OP_ENCRYPT = 3; + public static final byte COSE_KEY_OP_DECRYPT = 4; + + // AES GCM + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final short AES_GCM_TAG_SIZE = 16; + public static final short AES_GCM_KEY_SIZE = 32; + public static final short AES_GCM_KEY_SIZE_BITS = 256; + // Cose key parameters. + public static final byte COSE_KEY_KEY_TYPE = 1; + public static final byte COSE_KEY_KEY_ID = 2; + public static final byte COSE_KEY_ALGORITHM = 3; + public static final byte COSE_KEY_KEY_OPS = 4; + public static final byte COSE_KEY_CURVE = -1; + public static final byte COSE_KEY_PUBKEY_X = -2; + public static final byte COSE_KEY_PUBKEY_Y = -3; + public static final byte COSE_KEY_PRIV_KEY = -4; + public static byte[] COSE_TEST_KEY; // -70000 + public static final short COSE_KEY_MAX_SIZE = 4; + + // kdfcontext strings + public static byte[] client; + public static byte[] server; + //Context strings + public static byte[] MAC_CONTEXT; // MAC0 + public static byte[] SIGNATURE1_CONTEXT; // Signature1 + public static byte[] ENCRYPT_CONTEXT; // Encrypt + //Empty strings + public static byte[] EMPTY_MAC_KEY; // "Empty MAC key" + + // Certificate payload supported keys + public static final byte ISSUER = (byte) 0x01; + public static final byte SUBJECT = (byte) 0x02; + public static byte[] CODE_HASH; + public static byte[] CODE_DESCRIPTOR; + public static byte[] CONFIG_HASH; + public static byte[] CONFIG_DESCRIPTOR; + public static byte[] AUTHORITY_HASH; + public static byte[] AUTHORITY_DESCRIPTOR; + public static byte[] MODE; + public static byte[] SUBJECT_PUBLIC_KEY; + public static byte[] KEY_USAGE; + // text strings + public static byte[] TEST_ISSUER_NAME; // "Issuer" + public static byte[] TEST_SUBJECT_NAME; // "Subject" + public static byte[] KEY_USAGE_SIGN; // Key usage sign + public static byte[] MAC_DERIVE_KEY_CTX; // "Key to MAC public keys" + + private static KMCose prototype; + + public static void initStatics() { + COSE_TEST_KEY = new byte[]{(byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90}; // -70000 + client = new byte[]{0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74}; + server = new byte[]{0x73, 0x65, 0x72, 0x76, 0x65, 0x72}; + MAC_CONTEXT = new byte[]{0x4d, 0x41, 0x43, 0x30}; // MAC0 + SIGNATURE1_CONTEXT = new byte[] + {0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31}; // Signature1 + ENCRYPT_CONTEXT = new byte[]{0x45, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74}; // Encrypt + EMPTY_MAC_KEY = new byte[] + {0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, + 0x79}; // "Empty MAC key" + + CODE_HASH = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAF}; + CODE_DESCRIPTOR = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAE}; + CONFIG_HASH = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAD}; + CONFIG_DESCRIPTOR = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAC}; + AUTHORITY_HASH = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAB}; + AUTHORITY_DESCRIPTOR = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAA}; + MODE = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA9}; + SUBJECT_PUBLIC_KEY = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8}; + KEY_USAGE = new byte[]{(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7}; + // text strings + TEST_ISSUER_NAME = new byte[]{(byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72}; // "Issuer" + TEST_SUBJECT_NAME = new byte[]{0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74}; // "Subject" + KEY_USAGE_SIGN = new byte[]{0x20}; // Key usage sign + MAC_DERIVE_KEY_CTX = new byte[] + {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 KMCose getInstance() { + if (prototype == null) { + prototype = new KMCose(); + } + return prototype; + } + + /** + * Constructs the Cose MAC structure. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param extAad Bstr pointer which holds the external Aad. + * @param payload Bstr pointer which holds the payload of the MAC structure. + * @return KMArray instance of MAC structure. + */ + public short constructCoseMacStructure(short protectedHeader, short extAad, short payload) { + // Create MAC Structure and compute HMAC as per https://tools.ietf.org/html/rfc8152#section-6.3 + // MAC_structure = [ + // context : "MAC" / "MAC0", + // protected : empty_or_serialized_map, + // external_aad : bstr, + // payload : bstr + // ] + short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT); + // 1 - Context + KMArray.add(arrPtr, (short) 0, KMTextString.instance(KMCose.MAC_CONTEXT, (short) 0, + (short) KMCose.MAC_CONTEXT.length)); + // 2 - Protected headers. + KMArray.add(arrPtr, (short) 1, protectedHeader); + // 3 - external aad + KMArray.add(arrPtr, (short) 2, extAad); + // 4 - payload. + KMArray.add(arrPtr, (short) 3, payload); + return arrPtr; + } + + /** + * Constructs the COSE_MAC0 object. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param unprotectedHeader Bstr pointer which holds the unprotected header. + * @param payload Bstr pointer which holds the payload of the MAC structure. + * @param tag Bstr pointer which holds the tag value. + * @return KMArray instance of COSE_MAC0 object. + */ + public short constructCoseMac0(short protectedHeader, short unprotectedHeader, short payload, + short tag) { + // Construct Cose_MAC0 + // COSE_Mac0 = [ + // protectedHeader, + // unprotectedHeader, + // payload : bstr / nil, + // tag : bstr, + // ] + short arrPtr = KMArray.instance(KMCose.COSE_MAC0_ENTRY_COUNT); + // 1 - protected headers + KMArray.add(arrPtr, (short) 0, protectedHeader); + // 2 - unprotected headers + KMArray.add(arrPtr, (short) 1, unprotectedHeader); + // 2 - payload + KMArray.add(arrPtr, (short) 2, payload); + // 3 - tag + KMArray.add(arrPtr, (short) 3, tag); + // Do encode. + return arrPtr; + } + + /** + * Constructs the COSE_Signature structure. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param extAad Bstr pointer which holds the aad. + * @param payload Bstr pointer which holds the payload. + * @return KMArray instance of COSE_Signature object. + */ + public short constructCoseSignStructure(short protectedHeader, short extAad, short payload) { + // Sig_structure = [ + // context : "Signature" / "Signature1" / "CounterSignature", + // body_protected : empty_or_serialized_map, + // ? sign_protected : empty_or_serialized_map, + // external_aad : bstr, + // payload : bstr + // ] + short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT); + // 1 - Context + KMArray.add(arrPtr, (short) 0, KMTextString.instance(KMCose.SIGNATURE1_CONTEXT, (short) 0, + (short) KMCose.SIGNATURE1_CONTEXT.length)); + // 2 - Protected headers. + KMArray.add(arrPtr, (short) 1, protectedHeader); + // 3 - external aad + KMArray.add(arrPtr, (short) 2, extAad); + // 4 - payload. + KMArray.add(arrPtr, (short) 3, payload); + return arrPtr; + } + + /** + * Constructs the COSE_Sign1 object. + * + * @param protectedHeader Bstr pointer which holds the protected header. + * @param unProtectedHeader Bstr pointer which holds the unprotected header. + * @param payload Bstr pointer which holds the payload. + * @param signature Bstr pointer which holds the signature. + * @return KMArray instance of COSE_Sign1 object. + */ + public short constructCoseSign1(short protectedHeader, short unProtectedHeader, short payload, + short signature) { + // COSE_Sign = [ + // protectedHeader, + // unprotectedHeader, + // payload : bstr / nil, + // signatures : [+ COSE_Signature] + // ] + short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT); + // 1 - protected headers + KMArray.add(arrPtr, (short) 0, protectedHeader); + // 2 - unprotected headers + KMArray.add(arrPtr, (short) 1, unProtectedHeader); + // 2 - payload + KMArray.add(arrPtr, (short) 2, payload); + // 3 - tag + KMArray.add(arrPtr, (short) 3, signature); + return arrPtr; + } + + /** + * Constructs array based on the tag values provided. + * + * @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 short handleCosePairTags(short[] tags, 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++; + } + index += 3; + } + 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.add(arrPtr, var++, tags[(short) (index + 2)]); + } + index += 3; + } + return arrPtr; + } + + /** + * Constructs the COSE_sign1 payload for certificate. + * + * @param issuer instance of KMCosePairTextStringTag which contains issuer value. + * @param subject instance of KMCosePairTextStringTag which contains subject value. + * @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey. + * @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value. + * @return instance of KMArray. + */ + public short constructCoseCertPayload(short issuer, short subject, short subPublicKey, + short keyUsage) { + short certPayload = KMArray.instance((short) 4); + KMArray.add(certPayload, (short) 0, issuer); + KMArray.add(certPayload, (short) 1, subject); + KMArray.add(certPayload, (short) 2, subPublicKey); + KMArray.add(certPayload, (short) 3, keyUsage); + certPayload = KMCoseCertPayload.instance(certPayload); + KMCoseCertPayload.cast(certPayload).canonicalize(); + return certPayload; + } + + /** + * Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, COSE_Mac0 and + * COSE_Key. + * + * @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value. + * @param keyId instance of KMByteBlob which contains the key identifier. + * @param iv instance of KMByteblob which contains the iv buffer. + * @param ephemeralKey instance of KMCoseKey. + * @return instance of KMCoseHeaders. + */ + public 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); + ptr = KMCoseHeaders.instance(ptr); + KMCoseHeaders.cast(ptr).canonicalize(); + return ptr; + } + + /** + * Construct Recipients structure for COSE_Encrypt message. + * + * @param protectedHeaders instance of KMByteBlob which contains encoded KMCoseHeaders. + * @param unprotectedHeaders instance of KMCoseHeaders. + * @param cipherText instance of KMSimple + * @return instance of KMArray. + */ + public short constructRecipientsStructure(short protectedHeaders, short unprotectedHeaders, + short cipherText) { + // recipients : [+COSE_recipient] + // COSE_recipient = [ + // Headers, + // ciphertext : bstr / nil, + // ? recipients : [+COSE_recipient] + // ] + short arrPtr = KMArray.instance(COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT); + // 1 - protected headers + KMArray.add(arrPtr, (short) 0, protectedHeaders); + // 2 - unprotected headers + KMArray.add(arrPtr, (short) 1, unprotectedHeaders); + // 2 - payload + KMArray.add(arrPtr, (short) 2, cipherText); + + short recipientsArrayPtr = KMArray.instance((short) 1); + KMArray.add(recipientsArrayPtr, (short) 0, arrPtr); + return recipientsArrayPtr; + } + + /** + * Construct Encrypt structure required for COSE_Encrypt message. + * + * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders. + * @param aad instance of KMByteBlob. + * @return instance of KMArray. + */ + public short constructCoseEncryptStructure(short protectedHeader, short aad) { + // Enc_structure = [ + // context : "Encrypt" / "Encrypt0" / "Enc_Recipient" / + // "Mac_Recipient" / "Rec_Recipient", + // protected : empty_or_serialized_map, + // external_aad : bstr + // ] + short arrPtr = KMArray.instance(COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT); + // 1 - protected headers + KMArray.add(arrPtr, (short) 0, KMTextString.instance(KMCose.ENCRYPT_CONTEXT, (short) 0, + (short) KMCose.ENCRYPT_CONTEXT.length)); + // 2 - unprotected headers + KMArray.add(arrPtr, (short) 1, protectedHeader); + // 2 - payload + KMArray.add(arrPtr, (short) 2, aad); + return arrPtr; + } + + /** + * Constructs COSE_Encrypt message. + * + * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders. + * @param unProtectedHeader instance of KMCoseHeaders. + * @param cipherText instance of KMByteBlob containing the cipher text. + * @param recipients instance of KMArray containing the recipients instance + * @return instance of KMArray. + */ + public short constructCoseEncrypt(short protectedHeader, short unProtectedHeader, + short cipherText, + short recipients) { + // COSE_Encrypt = [ + // protectedHeader, + // unprotectedHeader, + // ciphertext : bstr / nil, + // recipients : [+COSE_recipient] + // ] + short arrPtr = KMArray.instance(KMCose.COSE_ENCRYPT_ENTRY_COUNT); + // 1 - protected headers + KMArray.add(arrPtr, (short) 0, protectedHeader); + // 2 - unprotected headers + KMArray.add(arrPtr, (short) 1, unProtectedHeader); + // 2 - payload + KMArray.add(arrPtr, (short) 2, cipherText); + // 3 - tag + KMArray.add(arrPtr, (short) 3, recipients); + return arrPtr; + } + + /** + * Constructs the instance of KMCosePair*Tag. + * + * @param key value of the key. + * @param valuePtr instance of one of KMType. + * @return instance of KMCosePair*Value object. + */ + public short buildCosePairTag(byte key, short valuePtr) { + short type = KMType.getKMType(valuePtr); + short keyPtr; + if (key < 0) { + keyPtr = KMNInteger.uint_8(key); + } else { + keyPtr = KMInteger.uint_8(key); + } + switch (type) { + case KMType.INTEGER_TYPE: + return KMCosePairIntegerTag.instance(keyPtr, valuePtr); + case KMType.NEG_INTEGER_TYPE: + return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr); + case KMType.BYTE_BLOB_TYPE: + return KMCosePairByteBlobTag.instance(keyPtr, valuePtr); + case KMType.TEXT_STRING_TYPE: + return KMCosePairTextStringTag.instance(keyPtr, valuePtr); + case KMType.COSE_KEY_TYPE: + return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + /** + * Constructs a CoseKey with the provided input paramters. + * + * @param keyType Instance of the identification of the key type. + * @param keyId Instance of key identification value. + * @param keyAlg Instance of the algorithm that is used with this key. + * @param keyOps Instance of the operation that this key is used for. + * @param curve Instance of the EC curve that is used with this key. + * @param pubKey Buffer containing the public key. + * @param pubKeyOff Start offset of the buffer. + * @param pubKeyLen Length of the public key. + * @param privKeyPtr Instance of the private key. + * @param testMode Represents if key is used in test mode or production mode. + * @return Instance of the CoseKey structure. + */ + public short constructCoseKey(short keyType, short keyId, short keyAlg, short keyOps, + short curve, byte[] pubKey, short pubKeyOff, short pubKeyLen, + short privKeyPtr, boolean testMode) { + if (pubKey[pubKeyOff] == 0x04) { // uncompressed format + pubKeyOff += 1; + pubKeyLen -= 1; + } + pubKeyLen = (short) (pubKeyLen / 2); + short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen); + short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen); + short coseKey = constructCoseKey(keyType, keyId, keyAlg, keyOps, curve, xPtr, yPtr, privKeyPtr, + testMode); + KMCoseKey.cast(coseKey).canonicalize(); + return coseKey; + } + + /** + * Constructs the cose key based on input parameters supplied. All the parameters must be + * instantiated from either KMInteger or KMNInteger or KMByteblob types. + * + * @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types. + * @param keyId instance of KMByteBlob which holds key identifier value. + * @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm. + * @param keyOps instance of KMInteger/KMNInteger which holds valid COSE key operations. + * @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve. + * @param pubX instance of KMByteBlob which holds EC public key's x value. + * @param pubY instance of KMByteBlob which holds EC public key's y value. + * @param priv instance of KMByteBlob which holds EC private value. + * @param includeTestKey flag which identifies whether to construct test key or production key. + * @return instance of the KMCoseKey object. + */ + public short constructCoseKey(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); + if (includeTestKey) { + short testKey = + KMCosePairSimpleValueTag.instance(KMNInteger.uint_32(KMCose.COSE_TEST_KEY, (short) 0), + KMSimpleValue.instance(KMSimpleValue.NULL)); + KMArray.add(arrPtr, (short) (KMArray.length(arrPtr) - 1), testKey); + } + arrPtr = KMCoseKey.instance(arrPtr); + KMCoseKey.cast(arrPtr).canonicalize(); + return arrPtr; + } + + /** + * Constructs key derivation context which is required to compute HKDF. + * + * @param publicKeyA public key buffer from the first party. + * @param publicKeyAOff start position of the public key buffer from first party. + * @param publicKeyALen length of the public key buffer from first party. + * @param publicKeyB public key buffer from the second party. + * @param publicKeyBOff start position of the public key buffer from second party. + * @param publicKeyBLen length of the public key buffer from second party. + * @param senderIsA true if caller is first party, false if caller is second party. + * @return instance of KMArray. + */ + public short constructKdfContext(byte[] publicKeyA, short publicKeyAOff, short publicKeyALen, + byte[] publicKeyB, short publicKeyBOff, short publicKeyBLen, + boolean senderIsA) { + short index = 0; + // Prepare sender info + short senderInfo = KMArray.instance((short) 3); + KMArray.add(senderInfo, index++, KMByteBlob.instance(client, (short) 0, (short) client.length)); + KMArray.add(senderInfo, index++, KMByteBlob.instance((short) 0)); + KMArray.add(senderInfo, index, senderIsA ? + KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen) : + KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)); + + // Prepare recipient info + index = 0; + short recipientInfo = KMArray.instance((short) 3); + KMArray.add(recipientInfo, index++, + KMByteBlob.instance(server, (short) 0, (short) server.length)); + KMArray.add(recipientInfo, index++, KMByteBlob.instance((short) 0)); + KMArray.add(recipientInfo, index, senderIsA ? + KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen) : + KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)); + + // supply public info + index = 0; + short publicInfo = KMArray.instance((short) 2); + KMArray.add(publicInfo, index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS)); + KMArray.add(publicInfo, index, KMByteBlob.instance((short) 0)); + + // construct kdf context + index = 0; + short arrPtr = KMArray.instance((short) 4); + KMArray.add(arrPtr, index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256)); + KMArray.add(arrPtr, index++, senderInfo); + KMArray.add(arrPtr, index++, recipientInfo); + KMArray.add(arrPtr, index, publicInfo); + + return arrPtr; + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCoseCertPayload.java b/Applet/src/com/android/javacard/kmdevice/KMCoseCertPayload.java new file mode 100644 index 00000000..1643c28a --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCoseCertPayload.java @@ -0,0 +1,137 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCoseCertPayload represents the COSE_Sign1 payload for each certificate in BCC. The supported + * key types are KMInteger, KMNInteger and the supported value types are KMByteBlob and + * KMTextString. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short + * arrayPtr } where arrayPtr is a pointer to array with any KMCosePairTagType subtype instances. + */ +public class KMCoseCertPayload extends KMCoseMap { + + private static KMCoseCertPayload prototype; + + private KMCoseCertPayload() { + } + + private static KMCoseCertPayload proto(short ptr) { + if (prototype == null) { + prototype = new KMCoseCertPayload(); + } + instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 2); + KMArray.add(arrPtr, (short) 0, KMCosePairTextStringTag.exp()); + KMArray.add(arrPtr, (short) 1, KMCosePairByteBlobTag.exp()); + return KMCoseCertPayload.instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(COSE_CERT_PAYLOAD_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMCoseCertPayload cast(short ptr) { + if (heap[ptr] != COSE_CERT_PAYLOAD_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + @Override + public short getVals() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] + TLV_HEADER_SIZE)); + } + + @Override + public short length() { + short arrPtr = getVals(); + return KMArray.length(arrPtr); + } + + @Override + public void canonicalize() { + KMCoseMap.canonicalize(getVals()); + } + + private short getValueType(short key, short significantKey) { + short arr = getVals(); + short length = length(); + short keyPtr; + short valPtr = 0; + short index = 0; + short tagType; + boolean found = false; + while (index < length) { + tagType = KMCosePairTagType.getTagValueType(KMArray.get(arr, index)); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + keyPtr = KMCosePairByteBlobTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == KMCosePairTagType.getKeyValueShort(keyPtr) && + significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) { + valPtr = KMCosePairByteBlobTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + keyPtr = KMCosePairTextStringTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairTextStringTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + default: + break; + + } + if (found) { + break; + } + index++; + } + return valPtr; + } + + public short getSubjectPublicKey() { + return getValueType(Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 2), // LSB + Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 0) // MSB (Significant) + ); + } + + public short getSubject() { + return getValueType(KMCose.SUBJECT, KMType.INVALID_VALUE); + } + + public short getIssuer() { + return getValueType(KMCose.ISSUER, KMType.INVALID_VALUE); + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCoseHeaders.java b/Applet/src/com/android/javacard/kmdevice/KMCoseHeaders.java new file mode 100644 index 00000000..a1c2a653 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCoseHeaders.java @@ -0,0 +1,203 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCoseHeaders represents headers section from the Cose standard https://datatracker.ietf.org/doc/html/rfc8152#section-3. + * The supported key types are KMInteger, KMNInteger and the supported value types are KMInteger, + * KMNInteger, KMByteBlob, KMCoseKey. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short + * length; short arrayPtr } where arrayPtr is a pointer to array with any KMTag subtype instances. + */ +public class KMCoseHeaders extends KMCoseMap { + + private static KMCoseHeaders prototype; + + private KMCoseHeaders() { + } + + private static KMCoseHeaders proto(short ptr) { + if (prototype == null) { + prototype = new KMCoseHeaders(); + } + instanceTable[KM_COSE_HEADERS_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 4); + // CoseKey is internally an Array so evaluate it separately. + short coseKeyValueExp = KMCosePairCoseKeyTag.exp(); + KMArray.add(arrPtr, (short) 0, KMCosePairIntegerTag.exp()); + KMArray.add(arrPtr, (short) 1, KMCosePairNegIntegerTag.exp()); + KMArray.add(arrPtr, (short) 2, KMCosePairByteBlobTag.exp()); + KMArray.add(arrPtr, (short) 3, coseKeyValueExp); + return KMCoseHeaders.instance(arrPtr); + } + + + public static short instance(short vals) { + short ptr = KMType.instance(COSE_HEADERS_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMCoseHeaders cast(short ptr) { + if (heap[ptr] != COSE_HEADERS_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + @Override + public short getVals() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_HEADERS_OFFSET] + TLV_HEADER_SIZE)); + } + + @Override + public short length() { + short arrPtr = getVals(); + return KMArray.length(arrPtr); + } + + @Override + public void canonicalize() { + KMCoseMap.canonicalize(getVals()); + } + + private short getValueType(short key) { + short index = 0; + short len = length(); + short arr = getVals(); + short tagType; + short valPtr = 0; + short keyPtr; + boolean found = false; + while (index < len) { + tagType = KMCosePairTagType.getTagValueType(KMArray.get(arr, index)); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + keyPtr = KMCosePairByteBlobTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairByteBlobTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + keyPtr = KMCosePairCoseKeyTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairCoseKeyTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_INT_TAG_TYPE: + keyPtr = KMCosePairIntegerTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairIntegerTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + keyPtr = KMCosePairNegIntegerTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairNegIntegerTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + default: + break; + } + if (found) { + break; + } + index++; + } + return valPtr; + } + + public short getKeyIdentifier() { + return getValueType(KMCose.COSE_LABEL_KEYID); + } + + public short getCoseKey() { + return getValueType(KMCose.COSE_LABEL_COSE_KEY); + } + + public short getIV() { + return getValueType(KMCose.COSE_LABEL_IV); + } + + public short getAlgorithm() { + return getValueType(KMCose.COSE_LABEL_ALGORITHM); + } + + public boolean isDataValid(short alg, short keyIdPtr) { + short[] headerTags = { + KMCose.COSE_LABEL_ALGORITHM, alg, + KMCose.COSE_LABEL_KEYID, keyIdPtr, + }; + boolean valid = false; + short value; + short ptr; + short tagIndex = 0; + while (tagIndex < headerTags.length) { + value = headerTags[(short) (tagIndex + 1)]; + if (value != KMType.INVALID_VALUE) { + valid = false; + ptr = getValueType(headerTags[tagIndex]); + switch (KMType.getKMType(ptr)) { + case KMType.BYTE_BLOB_TYPE: + if ((KMByteBlob.length(value) == KMByteBlob.length(ptr)) && + (0 == + Util.arrayCompare(KMByteBlob.getBuffer(value), + KMByteBlob.getStartOff(value), + KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), + KMByteBlob.length(ptr)))) { + valid = true; + } + break; + case KMType.INTEGER_TYPE: + if (value == KMInteger.getShort(ptr)) { + valid = true; + } + break; + case KMType.NEG_INTEGER_TYPE: + if ((byte) value == (byte) KMNInteger.getShort(ptr)) { + valid = true; + } + break; + default: + break; + } + if (!valid) { + break; + } + } + tagIndex += 2; + } + return valid; + } + + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCoseKey.java b/Applet/src/com/android/javacard/kmdevice/KMCoseKey.java new file mode 100644 index 00000000..97f73bf3 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCoseKey.java @@ -0,0 +1,239 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCoseKey represents COSE_Key section from the Cose standard https://datatracker.ietf.org/doc/html/rfc8152#section-7 + * The supported key types are KMNInteger, KMInteger and the supported value types are KMInteger, + * KMNInteger, KMKeymasterApplet, KMSimpleValue. It corresponds to a CBOR Map type. struct{byte + * TAG_TYPE; short length; short arrayPtr } where arrayPtr is a pointer to array with any KMTag + * subtype instances. + */ +public class KMCoseKey extends KMCoseMap { + + private static KMCoseKey prototype; + + private KMCoseKey() { + } + + private static KMCoseKey proto(short ptr) { + if (prototype == null) { + prototype = new KMCoseKey(); + } + instanceTable[KM_COSE_KEY_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 4); + KMArray.add(arrPtr, (short) 0, KMCosePairIntegerTag.exp()); + KMArray.add(arrPtr, (short) 1, KMCosePairNegIntegerTag.exp()); + KMArray.add(arrPtr, (short) 2, KMCosePairByteBlobTag.exp()); + KMArray.add(arrPtr, (short) 3, KMCosePairSimpleValueTag.exp()); + return KMCoseKey.instance(arrPtr); + } + + + public static short instance(short vals) { + short ptr = KMType.instance(COSE_KEY_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + public static KMCoseKey cast(short ptr) { + if (heap[ptr] != COSE_KEY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + @Override + public short getVals() { + return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_OFFSET] + TLV_HEADER_SIZE)); + } + + @Override + public short length() { + short arrPtr = getVals(); + return KMArray.length(arrPtr); + } + + private short getValueType(short key, short significantKey) { + short arr = getVals(); + short length = length(); + short keyPtr; + short valPtr = 0; + short index = 0; + short tagType; + boolean found = false; + while (index < length) { + tagType = KMCosePairTagType.getTagValueType(KMArray.get(arr, index)); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + keyPtr = KMCosePairByteBlobTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairByteBlobTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_INT_TAG_TYPE: + keyPtr = KMCosePairIntegerTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairIntegerTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + keyPtr = KMCosePairNegIntegerTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { + valPtr = KMCosePairNegIntegerTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + keyPtr = KMCosePairSimpleValueTag.cast(KMArray.get(arr, index)).getKeyPtr(); + if (key == KMCosePairTagType.getKeyValueShort(keyPtr) && + significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) { + valPtr = KMCosePairSimpleValueTag.cast(KMArray.get(arr, index)).getValuePtr(); + found = true; + } + break; + default: + break; + + } + if (found) { + break; + } + index++; + } + return valPtr; + } + + public short getKeyIdentifier() { + return getValueType(KMCose.COSE_KEY_KEY_ID, KMType.INVALID_VALUE); + } + + public short getEcdsa256PublicKey(byte[] pubKey, short pubKeyOff) { + short baseOffset = pubKeyOff; + pubKey[pubKeyOff] = (byte) 0x04; // uncompressed. + pubKeyOff++; + short ptr = getValueType(KMCose.COSE_KEY_PUBKEY_X, KMType.INVALID_VALUE); + Util.arrayCopy(KMByteBlob.getBuffer(ptr), KMByteBlob.getStartOff(ptr), + pubKey, pubKeyOff, KMByteBlob.length(ptr)); + pubKeyOff += KMByteBlob.length(ptr); + ptr = getValueType(KMCose.COSE_KEY_PUBKEY_Y, KMType.INVALID_VALUE); + Util.arrayCopy(KMByteBlob.getBuffer(ptr), KMByteBlob.getStartOff(ptr), + pubKey, pubKeyOff, KMByteBlob.length(ptr)); + pubKeyOff += KMByteBlob.length(ptr); + return (short) (pubKeyOff - baseOffset); + } + + public short getPrivateKey(byte[] priv, short privOff) { + short ptr = getValueType(KMCose.COSE_KEY_PRIV_KEY, KMType.INVALID_VALUE); + Util.arrayCopy(KMByteBlob.getBuffer(ptr), KMByteBlob.getStartOff(ptr), + priv, privOff, KMByteBlob.length(ptr)); + return KMByteBlob.length(ptr); + } + + public boolean isTestKey() { + short ptr = + getValueType( + Util.getShort(KMCose.COSE_TEST_KEY, (short) 2), // LSB + Util.getShort(KMCose.COSE_TEST_KEY, (short) 0) // MSB (Significant) + ); + boolean isTestKey = false; + if (ptr != 0) { + isTestKey = (KMSimpleValue.getValue(ptr) == KMSimpleValue.NULL); + } + return isTestKey; + } + + /** + * Verifies the KMCoseKey values against the input values. + * + * @param keyType value of the key type + * @param keyIdPtr instance of KMKeymasterApplet containing the key id. + * @param keyAlg value of the algorithm. + * @param keyOps value of the key operations. + * @param curve value of the curve. + * @return true if valid, otherwise false. + */ + public boolean isDataValid(short 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, + }; + boolean valid = false; + short ptr; + short tagIndex = 0; + short value; + while (tagIndex < coseKeyTags.length) { + value = coseKeyTags[(short) (tagIndex + 1)]; + if (value != KMType.INVALID_VALUE) { + valid = false; + ptr = getValueType(coseKeyTags[tagIndex], KMType.INVALID_VALUE); + switch (KMType.getKMType(ptr)) { + case KMType.BYTE_BLOB_TYPE: + if ((KMByteBlob.length(value) == KMByteBlob.length(ptr)) && + (0 == + Util.arrayCompare(KMByteBlob.getBuffer(value), + KMByteBlob.getStartOff(value), + KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), + KMByteBlob.length(ptr)))) { + valid = true; + } + break; + case KMType.INTEGER_TYPE: + if (value == KMInteger.getShort(ptr)) { + valid = true; + } + break; + case KMType.NEG_INTEGER_TYPE: + if ((byte) value == (byte) KMNInteger.getShort(ptr)) { + valid = true; + } + break; + } + if (!valid) { + break; + } + } + tagIndex += 2; + } + return valid; + } + + @Override + public void canonicalize() { + KMCoseMap.canonicalize(getVals()); + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCoseMap.java b/Applet/src/com/android/javacard/kmdevice/KMCoseMap.java new file mode 100644 index 00000000..c5ebf405 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCoseMap.java @@ -0,0 +1,165 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * This class represents either a Cose_key or Cose headers as defined in + * https://datatracker.ietf.org/doc/html/rfc8152 This is basically a map containing key value pairs. + * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class + * is confined to support only key and value types which are required for remote key provisioning. + * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported. + * KMCoseHeaders and KMCoseKey implements this class. + */ +public abstract class KMCoseMap extends KMType { + + public static byte[] scratchpad; + + /** + * This function creates an instance of either KMCoseHeaders or KMCoseKey based on the type + * information provided. + * + * @param typePtr type information of the underlying KMType. + * @param arrPtr instance of KMArray. + * @return instance type of either KMCoseHeaders or KMCoseKey. + */ + public static short createInstanceFromType(short typePtr, short arrPtr) { + short mapType = KMType.getKMType(typePtr); + switch (mapType) { + case KMType.COSE_HEADERS_TYPE: + return KMCoseHeaders.instance(arrPtr); + case KMType.COSE_KEY_TYPE: + return KMCoseKey.instance(arrPtr); + case KMType.COSE_CERT_PAYLOAD_TYPE: + return KMCoseCertPayload.instance(arrPtr); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + public static short getVals(short ptr) { + short mapType = KMType.getKMType(ptr); + switch (mapType) { + case KMType.COSE_HEADERS_TYPE: + return KMCoseHeaders.cast(ptr).getVals(); + case KMType.COSE_KEY_TYPE: + return KMCoseKey.cast(ptr).getVals(); + case KMType.COSE_CERT_PAYLOAD_TYPE: + return KMCoseCertPayload.cast(ptr).getVals(); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + abstract public short getVals(); + + abstract public short length(); + + abstract public void canonicalize(); + + private static short getKey(short tagPtr) { + short tagType = KMCosePairTagType.getTagValueType(tagPtr); + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + return KMCosePairByteBlobTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_INT_TAG_TYPE: + return KMCosePairIntegerTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + return KMCosePairNegIntegerTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + return KMCosePairSimpleValueTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + return KMCosePairCoseKeyTag.cast(tagPtr).getKeyPtr(); + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + return KMCosePairTextStringTag.cast(tagPtr).getKeyPtr(); + default: + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return 0; + } + + private static void createScratchBuffer() { + if (scratchpad == null) { + scratchpad = JCSystem.makeTransientByteArray((short) 120, JCSystem.CLEAR_ON_RESET); + } + } + + protected static void canonicalize(short arr) { + canonicalize(arr, KMArray.length(arr)); + } + + private static void swap(short ptr, short firstIndex, short secondIndex) { + if (KMType.getKMType(ptr) == KMType.ARRAY_TYPE) { + KMArray.swap(ptr, firstIndex, secondIndex); + } else { + KMMap.swap(ptr, firstIndex, secondIndex); + } + } + + private static boolean compareAndSwap(short ptr, short index) { + short firstKey; + short secondKey; + short firstKeyLen; + short secondKeyLen; + if (KMType.getKMType(ptr) == KMType.ARRAY_TYPE) { + firstKey = getKey(KMArray.get(ptr, index)); + secondKey = getKey(KMArray.get(ptr, (short) (index + 1))); + } else { // Map + firstKey = KMMap.getKey(ptr, index); + secondKey = KMMap.getKey(ptr, (short) (index + 1)); + } + firstKeyLen = KMKeymasterDevice.encoder.encode(firstKey, scratchpad, (short) 0); + secondKeyLen = KMKeymasterDevice.encoder.encode(secondKey, scratchpad, firstKeyLen); + if ((firstKeyLen > secondKeyLen) || + ((firstKeyLen == secondKeyLen) && + (0 < Util.arrayCompare(scratchpad, (short) 0, scratchpad, firstKeyLen, firstKeyLen)))) { + swap(ptr, index, (short) (index + 1)); + return true; + } + return false; + } + + /** + * Canonicalizes using bubble sort. + * + * @param ptr instance pointer of either array or map. + * @param length length of the array or map instance. + */ + public static void canonicalize(short ptr, short length) { + short index = 0; + short innerIndex = 0; + createScratchBuffer(); + boolean swapped; + while (index < length) { + swapped = false; + innerIndex = 0; + while (innerIndex < (short) (length - index - 1)) { + swapped |= compareAndSwap(ptr, innerIndex); + innerIndex++; + } + if (!swapped) { + break; + } + index++; + } + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCosePairByteBlobTag.java b/Applet/src/com/android/javacard/kmdevice/KMCosePairByteBlobTag.java new file mode 100644 index 00000000..d53de1dc --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCosePairByteBlobTag.java @@ -0,0 +1,134 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairByteBlobTag represents a key-value type, where key can be KMInteger or KMNInteger and + * value is KMByteBlob type. struct{byte TAG_TYPE; short length; struct{short BYTE_BLOB_TYPE; short + * key; short value}}. + */ +public class KMCosePairByteBlobTag extends KMCosePairTagType { + + private static KMCosePairByteBlobTag prototype; + + public static Object[] keys; + + private KMCosePairByteBlobTag() { + } + + private static KMCosePairByteBlobTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairByteBlobTag(); + } + instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMByteBlob.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + if (!isKeyValueValid(keyPtr)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (KMType.getKMType(valuePtr) != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairByteBlobTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getKMType(valuePtr) != BYTE_BLOB_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return BYTE_BLOB_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + private static void createKeys() { + if (keys == null) { + keys = new Object[]{ + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_X}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_Y}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PRIV_KEY}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_IV}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_KEYID}, + (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_KEY_ID}, + (Object) KMCose.SUBJECT_PUBLIC_KEY, + (Object) KMCose.KEY_USAGE + }; + } + } + + public static boolean isKeyValueValid(short keyPtr) { + createKeys(); + short type = KMType.getKMType(keyPtr); + short offset = 0; + if (type == INTEGER_TYPE) { + offset = KMInteger.getStartOff(keyPtr); + } else if (type == NEG_INTEGER_TYPE) { + offset = KMNInteger.getStartOff(keyPtr); + } else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short index = 0; + while (index < (short) keys.length) { + if (0 == Util.arrayCompare((byte[]) keys[index], (short) 0, heap, offset, + (short) ((byte[]) keys[index]).length)) { + return true; + } + index++; + } + return false; + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCosePairCoseKeyTag.java b/Applet/src/com/android/javacard/kmdevice/KMCosePairCoseKeyTag.java new file mode 100644 index 00000000..bcb12566 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCosePairCoseKeyTag.java @@ -0,0 +1,97 @@ +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairCoseKeyTag represents a key-value type, where key can be KMInteger or KMNInteger and + * value is KMCOseKey type. struct{byte TAG_TYPE; short length; struct{short COSE_KEY_VALUE_TYPE; + * short key; short value}}. + */ +public class KMCosePairCoseKeyTag extends KMCosePairTagType { + + public static byte[] keys; + private static KMCosePairCoseKeyTag prototype; + + public static void initStatics() { + keys = new byte[]{ + KMCose.COSE_LABEL_COSE_KEY + }; + } + + private KMCosePairCoseKeyTag() { + } + + private static KMCosePairCoseKeyTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairCoseKeyTag(); + } + instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMCoseKey.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (KMType.getKMType(valuePtr) != COSE_KEY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairCoseKeyTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getKMType(valuePtr) != COSE_KEY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return COSE_KEY_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + public static boolean isKeyValueValid(short keyVal) { + short index = 0; + while (index < (short) keys.length) { + if ((byte) (keyVal & 0xFF) == keys[index]) { + return true; + } + index++; + } + return false; + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCosePairIntegerTag.java b/Applet/src/com/android/javacard/kmdevice/KMCosePairIntegerTag.java new file mode 100644 index 00000000..94741357 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCosePairIntegerTag.java @@ -0,0 +1,96 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and + * value is KMInteger type. struct{byte TAG_TYPE; short length; struct{short INT_VALUE_TYPE; short + * key; short value}}. + */ +public class KMCosePairIntegerTag extends KMCosePairTagType { + + private static KMCosePairIntegerTag prototype; + + + private KMCosePairIntegerTag() { + } + + private static KMCosePairIntegerTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairIntegerTag(); + } + instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMInteger.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); + if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, + KMInteger.getShort(valuePtr))) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairIntegerTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value ptr. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (INTEGER_TYPE != getKMType(valuePtr)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return INTEGER_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCosePairNegIntegerTag.java b/Applet/src/com/android/javacard/kmdevice/KMCosePairNegIntegerTag.java new file mode 100644 index 00000000..d6582ba0 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCosePairNegIntegerTag.java @@ -0,0 +1,95 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairNegIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and + * value is KMNInteger type. struct{byte TAG_TYPE; short length; struct{short NINT_VALUE_TYPE; short + * key; short value}}. + */ +public class KMCosePairNegIntegerTag extends KMCosePairTagType { + + private static KMCosePairNegIntegerTag prototype; + + + private KMCosePairNegIntegerTag() { + } + + private static KMCosePairNegIntegerTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairNegIntegerTag(); + } + instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMNInteger.exp()); + return ptr; + } + + public static KMCosePairNegIntegerTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value ptr. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (NEG_INTEGER_TYPE != getKMType(valuePtr)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public static short instance(short keyPtr, short valuePtr) { + short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); + if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, + KMNInteger.getShort(valuePtr))) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public short getValueType() { + return NEG_INTEGER_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCosePairSimpleValueTag.java b/Applet/src/com/android/javacard/kmdevice/KMCosePairSimpleValueTag.java new file mode 100644 index 00000000..b239d9cc --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCosePairSimpleValueTag.java @@ -0,0 +1,78 @@ +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairSimpleValueTag represents a key-value type, where key can be KMInteger or KMNInteger + * and value is KMSimpleValue type. struct{byte TAG_TYPE; short length; struct{short + * SIMPLE_VALUE_TYPE; short key; short value}}. + */ +public class KMCosePairSimpleValueTag extends KMCosePairTagType { + + private static KMCosePairSimpleValueTag prototype; + + private KMCosePairSimpleValueTag() { + } + + private static KMCosePairSimpleValueTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairSimpleValueTag(); + } + instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMSimpleValue.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); + if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, + KMSimpleValue.getValue(valuePtr))) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairSimpleValueTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getKMType(valuePtr) != SIMPLE_VALUE_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return SIMPLE_VALUE_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCosePairTagType.java b/Applet/src/com/android/javacard/kmdevice/KMCosePairTagType.java new file mode 100644 index 00000000..9cde1468 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCosePairTagType.java @@ -0,0 +1,242 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * This class represents the COSE_Key as defined in https://datatracker.ietf.org/doc/html/rfc8152#section-7. + * This is basically a map containing key value pairs. The label for the key can be (uint / int / + * tstr) and the value can be of any type. But this class is confined to support only key and value + * types which are required for remote key provisioning. So keys of type (int / uint) and values of + * type (int / uint / simple / bstr) only are supported. The structure representing all the sub + * classes of KMCosePairTagType is as follows: KM_COSE_PAIR_TAG_TYPE(1byte), Length(2 bytes), + * COSE_PAIR_*_TAG_TYPE(2 bytes), Key(2 bytes), Value(2 bytes). Key can be either KMInteger or + * KMNInteger and Value can be either KMIntger or KMNinteger or KMSimpleValue or KMByteBlob or + * KMTextString or KMCoseKey. Each subclass of KMCosePairTagType is named after their corresponding + * value type of the Cose pair. + */ +public abstract class KMCosePairTagType extends KMType { + + /** + * Below table represents the allowed values for a key. The maximum length of the key can be 4 + * bytes so each key is represented as 4 bytes. The allowed values are placed next to their + * corresponding key. + */ + public static Object[] allowedKeyPairs; + + private static void createAllowedKeyPairs() { + if (allowedKeyPairs == null) { + allowedKeyPairs = + new Object[]{ + // Key type + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_KEY_TYPE}, + (Object) new byte[]{KMCose.COSE_KEY_TYPE_EC2, + KMCose.COSE_KEY_TYPE_SYMMETRIC_KEY}, + // Key Algorithm + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_ALGORITHM}, + (Object) new byte[]{KMCose.COSE_ALG_AES_GCM_256, KMCose.COSE_ALG_HMAC_256, + KMCose.COSE_ALG_ECDH_ES_HKDF_256, KMCose.COSE_ALG_ES256}, + // Key operations + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_KEY_OPS}, + (Object) new byte[]{KMCose.COSE_KEY_OP_SIGN, KMCose.COSE_KEY_OP_VERIFY, + KMCose.COSE_KEY_OP_ENCRYPT, KMCose.COSE_KEY_OP_DECRYPT}, + // Key Curve + (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_CURVE}, + (Object) new byte[]{KMCose.COSE_ECCURVE_256}, + // Header Label Algorithm + (Object) new byte[]{0, 0, 0, KMCose.COSE_LABEL_ALGORITHM}, + (Object) new byte[]{KMCose.COSE_ALG_AES_GCM_256, + KMCose.COSE_ALG_HMAC_256, KMCose.COSE_ALG_ES256, + KMCose.COSE_ALG_ECDH_ES_HKDF_256}, + // Test Key + KMCose.COSE_TEST_KEY, (Object) new byte[]{KMSimpleValue.NULL}, + }; + } + } + + + /** + * Validates the key and the values corresponding to key. + * + * @param key Buffer containing the key. + * @param keyOff Offset in the buffer from where key starts. + * @param keyLen Length of the key buffer. + * @param value Value corresponding to the key. + * @return true if key pair is valid, otherwise false. + */ + public static boolean isKeyPairValid(byte[] key, short keyOff, short keyLen, short value) { + short index = 0; + short valueIdx; + byte[] values; + boolean valid = false; + createAllowedKeyPairs(); + while (index < allowedKeyPairs.length) { + valueIdx = 0; + if (isEqual((byte[]) allowedKeyPairs[index], (short) 0, + (short) ((byte[]) allowedKeyPairs[index]).length, + key, keyOff, keyLen)) { + values = (byte[]) allowedKeyPairs[(short) (index + 1)]; + while (valueIdx < values.length) { + if (values[valueIdx] == (byte) value) { + valid = true; + break; + } + valueIdx++; + } + if (valid) { + break; + } + } + index += (short) 2; + } + return valid; + } + + /** + * Compares two key buffers. + * + * @param key1 First buffer containing the key. + * @param offset1 Offset of the first buffer. + * @param length1 Length of the first buffer. + * @param key2 Second buffer containing the key. + * @param offset2 Offset of the second buffer. + * @param length2 Length of the second buffer. + * @return true if both keys are equal, otherwise false. + */ + private static boolean isEqual(byte[] key1, short offset1, short length1, byte[] key2, + short offset2, + short length2) { + if (length1 != length2) { + return false; + } + return (0 == KMInteger.unsignedByteArrayCompare(key1, offset1, key2, offset2, length1)); + } + + /** + * Returns the short value of the key. + * + * @param keyPtr Pointer to either KMInteger or KMNInteger + * @return value of the key as short. + */ + public static short getKeyValueShort(short keyPtr) { + short type = KMType.getKMType(keyPtr); + short value = 0; + if (type == INTEGER_TYPE) { + value = KMInteger.getShort(keyPtr); + } else if (type == NEG_INTEGER_TYPE) { + value = KMNInteger.getShort(keyPtr); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return value; + } + + /** + * Returns the significant short value of the key. + * + * @param keyPtr Pointer to either KMInteger or KMNInteger + * @return value of the key as short. + */ + public static short getKeyValueSignificantShort(short keyPtr) { + short type = KMType.getKMType(keyPtr); + short value = 0; + if (type == INTEGER_TYPE) { + value = KMInteger.getSignificantShort(keyPtr); + } else if (type == NEG_INTEGER_TYPE) { + value = KMNInteger.getSignificantShort(keyPtr); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return value; + } + + public static void getKeyValue(short keyPtr, byte[] dest, short offset, short len) { + short type = KMType.getKMType(keyPtr); + if (type == INTEGER_TYPE) { + KMInteger.getValue(keyPtr, dest, offset, len); + } else if (type == NEG_INTEGER_TYPE) { + KMNInteger.getValue(keyPtr, dest, offset, len); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + /** + * Returns the key offset from the key pointer. + * + * @param keyPtr Pointer to either KMInteger or KMNInteger + * @return offset from where the key starts. + */ + public static short getKeyStartOffset(short keyPtr) { + short type = KMType.getKMType(keyPtr); + short offset = 0; + if (type == INTEGER_TYPE) { + offset = KMInteger.getStartOff(keyPtr); + } else if (type == NEG_INTEGER_TYPE) { + offset = KMNInteger.getStartOff(keyPtr); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return offset; + } + + /** + * Returns the key length. + * + * @param keyPtr pointer to either KMInteger/KMInteger. + * @return length of the key. + */ + public static short getKeyLength(short keyPtr) { + short type = KMType.getKMType(keyPtr); + short len = 0; + if (type == INTEGER_TYPE) { + len = KMInteger.length(keyPtr); + } else if (type == NEG_INTEGER_TYPE) { + len = KMNInteger.length(keyPtr); + } else { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return len; + } + + /** + * This function returns one of COSE_KEY_TAG_*_VALUE_TYPE tag information. + * + * @param ptr Pointer to one of the KMCoseKey*Value class. + * @return Tag value type. + */ + public static short getTagValueType(short ptr) { + return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + } + + /** + * This function returns the key pointer. + * + * @return key pointer. + */ + public abstract short getKeyPtr(); + + /** + * This function returns the value pointer. + * + * @return value pointer. + */ + public abstract short getValuePtr(); +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMCosePairTextStringTag.java b/Applet/src/com/android/javacard/kmdevice/KMCosePairTextStringTag.java new file mode 100644 index 00000000..7c927c05 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMCosePairTextStringTag.java @@ -0,0 +1,99 @@ +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMCosePairTextStringTag represents a key-value type, where key can be KMInteger or KMNInteger and + * value is KMTextString type. struct{byte TAG_TYPE; short length; struct{short TXT_STR_VALUE_TYPE; + * short key; short value}}. + */ +public class KMCosePairTextStringTag extends KMCosePairTagType { + + private static KMCosePairTextStringTag prototype; + + public static byte[] keys; + + public static void initStatics() { + keys = new byte[]{ + KMCose.ISSUER, + KMCose.SUBJECT, + }; + } + + private KMCosePairTextStringTag() { + } + + private static KMCosePairTextStringTag proto(short ptr) { + if (prototype == null) { + prototype = new KMCosePairTextStringTag(); + } + instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMTextString.exp()); + return ptr; + } + + public static short instance(short keyPtr, short valuePtr) { + if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (KMType.getKMType(valuePtr) != TEXT_STRING_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); + return ptr; + } + + public static KMCosePairTextStringTag cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != COSE_PAIR_TAG_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // Validate the value pointer. + short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); + if (KMType.getKMType(valuePtr) != TEXT_STRING_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getValueType() { + return TEXT_STRING_TYPE; + } + + @Override + public short getKeyPtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + @Override + public short getValuePtr() { + return Util.getShort(heap, + (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); + } + + public static boolean isKeyValueValid(short keyVal) { + short index = 0; + while (index < (short) keys.length) { + if ((byte) (keyVal & 0xFF) == keys[index]) { + return true; + } + index++; + } + return false; + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMDataStore.java b/Applet/src/com/android/javacard/kmdevice/KMDataStore.java new file mode 100644 index 00000000..9b78f34a --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMDataStore.java @@ -0,0 +1,180 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +public interface KMDataStore extends KMUpgradable { + + /** + * This function stores the data of the corresponding id into the persistent memory. + * + * @param id of the buffer to be stored. @see {@link KMDataStoreConstants} + * @param data is the buffer that contains the data to be stored. + * @param offset is the start offset of the buffer. + * @param length is the length of the buffer. + */ + void storeData(byte id, byte[] data, short offset, short length); + + /** + * This function returns the stored data of the corresponding id. + * + * @param id of the buffer to be stored.@see {@link KMDataStoreConstants} + * @param data is the buffer in which the data of the corresponding id is returned. + * @param offset is the start offset of the buffer. + * @return length of the data copied to the buffer. + */ + short getData(byte id, byte[] data, short offset); + + /** + * This function clears the data of the corresponding id in persistent memory. + * + * @param id of the buffer to be stored. @see {@link KMDataStoreConstants} + */ + void clearData(byte id); + + // Below functions are used to store and retrieve the auth tags for + // MAX_USES_PER_BOOT use case. + + /** + * This function stores the Auth tag associated with keyblob. + * + * @param data is the buffer containing the auth tag. + * @param offset is the start offset of the buffer. + * @param length is the length of the buffer. + * @param scracthPad is the buffer used to copy intermediate results. + * @param scratchPadOff is the start offset of the scratchPad. + * @return true if successfully copied otherwise false. + */ + boolean storeAuthTag(byte[] data, short offset, short length, byte[] scracthPad, + short scratchPadOff); + + /** + * This function checks if the auth tag is presisted in the database. + * + * @param data is the buffer containing the auth tag. + * @param offset is the start offset of the buffer. + * @param length is the length of the buffer. + * @param scratchPad is the buffer used to copy intermediate results. + * @param scratchPadOff is the start offset of the scratchPad. + * @return true if successfully copied otherwise false. + */ + boolean isAuthTagPersisted(byte[] data, short offset, short length, byte[] scratchPad, + short scratchPadOff); + + /** + * Clears all the persisted auth tags. + */ + void clearAllAuthTags(); + + /** + * This functions returns count, the number of times keyblob is used. + * + * @param data is the buffer containing the auth tag. + * @param offset is the start offset of the buffer. + * @param length is the length of the buffer. + * @param scratchPad is out buffer where the count is copied. + * @param scratchPadOff is the start offset of the scratchPad. + * @return length of the counter buffer. + */ + short getRateLimitedKeyCount(byte[] data, short offset, short length, byte[] scratchPad, + short scratchPadOff); + + /** + * This functions copied the count into the persistent memory. + * + * @param data is the buffer containing the auth tag. + * @param offset is the start offset of the buffer. + * @param length is the length of the buffer. + * @param counter is the buffer containing the counter values. + * @param counterOff is the start offset of the counter buffer. + * @param counterLen is the length of the counter buffer. + * @param scratchPad is the buffer used to copy intermediate results. + * @param scratchPadOff is the start offset of the scratchPad. + */ + void setRateLimitedKeyCount(byte[] data, short offset, short length, byte[] counter, + short counterOff, + short counterLen, byte[] scratchPad, short scratchPadOff); + + /** + * Stores the certificate chain, certificate issuer and certificate expire date in persistent + * memory. + * + * @param buffer is the buffer containing certificate chain, issuer and expire at different + * offets. + * @param certChainOff is the start offset of the certificate chain. + * @param certChainLen is the length of the certificate chain. + * @param certIssuerOff is the start offset of the certificate issuer. + * @param certIssuerLen is the length of the certificate issuer. + * @param certExpiryOff is the start offset of the certificate expire date. + * @param certExpiryLen is the length of the certificate expire date. + */ + void persistCertificateData(byte[] buffer, short certChainOff, short certChainLen, + short certIssuerOff, + short certIssuerLen, short certExpiryOff, short certExpiryLen); + + /** + * This function copies the requested certificate data into the provided out buffer. + * + * @param reqCertParam is the requested certificate parameter. @see {@link + * KMDataStoreConstants#CERTIFICATE_CHAIN} {@link KMDataStoreConstants#CERTIFICATE_ISSUER} {@link + * KMDataStoreConstants#CERTIFICATE_EXPIRY} + * @param buf is the out buffer where the requested data is copied. + * @param offset is the start offset of the out buffer. + * @return length of the returned data. + */ + short readCertificateData(byte reqCertParam, byte[] buf, short offset); + + /** + * This function returns the length of the requested certificate data requested. + * + * @param reqCertParam is the requested certificate parameter. @see {@link + * KMDataStoreConstants#CERTIFICATE_CHAIN} {@link KMDataStoreConstants#CERTIFICATE_ISSUER} {@link + * KMDataStoreConstants#CERTIFICATE_EXPIRY} + * @return length of the requested certificate data. + */ + short getCertificateDataLength(byte reqCertParam); + + // keys + + /** + * Returns the persisted computed hmac key. + * + * @return KMComputedHmacKey instance. + */ + KMComputedHmacKey getComputedHmacKey(); + + /** + * Returns the pre-shared key. + * + * @return KMPreSharedKey instance. + */ + KMPreSharedKey getPresharedKey(); + + /** + * Returns the master key. + * + * @return KMMasterKey instance. + */ + KMMasterKey getMasterKey(); + + /** + * Returns the attestation key. + * + * @return KMAttestationKey instance. + */ + KMAttestationKey getAttestationKey(); + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMDataStoreConstants.java b/Applet/src/com/android/javacard/kmdevice/KMDataStoreConstants.java new file mode 100644 index 00000000..dece7cae --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMDataStoreConstants.java @@ -0,0 +1,69 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +public class KMDataStoreConstants { + + public static final byte ATT_ID_BRAND = 0; + public static final byte ATT_ID_DEVICE = 1; + public static final byte ATT_ID_PRODUCT = 2; + public static final byte ATT_ID_SERIAL = 3; + public static final byte ATT_ID_IMEI = 4; + public static final byte ATT_ID_MEID = 5; + public static final byte ATT_ID_MANUFACTURER = 6; + public static final byte ATT_ID_MODEL = 7; + public static final byte COMPUTED_HMAC_KEY = 8; + public static final byte HMAC_NONCE = 9; + public static final byte CERT_ISSUER = 10; + public static final byte CERT_EXPIRY_TIME = 11; + public static final byte OS_VERSION = 12; + public static final byte OS_PATCH_LEVEL = 13; + public static final byte VENDOR_PATCH_LEVEL = 14; + public static final byte DEVICE_LOCKED_TIME = 15; + public static final byte DEVICE_LOCKED = 16; + public static final byte DEVICE_LOCKED_PASSWORD_ONLY = 17; + public static final byte BOOT_ENDED_STATUS = 18; + public static final byte EARLY_BOOT_ENDED_STATUS = 19; + public static final byte PROVISIONED_LOCKED = 20; + public static final byte PROVISIONED_STATUS = 21; + public static final byte MASTER_KEY = 22; + public static final byte PRE_SHARED_KEY = 23; + public static final byte ATTESTATION_KEY = 24; + public static final byte AUTH_TAG_1 = 25; + public static final byte AUTH_TAG_2 = 26; + public static final byte AUTH_TAG_3 = 27; + public static final byte AUTH_TAG_4 = 28; + public static final byte AUTH_TAG_5 = 29; + public static final byte AUTH_TAG_6 = 30; + public static final byte AUTH_TAG_7 = 31; + public static final byte AUTH_TAG_8 = 32; + public static final byte ADDITIONAL_CERT_CHAIN = 33; + public static final byte BOOT_CERT_CHAIN = 34; + + //certificate data constants. + public static final byte CERTIFICATE_CHAIN = 0; + public static final byte CERTIFICATE_EXPIRY = 1; + public static final byte CERTIFICATE_ISSUER = 2; + + // INTERFACE Types + public static final byte INTERFACE_TYPE_COMPUTED_HMAC_KEY = 0x01; + public static final byte INTERFACE_TYPE_ATTESTATION_KEY = 0x02; + public static final byte INTERFACE_TYPE_DEVICE_UNIQUE_KEY = 0x03; + public static final byte INTERFACE_TYPE_MASTER_KEY = 0x04; + public static final byte INTERFACE_TYPE_PRE_SHARED_KEY = 0x05; + + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/kmdevice/KMDecoder.java similarity index 50% rename from Applet/src/com/android/javacard/keymaster/KMDecoder.java rename to Applet/src/com/android/javacard/kmdevice/KMDecoder.java index 7bd0e6ec..55d1feb7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/kmdevice/KMDecoder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -25,9 +25,12 @@ public class KMDecoder { // major types private static final short UINT_TYPE = 0x00; + private static final short NEG_INT_TYPE = 0x20; private static final short BYTES_TYPE = 0x40; + private static final short TSTR_TYPE = 0x60; private static final short ARRAY_TYPE = 0x80; private static final short MAP_TYPE = 0xA0; + private static final short SIMPLE_VALUE_TYPE = 0xE0; // masks private static final short ADDITIONAL_MASK = 0x1F; @@ -48,7 +51,7 @@ public class KMDecoder { public KMDecoder() { bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - scratchBuf = (short[]) JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); + scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); bufferRef[0] = null; scratchBuf[START_OFFSET] = (short) 0; scratchBuf[LEN_OFFSET] = (short) 0; @@ -67,7 +70,7 @@ public short decodeArray(short exp, byte[] buffer, short startOff, short length) scratchBuf[START_OFFSET] = startOff; scratchBuf[LEN_OFFSET] = (short) (startOff + length); short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); - short expLength = KMArray.cast(exp).length(); + short expLength = KMArray.length(exp); if (payloadLength > expLength) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } @@ -76,23 +79,31 @@ public short decodeArray(short exp, byte[] buffer, short startOff, short length) short type; short arrPtr = KMArray.instance(payloadLength); while (index < payloadLength) { - type = KMArray.cast(exp).get(index); + type = KMArray.get(exp, index); obj = decode(type); - KMArray.cast(arrPtr).add(index, obj); + KMArray.add(arrPtr, index, obj); index++; } return arrPtr; } private short decode(short exp) { - byte type = KMType.getType(exp); + byte type = KMType.getKMType(exp); switch (type) { case KMType.BYTE_BLOB_TYPE: return decodeByteBlob(exp); + case KMType.TEXT_STRING_TYPE: + return decodeTstr(exp); case KMType.INTEGER_TYPE: return decodeInteger(exp); + case KMType.SIMPLE_VALUE_TYPE: + return decodeSimpleValue(exp); + case KMType.NEG_INTEGER_TYPE: + return decodeNegInteger(exp); case KMType.ARRAY_TYPE: return decodeArray(exp); + case KMType.MAP_TYPE: + return decodeMap(exp); case KMType.ENUM_TYPE: return decodeEnum(exp); case KMType.KEY_PARAM_TYPE: @@ -105,8 +116,15 @@ private short decode(short exp) { return decodeHmacSharingParam(exp); case KMType.HW_AUTH_TOKEN_TYPE: return decodeHwAuthToken(exp); + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + return decodeCoseMap(exp); + case KMType.COSE_PAIR_TAG_TYPE: + short tagValueType = KMCosePairTagType.getTagValueType(exp); + return decodeCosePairTag(tagValueType, exp); case KMType.TAG_TYPE: - short tagType = KMTag.getTagType(exp); + short tagType = KMTag.getKMTagType(exp); return decodeTag(tagType, exp); default: ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -116,6 +134,8 @@ private short decode(short exp) { private short decodeTag(short tagType, short exp) { switch (tagType) { + case KMType.BIGNUM_TAG: + return decodeBignumTag(exp); case KMType.BYTES_TAG: return decodeBytesTag(exp); case KMType.BOOL_TAG: @@ -138,31 +158,186 @@ private short decodeTag(short tagType, short exp) { } private short decodeVerificationToken(short exp) { - short vals = decode(KMVerificationToken.cast(exp).getVals()); + short vals = decode(KMVerificationToken.getVals(exp)); return KMVerificationToken.instance(vals); } private short decodeHwAuthToken(short exp) { - short vals = decode(KMHardwareAuthToken.cast(exp).getVals()); + short vals = decode(KMHardwareAuthToken.getVals(exp)); return KMHardwareAuthToken.instance(vals); } private short decodeHmacSharingParam(short exp) { - short vals = decode(KMHmacSharingParameters.cast(exp).getVals()); + short vals = decode(KMHmacSharingParameters.getVals(exp)); return KMHmacSharingParameters.instance(vals); } private short decodeKeyChar(short exp) { - short vals = decode(KMKeyCharacteristics.cast(exp).getVals()); + short vals = decode(KMKeyCharacteristics.getVals(exp)); return KMKeyCharacteristics.instance(vals); } + private short decodeCosePairKey(short exp) { + byte[] buffer = (byte[]) bufferRef[0]; + short startOff = scratchBuf[START_OFFSET]; + short keyPtr = (short) 0; + // Cose Key should be always either UINT or Negative int + if ((buffer[startOff] & MAJOR_TYPE_MASK) == UINT_TYPE) { + keyPtr = decodeInteger(exp); + } else if ((buffer[startOff] & MAJOR_TYPE_MASK) == NEG_INT_TYPE) { + keyPtr = decodeNegInteger(exp); + } else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return keyPtr; + } + + private short decodeCosePairSimpleValueTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairSimpleValueTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairSimpleValueTag.cast(exp).getValuePtr()); + return KMCosePairSimpleValueTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairIntegerValueTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairIntegerTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairIntegerTag.cast(exp).getValuePtr()); + return KMCosePairIntegerTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairNegIntegerTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairNegIntegerTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairNegIntegerTag.cast(exp).getValuePtr()); + return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairTxtStringTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairTextStringTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairTextStringTag.cast(exp).getValuePtr()); + return KMCosePairTextStringTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairCoseKeyTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairCoseKeyTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairCoseKeyTag.cast(exp).getValuePtr()); + return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr); + } + + private short decodeCosePairByteBlobTag(short exp) { + short keyPtr = decodeCosePairKey((KMCosePairByteBlobTag.cast(exp).getKeyPtr())); + short valuePtr = decode(KMCosePairByteBlobTag.cast(exp).getValuePtr()); + return KMCosePairByteBlobTag.instance(keyPtr, valuePtr); + } + + private short peekCosePairTagType() { + byte[] buffer = (byte[]) bufferRef[0]; + short startOff = scratchBuf[START_OFFSET]; + // Cose Key should be always either UINT or Negative int + if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE && + (buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + + short additionalMask = (short) (buffer[startOff] & ADDITIONAL_MASK); + short increment = 0; + if (additionalMask < UINT8_LENGTH) { + increment++; + } else if (additionalMask == UINT8_LENGTH) { + increment += 2; + } else if (additionalMask == UINT16_LENGTH) { + increment += 3; + } else if (additionalMask == UINT32_LENGTH) { + increment += 5; + } else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short majorType = (short) (buffer[(short) (startOff + increment)] & MAJOR_TYPE_MASK); + short tagValueType = 0; + if (majorType == BYTES_TYPE) { + tagValueType = KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE; + } else if (majorType == UINT_TYPE) { + tagValueType = KMType.COSE_PAIR_INT_TAG_TYPE; + } else if (majorType == NEG_INT_TYPE) { + tagValueType = KMType.COSE_PAIR_NEG_INT_TAG_TYPE; + } else if (majorType == MAP_TYPE) { + tagValueType = KMType.COSE_PAIR_COSE_KEY_TAG_TYPE; + } else if (majorType == SIMPLE_VALUE_TYPE) { + tagValueType = KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE; + } else if (majorType == TSTR_TYPE) { + tagValueType = KMType.COSE_PAIR_TEXT_STR_TAG_TYPE; + } else { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return tagValueType; + } + + private short decodeCosePairTag(short tagValueType, short exp) { + switch (tagValueType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + return decodeCosePairByteBlobTag(exp); + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + return decodeCosePairNegIntegerTag(exp); + case KMType.COSE_PAIR_INT_TAG_TYPE: + return decodeCosePairIntegerValueTag(exp); + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + return decodeCosePairSimpleValueTag(exp); + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + return decodeCosePairCoseKeyTag(exp); + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + return decodeCosePairTxtStringTag(exp); + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + return 0; + } + } + + private short decodeCoseMap(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); + // get allowed key pairs + short allowedKeyPairs = KMCoseMap.getVals(exp); + short vals = KMArray.instance(payloadLength); + short length = KMArray.length(allowedKeyPairs); + short index = 0; + boolean tagFound; + short tagInd; + short cosePairTagType; + short tagClass; + short allowedType; + short obj; + + // For each tag in payload ... + while (index < payloadLength) { + tagFound = false; + tagInd = 0; + cosePairTagType = peekCosePairTagType(); + // Check against the allowed tags ... + while (tagInd < length) { + tagClass = KMArray.get(allowedKeyPairs, tagInd); + allowedType = KMCosePairTagType.getTagValueType(tagClass); + if (allowedType == cosePairTagType) { + obj = decode(tagClass); + KMArray.add(vals, index, obj); + tagFound = true; + break; + } + tagInd++; + } + if (!tagFound) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } else { + index++; + } + } + return KMCoseMap.createInstanceFromType(exp, vals); + } + private short decodeKeyParam(short exp) { short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); // allowed tags - short allowedTags = KMKeyParameters.cast(exp).getVals(); + short allowedTags = KMKeyParameters.getVals(exp); + short tagRule = KMArray.get(allowedTags, (short) 0); + boolean ignoreInvalidTags = KMEnum.getVal(tagRule) == KMType.IGNORE_INVALID_TAGS; short vals = KMArray.instance(payloadLength); - short length = KMArray.cast(allowedTags).length(); + short length = KMArray.length(allowedTags); short index = 0; boolean tagFound; short tagInd; @@ -170,57 +345,92 @@ private short decodeKeyParam(short exp) { short tagClass; short allowedType; short obj; + short arrPos = 0; // For each tag in payload ... while (index < payloadLength) { tagFound = false; - tagInd = 0; + tagInd = 1; tagType = peekTagType(); // Check against the allowed tags ... while (tagInd < length) { - tagClass = KMArray.cast(allowedTags).get(tagInd); - allowedType = KMTag.getTagType(tagClass); + tagClass = KMArray.get(allowedTags, tagInd); + allowedType = KMTag.getKMTagType(tagClass); // If it is part of allowed tags ... if (tagType == allowedType) { // then decodeByteBlob and add that to the array. - obj = decode(tagClass); - KMArray.cast(vals).add(index, obj); - tagFound = true; - break; + try { + tagFound = true; + obj = decode(tagClass); + KMArray.add(vals, arrPos++, obj); + break; + } catch (KMException e) { + if (KMException.reason() == KMError.INVALID_TAG && + !ignoreInvalidTags) { + KMException.throwIt(KMError.INVALID_TAG); + } + break; + } } tagInd++; } if (!tagFound) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); + KMException.throwIt(KMError.INVALID_TAG); } else { index++; } } + KMArray.setLength(vals, arrPos); return KMKeyParameters.instance(vals); } private short decodeEnumArrayTag(short exp) { - readTagKey(KMEnumArrayTag.cast(exp).getTagType()); - return KMEnumArrayTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMEnumArrayTag.cast(exp).getValues())); + readTagKey(KMEnumArrayTag.getTagType(exp)); + return KMEnumArrayTag.instance(scratchBuf[TAG_KEY_OFFSET], + decode(KMEnumArrayTag.getValues(exp))); } private short decodeIntegerArrayTag(short exp) { - readTagKey(KMIntegerArrayTag.cast(exp).getTagType()); + readTagKey(KMIntegerArrayTag.getTagType(exp)); // the values are array of integers. - return KMIntegerArrayTag.instance(KMIntegerArrayTag.cast(exp).getTagType(), - scratchBuf[TAG_KEY_OFFSET], decode(KMIntegerArrayTag.cast(exp).getValues())); + return KMIntegerArrayTag.instance(KMIntegerArrayTag.getTagType(exp), + scratchBuf[TAG_KEY_OFFSET], decode(KMIntegerArrayTag.getValues(exp))); } private short decodeIntegerTag(short exp) { - readTagKey(KMIntegerTag.cast(exp).getTagType()); + readTagKey(KMIntegerTag.getTagType(exp)); // the value is an integer - return KMIntegerTag.instance(KMIntegerTag.cast(exp).getTagType(), - scratchBuf[TAG_KEY_OFFSET], decode(KMIntegerTag.cast(exp).getValue())); + return KMIntegerTag.instance(KMIntegerTag.getTagType(exp), + scratchBuf[TAG_KEY_OFFSET], decode(KMIntegerTag.getValue(exp))); } private short decodeBytesTag(short exp) { - readTagKey(KMByteTag.cast(exp).getTagType()); + readTagKey(KMByteTag.getTagType(exp)); + // The value must be byte blob + return KMByteTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMByteTag.getValue(exp))); + } + + private short decodeBignumTag(short exp) { + readTagKey(KMBignumTag.getTagType(exp)); // The value must be byte blob - return KMByteTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMByteTag.cast(exp).getValue())); + return KMBignumTag.instance(scratchBuf[TAG_KEY_OFFSET], decode(KMBignumTag.getValue(exp))); + } + + private short decodeMap(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); + short mapPtr = KMMap.instance(payloadLength); + short index = 0; + short type; + short keyobj; + short valueobj; + while (index < payloadLength) { + type = KMMap.getKey(exp, index); + keyobj = decode(type); + type = KMMap.getKeyValue(exp, index); + valueobj = decode(type); + KMMap.add(mapPtr, index, keyobj, valueobj); + index++; + } + return mapPtr; } private short decodeArray(short exp) { @@ -230,23 +440,23 @@ private short decodeArray(short exp) { short type; short obj; // check whether array contains one type of objects or multiple types - if (KMArray.cast(exp).containedType() == 0) {// multiple types specified by expression. - if (KMArray.cast(exp).length() != KMArray.ANY_ARRAY_LENGTH) { - if (KMArray.cast(exp).length() != payloadLength) { + if (KMArray.containedType(exp) == 0) {// multiple types specified by expression. + if (KMArray.length(exp) != KMArray.ANY_ARRAY_LENGTH) { + if (KMArray.length(exp) != payloadLength) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } } while (index < payloadLength) { - type = KMArray.cast(exp).get(index); + type = KMArray.get(exp, index); obj = decode(type); - KMArray.cast(arrPtr).add(index, obj); + KMArray.add(arrPtr, index, obj); index++; } } else { // Array is a Vector containing objects of one type - type = KMArray.cast(exp).containedType(); + type = KMArray.containedType(exp); while (index < payloadLength) { obj = decode(type); - KMArray.cast(arrPtr).add(index, obj); + KMArray.add(arrPtr, index, obj); index++; } } @@ -254,8 +464,8 @@ private short decodeArray(short exp) { } private short decodeEnumTag(short exp) { - readTagKey(KMEnumTag.cast(exp).getTagType()); - byte[] buffer = (byte[])bufferRef[0]; + readTagKey(KMEnumTag.getTagType(exp)); + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; // Enum Tag value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -281,8 +491,8 @@ private short decodeEnumTag(short exp) { } private short decodeBoolTag(short exp) { - readTagKey(KMBoolTag.cast(exp).getTagType()); - byte[] buffer = (byte[])bufferRef[0]; + readTagKey(KMBoolTag.getTagType(exp)); + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; // BOOL Tag is a leaf node and it must always have tiny encoded uint value = 1. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -296,7 +506,7 @@ private short decodeBoolTag(short exp) { } private short decodeEnum(short exp) { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; // Enum value will always be integer with max 1 byte length. if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { @@ -318,13 +528,24 @@ private short decodeEnum(short exp) { enumVal = buffer[startOff]; incrementStartOff((short) 1); } - return KMEnum.instance(KMEnum.cast(exp).getEnumType(), enumVal); + return KMEnum.instance(KMEnum.getEnumType(exp), enumVal); + } + + private short decodeSimpleValue(short exp) { + short startOff = scratchBuf[START_OFFSET]; + byte[] buffer = (byte[]) bufferRef[0]; + if ((buffer[startOff] & MAJOR_TYPE_MASK) != SIMPLE_VALUE_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + byte addInfo = (byte) (buffer[startOff] & ADDITIONAL_MASK); + incrementStartOff((short) 1); + return KMSimpleValue.instance(addInfo); } private short decodeInteger(short exp) { short inst; short startOff = scratchBuf[START_OFFSET]; - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } @@ -354,15 +575,80 @@ private short decodeInteger(short exp) { return inst; } + private short decodeNegIntegerValue(byte addInfo, byte[] buf, short startOffset) { + short inst; + short len = 0; + short scratchpad; + if (addInfo < UINT8_LENGTH) { + addInfo = (byte) (-1 - addInfo); + inst = KMNInteger.uint_8(addInfo); + } else { + switch (addInfo) { + case UINT8_LENGTH: + len = 1; + break; + case UINT16_LENGTH: + len = 2; + break; + case UINT32_LENGTH: + len = 4; + break; + case UINT64_LENGTH: + len = 8; + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + // Do (-1 - N), as per cbor negative integer decoding rule. + // N is the integer value. + scratchpad = KMByteBlob.instance((short) (len * 3)); + byte[] input = KMByteBlob.getBuffer(scratchpad); + short offset = KMByteBlob.getStartOff(scratchpad); + Util.arrayFillNonAtomic(input, offset, len, (byte) -1); + Util.arrayCopyNonAtomic(buf, startOffset, input, (short) (offset + len), len); + KMUtils.subtract(input, offset, (short) (offset + len), (short) (offset + 2 * len), + (byte) len); + inst = KMNInteger.instance(input, (short) (offset + 2 * len), len); + incrementStartOff(len); + } + return inst; + } + + private short decodeNegInteger(short exp) { + short startOff = scratchBuf[START_OFFSET]; + byte[] buffer = (byte[]) bufferRef[0]; + if ((buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short len = (short) (buffer[startOff] & ADDITIONAL_MASK); + if (len > UINT64_LENGTH) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + incrementStartOff((short) 1); + // startOff is incremented so update the startOff + // with latest value before using it. + startOff = scratchBuf[START_OFFSET]; + return decodeNegIntegerValue((byte) len, buffer, startOff); + } + + private short decodeTstr(short exp) { + short payloadLength = readMajorTypeWithPayloadLength(TSTR_TYPE); + short inst = KMTextString.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], + payloadLength); + incrementStartOff(payloadLength); + return inst; + } + private short decodeByteBlob(short exp) { short payloadLength = readMajorTypeWithPayloadLength(BYTES_TYPE); - short inst = KMByteBlob.instance((byte[])bufferRef[0], scratchBuf[START_OFFSET], payloadLength); + short inst = KMByteBlob.instance((byte[]) bufferRef[0], scratchBuf[START_OFFSET], + payloadLength); incrementStartOff(payloadLength); return inst; } private short peekTagType() { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -377,7 +663,7 @@ private short peekTagType() { } private void readTagKey(short expectedTagType) { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -395,7 +681,7 @@ private void readTagKey(short expectedTagType) { // payload length cannot be more then 16 bits. private short readMajorTypeWithPayloadLength(short majorType) { - short payloadLength = 0; + short payloadLength; byte val = readByte(); if ((short) (val & MAJOR_TYPE_MASK) != majorType) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -415,7 +701,7 @@ private short readMajorTypeWithPayloadLength(short majorType) { } private short readShort() { - byte[] buffer = (byte[])bufferRef[0]; + byte[] buffer = (byte[]) bufferRef[0]; short startOff = scratchBuf[START_OFFSET]; short val = Util.makeShort(buffer[startOff], buffer[(short) (startOff + 1)]); incrementStartOff((short) 2); @@ -424,7 +710,7 @@ private short readShort() { private byte readByte() { short startOff = scratchBuf[START_OFFSET]; - byte val = ((byte[])bufferRef[0])[startOff]; + byte val = ((byte[]) bufferRef[0])[startOff]; incrementStartOff((short) 1); return val; } @@ -435,7 +721,17 @@ private void incrementStartOff(short inc) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } - + + public short readCertificateChainLengthAndHeaderLen(byte[] buf, short bufOffset, + short bufLen) { + bufferRef[0] = buf; + scratchBuf[START_OFFSET] = bufOffset; + scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); + short totalLen = readMajorTypeWithPayloadLength(BYTES_TYPE); + totalLen += (short) (scratchBuf[START_OFFSET] - bufOffset); + return totalLen; + } + // Reads the offset and length values of the ByteBlobs from a CBOR array buffer. public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOffset, short bufLen, byte[] out, short outOff) { @@ -457,16 +753,6 @@ public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOff Util.setShort(out, outOff, byteBlobLength); // length outOff += 2; index++; - } - } - - public short getCborBytesStartOffset(byte[] buf, short bufOffset, short bufLen) { - bufferRef[0] = buf; - scratchBuf[START_OFFSET] = bufOffset; - scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); - - readMajorTypeWithPayloadLength(BYTES_TYPE); - return scratchBuf[START_OFFSET]; + } } - } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/src/com/android/javacard/kmdevice/KMDeviceUniqueKey.java similarity index 53% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java rename to Applet/src/com/android/javacard/kmdevice/KMDeviceUniqueKey.java index 258dc461..517a5b06 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java +++ b/Applet/src/com/android/javacard/kmdevice/KMDeviceUniqueKey.java @@ -1,5 +1,5 @@ /* - * Copyright(C) 2020 The Android Open Source Project + * Copyright(C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,27 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; -import javacard.security.AESKey; +public interface KMDeviceUniqueKey { -public class KMAESKey implements KMMasterKey { - - private AESKey aesKey; - - public KMAESKey(AESKey key) { - aesKey = key; - } - - public void setKey(byte[] keyData, short kOff) { - aesKey.setKey(keyData, kOff); - } - - public byte getKey(byte[] keyData, short kOff) { - return aesKey.getKey(keyData, kOff); - } - - public short getKeySizeBits() { - return aesKey.getSize(); - } + short getPublicKey(byte[] buf, short offset); } diff --git a/Applet/src/com/android/javacard/kmdevice/KMEncoder.java b/Applet/src/com/android/javacard/kmdevice/KMEncoder.java new file mode 100644 index 00000000..1519fb61 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMEncoder.java @@ -0,0 +1,761 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +public class KMEncoder { + + // major types + private static final byte UINT_TYPE = 0x00; + private static final byte NEG_INT_TYPE = 0x20; + private static final byte BYTES_TYPE = 0x40; + private static final byte TSTR_TYPE = 0x60; + private static final byte ARRAY_TYPE = (byte) 0x80; + private static final byte MAP_TYPE = (byte) 0xA0; + private static final byte SIMPLE_VALUE_TYPE = (byte) 0xE0; + + // masks + private static final byte ADDITIONAL_MASK = 0x1F; + + // value length + private static final byte UINT8_LENGTH = (byte) 0x18; + private static final byte UINT16_LENGTH = (byte) 0x19; + private static final byte UINT32_LENGTH = (byte) 0x1A; + private static final byte UINT64_LENGTH = (byte) 0x1B; + private static final short TINY_PAYLOAD = 0x17; + private static final short SHORT_PAYLOAD = 0x100; + private static final short STACK_SIZE = (short) 50; + private static final short SCRATCH_BUF_SIZE = (short) 6; + private static final short START_OFFSET = (short) 0; + private static final short LEN_OFFSET = (short) 2; + private static final short STACK_PTR_OFFSET = (short) 4; + + private Object[] bufferRef; + private short[] scratchBuf; + private short[] stack; + + public KMEncoder() { + bufferRef = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); + scratchBuf = JCSystem.makeTransientShortArray(SCRATCH_BUF_SIZE, JCSystem.CLEAR_ON_RESET); + stack = JCSystem.makeTransientShortArray(STACK_SIZE, JCSystem.CLEAR_ON_RESET); + bufferRef[0] = null; + scratchBuf[START_OFFSET] = (short) 0; + scratchBuf[LEN_OFFSET] = (short) 0; + scratchBuf[STACK_PTR_OFFSET] = (short) 0; + } + + private void push(short objPtr) { + stack[scratchBuf[STACK_PTR_OFFSET]] = objPtr; + scratchBuf[STACK_PTR_OFFSET]++; + } + + private short pop() { + scratchBuf[STACK_PTR_OFFSET]--; + return stack[scratchBuf[STACK_PTR_OFFSET]]; + } + + private void encode(short obj) { + push(obj); + } + + public short encode(short object, byte[] buffer, short startOff) { + scratchBuf[STACK_PTR_OFFSET] = 0; + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = startOff; + short len = (short) (buffer.length - startOff); + if ((len < 0) || len > KMKeymasterDevice.MAX_LENGTH) { + scratchBuf[LEN_OFFSET] = KMKeymasterDevice.MAX_LENGTH; + } else { + scratchBuf[LEN_OFFSET] = (short) buffer.length; + } + //this.length = (short)(startOff + length); + push(object); + encode(); + return (short) (scratchBuf[START_OFFSET] - startOff); + } + + // array{KMError.OK,Array{KMByteBlobs}} + public void encodeCertChain(byte[] buffer, short offset, short length, short errInt32Ptr, + short certChainOff, short certChainLen) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = offset; + scratchBuf[LEN_OFFSET] = (short) (offset + length + 1); + + writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements + encodeUnsignedInteger(errInt32Ptr); + writeMajorTypeWithLength(BYTES_TYPE, certChainLen); + writeBytes(buffer, certChainOff, certChainLen); + } + + //array{KMError.OK,Array{KMByteBlobs}} + public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength, + short errInt32Ptr) { + bufferRef[0] = certBuffer; + scratchBuf[START_OFFSET] = certStart; + scratchBuf[LEN_OFFSET] = (short) (certStart + 1); + //Array header - 2 elements i.e. 1 byte + scratchBuf[START_OFFSET]--; + // errInt32Ptr - PowerResetStatus + ErrorCode - 4 bytes + // Integer header - 1 byte + scratchBuf[START_OFFSET] -= getEncodedIntegerLength(errInt32Ptr); + //Array header - 2 elements i.e. 1 byte + scratchBuf[START_OFFSET]--; + // Cert Byte blob - typically 2 bytes length i.e. 3 bytes header + scratchBuf[START_OFFSET] -= 2; + if (certLength >= SHORT_PAYLOAD) { + scratchBuf[START_OFFSET]--; + } + if (scratchBuf[START_OFFSET] < bufferStart) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + bufferStart = scratchBuf[START_OFFSET]; + writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements + encodeUnsignedInteger(errInt32Ptr); //PowerResetStatus + ErrorCode + writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 element + writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length + return bufferStart; + } + + public short encodeError(short errInt32Ptr, byte[] buffer, short startOff, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = startOff; + scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); + encodeUnsignedInteger(errInt32Ptr); + return (short) (scratchBuf[START_OFFSET] - startOff); + } + + private void encode() { + while (scratchBuf[STACK_PTR_OFFSET] > 0) { + short exp = pop(); + byte type = KMType.getKMType(exp); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + encodeByteBlob(exp); + break; + case KMType.TEXT_STRING_TYPE: + encodeTextString(exp); + break; + case KMType.INTEGER_TYPE: + encodeUnsignedInteger(exp); + break; + case KMType.SIMPLE_VALUE_TYPE: + encodeSimpleValue(exp); + break; + case KMType.NEG_INTEGER_TYPE: + encodeNegInteger(exp); + break; + case KMType.ARRAY_TYPE: + encodeArray(exp); + break; + case KMType.MAP_TYPE: + encodeMap(exp); + break; + case KMType.ENUM_TYPE: + encodeEnum(exp); + break; + case KMType.KEY_PARAM_TYPE: + encodeKeyParam(exp); + break; + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + encodeCoseMap(exp); + break; + case KMType.KEY_CHAR_TYPE: + encodeKeyChar(exp); + break; + case KMType.VERIFICATION_TOKEN_TYPE: + encodeVeriToken(exp); + break; + case KMType.HMAC_SHARING_PARAM_TYPE: + encodeHmacSharingParam(exp); + break; + case KMType.HW_AUTH_TOKEN_TYPE: + encodeHwAuthToken(exp); + break; + case KMType.TAG_TYPE: + short tagType = KMTag.getKMTagType(exp); + encodeTag(tagType, exp); + break; + case KMType.COSE_PAIR_TAG_TYPE: + short cosePairTagType = KMCosePairTagType.getTagValueType(exp); + encodeCosePairTag(cosePairTagType, exp); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + } + + private void encodeCosePairIntegerTag(short exp) { + KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairIntTag.getValuePtr()); + encode(cosePairIntTag.getKeyPtr()); + } + + private void encodeCosePairByteBlobTag(short exp) { + KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairByteBlobTag.getValuePtr()); + encode(cosePairByteBlobTag.getKeyPtr()); + } + + private void encodeCosePairCoseKeyTag(short exp) { + KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairCoseKeyTag.getValuePtr()); + encode(cosePairCoseKeyTag.getKeyPtr()); + } + + private void encodeCosePairTextStringTag(short exp) { + KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairTextStringTag.getValuePtr()); + encode(cosePairTextStringTag.getKeyPtr()); + } + + private void encodeCosePairSimpleValueTag(short exp) { + KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairSimpleValueTag.getValuePtr()); + encode(cosePairSimpleValueTag.getKeyPtr()); + } + + private void encodeCosePairNegIntegerTag(short exp) { + KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); + // push key and value ptr in stack to get encoded. + encode(cosePairNegIntegerTag.getValuePtr()); + encode(cosePairNegIntegerTag.getKeyPtr()); + } + + private void encodeCosePairTag(short tagType, short exp) { + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + encodeCosePairByteBlobTag(exp); + return; + case KMType.COSE_PAIR_INT_TAG_TYPE: + encodeCosePairIntegerTag(exp); + return; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + encodeCosePairNegIntegerTag(exp); + return; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + encodeCosePairSimpleValueTag(exp); + return; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + encodeCosePairTextStringTag(exp); + return; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + encodeCosePairCoseKeyTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + + private void encodeTag(short tagType, short exp) { + switch (tagType) { + case KMType.BYTES_TAG: + encodeBytesTag(exp); + return; + case KMType.BOOL_TAG: + encodeBoolTag(exp); + return; + case KMType.UINT_TAG: + case KMType.ULONG_TAG: + case KMType.DATE_TAG: + encodeIntegerTag(exp); + return; + case KMType.ULONG_ARRAY_TAG: + case KMType.UINT_ARRAY_TAG: + encodeIntegerArrayTag(exp); + return; + case KMType.ENUM_TAG: + encodeEnumTag(exp); + return; + case KMType.ENUM_ARRAY_TAG: + encodeEnumArrayTag(exp); + return; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } + + private void encodeCoseMap(short obj) { + encodeAsMap(KMCoseMap.getVals(obj)); + } + + private void encodeKeyParam(short obj) { + encodeAsMap(KMKeyParameters.getVals(obj)); + } + + private void encodeKeyChar(short obj) { + encode(KMKeyCharacteristics.getVals(obj)); + } + + private void encodeVeriToken(short obj) { + encode(KMVerificationToken.getVals(obj)); + } + + private void encodeHwAuthToken(short obj) { + encode(KMHardwareAuthToken.getVals(obj)); + } + + private void encodeHmacSharingParam(short obj) { + encode(KMHmacSharingParameters.getVals(obj)); + } + + private void encodeArray(short obj) { + writeMajorTypeWithLength(ARRAY_TYPE, KMArray.length(obj)); + short len = KMArray.length(obj); + short index = (short) (len - 1); + short subObj; + while (index >= 0) { + subObj = KMArray.get(obj, index); + if (subObj != KMType.INVALID_VALUE) { + encode(subObj); + } + index--; + } + } + + public void encodeArrayOnlyLength(short arrLength, byte[] buffer, short offset, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = offset; + scratchBuf[LEN_OFFSET] = (short) (offset + length + 1); + writeMajorTypeWithLength(ARRAY_TYPE, length); + } + + private void encodeMap(short obj) { + writeMajorTypeWithLength(MAP_TYPE, KMMap.length(obj)); + short len = KMMap.length(obj); + short index = (short) (len - 1); + while (index >= 0) { + encode(KMMap.getKeyValue(obj, index)); + encode(KMMap.getKey(obj, index)); + index--; + } + } + + private void encodeAsMap(short obj) { + writeMajorTypeWithLength(MAP_TYPE, KMArray.length(obj)); + short len = KMArray.length(obj); + short index = (short) (len - 1); + short inst; + while (index >= 0) { + inst = KMArray.get(obj, index); + encode(inst); + index--; + } + } + + private void encodeIntegerArrayTag(short obj) { + writeTag(KMIntegerArrayTag.getTagType(obj), KMIntegerArrayTag.getKey(obj)); + encode(KMIntegerArrayTag.getValues(obj)); + } + + private void encodeEnumArrayTag(short obj) { + writeTag(KMEnumArrayTag.getTagType(obj), KMEnumArrayTag.getKey(obj)); + encode(KMEnumArrayTag.getValues(obj)); + } + + private void encodeIntegerTag(short obj) { + writeTag(KMIntegerTag.getTagType(obj), KMIntegerTag.getKey(obj)); + encode(KMIntegerTag.getValue(obj)); + } + + private void encodeBytesTag(short obj) { + writeTag(KMByteTag.getTagType(obj), KMByteTag.getKey(obj)); + encode(KMByteTag.getValue(obj)); + } + + private void encodeBoolTag(short obj) { + writeTag(KMBoolTag.getTagType(obj), KMBoolTag.getKey(obj)); + writeByteValue(KMBoolTag.getVal(obj)); + } + + private void encodeEnumTag(short obj) { + writeTag(KMEnumTag.getTagType(obj), KMEnumTag.getKey(obj)); + writeByteValue(KMEnumTag.getValue(obj)); + } + + private void encodeEnum(short obj) { + writeByteValue(KMEnum.getVal(obj)); + } + + private void encodeInteger(byte[] val, short len, short startOff, short majorType) { + // find out the most significant byte + short msbIndex = findMsb(val, startOff, len); + // find the difference between most significant byte and len + short diff = (short) (len - msbIndex); + if (diff == 0) { + writeByte((byte) (majorType | 0)); + } else if ((diff == 1) && (val[(short) (startOff + msbIndex)] < UINT8_LENGTH) + && (val[(short) (startOff + msbIndex)] >= 0)) { + writeByte((byte) (majorType | val[(short) (startOff + msbIndex)])); + } else if (diff == 1) { + writeByte((byte) (majorType | UINT8_LENGTH)); + writeByte(val[(short) (startOff + msbIndex)]); + } else if (diff == 2) { + writeByte((byte) (majorType | UINT16_LENGTH)); + writeBytes(val, (short) (startOff + msbIndex), (short) 2); + } else if (diff <= 4) { + writeByte((byte) (majorType | UINT32_LENGTH)); + writeBytes(val, (short) (startOff + len - 4), (short) 4); + } else { + writeByte((byte) (majorType | UINT64_LENGTH)); + writeBytes(val, startOff, (short) 8); + } + } + + // find out the most significant byte + public short findMsb(byte[] buf, short offset, short len) { + byte index = 0; + // find out the most significant byte + while (index < len) { + if (buf[(short) (offset + index)] > 0) { + break; + } else if (buf[(short) (offset + index)] < 0) { + break; + } + index++; // index will be equal to len if value is 0. + } + return index; + } + + public void computeOnesCompliment(short msbIndex, byte[] buf, short offset, short len) { + // find the difference between most significant byte and len + short diff = (short) (len - msbIndex); + short correctedOffset = offset; + short correctedLen = len; + // The offset and length of the buffer for Short and Byte types should be + // corrected before computing the 1s compliment. The reason for doing this + // is to avoid computation of 1s compliment on the MSB bytes. + if (diff == 0) { + // Fail + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } else if (diff == 1) { + correctedOffset = (short) (offset + 3); + correctedLen = 1; + } else if (diff == 2) { + correctedOffset = (short) (offset + 2); + correctedLen = 2; + } + // For int and long values the len and offset values are always proper. + // int - 4 bytes + // long - 8 bytes. + KMUtils.computeOnesCompliment(buf, correctedOffset, correctedLen); + } + + // Encoding rule for negative Integers is taken from + // https://datatracker.ietf.org/doc/html/rfc7049#section-2.1, Major type 1. + public short handleNegIntegerEncodingRule(byte[] buf, short offset, short len) { + short msbIndex = findMsb(buf, offset, len); + // Do -1-N, where N is the negative integer + // The value of -1-N is equal to the 1s compliment of N. + computeOnesCompliment(msbIndex, buf, offset, len); + return msbIndex; + } + + // Note: This function modifies the buffer's actual value. So after encoding, restore the original + // value by calling removeNegIntegerEncodingRule(). + public short applyNegIntegerEncodingRule(byte[] buf, short offset, short len) { + return handleNegIntegerEncodingRule(buf, offset, len); + } + + public void removeNegIntegerEncodingRule(byte[] buf, short offset, short len, + short origMsbIndex) { + // Do -1-N, where N is the negative integer + // The value of -1-N is equal to the 1s compliment of N. + computeOnesCompliment(origMsbIndex, buf, offset, len); + } + + private void encodeNegInteger(short obj) { + byte[] val = KMNInteger.getBuffer(obj); + short len = KMNInteger.length(obj); + short startOff = KMNInteger.getStartOff(obj); + short msbIndex = applyNegIntegerEncodingRule(val, startOff, len); + encodeInteger(val, len, startOff, NEG_INT_TYPE); + removeNegIntegerEncodingRule(val, startOff, len, msbIndex); + } + + private void encodeUnsignedInteger(short obj) { + byte[] val = KMInteger.getBuffer(obj); + short len = KMInteger.length(obj); + short startOff = KMInteger.getStartOff(obj); + encodeInteger(val, len, startOff, UINT_TYPE); + } + + private void encodeSimpleValue(short obj) { + byte value = KMSimpleValue.getValue(obj); + writeByte((byte) (SIMPLE_VALUE_TYPE | value)); + } + + private void encodeTextString(short obj) { + writeMajorTypeWithLength(TSTR_TYPE, KMTextString.length(obj)); + writeBytes(KMTextString.getBuffer(obj), KMTextString.getStartOff(obj), + KMTextString.length(obj)); + } + + public short encodeByteBlobHeader(short bufLen, byte[] buffer, short startOff, short length) { + bufferRef[0] = buffer; + scratchBuf[START_OFFSET] = startOff; + scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); + writeMajorTypeWithLength(BYTES_TYPE, bufLen); + return (short) (scratchBuf[START_OFFSET] - startOff); + } + + private void encodeByteBlob(short obj) { + writeMajorTypeWithLength(BYTES_TYPE, KMByteBlob.length(obj)); + writeBytes(KMByteBlob.getBuffer(obj), KMByteBlob.getStartOff(obj), + KMByteBlob.length(obj)); + } + + public short getEncodedLength(short ptr) { + short len = 0; + short type = KMType.getKMType(ptr); + switch (type) { + case KMType.BYTE_BLOB_TYPE: + len += getEncodedByteBlobLength(ptr); + break; + case KMType.TEXT_STRING_TYPE: + len += getEncodedTextStringLength(ptr); + break; + case KMType.INTEGER_TYPE: + len += getEncodedIntegerLength(ptr); + break; + case KMType.NEG_INTEGER_TYPE: + len += getEncodedNegIntegerLength(ptr); + break; + case KMType.ARRAY_TYPE: + len += getEncodedArrayLen(ptr); + break; + case KMType.MAP_TYPE: + len += getEncodedMapLen(ptr); + break; + case KMType.COSE_PAIR_TAG_TYPE: + short cosePairTagType = KMCosePairTagType.getTagValueType(ptr); + len += getEncodedCosePairTagLen(cosePairTagType, ptr); + break; + case KMType.COSE_KEY_TYPE: + case KMType.COSE_HEADERS_TYPE: + case KMType.COSE_CERT_PAYLOAD_TYPE: + len += getEncodedArrayLen(KMCoseMap.getVals(ptr)); + break; + default: + KMException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return len; + } + + private short getEncodedCosePairTagLen(short tagType, short exp) { + short length = 0; + switch (tagType) { + case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: + KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); + length = getEncodedLength(cosePairByteBlobTag.getKeyPtr()); + length += getEncodedLength(cosePairByteBlobTag.getValuePtr()); + break; + case KMType.COSE_PAIR_INT_TAG_TYPE: + KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); + length = getEncodedLength(cosePairIntTag.getValuePtr()); + length += getEncodedLength(cosePairIntTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: + KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); + length = getEncodedLength(cosePairNegIntegerTag.getValuePtr()); + length += getEncodedLength(cosePairNegIntegerTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: + KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); + length = getEncodedLength(cosePairSimpleValueTag.getValuePtr()); + length += getEncodedLength(cosePairSimpleValueTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: + KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); + length = getEncodedLength(cosePairTextStringTag.getValuePtr()); + length += getEncodedLength(cosePairTextStringTag.getKeyPtr()); + break; + case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: + KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); + length = getEncodedLength(cosePairCoseKeyTag.getValuePtr()); + length += getEncodedLength(cosePairCoseKeyTag.getKeyPtr()); + break; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return length; + } + + private short getEncodedMapLen(short obj) { + short mapLen = KMMap.length(obj); + short len = getEncodedBytesLength(mapLen); + short index = 0; + while (index < mapLen) { + len += getEncodedLength(KMMap.getKey(obj, index)); + len += getEncodedLength(KMMap.getKeyValue(obj, index)); + index++; + } + return len; + } + + private short getEncodedArrayLen(short obj) { + short arrLen = KMArray.length(obj); + short len = getEncodedBytesLength(arrLen); + short index = 0; + short subObj; + while (index < arrLen) { + subObj = KMArray.get(obj, index); + if (subObj != KMType.INVALID_VALUE) { + len += getEncodedLength(subObj); + } + index++; + } + return len; + } + + private short getEncodedBytesLength(short len) { + short ret = 0; + if (len < KMEncoder.UINT8_LENGTH && len >= 0) { + ret = 1; + } else if (len >= KMEncoder.UINT8_LENGTH && len <= (short) 0x00FF) { + ret = 2; + } else if (len > (short) 0x00FF && len <= (short) 0x7FFF) { + ret = 3; + } else { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + return ret; + } + + private short getEncodedByteBlobLength(short obj) { + short len = KMByteBlob.length(obj); + len += getEncodedBytesLength(len); + return len; + } + + private short getEncodedTextStringLength(short obj) { + //short len = KMTextString.byteBlobLength(obj); + //len += getEncodedBytesLength(len); + return 0;//len; + } + + private short getEncodedNegIntegerLength(short obj) { + byte[] buf = KMNInteger.getBuffer(obj); + short len = KMNInteger.length(obj); + short offset = KMNInteger.getStartOff(obj); + short msbIndex = applyNegIntegerEncodingRule(buf, offset, len); + short ret = getEncodedIntegerLength(buf, offset, len); + removeNegIntegerEncodingRule(buf, offset, len, msbIndex); + return ret; + } + + private short getEncodedIntegerLength(byte[] val, short startOff, short len) { + short msbIndex = findMsb(val, startOff, len); + // find the difference between most significant byte and len + short diff = (short) (len - msbIndex); + switch (diff) { + case 0: + case 1: //Byte + if ((val[(short) (startOff + msbIndex)] < KMEncoder.UINT8_LENGTH) && + (val[(short) (startOff + msbIndex)] >= 0)) { + return (short) 1; + } else { + return (short) 2; + } + case 2: //Short + return (short) 3; + case 3: + case 4: //UInt32 + return (short) 5; + case 5: + case 6: + case 7: + case 8: //UInt64 + return (short) 9; + default: + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + return 0; + } + + private short getEncodedIntegerLength(short obj) { + byte[] val = KMInteger.getBuffer(obj); + short len = KMInteger.length(obj); + short startOff = KMInteger.getStartOff(obj); + return getEncodedIntegerLength(val, startOff, len); + } + + private void writeByteValue(byte val) { + if ((val < UINT8_LENGTH) && (val >= 0)) { + writeByte((byte) (UINT_TYPE | val)); + } else { + writeByte((byte) (UINT_TYPE | UINT8_LENGTH)); + writeByte(val); + } + } + + private void writeTag(short tagType, short tagKey) { + writeByte((byte) (UINT_TYPE | UINT32_LENGTH)); + writeShort(tagType); + writeShort(tagKey); + } + + private void writeMajorTypeWithLength(byte majorType, short len) { + if (len <= TINY_PAYLOAD) { + writeByte((byte) (majorType | (byte) (len & ADDITIONAL_MASK))); + } else if (len < SHORT_PAYLOAD) { + writeByte((byte) (majorType | UINT8_LENGTH)); + writeByte((byte) (len & 0xFF)); + } else { + writeByte((byte) (majorType | UINT16_LENGTH)); + writeShort(len); + } + } + + private void writeBytes(byte[] buf, short start, short len) { + byte[] buffer = (byte[]) bufferRef[0]; + Util.arrayCopyNonAtomic(buf, start, buffer, scratchBuf[START_OFFSET], len); + incrementStartOff(len); + } + + private void writeShort(short val) { + byte[] buffer = (byte[]) bufferRef[0]; + buffer[scratchBuf[START_OFFSET]] = (byte) ((val >> 8) & 0xFF); + incrementStartOff((short) 1); + buffer[scratchBuf[START_OFFSET]] = (byte) ((val & 0xFF)); + incrementStartOff((short) 1); + } + + private void writeByte(byte val) { + byte[] buffer = (byte[]) bufferRef[0]; + buffer[scratchBuf[START_OFFSET]] = val; + incrementStartOff((short) 1); + } + + private void incrementStartOff(short inc) { + scratchBuf[START_OFFSET] += inc; + if (scratchBuf[START_OFFSET] >= scratchBuf[LEN_OFFSET]) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMEnum.java b/Applet/src/com/android/javacard/kmdevice/KMEnum.java similarity index 61% rename from Applet/src/com/android/javacard/keymaster/KMEnum.java rename to Applet/src/com/android/javacard/kmdevice/KMEnum.java index a55c243d..d051f991 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnum.java +++ b/Applet/src/com/android/javacard/kmdevice/KMEnum.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -30,27 +30,32 @@ public class KMEnum extends KMType { private static KMEnum prototype; // The allowed enum types. - private static final short[] types = { - HARDWARE_TYPE, - KEY_FORMAT, - KEY_DERIVATION_FUNCTION, - VERIFIED_BOOT_STATE, - DEVICE_LOCKED, - USER_AUTH_TYPE, - PURPOSE, - ECCURVE - }; + private static short[] types; private static Object[] enums = null; private KMEnum() { } + public static void initStatics() { + types = new short[]{ + HARDWARE_TYPE, + KEY_FORMAT, + KEY_DERIVATION_FUNCTION, + VERIFIED_BOOT_STATE, + DEVICE_LOCKED, + USER_AUTH_TYPE, + PURPOSE, + ECCURVE, + RULE + }; + } + private static KMEnum proto(short ptr) { if (prototype == null) { prototype = new KMEnum(); } - instanceTable[KM_ENUM_OFFSET] = ptr; + KMType.instanceTable[KM_ENUM_OFFSET] = ptr; return prototype; } @@ -60,17 +65,21 @@ public static short exp() { } public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_OFFSET] + 1)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + 1)); + } + + private static KMEnum cast(short ptr) { + validate(ptr); + return proto(ptr); } - public static KMEnum cast(short ptr) { + public static void validate(short ptr) { if (heap[ptr] != ENUM_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - return proto(ptr); } public static short instance(short enumType) { @@ -96,40 +105,41 @@ private static void create() { // The allowed enum values to corresponding enum types in the types array. if (enums == null) { enums = - new Object[]{ - new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX}, - new byte[]{X509, PKCS8, RAW}, - new byte[]{ - DERIVATION_NONE, - RFC5869_SHA256, - ISO18033_2_KDF1_SHA1, - ISO18033_2_KDF1_SHA256, - ISO18033_2_KDF2_SHA1, - ISO18033_2_KDF2_SHA256 - }, - new byte[]{SELF_SIGNED_BOOT, VERIFIED_BOOT, UNVERIFIED_BOOT, FAILED_BOOT}, - new byte[]{DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE}, - new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH}, - new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, - new byte[]{P_224, P_256, P_384, P_521} - }; + new Object[]{ + new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX}, + new byte[]{X509, PKCS8, RAW}, + new byte[]{ + DERIVATION_NONE, + RFC5869_SHA256, + ISO18033_2_KDF1_SHA1, + ISO18033_2_KDF1_SHA256, + ISO18033_2_KDF2_SHA1, + ISO18033_2_KDF2_SHA256 + }, + new byte[]{SELF_SIGNED_BOOT, VERIFIED_BOOT, UNVERIFIED_BOOT, FAILED_BOOT}, + new byte[]{DEVICE_LOCKED_TRUE, DEVICE_LOCKED_FALSE}, + new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH}, + new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY}, + new byte[]{P_224, P_256, P_384, P_521}, + new byte[]{IGNORE_INVALID_TAGS, FAIL_ON_INVALID_TAGS} + }; } } public void setVal(byte val) { - heap[(short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val; + heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)] = val; } public byte getVal() { - return heap[(short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)]; + return heap[(short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE + 2)]; } public void setEnumType(short type) { - Util.setShort(heap, (short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type); + Util.setShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE), type); } public short getEnumType() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, (short) (KMType.instanceTable[KM_ENUM_OFFSET] + TLV_HEADER_SIZE)); } // isValidTag enumeration keys and values. @@ -162,4 +172,22 @@ private static boolean validateEnum(short key, byte value) { // return false if key does not exist return false; } + + public static void setVal(short bPtr, byte val) { + KMEnum.cast(bPtr).setVal(val); + } + + public static byte getVal(short bPtr) { + return KMEnum.cast(bPtr).getVal(); + } + + public static void setEnumType(short bPtr, short type) { + KMEnum.cast(bPtr).setEnumType(type); + } + + public static short getEnumType(short bPtr) { + return KMEnum.cast(bPtr).getEnumType(); + } + + } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java b/Applet/src/com/android/javacard/kmdevice/KMEnumArrayTag.java similarity index 73% rename from Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java rename to Applet/src/com/android/javacard/kmdevice/KMEnumArrayTag.java index cd3981c4..3a8713cc 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumArrayTag.java +++ b/Applet/src/com/android/javacard/kmdevice/KMEnumArrayTag.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -29,11 +29,15 @@ public class KMEnumArrayTag extends KMTag { private static KMEnumArrayTag prototype; // The allowed tag keys of enum array type. - private static final short[] tags = {PURPOSE, BLOCK_MODE, DIGEST, PADDING}; + private static short[] tags; // Tag Values. private static Object[] enums = null; + public static void initStatics() { + tags = new short[]{PURPOSE, BLOCK_MODE, DIGEST, PADDING, RSA_OAEP_MGF_DIGEST}; + } + private KMEnumArrayTag() { } @@ -41,7 +45,7 @@ private static KMEnumArrayTag proto(short ptr) { if (prototype == null) { prototype = new KMEnumArrayTag(); } - instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] = ptr; return prototype; } @@ -69,15 +73,14 @@ public static short instance(short key, short byteBlob) { if (allowedVals == null) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } - KMByteBlob blob = KMByteBlob.cast(byteBlob); short byteIndex = 0; short enumIndex; boolean validValue; - while (byteIndex < blob.length()) { + while (byteIndex < KMByteBlob.length(byteBlob)) { enumIndex = 0; validValue = false; while (enumIndex < allowedVals.length) { - if (blob.get(byteIndex) == allowedVals[enumIndex]) { + if (KMByteBlob.get(byteBlob, byteIndex) == allowedVals[enumIndex]) { validValue = true; break; } @@ -95,7 +98,7 @@ public static short instance(short key, short byteBlob) { return ptr; } - public static KMEnumArrayTag cast(short ptr) { + private static KMEnumArrayTag cast(short ptr) { if (heap[ptr] != TAG_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -106,7 +109,8 @@ public static KMEnumArrayTag cast(short ptr) { } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -114,26 +118,30 @@ public short getTagType() { } public short getValues() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { - short blobPtr = Util.getShort(heap, (short) (instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); - return KMByteBlob.cast(blobPtr).length(); + short blobPtr = Util.getShort(heap, + (short) (KMType.instanceTable[KM_ENUM_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return KMByteBlob.length(blobPtr); } public static void create() { if (enums == null) { // allowed tag values. enums = - new Object[]{ - new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY}, - new byte[]{ECB, CBC, CTR, GCM}, - new byte[]{DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, - new byte[]{ - PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7 - } - }; + new Object[]{ + new byte[]{ENCRYPT, DECRYPT, SIGN, VERIFY, WRAP_KEY, ATTEST_KEY, AGREE_KEY}, + new byte[]{ECB, CBC, CTR, GCM}, + new byte[]{DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, + new byte[]{ + PADDING_NONE, RSA_OAEP, RSA_PSS, RSA_PKCS1_1_5_ENCRYPT, RSA_PKCS1_1_5_SIGN, PKCS7 + }, + new byte[]{DIGEST_NONE, MD5, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512}, + + }; } } @@ -149,20 +157,20 @@ private static byte[] getAllowedEnumValues(short key) { } public static short getValues(short tagId, short params, byte[] buf, short start) { - short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + short tag = KMKeyParameters.findTag(params, KMType.ENUM_ARRAY_TAG, tagId); if (tag == KMType.INVALID_VALUE) { return KMType.INVALID_VALUE; } tag = KMEnumArrayTag.cast(tag).getValues(); - return KMByteBlob.cast(tag).getValues(buf, start); + return KMByteBlob.getValues(tag, buf, start); } public short get(short index) { - return KMByteBlob.cast(getValues()).get(index); + return KMByteBlob.get(getValues(), index); } public static boolean contains(short tagId, short tagValue, short params) { - short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + short tag = KMKeyParameters.findTag(params, KMType.ENUM_ARRAY_TAG, tagId); if (tag != KMType.INVALID_VALUE) { short index = 0; while (index < KMEnumArrayTag.cast(tag).length()) { @@ -176,7 +184,7 @@ public static boolean contains(short tagId, short tagValue, short params) { } public static short length(short tagId, short params) { - short tag = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, tagId, params); + short tag = KMKeyParameters.findTag(params, KMType.ENUM_ARRAY_TAG, tagId); if (tag != KMType.INVALID_VALUE) { return KMEnumArrayTag.cast(tag).length(); } @@ -233,10 +241,10 @@ public boolean isValidPaddingModes(byte alg) { switch (alg) { case KMType.RSA: if (padding != KMType.RSA_OAEP - && padding != KMType.PADDING_NONE - && padding != KMType.RSA_PKCS1_1_5_SIGN - && padding != KMType.RSA_PKCS1_1_5_ENCRYPT - && padding != KMType.RSA_PSS) { + && padding != KMType.PADDING_NONE + && padding != KMType.RSA_PKCS1_1_5_SIGN + && padding != KMType.RSA_PKCS1_1_5_ENCRYPT + && padding != KMType.RSA_PSS) { return false; } break; @@ -298,4 +306,30 @@ public boolean isValidBlockMode(byte alg) { return false; } } + + public static short get(short bPtr, short index) { + return KMEnumArrayTag.cast(bPtr).get(index); + } + + public static short length(short bPtr) { + return KMEnumArrayTag.cast(bPtr).length(); + } + + public static short getValues(short bPtr) { + return KMEnumArrayTag.cast(bPtr).getValues(); + } + + public static short getTagType(short bPtr) { + return KMType.ENUM_ARRAY_TAG; + } + + public static short getKey(short bPtr) { + return KMEnumArrayTag.cast(bPtr).getKey(); + } + + public static boolean contains(short bPtr, short tagValue) { + return KMEnumArrayTag.cast(bPtr).contains(tagValue); + } + + } diff --git a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java b/Applet/src/com/android/javacard/kmdevice/KMEnumTag.java similarity index 73% rename from Applet/src/com/android/javacard/keymaster/KMEnumTag.java rename to Applet/src/com/android/javacard/kmdevice/KMEnumTag.java index f69aaf51..b796832c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEnumTag.java +++ b/Applet/src/com/android/javacard/kmdevice/KMEnumTag.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -29,14 +29,17 @@ public class KMEnumTag extends KMTag { private static KMEnumTag prototype; - // The allowed tag keys of type enum tag. - private static final short[] tags = { - ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE - }; + private static short[] tags; private static Object[] enums = null; + public static void initStatics() { + tags = new short[]{ + ALGORITHM, ECCURVE, BLOB_USAGE_REQ, USER_AUTH_TYPE, ORIGIN, HARDWARE_TYPE + }; + } + private KMEnumTag() { } @@ -44,7 +47,7 @@ private static KMEnumTag proto(short ptr) { if (prototype == null) { prototype = new KMEnumTag(); } - instanceTable[KM_ENUM_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_ENUM_TAG_OFFSET] = ptr; return prototype; } @@ -76,7 +79,7 @@ public static short instance(short key, byte val) { return ptr; } - public static KMEnumTag cast(short ptr) { + private static KMEnumTag cast(short ptr) { if (heap[ptr] != TAG_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -87,7 +90,8 @@ public static KMEnumTag cast(short ptr) { } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getTagType() { @@ -95,21 +99,21 @@ public short getTagType() { } public byte getValue() { - return heap[(short) (instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; + return heap[(short) (KMType.instanceTable[KM_ENUM_TAG_OFFSET] + TLV_HEADER_SIZE + 4)]; } public static void create() { if (enums == null) { // enum tag values. enums = - new Object[]{ - new byte[]{RSA, DES, EC, AES, HMAC}, - new byte[]{P_224, P_256, P_384, P_521}, - new byte[]{STANDALONE, REQUIRES_FILE_SYSTEM}, - new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY}, - new byte[]{GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, - new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} - }; + new Object[]{ + new byte[]{RSA, DES, EC, AES, HMAC}, + new byte[]{P_224, P_256, P_384, P_521}, + new byte[]{STANDALONE, REQUIRES_FILE_SYSTEM}, + new byte[]{USER_AUTH_NONE, PASSWORD, FINGERPRINT, BOTH, ANY}, + new byte[]{GENERATED, DERIVED, IMPORTED, UNKNOWN, SECURELY_IMPORTED}, + new byte[]{SOFTWARE, TRUSTED_ENVIRONMENT, STRONGBOX} + }; } } @@ -144,11 +148,24 @@ private static boolean validateEnum(short key, byte value) { return false; } - public static short getValue(short tagType, short keyParameters) { - short tagPtr = KMKeyParameters.findTag(KMType.ENUM_TAG, tagType, keyParameters); + public static short getValue(short tagKey, short keyParameters) { + short tagPtr = KMKeyParameters.findTag(keyParameters, KMType.ENUM_TAG, tagKey); if (tagPtr != KMType.INVALID_VALUE) { return heap[(short) (tagPtr + TLV_HEADER_SIZE + 4)]; } return KMType.INVALID_VALUE; } + + public static byte getValue(short bPtr) { + return KMEnumTag.cast(bPtr).getValue(); + } + + public static short getTagType(short bPtr) { + return KMEnumTag.cast(bPtr).getTagType(); + } + + public static short getKey(short bPtr) { + return KMEnumTag.cast(bPtr).getKey(); + } + } diff --git a/Applet/src/com/android/javacard/kmdevice/KMError.java b/Applet/src/com/android/javacard/kmdevice/KMError.java new file mode 100644 index 00000000..6d932a40 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMError.java @@ -0,0 +1,137 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +/** + * KMError includes all the error codes from android keymaster hal specifications. The values are + * positive unlike negative values in keymaster hal. + */ +public class KMError { + + public static final short OK = 0; + public static final short UNSUPPORTED_PURPOSE = 2; + public static final short INCOMPATIBLE_PURPOSE = 3; + public static final short UNSUPPORTED_ALGORITHM = 4; + public static final short INCOMPATIBLE_ALGORITHM = 5; + public static final short UNSUPPORTED_KEY_SIZE = 6; + public static final short UNSUPPORTED_BLOCK_MODE = 7; + public static final short INCOMPATIBLE_BLOCK_MODE = 8; + public static final short UNSUPPORTED_MAC_LENGTH = 9; + public static final short UNSUPPORTED_PADDING_MODE = 10; + public static final short INCOMPATIBLE_PADDING_MODE = 11; + public static final short UNSUPPORTED_DIGEST = 12; + public static final short INCOMPATIBLE_DIGEST = 13; + + public static final short UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = 19; + + /** + * For PKCS8 & PKCS12 + */ + public static final short INVALID_INPUT_LENGTH = 21; + + + public static final short KEY_USER_NOT_AUTHENTICATED = 26; + public static final short INVALID_OPERATION_HANDLE = 28; + public static final short VERIFICATION_FAILED = 30; + public static final short TOO_MANY_OPERATIONS = 31; + public static final short INVALID_KEY_BLOB = 33; + + public static final short INVALID_ARGUMENT = 38; + public static final short UNSUPPORTED_TAG = 39; + public static final short INVALID_TAG = 40; + public static final short IMPORT_PARAMETER_MISMATCH = 44; + public static final short OPERATION_CANCELLED = 46; + + public static final short MISSING_NONCE = 51; + public static final short INVALID_NONCE = 52; + public static final short MISSING_MAC_LENGTH = 53; + public static final short CALLER_NONCE_PROHIBITED = 55; + public static final short KEY_MAX_OPS_EXCEEDED = 56; + public static final short INVALID_MAC_LENGTH = 57; + public static final short MISSING_MIN_MAC_LENGTH = 58; + public static final short UNSUPPORTED_MIN_MAC_LENGTH = 59; + public static final short UNSUPPORTED_EC_CURVE = 61; + public static final short KEY_REQUIRES_UPGRADE = 62; + + public static final short ATTESTATION_CHALLENGE_MISSING = 63; + public static final short ATTESTATION_APPLICATION_ID_MISSING = 65; + public static final short CANNOT_ATTEST_IDS = 66; + public static final short ROLLBACK_RESISTANCE_UNAVAILABLE = 67; + + public static final short NO_USER_CONFIRMATION = 71; + public static final short DEVICE_LOCKED = 72; + public static final short EARLY_BOOT_ENDED = 73; + public static final short ATTESTATION_KEYS_NOT_PROVISIONED = 74; + public static final short INCOMPATIBLE_MGF_DIGEST = 78; + public static final short UNSUPPORTED_MGF_DIGEST = 79; + public static final short MISSING_NOT_BEFORE = 80; + public static final short MISSING_NOT_AFTER = 81; + public static final short MISSING_ISSUER_SUBJECT_NAME = 82; + public static final short INVALID_ISSUER_SUBJECT_NAME = 83; + + public static final short UNIMPLEMENTED = 100; + public static final short UNKNOWN_ERROR = 1000; + + //Extended errors + public static final short SW_CONDITIONS_NOT_SATISFIED = 10001; + public static final short UNSUPPORTED_CLA = 10002; + public static final short INVALID_P1P2 = 10003; + public static final short UNSUPPORTED_INSTRUCTION = 10004; + public static final short CMD_NOT_ALLOWED = 10005; + public static final short SW_WRONG_LENGTH = 10006; + public static final short INVALID_DATA = 10007; + + //Crypto errors + public static final short CRYPTO_ILLEGAL_USE = 10008; + public static final short CRYPTO_ILLEGAL_VALUE = 10009; + public static final short CRYPTO_INVALID_INIT = 10010; + public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011; + public static final short CRYPTO_UNINITIALIZED_KEY = 10012; + //Generic Unknown error. + public static final short GENERIC_UNKNOWN_ERROR = 10013; + public static final short PUBLIC_KEY_OPERATION = 10014; + + // Remote key provisioning error codes. + public static final short STATUS_FAILED = 32000; + public static final short STATUS_INVALID_MAC = 32001; + public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; + public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; + public static final short STATUS_INVALID_EEK = 32004; + public static final short INVALID_STATE = 32005; + + public static short translate(short err) { + switch (err) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + case UNKNOWN_ERROR: + return UNKNOWN_ERROR; + case CRYPTO_NO_SUCH_ALGORITHM: + return UNSUPPORTED_ALGORITHM; + case UNSUPPORTED_INSTRUCTION: + case CMD_NOT_ALLOWED: + case SW_WRONG_LENGTH: + return UNIMPLEMENTED; + } + return err; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMException.java b/Applet/src/com/android/javacard/kmdevice/KMException.java similarity index 70% rename from Applet/src/com/android/javacard/keymaster/KMException.java rename to Applet/src/com/android/javacard/kmdevice/KMException.java index bf588aba..36a3eb55 100644 --- a/Applet/src/com/android/javacard/keymaster/KMException.java +++ b/Applet/src/com/android/javacard/kmdevice/KMException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.JCSystem; @@ -24,33 +24,34 @@ */ public class KMException extends RuntimeException { - public short[] reason; - public static KMException exception; + private static short[] reason; + private static KMException exception; private KMException() { - reason = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); } - public static void throwIt(short reason) { - instance(); - exception.reason[(short) 0] = reason; - throw exception; + public static short reason() { + return reason[0]; } + public static void throwIt(short e) { + if (reason == null) { + reason = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_DESELECT); + } + if (exception == null) { + exception = new KMException(); + } + reason[0] = e; + throw exception; + } +/* public static KMException instance() { if (exception == null) { exception = new KMException(); } return exception; } - - public void clear() { - exception.reason[(short) 0] = KMError.UNKNOWN_ERROR; - } - - public static short getReason() { - return exception.reason[0]; - } +*/ } diff --git a/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java b/Applet/src/com/android/javacard/kmdevice/KMHardwareAuthToken.java similarity index 50% rename from Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java rename to Applet/src/com/android/javacard/kmdevice/KMHardwareAuthToken.java index 900e9069..05119173 100644 --- a/Applet/src/com/android/javacard/keymaster/KMHardwareAuthToken.java +++ b/Applet/src/com/android/javacard/kmdevice/KMHardwareAuthToken.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -43,13 +43,12 @@ private KMHardwareAuthToken() { public static short exp() { short arrPtr = KMArray.instance((short) 6); - KMArray arr = KMArray.cast(arrPtr); - arr.add(CHALLENGE, KMInteger.exp()); - arr.add(USER_ID, KMInteger.exp()); - arr.add(AUTHENTICATOR_ID, KMInteger.exp()); - arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE)); - arr.add(TIMESTAMP, KMInteger.exp()); - arr.add(MAC, KMByteBlob.exp()); + KMArray.add(arrPtr, CHALLENGE, KMInteger.exp()); + KMArray.add(arrPtr, USER_ID, KMInteger.exp()); + KMArray.add(arrPtr, AUTHENTICATOR_ID, KMInteger.exp()); + KMArray.add(arrPtr, HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE)); + KMArray.add(arrPtr, TIMESTAMP, KMInteger.exp()); + KMArray.add(arrPtr, MAC, KMByteBlob.exp()); return instance(arrPtr); } @@ -57,25 +56,24 @@ private static KMHardwareAuthToken proto(short ptr) { if (prototype == null) { prototype = new KMHardwareAuthToken(); } - instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] = ptr; + KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] = ptr; return prototype; } public static short instance() { short arrPtr = KMArray.instance((short) 6); - KMArray arr = KMArray.cast(arrPtr); - arr.add(CHALLENGE, KMInteger.uint_16((short) 0)); - arr.add(USER_ID, KMInteger.uint_16((short) 0)); - arr.add(AUTHENTICATOR_ID, KMInteger.uint_16((short) 0)); - arr.add(HW_AUTHENTICATOR_TYPE, KMEnum.instance(KMType.USER_AUTH_TYPE, KMType.USER_AUTH_NONE)); - arr.add(TIMESTAMP, KMInteger.uint_16((short) 0)); - arr.add(MAC, KMByteBlob.instance((short) 0)); + KMArray.add(arrPtr, CHALLENGE, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, USER_ID, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, AUTHENTICATOR_ID, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, HW_AUTHENTICATOR_TYPE, + KMEnum.instance(KMType.USER_AUTH_TYPE, KMType.USER_AUTH_NONE)); + KMArray.add(arrPtr, TIMESTAMP, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, MAC, KMByteBlob.instance((short) 0)); return instance(arrPtr); } public static short instance(short vals) { - KMArray arr = KMArray.cast(vals); - if (arr.length() != 6) { + if (KMArray.length(vals) != 6) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } short ptr = KMType.instance(HW_AUTH_TOKEN_TYPE, (short) 2); @@ -83,7 +81,7 @@ public static short instance(short vals) { return ptr; } - public static KMHardwareAuthToken cast(short ptr) { + private static KMHardwareAuthToken cast(short ptr) { if (heap[ptr] != HW_AUTH_TOKEN_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -95,77 +93,134 @@ public static KMHardwareAuthToken cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_HARDWARE_AUTH_TOKEN_OFFSET] + TLV_HEADER_SIZE)); } public short length() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); + return KMArray.length(arrPtr); } public short getChallenge() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(CHALLENGE); + return KMArray.get(arrPtr, CHALLENGE); } public void setChallenge(short vals) { - KMInteger.cast(vals); + KMInteger.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(CHALLENGE, vals); + KMArray.add(arrPtr, CHALLENGE, vals); } public short getUserId() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(USER_ID); + return KMArray.get(arrPtr, USER_ID); } public void setUserId(short vals) { - KMInteger.cast(vals); + KMInteger.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(USER_ID, vals); + KMArray.add(arrPtr, USER_ID, vals); } public short getAuthenticatorId() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(AUTHENTICATOR_ID); + return KMArray.get(arrPtr, AUTHENTICATOR_ID); } public void setAuthenticatorId(short vals) { - KMInteger.cast(vals); + KMInteger.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(AUTHENTICATOR_ID, vals); + KMArray.add(arrPtr, AUTHENTICATOR_ID, vals); } public short getHwAuthenticatorType() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(HW_AUTHENTICATOR_TYPE); + return KMArray.get(arrPtr, HW_AUTHENTICATOR_TYPE); } public void setHwAuthenticatorType(short vals) { - KMEnum.cast(vals); + KMEnum.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(HW_AUTHENTICATOR_TYPE, vals); + KMArray.add(arrPtr, HW_AUTHENTICATOR_TYPE, vals); } public short getTimestamp() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(TIMESTAMP); + return KMArray.get(arrPtr, TIMESTAMP); } public void setTimestamp(short vals) { - KMInteger.cast(vals); + KMInteger.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(TIMESTAMP, vals); + KMArray.add(arrPtr, TIMESTAMP, vals); } public short getMac() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(MAC); + return KMArray.get(arrPtr, MAC); } public void setMac(short vals) { - KMByteBlob.cast(vals); + KMByteBlob.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(MAC, vals); + KMArray.add(arrPtr, MAC, vals); + } + + public static short getVals(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).getVals(); + } + + public static short length(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).length(); + } + + public static short getChallenge(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).getChallenge(); + } + + public static void setChallenge(short bPtr, short vals) { + KMHardwareAuthToken.cast(bPtr).setChallenge(vals); + } + + public static short getUserId(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).getUserId(); + } + + public static void setUserId(short bPtr, short vals) { + KMHardwareAuthToken.cast(bPtr).setUserId(vals); + } + + public static short getAuthenticatorId(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).getAuthenticatorId(); + } + + public static void setAuthenticatorId(short bPtr, short vals) { + KMHardwareAuthToken.cast(bPtr).setAuthenticatorId(vals); + } + + public static short getHwAuthenticatorType(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).getHwAuthenticatorType(); + } + + public static void setHwAuthenticatorType(short bPtr, short vals) { + KMHardwareAuthToken.cast(bPtr).setHwAuthenticatorType(vals); + } + + public static short getTimestamp(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).getTimestamp(); + } + + public static void setTimestamp(short bPtr, short vals) { + KMHardwareAuthToken.cast(bPtr).setTimestamp(vals); + } + + public static short getMac(short bPtr) { + return KMHardwareAuthToken.cast(bPtr).getMac(); + } + + public static void setMac(short bPtr, short vals) { + KMHardwareAuthToken.cast(bPtr).setMac(vals); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java b/Applet/src/com/android/javacard/kmdevice/KMHmacSharingParameters.java similarity index 66% rename from Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java rename to Applet/src/com/android/javacard/kmdevice/KMHmacSharingParameters.java index f89ac608..772864ed 100644 --- a/Applet/src/com/android/javacard/keymaster/KMHmacSharingParameters.java +++ b/Applet/src/com/android/javacard/kmdevice/KMHmacSharingParameters.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -38,9 +38,8 @@ private KMHmacSharingParameters() { public static short exp() { short arrPtr = KMArray.instance((short) 2); - KMArray arr = KMArray.cast(arrPtr); - arr.add(SEED, KMByteBlob.exp()); - arr.add(NONCE, KMByteBlob.exp()); + KMArray.add(arrPtr, SEED, KMByteBlob.exp()); + KMArray.add(arrPtr, NONCE, KMByteBlob.exp()); return instance(arrPtr); } @@ -48,7 +47,7 @@ private static KMHmacSharingParameters proto(short ptr) { if (prototype == null) { prototype = new KMHmacSharingParameters(); } - instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] = ptr; + KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] = ptr; return prototype; } @@ -59,14 +58,14 @@ public static short instance() { public static short instance(short vals) { short ptr = KMType.instance(HMAC_SHARING_PARAM_TYPE, (short) 2); - if (KMArray.cast(vals).length() != 2) { + if (KMArray.length(vals) != 2) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); return ptr; } - public static KMHmacSharingParameters cast(short ptr) { + private static KMHmacSharingParameters cast(short ptr) { if (heap[ptr] != HMAC_SHARING_PARAM_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -78,33 +77,58 @@ public static KMHmacSharingParameters cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_HMAC_SHARING_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); } public short length() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); + return KMArray.length(arrPtr); } public void setSeed(short vals) { - KMByteBlob.cast(vals); + KMByteBlob.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(SEED, vals); + KMArray.add(arrPtr, SEED, vals); } public void setNonce(short vals) { - KMByteBlob.cast(vals); + KMByteBlob.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(NONCE, vals); + KMArray.add(arrPtr, NONCE, vals); } public short getNonce() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(NONCE); + return KMArray.get(arrPtr, NONCE); } public short getSeed() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(SEED); + return KMArray.get(arrPtr, SEED); + } + + public static short getVals(short bPtr) { + return KMHmacSharingParameters.cast(bPtr).getVals(); + } + + public static short length(short bPtr) { + return KMHmacSharingParameters.cast(bPtr).length(); + } + + public static void setSeed(short bPtr, short vals) { + KMHmacSharingParameters.cast(bPtr).setSeed(vals); + } + + public static void setNonce(short bPtr, short vals) { + KMHmacSharingParameters.cast(bPtr).setNonce(vals); + } + + public static short getNonce(short bPtr) { + return KMHmacSharingParameters.cast(bPtr).getNonce(); + } + + public static short getSeed(short bPtr) { + return KMHmacSharingParameters.cast(bPtr).getSeed(); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/kmdevice/KMInteger.java similarity index 59% rename from Applet/src/com/android/javacard/keymaster/KMInteger.java rename to Applet/src/com/android/javacard/kmdevice/KMInteger.java index 2ae32ac1..85da5ce7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/kmdevice/KMInteger.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -30,14 +30,14 @@ public class KMInteger extends KMType { public static final short UINT_64 = 8; private static KMInteger prototype; - private KMInteger() { + protected KMInteger() { } private static KMInteger proto(short ptr) { if (prototype == null) { prototype = new KMInteger(); } - instanceTable[KM_INTEGER_OFFSET] = ptr; + KMType.instanceTable[KM_INTEGER_OFFSET] = ptr; return prototype; } @@ -73,7 +73,12 @@ public static short instance(byte[] num, short srcOff, short length) { } } - public static KMInteger cast(short ptr) { + private static KMInteger cast(short ptr) { + validate(ptr); + return proto(ptr); + } + + public static void validate(short ptr) { byte[] heap = repository.getHeap(); if (heap[ptr] != INTEGER_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); @@ -81,7 +86,6 @@ public static KMInteger cast(short ptr) { if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - return proto(ptr); } // create integer and copy byte value @@ -101,33 +105,33 @@ public static short uint_16(short num) { // create integer and copy integer value public static short uint_32(byte[] num, short offset) { short ptr = instance(UINT_32); - Util.arrayCopyNonAtomic(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_32); return ptr; } // create integer and copy integer value public static short uint_64(byte[] num, short offset) { short ptr = instance(UINT_64); - Util.arrayCopyNonAtomic(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), UINT_64); return ptr; } // Get the length of the integer - public short length() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + 1)); + private short length() { + return Util.getShort(heap, (short) (getBaseOffset() + 1)); } // Get the buffer pointer in which blob is contained. - public byte[] getBuffer() { + private byte[] getBuffer() { return heap; } // Get the start of value - public short getStartOff() { - return (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE); + private short getStartOff() { + return (short) (getBaseOffset() + TLV_HEADER_SIZE); } - public void getValue(byte[] dest, short destOff, short length) { + private void getValue(byte[] dest, short destOff, short length) { if (length < length()) { KMException.throwIt(KMError.UNKNOWN_ERROR); } @@ -135,19 +139,19 @@ public void getValue(byte[] dest, short destOff, short length) { length = length(); destOff += length; } - Util.arrayCopyNonAtomic(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE), dest, destOff, length); + Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length); } - public void setValue(byte[] src, short srcOff) { - Util.arrayCopyNonAtomic(src, srcOff, heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE), length()); + private void setValue(byte[] src, short srcOff) { + Util.arrayCopyNonAtomic(src, srcOff, heap, getStartOff(), length()); } - public short value(byte[] dest, short destOff) { - Util.arrayCopyNonAtomic(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE), dest, destOff, length()); + private short value(byte[] dest, short destOff) { + Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length()); return length(); } - public short toLittleEndian(byte[] dest, short destOff) { + private short toLittleEndian(byte[] dest, short destOff) { short index = (short) (length() - 1); while (index >= 0) { dest[destOff++] = heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + index)]; @@ -156,19 +160,19 @@ public short toLittleEndian(byte[] dest, short destOff) { return length(); } - public short getShort() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + 2)); + protected short getShort() { + return Util.getShort(heap, (short) (getStartOff() + 2)); } - public short getSignificantShort() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE)); + private short getSignificantShort() { + return Util.getShort(heap, getStartOff()); } - public byte getByte() { - return heap[(short) (instanceTable[KM_INTEGER_OFFSET] + TLV_HEADER_SIZE + 3)]; + private byte getByte() { + return heap[(short) (getStartOff() + 3)]; } - public boolean isZero() { + private boolean isZero() { if (getShort() == 0 && getSignificantShort() == 0) { return true; } @@ -180,18 +184,18 @@ public static short compare(short num1, short num2) { short num2Buf = repository.alloc((short) 8); Util.arrayFillNonAtomic(repository.getHeap(), num1Buf, (short) 8, (byte) 0); Util.arrayFillNonAtomic(repository.getHeap(), num2Buf, (short) 8, (byte) 0); - short len = KMInteger.cast(num1).length(); - KMInteger.cast(num1).getValue(repository.getHeap(), (short) (num1Buf + (short) (8 - len)), len); - len = KMInteger.cast(num2).length(); - KMInteger.cast(num2).getValue(repository.getHeap(), (short) (num2Buf + (short) (8 - len)), len); + short len = KMInteger.length(num1); + KMInteger.getValue(num1, repository.getHeap(), (short) (num1Buf + (short) (8 - len)), len); + len = KMInteger.length(num2); + KMInteger.getValue(num2, repository.getHeap(), (short) (num2Buf + (short) (8 - len)), len); return KMInteger.unsignedByteArrayCompare( - repository.getHeap(), num1Buf, - repository.getHeap(), num2Buf, - (short) 8); + repository.getHeap(), num1Buf, + repository.getHeap(), num2Buf, + (short) 8); } public static byte unsignedByteArrayCompare(byte[] a1, short offset1, byte[] a2, short offset2, - short length) { + short length) { byte count = (byte) 0; short val1 = (short) 0; short val2 = (short) 0; @@ -209,4 +213,55 @@ public static byte unsignedByteArrayCompare(byte[] a1, short offset1, byte[] a2, } return 0; } + + protected short getBaseOffset() { + return instanceTable[KM_INTEGER_OFFSET]; + } + + // Get the length of the integer + public static short length(short bPtr) { + return KMInteger.cast(bPtr).length(); + } + + // Get the buffer pointer in which blob is contained. + public static byte[] getBuffer(short bPtr) { + return KMInteger.cast(bPtr).getBuffer(); + } + + // Get the start of value + public static short getStartOff(short bPtr) { + return KMInteger.cast(bPtr).getStartOff(); + } + + public static void getValue(short bPtr, byte[] dest, short destOff, short length) { + KMInteger.cast(bPtr).getValue(dest, destOff, length); + } + + public static void setValue(short bPtr, byte[] src, short srcOff) { + KMInteger.cast(bPtr).setValue(src, srcOff); + } + + public static short value(short bPtr, byte[] dest, short destOff) { + return KMInteger.cast(bPtr).value(dest, destOff); + } + + public static short toLittleEndian(short bPtr, byte[] dest, short destOff) { + return KMInteger.cast(bPtr).toLittleEndian(dest, destOff); + } + + public static short getShort(short bPtr) { + return KMInteger.cast(bPtr).getShort(); + } + + public static short getSignificantShort(short bPtr) { + return KMInteger.cast(bPtr).getSignificantShort(); + } + + public static byte getByte(short bPtr) { + return KMInteger.cast(bPtr).getByte(); + } + + public static boolean isZero(short bPtr) { + return KMInteger.cast(bPtr).isZero(); + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java b/Applet/src/com/android/javacard/kmdevice/KMIntegerArrayTag.java similarity index 72% rename from Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java rename to Applet/src/com/android/javacard/kmdevice/KMIntegerArrayTag.java index 558e44e2..8c16051a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerArrayTag.java +++ b/Applet/src/com/android/javacard/kmdevice/KMIntegerArrayTag.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -29,7 +29,11 @@ public class KMIntegerArrayTag extends KMTag { private static KMIntegerArrayTag prototype; - private static final short[] tags = {USER_SECURE_ID}; + private static short[] tags; + + public static void initStatics() { + tags = new short[]{USER_SECURE_ID}; + } private KMIntegerArrayTag() { } @@ -38,7 +42,7 @@ private static KMIntegerArrayTag proto(short ptr) { if (prototype == null) { prototype = new KMIntegerArrayTag(); } - instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] = ptr; return prototype; } @@ -82,7 +86,7 @@ public static short instance(short tagType, short key, short arrObj) { return ptr; } - public static KMIntegerArrayTag cast(short ptr) { + private static KMIntegerArrayTag cast(short ptr) { if (heap[ptr] != TAG_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -94,30 +98,31 @@ public static KMIntegerArrayTag cast(short ptr) { } public short getTagType() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE)); } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getValues() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_INTEGER_ARRAY_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { short ptr = getValues(); - return KMArray.cast(ptr).length(); + return KMArray.length(ptr); } public void add(short index, short val) { - KMArray arr = KMArray.cast(getValues()); - arr.add(index, val); + KMArray.add(getValues(), index, val); } public short get(short index) { - KMArray arr = KMArray.cast(getValues()); - return arr.get(index); + return KMArray.get(getValues(), index); } private static boolean validateKey(short key) { @@ -134,25 +139,14 @@ private static boolean validateTagType(short tagType) { return (tagType == ULONG_ARRAY_TAG) || (tagType == UINT_ARRAY_TAG); } - public boolean contains(short tagValue) { - short index = 0; - while (index < length()) { - if (KMInteger.compare(tagValue, get(index)) == 0) { - return true; - } - index++; - } - return false; - } - public static boolean contains(short tagId, short tagValue, short params) { short tag = - KMKeyParameters.findTag(KMType.UINT_ARRAY_TAG, tagId, params); + KMKeyParameters.findTag(params, KMType.UINT_ARRAY_TAG, tagId); if (tag != KMType.INVALID_VALUE) { short index = 0; tag = KMIntegerArrayTag.cast(tag).getValues(); - while (index < KMArray.cast(tag).length()) { - if (KMInteger.compare(tagValue, KMArray.cast(tag).get(index)) == 0) { + while (index < KMArray.length(tag)) { + if (KMInteger.compare(tagValue, KMArray.get(tag, index)) == 0) { return true; } index++; @@ -161,4 +155,40 @@ public static boolean contains(short tagId, short tagValue, short params) { return false; } + public boolean contains(short tagValue) { + short index = 0; + while (index < length()) { + if (KMInteger.compare(tagValue, get(index)) == 0) { + return true; + } + index++; + } + return false; + } + + public static boolean contains(short bPtr, short tagValue) { + return KMIntegerArrayTag.cast(bPtr).contains(tagValue); + } + + public static short getValues(short bPtr) { + return KMIntegerArrayTag.cast(bPtr).getValues(); + } + + public static short get(short bPtr, short index) { + return KMIntegerArrayTag.cast(bPtr).get(index); + } + + public static short getTagType(short bPtr) { + return KMIntegerArrayTag.cast(bPtr).getTagType(); + } + + public static short getKey(short bPtr) { + return KMIntegerArrayTag.cast(bPtr).getKey(); + } + + public static short length(short bPtr) { + return KMIntegerArrayTag.cast(bPtr).length(); + } + + } diff --git a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java b/Applet/src/com/android/javacard/kmdevice/KMIntegerTag.java similarity index 67% rename from Applet/src/com/android/javacard/keymaster/KMIntegerTag.java rename to Applet/src/com/android/javacard/kmdevice/KMIntegerTag.java index c4bab026..8bd3c5e7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMIntegerTag.java +++ b/Applet/src/com/android/javacard/kmdevice/KMIntegerTag.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -28,30 +28,38 @@ public class KMIntegerTag extends KMTag { private static KMIntegerTag prototype; + // Allowed tag keys. - private static final short[] tags = { - // UINT - KEYSIZE, - MIN_MAC_LENGTH, - MIN_SEC_BETWEEN_OPS, - MAX_USES_PER_BOOT, - USERID, - AUTH_TIMEOUT, - OS_VERSION, - OS_PATCH_LEVEL, - VENDOR_PATCH_LEVEL, - BOOT_PATCH_LEVEL, - MAC_LENGTH, - // ULONG - RSA_PUBLIC_EXPONENT, - // DATE - ACTIVE_DATETIME, - ORIGINATION_EXPIRE_DATETIME, - USAGE_EXPIRE_DATETIME, - CREATION_DATETIME, - // Custom tag. - AUTH_TIMEOUT_MILLIS - }; + private static short[] tags; + + public static void initStatics() { + tags = new short[]{ + // UINT + KEYSIZE, + MIN_MAC_LENGTH, + MIN_SEC_BETWEEN_OPS, + MAX_USES_PER_BOOT, + USERID, + AUTH_TIMEOUT, + OS_VERSION, + OS_PATCH_LEVEL, + VENDOR_PATCH_LEVEL, + BOOT_PATCH_LEVEL, + MAC_LENGTH, + // ULONG + RSA_PUBLIC_EXPONENT, + // DATE + ACTIVE_DATETIME, + ORIGINATION_EXPIRE_DATETIME, + USAGE_EXPIRE_DATETIME, + CREATION_DATETIME, + CERTIFICATE_NOT_BEFORE, + CERTIFICATE_NOT_AFTER, + USAGE_COUNT_LIMIT, + // custom tag + AUTH_TIMEOUT_MILLIS, + }; + } private KMIntegerTag() { } @@ -60,7 +68,7 @@ private static KMIntegerTag proto(short ptr) { if (prototype == null) { prototype = new KMIntegerTag(); } - instanceTable[KM_INTEGER_TAG_OFFSET] = ptr; + KMType.instanceTable[KM_INTEGER_TAG_OFFSET] = ptr; return prototype; } @@ -104,7 +112,7 @@ public static short instance(short tagType, short key, short intObj) { return ptr; } - public static KMIntegerTag cast(short ptr) { + private static KMIntegerTag cast(short ptr) { if (heap[ptr] != TAG_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -116,20 +124,22 @@ public static KMIntegerTag cast(short ptr) { } public short getTagType() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE)); } public short getKey() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 2)); } public short getValue() { - return Util.getShort(heap, (short) (instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_INTEGER_TAG_OFFSET] + TLV_HEADER_SIZE + 4)); } public short length() { - KMInteger obj = KMInteger.cast(getValue()); - return obj.length(); + return KMInteger.length(getValue()); } private static boolean validateKey(short key) { @@ -149,11 +159,11 @@ private static boolean validateTagType(short tagType) { public static short getShortValue(short tagType, short tagKey, short keyParameters) { short ptr; if (tagType == UINT_TAG) { - ptr = KMKeyParameters.findTag(KMType.UINT_TAG, tagKey, keyParameters); + ptr = KMKeyParameters.findTag(keyParameters, KMType.UINT_TAG, tagKey); if (ptr != KMType.INVALID_VALUE) { ptr = KMIntegerTag.cast(ptr).getValue(); - if (KMInteger.cast(ptr).getSignificantShort() == 0) { - return KMInteger.cast(ptr).getShort(); + if (KMInteger.getSignificantShort(ptr) == 0) { + return KMInteger.getShort(ptr); } } } @@ -161,24 +171,24 @@ public static short getShortValue(short tagType, short tagKey, short keyParamete } public static short getValue( - byte[] buf, short offset, short tagType, short tagKey, short keyParameters) { + byte[] buf, short offset, short tagType, short tagKey, short keyParameters) { short ptr; if ((tagType == UINT_TAG) || (tagType == ULONG_TAG) || (tagType == DATE_TAG)) { - ptr = KMKeyParameters.findTag(tagType, tagKey, keyParameters); + ptr = KMKeyParameters.findTag(keyParameters, tagType, tagKey); if (ptr != KMType.INVALID_VALUE) { ptr = KMIntegerTag.cast(ptr).getValue(); - return KMInteger.cast(ptr).value(buf, offset); + return KMInteger.value(ptr, buf, offset); } } return KMType.INVALID_VALUE; } public boolean isValidKeySize(byte alg) { - short val = KMIntegerTag.cast(instanceTable[KM_INTEGER_TAG_OFFSET]).getValue(); - if (KMInteger.cast(val).getSignificantShort() != 0) { + short val = KMIntegerTag.cast(KMType.instanceTable[KM_INTEGER_TAG_OFFSET]).getValue(); + if (KMInteger.getSignificantShort(val) != 0) { return false; } - val = KMInteger.cast(val).getShort(); + val = KMInteger.getShort(val); switch (alg) { case KMType.RSA: if (val == 2048) { @@ -191,7 +201,7 @@ public boolean isValidKeySize(byte alg) { } break; case KMType.DES: - if (val == 192 || val == 168) { + if (val == 168) { return true; } break; @@ -210,4 +220,20 @@ public boolean isValidKeySize(byte alg) { } return false; } + + public static boolean isValidKeySize(short bPtr, byte alg) { + return KMIntegerTag.cast(bPtr).isValidKeySize(alg); + } + + public static short getTagType(short bPtr) { + return KMIntegerTag.cast(bPtr).getTagType(); + } + + public static short getValue(short bPtr) { + return KMIntegerTag.cast(bPtr).getValue(); + } + + public static short getKey(short bPtr) { + return KMIntegerTag.cast(bPtr).getKey(); + } } diff --git a/Applet/src/com/android/javacard/kmdevice/KMKeyCharacteristics.java b/Applet/src/com/android/javacard/kmdevice/KMKeyCharacteristics.java new file mode 100644 index 00000000..18572462 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMKeyCharacteristics.java @@ -0,0 +1,159 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMKeyCharacteristics represents KeyCharacteristics structure from android keymaster hal + * specifications. It corresponds to CBOR array type. struct{byte KEY_CHAR_TYPE; short length=3; + * short arrayPtr} where arrayPtr is a pointer to ordered array with 1 or 3 following elements: + * {KMKeyParameters sb; KMKeyParameters tee; KMKeyParameters keystore} + */ +public class KMKeyCharacteristics extends KMType { + + public static final byte KEYSTORE_ENFORCED = 0x00; + public static final byte STRONGBOX_ENFORCED = 0x01; + public static final byte TEE_ENFORCED = 0x02; + private static KMKeyCharacteristics prototype; + + private KMKeyCharacteristics() { + } + + public static short exp() { + short sb = KMKeyParameters.exp(); + short tee = KMKeyParameters.exp(); + short keystore = KMKeyParameters.exp(); + short arrPtr = KMArray.instance((short) 3); + + KMArray.add(arrPtr, STRONGBOX_ENFORCED, sb); + KMArray.add(arrPtr, TEE_ENFORCED, tee); + KMArray.add(arrPtr, KEYSTORE_ENFORCED, keystore); + return instance(arrPtr); + } + + private static KMKeyCharacteristics proto(short ptr) { + if (prototype == null) { + prototype = new KMKeyCharacteristics(); + } + KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] = ptr; + return prototype; + } + + public static short instance() { + short arrPtr = KMArray.instance((short) 3); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(KEY_CHAR_TYPE, (short) 3); + if (KMArray.length(vals) != 3) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + private static KMKeyCharacteristics cast(short ptr) { + if (heap[ptr] != KEY_CHAR_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public short getVals() { + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_KEY_CHARACTERISTICS_OFFSET] + TLV_HEADER_SIZE)); + } + + public short length() { + short arrPtr = getVals(); + return KMArray.length(arrPtr); + } + + public short getKeystoreEnforced() { + short arrPtr = getVals(); + return KMArray.get(arrPtr, KEYSTORE_ENFORCED); + } + + public short getTeeEnforced() { + short arrPtr = getVals(); + return KMArray.get(arrPtr, TEE_ENFORCED); + } + + public short getStrongboxEnforced() { + short arrPtr = getVals(); + return KMArray.get(arrPtr, STRONGBOX_ENFORCED); + } + + public void setKeystoreEnforced(short ptr) { + KMKeyParameters.validate(ptr); + short arrPtr = getVals(); + KMArray.add(arrPtr, KEYSTORE_ENFORCED, ptr); + } + + public void setTeeEnforced(short ptr) { + KMKeyParameters.validate(ptr); + short arrPtr = getVals(); + KMArray.add(arrPtr, TEE_ENFORCED, ptr); + } + + public void setStrongboxEnforced(short ptr) { + KMKeyParameters.validate(ptr); + short arrPtr = getVals(); + KMArray.add(arrPtr, STRONGBOX_ENFORCED, ptr); + } + + public static short getVals(short bPtr) { + return KMKeyCharacteristics.cast(bPtr).getVals(); + } + + public static short length(short bPtr) { + return KMKeyCharacteristics.cast(bPtr).length(); + } + + public static short getKeystoreEnforced(short bPtr) { + return KMKeyCharacteristics.cast(bPtr).getKeystoreEnforced(); + } + + public static short getTeeEnforced(short bPtr) { + return KMKeyCharacteristics.cast(bPtr).getTeeEnforced(); + } + + public static short getStrongboxEnforced(short bPtr) { + return KMKeyCharacteristics.cast(bPtr).getStrongboxEnforced(); + } + + public static void setKeystoreEnforced(short bPtr, short ptr) { + KMKeyCharacteristics.cast(bPtr).setKeystoreEnforced(ptr); + } + + public static void setTeeEnforced(short bPtr, short ptr) { + KMKeyCharacteristics.cast(bPtr).setTeeEnforced(ptr); + } + + public static void setStrongboxEnforced(short bPtr, short ptr) { + KMKeyCharacteristics.cast(bPtr).setStrongboxEnforced(ptr); + } + +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMKeyParameters.java b/Applet/src/com/android/javacard/kmdevice/KMKeyParameters.java new file mode 100644 index 00000000..675747da --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMKeyParameters.java @@ -0,0 +1,519 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMKeyParameters represents KeyParameters structure from android keymaster hal specifications. It + * corresponds to CBOR map type. struct{byte KEY_PARAM_TYPE; short length=2; short arrayPtr} where + * arrayPtr is a pointer to array with any KMTag subtype instances. + */ +public class KMKeyParameters extends KMType { + + private static KMKeyParameters prototype; + + private static short[] customTags; + private static short[] invalidTagsArr; + private static short[] unsupportedTagArr; + private static short[] hwEnforcedTagArr; + private static short[] swEnforcedTagsArr; + private static short[] teeEnforcedTagsArr; + + private KMKeyParameters() { + } + + public static void initStatics() { + customTags = new short[]{ + KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + }; + invalidTagsArr = new short[]{ + KMType.BYTES_TAG, KMType.NONCE, + KMType.BYTES_TAG, KMType.ASSOCIATED_DATA, + KMType.BYTES_TAG, KMType.UNIQUE_ID, + KMType.UINT_TAG, KMType.MAC_LENGTH, + }; + unsupportedTagArr = new short[]{ + // Unsupported tags. + KMType.BOOL_TAG, KMType.TRUSTED_USER_PRESENCE_REQUIRED, + KMType.UINT_TAG, KMType.MIN_SEC_BETWEEN_OPS + }; + hwEnforcedTagArr = new short[]{ + // HW Enforced + KMType.ENUM_TAG, KMType.ORIGIN, + 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.RESET_SINCE_ID_ROTATION, + 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, + }; + swEnforcedTagsArr = new short[]{ + 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 + }; + teeEnforcedTagsArr = new short[]{ + KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, + KMType.UINT_TAG, KMType.AUTH_TIMEOUT, + KMType.ENUM_TAG, KMType.USER_AUTH_TYPE, + }; + + } + + private static KMKeyParameters proto(short ptr) { + if (prototype == null) { + prototype = new KMKeyParameters(); + } + KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short arrPtr = KMArray.instance((short) 11); + KMArray.add(arrPtr, (short) 0, KMEnum.instance(KMType.RULE, KMType.FAIL_ON_INVALID_TAGS)); + KMArray.add(arrPtr, (short) 1, KMIntegerTag.exp(UINT_TAG)); + KMArray.add(arrPtr, (short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); + KMArray.add(arrPtr, (short) 3, KMIntegerTag.exp(ULONG_TAG)); + KMArray.add(arrPtr, (short) 4, KMIntegerTag.exp(DATE_TAG)); + KMArray.add(arrPtr, (short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); + KMArray.add(arrPtr, (short) 6, KMEnumTag.exp()); + KMArray.add(arrPtr, (short) 7, KMEnumArrayTag.exp()); + KMArray.add(arrPtr, (short) 8, KMByteTag.exp()); + KMArray.add(arrPtr, (short) 9, KMBoolTag.exp()); + KMArray.add(arrPtr, (short) 10, KMBignumTag.exp()); + return instance(arrPtr); + } + + public static short expAny() { + short arrPtr = KMArray.instance((short) 11); + KMArray.add(arrPtr, (short) 0, KMEnum.instance(KMType.RULE, KMType.IGNORE_INVALID_TAGS)); + KMArray.add(arrPtr, (short) 1, KMIntegerTag.exp(UINT_TAG)); + KMArray.add(arrPtr, (short) 2, KMIntegerArrayTag.exp(UINT_ARRAY_TAG)); + KMArray.add(arrPtr, (short) 3, KMIntegerTag.exp(ULONG_TAG)); + KMArray.add(arrPtr, (short) 4, KMIntegerTag.exp(DATE_TAG)); + KMArray.add(arrPtr, (short) 5, KMIntegerArrayTag.exp(ULONG_ARRAY_TAG)); + KMArray.add(arrPtr, (short) 6, KMEnumTag.exp()); + KMArray.add(arrPtr, (short) 7, KMEnumArrayTag.exp()); + KMArray.add(arrPtr, (short) 8, KMByteTag.exp()); + KMArray.add(arrPtr, (short) 9, KMBoolTag.exp()); + KMArray.add(arrPtr, (short) 10, KMBignumTag.exp()); + return instance(arrPtr); + } + + public static short instance(short vals) { + short ptr = KMType.instance(KEY_PARAM_TYPE, (short) 2); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); + return ptr; + } + + private static KMKeyParameters cast(short ptr) { + validate(ptr); + return proto(ptr); + } + + public static void validate(short ptr) { + if (heap[ptr] != KEY_PARAM_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + if (heap[arrPtr] != ARRAY_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + public short getVals() { + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_KEY_PARAMETERS_OFFSET] + TLV_HEADER_SIZE)); + } + + public short length() { + short arrPtr = getVals(); + return KMArray.length(arrPtr); + } + + public static short getVals(short bPtr) { + return KMKeyParameters.cast(bPtr).getVals(); + } + + public static short length(short bPtr) { + return KMKeyParameters.cast(bPtr).length(); + } + + public short findTag(short tagType, short tagKey) { + short index = 0; + short length = KMArray.length(getVals()); + short key; + short type; + short ret = KMType.INVALID_VALUE; + short obj; + while (index < length) { + obj = KMArray.get(getVals(), index); + key = KMTag.getKMTagKey(obj); + type = KMTag.getKMTagType(obj); + if ((tagKey == key) && (tagType == type)) { + ret = obj; + break; + } + index++; + } + return ret; + } + + public static short findTag(short bPtr, short tagType, short tagKey) { + return KMKeyParameters.cast(bPtr).findTag(tagType, tagKey); + } + + public static boolean hasUnsupportedTags(short keyParamsPtr) { + + byte index = 0; + short tagInd; + short tagPtr; + short tagKey; + short tagType; + short arrPtr = KMKeyParameters.getVals(keyParamsPtr); + short len = KMArray.length(arrPtr); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.get(arrPtr, index); + tagKey = KMTag.getKMTagKey(tagPtr); + tagType = KMTag.getKMTagType(tagPtr); + while (tagInd < (short) unsupportedTagArr.length) { + if ((unsupportedTagArr[tagInd] == tagType) + && (unsupportedTagArr[(short) (tagInd + 1)] == tagKey)) { + return true; + } + tagInd += 2; + } + index++; + } + return false; + } + + // KDF, ECIES_SINGLE_HASH_MODE missing from types.hal + public static short makeSbEnforced(short keyParamsPtr, byte origin, + short osVersionObjPtr, short osPatchObjPtr, short vendorPatchObjPtr, + short bootPatchObjPtr, byte[] scratchPad) { + + byte index = 0; + short tagInd; + short arrInd = 0; + short tagPtr; + short tagKey; + short tagType; + short arrPtr = KMKeyParameters.getVals(keyParamsPtr); + short len = KMArray.length(arrPtr); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.get(arrPtr, index); + tagKey = KMTag.getKMTagKey(tagPtr); + tagType = KMTag.getKMTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) hwEnforcedTagArr.length) { + if ((hwEnforcedTagArr[tagInd] == tagType) + && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + short originTag = KMEnumTag.instance(KMType.ORIGIN, origin); + Util.setShort(scratchPad, arrInd, originTag); + arrInd += 2; + short osVersionTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_VERSION, osVersionObjPtr); + Util.setShort(scratchPad, arrInd, osVersionTag); + arrInd += 2; + short osPatchTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, osPatchObjPtr); + Util.setShort(scratchPad, arrInd, osPatchTag); + arrInd += 2; + short vendorPatchTag = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.VENDOR_PATCH_LEVEL, vendorPatchObjPtr); + Util.setShort(scratchPad, arrInd, vendorPatchTag); + arrInd += 2; + short bootPatchTag = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.BOOT_PATCH_LEVEL, bootPatchObjPtr); + Util.setShort(scratchPad, arrInd, bootPatchTag); + arrInd += 2; + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeSbEnforced(short keyParamsPtr, byte[] scratchPad) { + byte index = 0; + short tagInd; + short arrInd = 0; + short tagPtr; + short tagKey; + short tagType; + short arrPtr = KMKeyParameters.getVals(keyParamsPtr); + short len = KMArray.length(arrPtr); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.get(arrPtr, index); + tagKey = KMTag.getKMTagKey(tagPtr); + tagType = KMTag.getKMTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) hwEnforcedTagArr.length) { + if ((hwEnforcedTagArr[tagInd] == tagType) + && (hwEnforcedTagArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeHwEnforced(short sb, short tee) { + short len = KMKeyParameters.length(sb); + len += KMKeyParameters.length(tee); + short hwEnf = KMArray.instance(len); + sb = KMKeyParameters.getVals(sb); + tee = KMKeyParameters.getVals(tee); + len = KMArray.length(sb); + short src = 0; + short dest = 0; + short val = 0; + while (src < len) { + val = KMArray.get(sb, src); + KMArray.add(hwEnf, dest, val); + src++; + dest++; + } + src = 0; + len = KMArray.length(tee); + while (src < len) { + val = KMArray.get(tee, src); + KMArray.add(hwEnf, dest, val); + src++; + dest++; + } + return KMKeyParameters.instance(hwEnf); + } + + // ALL_USERS, EXPORTABLE missing from types.hal + public static short makeKeystoreEnforced(short keyParamsPtr, byte[] scratchPad) { + byte index = 0; + short tagInd; + short arrInd = 0; + short tagPtr; + short tagKey; + short tagType; + short arrPtr = KMKeyParameters.getVals(keyParamsPtr); + short len = KMArray.length(arrPtr); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.get(arrPtr, index); + tagKey = KMTag.getKMTagKey(tagPtr); + tagType = KMTag.getKMTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) swEnforcedTagsArr.length) { + if ((swEnforcedTagsArr[tagInd] == tagType) + && (swEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeTeeEnforced(short keyParamsPtr, byte[] scratchPad) { + byte index = 0; + short tagInd; + short arrInd = 0; + short tagPtr; + short tagKey; + short tagType; + short arrPtr = KMKeyParameters.getVals(keyParamsPtr); + short len = KMArray.length(arrPtr); + while (index < len) { + tagInd = 0; + tagPtr = KMArray.get(arrPtr, index); + tagKey = KMTag.getKMTagKey(tagPtr); + tagType = KMTag.getKMTagType(tagPtr); + if (!isValidTag(tagType, tagKey)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + while (tagInd < (short) teeEnforcedTagsArr.length) { + if ((teeEnforcedTagsArr[tagInd] == tagType) + && (teeEnforcedTagsArr[(short) (tagInd + 1)] == tagKey)) { + Util.setShort(scratchPad, arrInd, tagPtr); + arrInd += 2; + break; + } + tagInd += 2; + } + index++; + } + // Add custom tags at the end of the array. So it becomes easy to + // delete them when sending key characteristics back to HAL. + arrInd = addCustomTags(keyParamsPtr, scratchPad, arrInd); + return createKeyParameters(scratchPad, (short) (arrInd / 2)); + } + + public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] scratchPad) { + short appId = KMKeyParameters.findTag(keyParamsPtr, KMType.BYTES_TAG, KMType.APPLICATION_ID); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.getValue(appId); + } + short appData = + KMKeyParameters.findTag(keyParamsPtr, KMType.BYTES_TAG, KMType.APPLICATION_DATA); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.getValue(appData); + } + return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); + } + + public static short makeHidden(short appIdBlob, short appDataBlob, short rootOfTrustBlob, + byte[] scratchPad) { + // Order in which the hidden array is created should not change. + short index = 0; + KMByteBlob.validate(rootOfTrustBlob); + Util.setShort(scratchPad, index, rootOfTrustBlob); + index += 2; + if (appIdBlob != KMTag.INVALID_VALUE) { + KMByteBlob.validate(appIdBlob); + Util.setShort(scratchPad, index, appIdBlob); + index += 2; + } + if (appDataBlob != KMTag.INVALID_VALUE) { + Util.setShort(scratchPad, index, appDataBlob); + index += 2; + } + return createKeyParameters(scratchPad, (short) (index / 2)); + + } + + public static boolean isValidTag(short tagType, short tagKey) { + short index = 0; + if (tagKey == KMType.INVALID_TAG) { + return false; + } + while (index < invalidTagsArr.length) { + if ((tagType == invalidTagsArr[index]) && (tagKey == invalidTagsArr[(short) (index + 1)])) { + return false; + } + index += 2; + } + return true; + } + + public static short createKeyParameters(byte[] ptrArr, short len) { + short arrPtr = KMArray.instance(len); + short index = 0; + short ptr = 0; + while (index < len) { + KMArray.add(arrPtr, index, Util.getShort(ptrArr, ptr)); + index++; + ptr += 2; + } + return KMKeyParameters.instance(arrPtr); + } + + public static short addCustomTags(short keyParams, byte[] scratchPad, short offset) { + short index = 0; + short tagPtr; + short len = (short) customTags.length; + short tagType; + while (index < len) { + tagType = customTags[(short) (index + 1)]; + switch (tagType) { + case KMType.AUTH_TIMEOUT_MILLIS: + short authTimeOutTag = + KMKeyParameters.findTag(keyParams, KMType.UINT_TAG, KMType.AUTH_TIMEOUT); + if (authTimeOutTag != KMType.INVALID_VALUE) { + tagPtr = createAuthTimeOutMillisTag(authTimeOutTag, scratchPad, offset); + Util.setShort(scratchPad, offset, tagPtr); + offset += 2; + } + break; + default: + break; + } + index += 2; + } + return offset; + } + + public void deleteCustomTags() { + short arrPtr = getVals(); + short index = (short) (customTags.length - 1); + short obj; + while (index >= 0) { + obj = findTag(customTags[(short) (index - 1)], customTags[index]); + if (obj != KMType.INVALID_VALUE) { + KMArray.deleteLastEntry(arrPtr); + } + index -= 2; + } + } + + public static void deleteCustomTags(short bPtr) { + KMKeyParameters.cast(bPtr).deleteCustomTags(); + } + + public static short createAuthTimeOutMillisTag(short authTimeOutTag, byte[] scratchPad, + short offset) { + short authTime = KMIntegerTag.getValue(authTimeOutTag); + Util.arrayFillNonAtomic(scratchPad, offset, (short) 40, (byte) 0); + Util.arrayCopyNonAtomic( + KMInteger.getBuffer(authTime), + KMInteger.getStartOff(authTime), + scratchPad, + (short) (offset + 8 - KMInteger.length(authTime)), + KMInteger.length(authTime)); + KMUtils.convertToMilliseconds(scratchPad, offset, (short) (offset + 8), (short) (offset + 16)); + return KMIntegerTag.instance(KMType.ULONG_TAG, KMType.AUTH_TIMEOUT_MILLIS, + KMInteger.uint_64(scratchPad, (short) (offset + 8))); + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMKeymasterDevice.java b/Applet/src/com/android/javacard/kmdevice/KMKeymasterDevice.java new file mode 100644 index 00000000..e25d05a1 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMKeymasterDevice.java @@ -0,0 +1,4624 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.CryptoException; + +/** + * KMKeymasterApplet implements the javacard applet. It creates repository and other install time + * objects. It also implements the keymaster state machine and handles javacard applet life cycle + * events. + */ +public class KMKeymasterDevice { + + // Constants. + public static byte[] F4; + public static final byte AES_BLOCK_SIZE = 16; + public static final byte DES_BLOCK_SIZE = 8; + public static final short MAX_LENGTH = 15000; + public static final short WRAPPING_KEY_SIZE = 32; + public static final short MAX_OPERATIONS_COUNT = 4; + public static final short VERIFIED_BOOT_KEY_SIZE = 32; + public static final short VERIFIED_BOOT_HASH_SIZE = 32; + public static final short BOOT_PATCH_LVL_SIZE = 4; + public static final short KEYMINT_HAL_VERSION = (short) 0x5000; + public static final short KEYMASTER_HAL_VERSION = (short) 0x4000; + private static final short MAX_AUTH_DATA_SIZE = (short) 512; + private static final short DERIVE_KEY_INPUT_SIZE = (short) 256; + public static final byte TRUSTED_ENVIRONMENT = 1; + + // "Keymaster HMAC Verification" - used for HMAC key verification. + public static byte[] sharingCheck; + + // "KeymasterSharedMac" + public static byte[] ckdfLable; + + // "Auth Verification" + public static byte[] authVerification; + + // "confirmation token" + public static byte[] confirmationToken; + // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys + private static byte[] defaultSubject; + + // Top 32 commands are reserved for provisioning. + private static final byte KEYMINT_CMD_APDU_START = 0x20; + + // Master key size + private static final short MASTER_KEY_SIZE = 16; + private static final short HMAC_SEED_NONCE_SIZE = 32; + + protected static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; //0x21 + private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; //0x22 + private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; //0x23 + private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; //0x24 + private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; //0x25 + private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; //0x26 + private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; //0x27 + private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; //0x28 + private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; //0x29 + private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; //0x2A + private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; //0x2B + private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; //0x2C + private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; //0x2D + private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; //0x2E + private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; //0x2F + protected static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; //0x30 + private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; //0x31 + private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; //0x32 + private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; //0x33 + private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20;//0x34 + private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; //0x35 + private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; //0x36 + private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; //0x37 + private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; //0x38 + private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; //0x39 + private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; //0x3A + // RKP + public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; //0x3B + public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; //0x3C + public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; //0x3D + public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; //0x3E + public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; //0x3F + public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; //0x40 + public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; //0x41 + public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; //0x42 + private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 35; //0x43 + + private static final byte INS_END_KM_CMD = 0x7F; + + // Data Dictionary items + private static final byte DATA_ARRAY_SIZE = 40; + private static final byte TMP_VARIABLE_ARRAY_SIZE = 5; + + protected static final byte KEY_PARAMETERS = 0; + private static final byte KEY_CHARACTERISTICS = 1; + private static final byte HIDDEN_PARAMETERS = 2; + protected static final byte HW_PARAMETERS = 3; + private static final byte SW_PARAMETERS = 4; + private static final byte AUTH_DATA = 5; + private static final byte AUTH_TAG = 6; + private static final byte NONCE = 7; + private static final byte KEY_BLOB = 8; + private static final byte AUTH_DATA_LENGTH = 9; + protected static final byte SECRET = 10; + private static final byte ROT = 11; + private static final byte DERIVED_KEY = 12; + private static final byte RSA_PUB_EXPONENT = 13; + private static final byte APP_ID = 14; + private static final byte APP_DATA = 15; + private static final byte PUB_KEY = 16; + private static final byte IMPORTED_KEY_BLOB = 17; + private static final byte ORIGIN = 18; + private static final byte NOT_USED = 19; + private static final byte MASKING_KEY = 20; + private static final byte HMAC_SHARING_PARAMS = 21; + private static final byte OP_HANDLE = 22; + private static final byte IV = 23; + protected static final byte INPUT_DATA = 24; + protected static final byte OUTPUT_DATA = 25; + private static final byte HW_TOKEN = 26; + private static final byte VERIFICATION_TOKEN = 27; + private static final byte SIGNATURE = 28; + private static final byte ATTEST_KEY_BLOB = 29; + private static final byte ATTEST_KEY_PARAMS = 30; + private static final byte ATTEST_KEY_ISSUER = 31; + private static final byte CERTIFICATE = 32; + private static final byte PLAIN_SECRET = 33; + private static final byte TEE_PARAMETERS = 34; + private static final byte SB_PARAMETERS = 35; + private static final byte CONFIRMATION_TOKEN = 36; + + // AddRngEntropy + private static final short MAX_SEED_SIZE = 2048; + + // Keyblob constants + public static final byte KEY_BLOB_SECRET = 0; + public static final byte KEY_BLOB_NONCE = 1; + public static final byte KEY_BLOB_AUTH_TAG = 2; + public static final byte KEY_BLOB_PARAMS = 3; + public static final byte KEY_BLOB_PUB_KEY = 4; + // AES GCM constants + public static final byte AES_GCM_AUTH_TAG_LENGTH = 16; + public static final byte AES_GCM_NONCE_LENGTH = 12; + // ComputeHMAC constants + private static final short HMAC_SHARED_PARAM_MAX_SIZE = 64; + protected static final short MAX_CERT_SIZE = 2048; + + protected static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + + //getHardwareInfo constants. + private static byte[] JAVACARD_KEYMASTER_DEVICE; + private static byte[] GOOGLE; + private static byte[] X509Subject; + + private static short[] ATTEST_ID_TAGS; + private static final byte SERIAL_NUM = (byte) 0x01; + + protected KMDecoder decoder; + protected KMRepository repository; + // TODO Remove static + protected static KMEncoder encoder; + protected KMSEProvider seProvider; + protected KMDataStore storeDataInst; + protected KMBootDataStore bootParamsProv; + protected KMOperationState[] opTable; + protected short[] tmpVariables; + protected static short[] data; + protected byte[] wrappingKey; + + + /** + * Registers this applet. + */ + public KMKeymasterDevice(KMSEProvider seImpl, KMRepository repoInst, KMEncoder encoderInst, + KMDecoder decoderInst, KMDataStore storeData, + KMBootDataStore bootParamsProvider) { + initKMDeviceStatics(); + seProvider = seImpl; + bootParamsProv = bootParamsProvider; + storeDataInst = storeData; + repository = repoInst; + encoder = encoderInst; + decoder = decoderInst; + data = JCSystem.makeTransientShortArray(DATA_ARRAY_SIZE, JCSystem.CLEAR_ON_DESELECT); + tmpVariables = JCSystem.makeTransientShortArray(TMP_VARIABLE_ARRAY_SIZE, + JCSystem.CLEAR_ON_DESELECT); + wrappingKey = JCSystem.makeTransientByteArray((short) (WRAPPING_KEY_SIZE + 1), + JCSystem.CLEAR_ON_RESET); + resetWrappingKey(); + opTable = new KMOperationState[MAX_OPERATIONS_COUNT]; + short index = 0; + while (index < MAX_OPERATIONS_COUNT) { + opTable[index] = new KMOperationState(); + index++; + } + KMType.initialize(); + if (!seProvider.isUpgrading()) { + initializeDefaultValues(); + } + + } + + private void initializeDefaultValues() { + short offset = repository.alloc((short) 32); + // Initialize master key + byte[] buffer = repository.getHeap(); + seProvider.getTrueRandomNumber(buffer, offset, MASTER_KEY_SIZE); + storeDataInst.storeData(KMDataStoreConstants.MASTER_KEY, buffer, offset, MASTER_KEY_SIZE); + // initialize default values + initHmacNonceAndSeed(buffer, offset); + initSystemBootParams(); + writeBoolean(KMDataStoreConstants.DEVICE_LOCKED, false, buffer, offset); + writeBoolean(KMDataStoreConstants.DEVICE_LOCKED_PASSWORD_ONLY, false, buffer, offset); + writeBoolean(KMDataStoreConstants.BOOT_ENDED_STATUS, false, buffer, offset); + writeBoolean(KMDataStoreConstants.EARLY_BOOT_ENDED_STATUS, false, buffer, offset); + writeBoolean(KMDataStoreConstants.PROVISIONED_LOCKED, false, buffer, offset); + } + + public static void initStatics() { + F4 = new byte[]{0x01, 0x00, 0x01}; + // "Keymaster HMAC Verification" - used for HMAC key verification. + sharingCheck = new byte[]{ + 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, + 0x56, + 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E + }; + + // "KeymasterSharedMac" + ckdfLable = new byte[]{ + 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, + 0x4D, + 0x61, 0x63 + }; + + // "Auth Verification" + authVerification = new byte[]{ + 0x41, 0x75, 0x74, 0x68, 0x20, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6F, + 0x6E + }; + // "confirmation token" + confirmationToken = new byte[]{ + 0x63, 0x6F, 0x6E, 0x66, 0x69, 0x72, 0x6D, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x74, 0x6F, + 0x6B, + 0x65, 0x6E + }; + // Subject is a fixed field with only CN= Android Keystore Key - same for all the keys + defaultSubject = new byte[]{ + 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, + 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, + 0x65, + 0x79 + }; + //getHardwareInfo constants. + JAVACARD_KEYMASTER_DEVICE = new byte[]{ + 0x4A, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4B, 0x65, 0x79, 0x6D, 0x61, 0x73, 0x74, + 0x65, 0x72, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, + }; + GOOGLE = new byte[]{0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + X509Subject = new byte[]{ + 0x30, 0x1F, 0x31, 0x1D, 0x30, 0x1B, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x14, 0x41, 0x6e, + 0x64, + 0x72, 0x6f, 0x69, 0x64, 0x20, 0x4B, 0x65, 0x79, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x4B, + 0x65, + 0x79 + }; + + ATTEST_ID_TAGS = 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 + }; + } + + private static void initKMDeviceStatics() { + initStatics(); + KMAttestationCertImpl.initStatics(); + KMBignumTag.initStatics(); + KMCosePairCoseKeyTag.initStatics(); + KMEnumTag.initStatics(); + KMCosePairTextStringTag.initStatics(); + KMByteTag.initStatics(); + KMEnum.initStatics(); + KMIntegerTag.initStatics(); + KMCose.initStatics(); + KMKeyParameters.initStatics(); + KMUtils.initStatics(); + KMBoolTag.initStatics(); + KMPKCS8Decoder.initStatics(); + KMEnumArrayTag.initStatics(); + KMIntegerArrayTag.initStatics(); + } + + public void clean() { + repository.clean(); + } + + protected void initHmacNonceAndSeed(byte[] scratchPad, short offset) { + seProvider.newRandomNumber(scratchPad, offset, HMAC_SEED_NONCE_SIZE); + storeDataInst.storeData(KMDataStoreConstants.HMAC_NONCE, scratchPad, offset, + HMAC_SEED_NONCE_SIZE); + } + + private void releaseAllOperations() { + short index = 0; + while (index < MAX_OPERATIONS_COUNT) { + opTable[index].reset(); + index++; + } + } + + private KMOperationState reserveOperation(short algorithm, short opHandle) { + short index = 0; + while (index < MAX_OPERATIONS_COUNT) { + if (opTable[index].getAlgorithm() == KMType.INVALID_VALUE) { + opTable[index].reset(); + opTable[index].setAlgorithm(algorithm); + opTable[index].setHandle(KMInteger.getBuffer(opHandle), + KMInteger.getStartOff(opHandle), + KMInteger.length(opHandle)); + return opTable[index]; + } + index++; + } + return null; + } + + private KMOperationState findOperation(short handle) { + return findOperation(KMInteger.getBuffer(handle), + KMInteger.getStartOff(handle), + KMInteger.length(handle)); + } + + private KMOperationState findOperation(byte[] opHandle, short start, short len) { + short index = 0; + while (index < MAX_OPERATIONS_COUNT) { + if (opTable[index].compare(opHandle, start, len) == 0) { + if (opTable[index].getAlgorithm() != KMType.INVALID_VALUE) { + return opTable[index]; + } + } + index++; + } + return null; + } + + private void releaseOperation(KMOperationState op) { + op.reset(); + } + + /** + * Selects this applet. + * + * @return Returns true if the keymaster is in correct state + */ + public boolean onSelect() { + repository.onSelect(); + return true; + } + + /** + * De-selects this applet. + */ + public void onDeselect() { + repository.onDeselect(); + } + + public void onUninstall() { + repository.onUninstall(); + } + + public short mapISOErrorToKMError(short reason) { + switch (reason) { + case ISO7816.SW_CLA_NOT_SUPPORTED: + return KMError.UNSUPPORTED_CLA; + case ISO7816.SW_CONDITIONS_NOT_SATISFIED: + return KMError.SW_CONDITIONS_NOT_SATISFIED; + case ISO7816.SW_COMMAND_NOT_ALLOWED: + return KMError.CMD_NOT_ALLOWED; + case ISO7816.SW_DATA_INVALID: + return KMError.INVALID_DATA; + case ISO7816.SW_INCORRECT_P1P2: + return KMError.INVALID_P1P2; + case ISO7816.SW_INS_NOT_SUPPORTED: + return KMError.UNSUPPORTED_INSTRUCTION; + case ISO7816.SW_WRONG_LENGTH: + return KMError.SW_WRONG_LENGTH; + case ISO7816.SW_UNKNOWN: + default: + return KMError.UNKNOWN_ERROR; + } + } + + public short mapCryptoErrorToKMError(short reason) { + switch (reason) { + case CryptoException.ILLEGAL_USE: + return KMError.CRYPTO_ILLEGAL_USE; + case CryptoException.ILLEGAL_VALUE: + return KMError.CRYPTO_ILLEGAL_VALUE; + case CryptoException.INVALID_INIT: + return KMError.CRYPTO_INVALID_INIT; + case CryptoException.NO_SUCH_ALGORITHM: + return KMError.CRYPTO_NO_SUCH_ALGORITHM; + case CryptoException.UNINITIALIZED_KEY: + return KMError.CRYPTO_UNINITIALIZED_KEY; + default: + return KMError.UNKNOWN_ERROR; + } + } + + /** + * Processes an incoming APDU and handles it using command objects. + * + * @param apdu the incoming APDU + */ + public void process(APDU apdu) { + try { + resetData(); + repository.onProcess(); + // Validate APDU Header. + 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_ATTEST_KEY_CMD: + processAttestKeyCmd(apdu); + break; + case INS_IMPORT_KEY_CMD: + processImportKeyCmd(apdu); + break; + case INS_IMPORT_WRAPPED_KEY_CMD: + processImportWrappedKeyCmd(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_GET_CERT_CHAIN_CMD: + processGetCertChainCmd(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } catch (KMException exception) { + freeOperations(); + resetWrappingKey(); + sendError(apdu, KMException.reason()); + } catch (ISOException exp) { + freeOperations(); + resetWrappingKey(); + sendError(apdu, mapISOErrorToKMError(exp.getReason())); + } catch (CryptoException e) { + freeOperations(); + resetWrappingKey(); + sendError(apdu, mapCryptoErrorToKMError(e.getReason())); + } catch (Exception e) { + freeOperations(); + resetWrappingKey(); + sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); + } finally { + repository.clean(); + } + } + + private void generateUniqueOperationHandle(byte[] buf, short offset, short len) { + do { + seProvider.newRandomNumber(buf, offset, len); + } while (null != findOperation(buf, offset, len)); + } + + private void freeOperations() { + if (data[OP_HANDLE] != KMType.INVALID_VALUE) { + KMOperationState op = findOperation(data[OP_HANDLE]); + if (op != null) { + releaseOperation(op); + } + } + } + + private void processEarlyBootEndedCmd(APDU apdu) { + writeBoolean(KMDataStoreConstants.EARLY_BOOT_ENDED_STATUS, true); + } + + private short deviceLockedCmd(APDU apdu) { + short cmd = KMArray.instance((short) 2); + // passwordOnly + KMArray.add(cmd, (short) 0, KMInteger.exp()); + // verification token + KMArray.add(cmd, (short) 1, getKMVerificationTokenExp()); + return receiveIncoming(apdu, cmd); + } + + protected boolean isProvisionLocked(byte[] scratchPad, short scratchPadOff) { + return readBoolean(KMDataStoreConstants.PROVISIONED_LOCKED, scratchPad, scratchPadOff); + } + + protected boolean readBoolean(byte storeDataId, byte[] scratchPad, short scratchPadOff) { + short len = storeDataInst.getData(storeDataId, scratchPad, scratchPadOff); + if (len == 0) { + KMException.throwIt(KMError.INVALID_DATA); + } + return scratchPad[scratchPadOff] == 0x01; + } + + protected void writeBoolean(byte storeDataId, boolean flag, byte[] scratchPad, short offset) { + if (flag) { + scratchPad[offset] = (byte) 0x01; + } else { + scratchPad[offset] = (byte) 0x00; + } + storeDataInst.storeData(storeDataId, scratchPad, offset, (short) 1); + } + + protected void writeBoolean(byte storeDataId, boolean flag) { + short start = repository.alloc((short) 1); + byte[] buffer = repository.getHeap(); + writeBoolean(storeDataId, flag, buffer, start); + } + + protected void writeData(byte storeDataId, byte[] data, short offset, short len) { + storeDataInst.storeData(storeDataId, data, offset, len); + } + + protected short readData(byte storeDataId, byte[] scratchPad, short offset) { + short len = storeDataInst.getData(storeDataId, scratchPad, offset); + if (len == 0) { + KMException.throwIt(KMError.INVALID_DATA); + } + return len; + } + + protected short readBlob(byte storeDataId, byte[] scratchPad, short offset) { + short len = readData(storeDataId, scratchPad, offset); + return KMByteBlob.instance(scratchPad, offset, len); + } + + protected short readInteger32(byte storeDataId, byte[] scratchPad, short offset) { + readData(storeDataId, scratchPad, offset); + return KMInteger.uint_32(scratchPad, offset); + } + + protected short readInteger64(byte storeDataId, byte[] scratchPad, short offset) { + readData(storeDataId, scratchPad, offset); + return KMInteger.uint_64(scratchPad, offset); + } + + private void processDeviceLockedCmd(APDU apdu) { + short cmd = deviceLockedCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + short passwordOnly = KMArray.get(cmd, (short) 0); + short verToken = KMArray.get(cmd, (short) 1); + passwordOnly = KMInteger.getByte(passwordOnly); + validateVerificationToken(verToken, scratchPad); + short verTime = KMVerificationToken.getTimestamp(verToken); + short len = storeDataInst.getData(KMDataStoreConstants.DEVICE_LOCKED_TIME, scratchPad, + (short) 0); + short lastDeviceLockedTime = KMByteBlob.instance(scratchPad, (short) 0, len); + if (KMInteger.compare(verTime, lastDeviceLockedTime) > 0) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); + KMInteger.getValue(verTime, scratchPad, (short) 0, (short) 8); + writeBoolean(KMDataStoreConstants.DEVICE_LOCKED, true); + writeBoolean(KMDataStoreConstants.DEVICE_LOCKED_PASSWORD_ONLY, passwordOnly == 0x01); + storeDataInst.storeData(KMDataStoreConstants.DEVICE_LOCKED_TIME, scratchPad, (short) 0, + (short) 8); + } + sendError(apdu, KMError.OK); + } + + private void resetWrappingKey() { + if (!isValidWrappingKey()) { + return; + } + Util.arrayFillNonAtomic(wrappingKey, (short) 1, WRAPPING_KEY_SIZE, (byte) 0); + wrappingKey[0] = -1; + } + + private boolean isValidWrappingKey() { + return wrappingKey[0] != -1; + } + + private void setWrappingKey(short key) { + if (KMByteBlob.length(key) != WRAPPING_KEY_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + wrappingKey[0] = 0; + Util.arrayCopyNonAtomic(KMByteBlob.getBuffer(key), + KMByteBlob.getStartOff(key), wrappingKey, (short) 1, WRAPPING_KEY_SIZE); + } + + private short getWrappingKey() { + return KMByteBlob.instance(wrappingKey, (short) 1, WRAPPING_KEY_SIZE); + } + + protected void resetData() { + short index = 0; + while (index < data.length) { + data[index] = KMType.INVALID_VALUE; + index++; + } + index = 0; + while (index < tmpVariables.length) { + tmpVariables[index] = KMType.INVALID_VALUE; + index++; + } + } + + /** + * Sends a response, may be extended response, as requested by the command. + */ + public void sendOutgoing(APDU apdu, short resp) { + //TODO handle the extended buffer stuff. We can reuse this. + short bufferStartOffset = repository.allocAvailableMemory(); + byte[] buffer = repository.getHeap(); + // TODO we can change the following to incremental send. + short bufferLength = encoder.encode(resp, buffer, bufferStartOffset); + if (((short) (bufferLength + bufferStartOffset)) > ((short) repository + .getHeap().length)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + // Send data + apdu.setOutgoing(); + apdu.setOutgoingLength(bufferLength); + apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); + } + + /** + * Receives data, which can be extended data, as requested by the command instance. + */ + public short receiveIncoming(APDU apdu, short reqExp) { + short recvLen = apdu.setIncomingAndReceive(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); + short req = receiveIncoming(apdu, reqExp, repository.getHeap(), bufferLength, bufferStartOffset, + recvLen); + repository.reclaimMemory(bufferLength); + return req; + } + + public short receiveIncoming(APDU apdu, short reqExp, byte[] reclamBuf, short bLen, + short bStartOffset, short incomingReceivedLen) { + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = incomingReceivedLen; + short srcOffset = apdu.getOffsetCdata(); + // TODO add logic to handle the extended length buffer. In this case the memory can be reused + // from extended buffer. + short index = bStartOffset; + while (recvLen > 0 && ((short) (index - bStartOffset) < bLen)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, reclamBuf, index, recvLen); + index += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + return decoder.decode(reqExp, reclamBuf, bStartOffset, bLen); + } + + public void receiveIncomingCertData(APDU apdu, byte[] reclamBuf, short bLen, short bStartOffset, + short incomingReceivedLen, byte[] outBuf, short outOff) { + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = incomingReceivedLen; + short srcOffset = apdu.getOffsetCdata(); + short index = bStartOffset; + while (recvLen > 0 && ((short) (index - bStartOffset) < bLen)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, reclamBuf, index, recvLen); + index += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + decoder.decodeCertificateData((short) 3, + reclamBuf, bStartOffset, bLen, + outBuf, outOff); + } + + + private void processGetHwInfoCmd(APDU apdu) { + // No arguments expected + short respPtr = getHardwareInfo(); + // send buffer to master + sendOutgoing(apdu, respPtr); + } + + private short addRngEntropyCmd(APDU apdu) { + short cmd = KMArray.instance((short) 1); + // Rng entropy + KMArray.add(cmd, (short) 0, KMByteBlob.exp()); + return receiveIncoming(apdu, cmd); + } + + private void processAddRngEntropyCmd(APDU apdu) { + // Receive the incoming request fully from the master. + short cmd = addRngEntropyCmd(apdu); + // Process + short blob = KMArray.get(cmd, (short) 0); + // Maximum 2KiB of seed is allowed. + if (KMByteBlob.length(blob) > MAX_SEED_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + seProvider.addRngEntropy(KMByteBlob.getBuffer(blob), KMByteBlob.getStartOff(blob), + KMByteBlob.length(blob)); + sendError(apdu, KMError.OK); + } + + private short getKeyCharacteristicsCmd(APDU apdu) { + short cmd = KMArray.instance((short) 3); + KMArray.add(cmd, (short) 0, KMByteBlob.exp()); + KMArray.add(cmd, (short) 1, KMByteBlob.exp()); + KMArray.add(cmd, (short) 2, KMByteBlob.exp()); + return receiveIncoming(apdu, cmd); + } + + private void processGetKeyCharacteristicsCmd(APDU apdu) { + // Receive the incoming request fully from the master. + short cmd = getKeyCharacteristicsCmd(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + data[KEY_BLOB] = KMArray.get(cmd, (short) 0); + data[APP_ID] = KMArray.get(cmd, (short) 1); + data[APP_DATA] = KMArray.get(cmd, (short) 2); + if (!KMByteBlob.isValid(data[APP_ID])) { + data[APP_ID] = KMType.INVALID_VALUE; + } + if (!KMByteBlob.isValid(data[APP_DATA])) { + data[APP_DATA] = KMType.INVALID_VALUE; + } + // Parse Key Blob + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + // Check Version and Patch Level + checkVersionAndPatchLevel(scratchPad); + // Remove custom tags from key characteristics + short teeParams = KMKeyCharacteristics.getTeeEnforced(data[KEY_CHARACTERISTICS]); + if (teeParams != KMType.INVALID_VALUE) { + KMKeyParameters.deleteCustomTags(teeParams); + } + // make response. + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, data[KEY_CHARACTERISTICS]); + sendOutgoing(apdu, resp); + } + + private void processGetHmacSharingParamCmd(APDU apdu) { + // No Arguments + // Create HMAC Sharing Parameters + byte[] scratchPad = apdu.getBuffer(); + short params = KMHmacSharingParameters.instance(); + short nonce = readBlob(KMDataStoreConstants.HMAC_NONCE, scratchPad, (short) 0); + short seed = KMByteBlob.instance((short) 0); + KMHmacSharingParameters.setNonce(params, nonce); + KMHmacSharingParameters.setSeed(params, seed); + // prepare the response + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, params); + sendOutgoing(apdu, resp); + } + + private void processDeleteAllKeysCmd(APDU apdu) { + // No arguments + // Send ok + sendError(apdu, KMError.OK); + } + + private short deleteKeyCmd(APDU apdu) { + short cmd = KMArray.instance((short) 1); + KMArray.add(cmd, (short) 0, KMByteBlob.exp()); + return receiveIncoming(apdu, cmd); + } + + private short keyBlob() { + short keyBlob = KMArray.instance((short) 5); + KMArray.add(keyBlob, KMKeymasterDevice.KEY_BLOB_SECRET, KMByteBlob.exp()); + KMArray.add(keyBlob, KMKeymasterDevice.KEY_BLOB_AUTH_TAG, KMByteBlob.exp()); + KMArray.add(keyBlob, KMKeymasterDevice.KEY_BLOB_NONCE, KMByteBlob.exp()); + short keyChar = getKeyCharacteristicsExp(); + KMArray.add(keyBlob, KMKeymasterDevice.KEY_BLOB_PARAMS, keyChar); + KMArray.add(keyBlob, KMKeymasterDevice.KEY_BLOB_PUB_KEY, KMByteBlob.exp()); + return keyBlob; + } + + private void processDeleteKeyCmd(APDU apdu) { + short cmd = deleteKeyCmd(apdu); + data[KEY_BLOB] = KMArray.get(cmd, (short) 0); + try { + data[KEY_BLOB] = decoder.decodeArray(keyBlob(), + KMByteBlob.getBuffer(data[KEY_BLOB]), + KMByteBlob.getStartOff(data[KEY_BLOB]), + KMByteBlob.length(data[KEY_BLOB])); + } catch (ISOException e) { + // As per VTS, deleteKey should return KMError.OK but in case if + // input is empty then VTS accepts UNIMPLEMENTED errorCode as well. + KMException.throwIt(KMError.UNIMPLEMENTED); + } + if (KMArray.length(data[KEY_BLOB]) < 4) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Send ok + sendError(apdu, KMError.OK); + } + + private short computeSharedHmacCmd(APDU apdu) { + short params = KMHmacSharingParameters.exp(); + short paramsVec = KMArray.exp(params); + short cmd = KMArray.instance((short) 1); + KMArray.add(cmd, (short) 0, paramsVec); + return receiveIncoming(apdu, cmd); + } + + private void processComputeSharedHmacCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = computeSharedHmacCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + data[HMAC_SHARING_PARAMS] = KMArray.get(cmd, (short) 0); + // Concatenate HMAC Params + //tmpVariables[0] + short paramsLen = KMArray.length(data[HMAC_SHARING_PARAMS]); // total number of params + //tmpVariables[1] + short concateBuffer = repository.alloc((short) (paramsLen * HMAC_SHARED_PARAM_MAX_SIZE)); + //tmpVariables[2] + short paramIndex = 0; // index for params + //tmpVariables[3] + short bufferIndex = 0; // index for concatenation buffer + // To check if nonce created by Strongbox is found. This value becomes 1 if both + // seed and nonce created here are found in hmac sharing parameters received. + //tmpVariables[7] = 0; + short found = 0; + //tmpVariables[9] + short nonce = readBlob(KMDataStoreConstants.HMAC_NONCE, scratchPad, (short) 0); + + while (paramIndex < paramsLen) { + // read HmacSharingParam + //tmpVariables[4] + short param = KMArray.get(data[HMAC_SHARING_PARAMS], paramIndex); + // get seed - 32 bytes max + //tmpVariables[5] + short seed = KMHmacSharingParameters.getSeed(param); + //tmpVariables[6] + short seedLength = KMByteBlob.length(seed); + // if seed is present + if (seedLength != 0) { + // then copy that to concatenation buffer + Util.arrayCopyNonAtomic( + KMByteBlob.getBuffer(seed), + KMByteBlob.getStartOff(seed), + repository.getHeap(), + (short) (concateBuffer + bufferIndex), // concat index + seedLength); + bufferIndex += seedLength; // increment the concat index + } else if (found == 0) { + found = 1; // Applet does not have any seed. Potentially + } + // if nonce is present get nonce - 32 bytes + //tmpVariables[5] + short paramNonce = KMHmacSharingParameters.getNonce(param); + short nonceLen = KMByteBlob.length(paramNonce); + // if nonce is less then 32 - it is an error + if (nonceLen < 32) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // copy nonce to concatenation buffer + Util.arrayCopyNonAtomic( + KMByteBlob.getBuffer(paramNonce), + KMByteBlob.getStartOff(paramNonce), + repository.getHeap(), + (short) (concateBuffer + bufferIndex), // index + nonceLen); + + // Check if the nonce generated here is present in the hmacSharingParameters array. + // Otherwise throw INVALID_ARGUMENT error. + if (found == 1) { + if (0 + == Util.arrayCompare( + repository.getHeap(), + (short) (concateBuffer + bufferIndex), + KMByteBlob.getBuffer(nonce), + KMByteBlob.getStartOff(nonce), + nonceLen)) { + found = 2; // hmac nonce for this keymaster found. + } else { + found = 0; + } + } + bufferIndex += nonceLen; // increment by nonce length + paramIndex++; // go to next hmac param in the vector + } + if (found != 2) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // generate the key and store it in scratch pad - 32 bytes + //tmpVariables[6] + short keyLen = + seProvider.cmacKDF( + storeDataInst.getPresharedKey(), + ckdfLable, + (short) 0, + (short) ckdfLable.length, + repository.getHeap(), + concateBuffer, + bufferIndex, + scratchPad, + (short) 0); + + // persist the computed hmac key. + writeData(KMDataStoreConstants.COMPUTED_HMAC_KEY, scratchPad, (short) 0, keyLen); + // Generate sharingKey verification signature and store that in scratch pad. + //tmpVariables[5] + short signLen = + seProvider.hmacSign( + scratchPad, + (short) 0, + keyLen, + sharingCheck, + (short) 0, + (short) sharingCheck.length, + scratchPad, + keyLen); + // verification signature blob - 32 bytes + //tmpVariables[1] + short signature = KMByteBlob.instance(scratchPad, keyLen, signLen); + // prepare the response + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, signature); + sendOutgoing(apdu, resp); + } + + private short upgradeKeyCmd(APDU apdu) { + short cmd = KMArray.instance((short) 2); + short keyParams = KMKeyParameters.exp(); + KMArray.add(cmd, (short) 0, KMByteBlob.exp()); // Key Blob + KMArray.add(cmd, (short) 1, keyParams); // Key Params + return receiveIncoming(apdu, cmd); + } + + private boolean isKeyUpgradeRequired(short tag, short systemParam) { + // validate the tag and check if key needs upgrade. + short tagValue = KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.UINT_TAG, tag); + tagValue = KMIntegerTag.getValue(tagValue); + short zero = KMInteger.uint_8((byte) 0); + if (tagValue != KMType.INVALID_VALUE) { + // OS version in key characteristics must be less the OS version stored in Javacard or the + // stored version must be zero. Then only upgrade is allowed else it is invalid argument. + if ((tag == KMType.OS_VERSION + && KMInteger.compare(tagValue, systemParam) == 1 + && KMInteger.compare(systemParam, zero) == 0)) { + // Key needs upgrade. + return 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; + } else if (KMInteger.compare(tagValue, systemParam) == 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + return false; + } + + private void processUpgradeKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = upgradeKeyCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + + data[KEY_BLOB] = KMArray.get(cmd, (short) 0); + data[KEY_PARAMETERS] = KMArray.get(cmd, (short) 1); + //tmpVariables[0] + short appId = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_ID); + if (appId != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.getValue(appId); + } + short appData = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_DATA); + if (appData != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.getValue(appData); + } + // parse existing key blob + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + boolean isKeyUpgradeRequired = false; + // Check if key requires upgrade. + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_VERSION, + readInteger32(KMDataStoreConstants.OS_VERSION, scratchPad, (short) 0)); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.OS_PATCH_LEVEL, + readInteger32(KMDataStoreConstants.OS_PATCH_LEVEL, scratchPad, (short) 0)); + isKeyUpgradeRequired |= + isKeyUpgradeRequired(KMType.VENDOR_PATCH_LEVEL, + readInteger32(KMDataStoreConstants.VENDOR_PATCH_LEVEL, scratchPad, (short) 0)); + // Get boot patch level. + bootParamsProv.getBootPatchLevel(scratchPad, (short) 0); + isKeyUpgradeRequired |= isKeyUpgradeRequired(KMType.BOOT_PATCH_LEVEL, + KMInteger.uint_32(scratchPad, (short) 0)); + + if (isKeyUpgradeRequired) { + // copy origin + data[ORIGIN] = KMEnumTag.getValue(KMType.ORIGIN, data[HW_PARAMETERS]); + makeKeyCharacteristics(scratchPad); + // create new key blob with current os version etc. + createEncryptedKeyBlob(scratchPad); + } else { + data[KEY_BLOB] = KMByteBlob.instance((short) 0); + } + // prepare the response + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, data[KEY_BLOB]); + sendOutgoing(apdu, resp); + } + + private void processExportKeyCmd(APDU apdu) { + sendError(apdu, KMError.UNIMPLEMENTED); + } + + private void processWrappingKeyBlob(short keyBlob, short wrapParams, byte[] scratchPad) { + // Read App Id and App Data if any from un wrapping key params + short appId = + KMKeyParameters.findTag(wrapParams, KMType.BYTES_TAG, KMType.APPLICATION_ID); + short appData = + KMKeyParameters.findTag(wrapParams, KMType.BYTES_TAG, KMType.APPLICATION_DATA); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.getValue(appId); + } + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.getValue(appData); + } + data[APP_ID] = appId; + data[APP_DATA] = appData; + data[KEY_PARAMETERS] = wrapParams; + data[KEY_BLOB] = keyBlob; + // parse the wrapping key blob + parseEncryptedKeyBlob(keyBlob, appId, appData, scratchPad); + validateWrappingKeyBlob(); + } + + private void validateWrappingKeyBlob() { + // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest + // SHA2_256. + KMTag.assertPresence(data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, + KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); + if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); + } + if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + if (!KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, data[HW_PARAMETERS])) { + KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE)); + } + } + + private short decryptTransportKey(short privExp, short modulus, short transportKey, + byte[] scratchPad) { + short length = + seProvider.rsaDecipherOAEP256( + KMByteBlob.getBuffer(privExp), + KMByteBlob.getStartOff(privExp), + KMByteBlob.length(privExp), + KMByteBlob.getBuffer(modulus), + KMByteBlob.getStartOff(modulus), + KMByteBlob.length(modulus), + KMByteBlob.getBuffer(transportKey), + KMByteBlob.getStartOff(transportKey), + KMByteBlob.length(transportKey), + scratchPad, + (short) 0); + return KMByteBlob.instance(scratchPad, (short) 0, length); + + } + + private void unmask(short data, short maskingKey) { + short dataLength = KMByteBlob.length(data); + short maskLength = KMByteBlob.length(maskingKey); + // Length of masking key and transport key must be same. + if (maskLength != dataLength) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + short index = 0; // index + // Xor every byte of masking and key and store the result in data[SECRET] + while (index < maskLength) { + short var1 = + (short) (((short) KMByteBlob.get(maskingKey, index)) & 0x00FF); + short var2 = + (short) (((short) KMByteBlob.get(data, index)) & 0x00FF); + KMByteBlob.add(data, index, (byte) (var1 ^ var2)); + index++; + } + } + + private short beginImportWrappedKeyCmd(APDU apdu) { + short cmd = KMArray.instance((short) 4); + short params = KMKeyParameters.expAny(); + KMArray.add(cmd, (short) 0, KMByteBlob.exp()); // Encrypted Transport Key + KMArray.add(cmd, (short) 1, KMByteBlob.exp()); // Wrapping Key KeyBlob + KMArray.add(cmd, (short) 2, KMByteBlob.exp()); // Masking Key + params = KMKeyParameters.exp(); + KMArray.add(cmd, (short) 3, params); // Wrapping key blob Params + return receiveIncoming(apdu, cmd); + } + + private void processBeginImportWrappedKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = beginImportWrappedKeyCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + // Step -1 parse the wrapping key blob + // read wrapping key blob + short keyBlob = KMArray.get(cmd, (short) 1); + // read un wrapping key params + short wrappingKeyParameters = KMArray.get(cmd, (short) 3); + processWrappingKeyBlob(keyBlob, wrappingKeyParameters, scratchPad); + // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key + short transportKey = decryptTransportKey(data[SECRET], data[PUB_KEY], + KMArray.get(cmd, (short) 0), scratchPad); + // Step 3 - XOR the decrypted AES-GCM key with with masking key + unmask(transportKey, KMArray.get(cmd, (short) 2)); + if (isValidWrappingKey()) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + setWrappingKey(transportKey); + sendError(apdu, KMError.OK); + } + + private short aesGCMDecrypt(short aesSecret, short input, short nonce, short authData, + short authTag, byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.length(input), (byte) 0); + if (!seProvider.aesGCMDecrypt( + KMByteBlob.getBuffer(aesSecret), + KMByteBlob.getStartOff(aesSecret), + KMByteBlob.length(aesSecret), + KMByteBlob.getBuffer(input), + KMByteBlob.getStartOff(input), + KMByteBlob.length(input), + scratchPad, + (short) 0, + KMByteBlob.getBuffer(nonce), + KMByteBlob.getStartOff(nonce), + KMByteBlob.length(nonce), + KMByteBlob.getBuffer(authData), + KMByteBlob.getStartOff(authData), + KMByteBlob.length(authData), + KMByteBlob.getBuffer(authTag), + KMByteBlob.getStartOff(authTag), + KMByteBlob.length(authTag))) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + return KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.length(input)); + } + + private short finishImportWrappedKeyCmd(APDU apdu) { + short cmd = KMArray.instance((short) 8); + short params = KMKeyParameters.expAny(); + KMArray.add(cmd, (short) 0, params); // Key Params of wrapped key + KMArray.add(cmd, (short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format + KMArray.add(cmd, (short) 2, KMByteBlob.exp()); // Wrapped Import Key Blob + KMArray.add(cmd, (short) 3, KMByteBlob.exp()); // Auth Tag + KMArray.add(cmd, (short) 4, KMByteBlob.exp()); // IV - Nonce + KMArray.add(cmd, (short) 5, KMByteBlob.exp()); // Wrapped Key ASSOCIATED AUTH DATA + KMArray.add(cmd, (short) 6, KMInteger.exp()); // Password Sid + KMArray.add(cmd, (short) 7, KMInteger.exp()); // Biometric Sid + return receiveIncoming(apdu, cmd); + } + + //TODO remove cmd later on + private void processFinishImportWrappedKeyCmd(APDU apdu) { + short cmd = finishImportWrappedKeyCmd(apdu); + data[KEY_PARAMETERS] = KMArray.get(cmd, (short) 0); + short keyFmt = KMArray.get(cmd, (short) 1); + keyFmt = KMEnum.getVal(keyFmt); + validateImportKey(data[KEY_PARAMETERS], keyFmt); + byte[] scratchPad = apdu.getBuffer(); + // Step 4 - AES-GCM decrypt the wrapped key + data[INPUT_DATA] = KMArray.get(cmd, (short) 2); + data[AUTH_TAG] = KMArray.get(cmd, (short) 3); + data[NONCE] = KMArray.get(cmd, (short) 4); + data[AUTH_DATA] = KMArray.get(cmd, (short) 5); + + if (!isValidWrappingKey()) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + data[IMPORTED_KEY_BLOB] = aesGCMDecrypt(getWrappingKey(), data[INPUT_DATA], data[NONCE], + data[AUTH_DATA], data[AUTH_TAG], scratchPad); + resetWrappingKey(); + // Step 5 - Import decrypted key + data[ORIGIN] = KMType.SECURELY_IMPORTED; + // create key blob array + importKey(apdu, keyFmt, scratchPad); + } + + //TODO remove hwParameters when this is refactored. + private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyParam, + short attChallenge, short issuer, short hwParameters, short swParameters, short keyParams, + byte[] scratchPad) { + KMAttestationCert cert = makeCommonCert(swParameters, hwParameters, + keyParams, scratchPad, seProvider); + + short subject = KMKeyParameters.findTag(keyParams, KMType.BYTES_TAG, + KMType.CERTIFICATE_SUBJECT_NAME); + + // If no subject name is specified then use the default subject name. + if (subject == KMType.INVALID_VALUE || KMByteTag.length(subject) == 0) { + subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length); + } else { + subject = KMByteTag.getValue(subject); + } + cert.subjectName(subject); + + // App Id and App Data, + short appId = KMType.INVALID_VALUE; + short appData = KMType.INVALID_VALUE; + if (attKeyParam != KMType.INVALID_VALUE) { + appId = + KMKeyParameters.findTag(attKeyParam, KMType.BYTES_TAG, KMType.APPLICATION_ID); + if (appId != KMTag.INVALID_VALUE) { + appId = KMByteTag.getValue(appId); + } + appData = + KMKeyParameters.findTag(attKeyParam, KMType.BYTES_TAG, KMType.APPLICATION_DATA); + if (appData != KMTag.INVALID_VALUE) { + appData = KMByteTag.getValue(appData); + } + } + //TODO remove following line + short origBlob = data[KEY_BLOB]; + short pubKey = data[PUB_KEY]; + short keyBlob = parseEncryptedKeyBlob(attKeyBlob, appId, appData, scratchPad); + short attestationKeySecret = KMArray.get(keyBlob, KEY_BLOB_SECRET); + short attestParam = KMArray.get(keyBlob, KEY_BLOB_PARAMS); + attestParam = KMKeyCharacteristics.getStrongboxEnforced(attestParam); + short attKeyPurpose = + KMKeyParameters.findTag(attestParam, KMType.ENUM_ARRAY_TAG, KMType.PURPOSE); + // If the attest key's purpose is not "attest key" then error. + if (!KMEnumArrayTag.contains(attKeyPurpose, KMType.ATTEST_KEY)) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + // If issuer is not present then it is an error + if (KMByteBlob.length(issuer) <= 0) { + KMException.throwIt(KMError.MISSING_ISSUER_SUBJECT_NAME); + } + short alg = KMKeyParameters.findTag(attestParam, KMType.ENUM_TAG, KMType.ALGORITHM); + + if (KMEnumTag.getValue(alg) == KMType.RSA) { + short attestationKeyPublic = KMArray.get(keyBlob, KEY_BLOB_PUB_KEY); + cert.rsaAttestKey(attestationKeySecret, attestationKeyPublic, KMType.ATTESTATION_CERT); + } else { + cert.ecAttestKey(attestationKeySecret, KMType.ATTESTATION_CERT); + } + cert.attestationChallenge(attChallenge); + cert.issuer(issuer); + //TODO remove following line + data[PUB_KEY] = pubKey; + cert.publicKey(data[PUB_KEY]); + + // Save attestation application id - must be present. + short attAppId = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, + KMType.ATTESTATION_APPLICATION_ID); + if (attAppId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); + } + cert.extensionTag(attAppId, false); + // unique id byte blob - uses application id and temporal month count of creation time. + setUniqueId(cert, scratchPad); + // Add Attestation Ids if present + addAttestationIds(cert, scratchPad); + + // Add Tags + addTags(hwParameters, true, cert); + addTags(swParameters, false, cert); + // Add Device Boot locked status + cert.deviceLocked(bootParamsProv.isDeviceBootLocked()); + // VB data + cert.verifiedBootHash(getVerifiedBootHash(scratchPad)); + cert.verifiedBootKey(getBootKey(scratchPad)); + cert.verifiedBootState((byte) bootParamsProv.getBootState()); + + //TODO remove the following line + makeKeyCharacteristics(scratchPad); + data[KEY_BLOB] = origBlob; + return cert; + } + + private KMAttestationCert makeCertWithFactoryProvisionedKey(short attChallenge, + byte[] scratchPad) { + KMAttestationCert cert = makeCommonCert(data[SW_PARAMETERS], data[HW_PARAMETERS], + data[KEY_PARAMETERS], scratchPad, seProvider); + cert.attestationChallenge(attChallenge); + cert.publicKey(data[PUB_KEY]); + cert.factoryAttestKey(storeDataInst.getAttestationKey(), + KMType.FACTORY_PROVISIONED_ATTEST_CERT); + + // Save attestation application id - must be present. + short attAppId = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, + KMType.ATTESTATION_APPLICATION_ID); + if (attAppId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); + } + cert.extensionTag(attAppId, false); + // unique id byte blob - uses application id and temporal month count of creation time. + setUniqueId(cert, scratchPad); + // Add Attestation Ids if present + addAttestationIds(cert, scratchPad); + + // Add Tags + addTags(data[HW_PARAMETERS], true, cert); + addTags(data[SW_PARAMETERS], false, cert); + // Add Device Boot locked status + cert.deviceLocked(bootParamsProv.isDeviceBootLocked()); + // VB data + cert.verifiedBootHash(getVerifiedBootHash(scratchPad)); + cert.verifiedBootKey(getBootKey(scratchPad)); + cert.verifiedBootState((byte) bootParamsProv.getBootState()); + + //TODO remove the following line + //makeKeyCharacteristics(scratchPad); + //data[KEY_BLOB] = origBlob; + return cert; + } + + private KMAttestationCert makeSelfSignedCert(short attPrivKey, short attPubKey, + byte[] scratchPad) { + //KMAttestationCert cert = makeCommonCert(scratchPad); + KMAttestationCert cert = + makeCommonCert(data[SW_PARAMETERS], data[HW_PARAMETERS], + data[KEY_PARAMETERS], scratchPad, seProvider); + short alg = KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM); + byte mode = KMType.FAKE_CERT; + if (attPrivKey != KMType.INVALID_VALUE) { + mode = KMType.SELF_SIGNED_CERT; + } + short subject = KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, + KMType.CERTIFICATE_SUBJECT_NAME); + // If no subject name is specified then use the default subject name. + if (subject == KMType.INVALID_VALUE || KMByteTag.length(subject) == 0) { + subject = KMByteBlob.instance(defaultSubject, (short) 0, (short) defaultSubject.length); + } else { + subject = KMByteTag.getValue(subject); + } + + if (KMEnumTag.getValue(alg) == KMType.RSA) { + cert.rsaAttestKey(attPrivKey, attPubKey, mode); + } else { + cert.ecAttestKey(attPrivKey, mode); + } + cert.issuer(subject); + cert.subjectName(subject); + cert.publicKey(attPubKey); + return cert; + } + + protected short getBootKey(byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE, (byte) 0); + short len = bootParamsProv.getBootKey(scratchPad, (short) 0); + if (len != VERIFIED_BOOT_KEY_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_KEY_SIZE); + } + + protected short getVerifiedBootHash(byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE, (byte) 0); + short len = bootParamsProv.getVerifiedBootHash(scratchPad, (short) 0); + if (len != VERIFIED_BOOT_HASH_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return KMByteBlob.instance(scratchPad, (short) 0, VERIFIED_BOOT_HASH_SIZE); + } + + public short mapAttestIdToStoreId(short tag) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + return KMDataStoreConstants.ATT_ID_BRAND; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + return KMDataStoreConstants.ATT_ID_DEVICE; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + return KMDataStoreConstants.ATT_ID_PRODUCT; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + return KMDataStoreConstants.ATT_ID_SERIAL; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + return KMDataStoreConstants.ATT_ID_IMEI; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + return KMDataStoreConstants.ATT_ID_MEID; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + return KMDataStoreConstants.ATT_ID_MANUFACTURER; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + return KMDataStoreConstants.ATT_ID_MODEL; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return KMType.INVALID_VALUE; + } + + // -------------------------------- + // Only add the Attestation ids which are requested in the attestation parameters. + // If the requested attestation ids are not provisioned or deleted then + // throw CANNOT_ATTEST_IDS error. If there is mismatch in the attestation + // id values of both the requested parameters and the provisioned parameters + // then throw INVALID_TAG error. + private void addAttestationIds(KMAttestationCert cert, byte[] scratchPad) { + byte index = 0; + short attIdTag; + short attIdTagValue; + short storedAttIdLen; + while (index < (short) ATTEST_ID_TAGS.length) { + attIdTag = KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, + ATTEST_ID_TAGS[index]); + if (attIdTag != KMType.INVALID_VALUE) { + attIdTagValue = KMByteTag.getValue(attIdTag); + storedAttIdLen = storeDataInst.getData((byte) mapAttestIdToStoreId(ATTEST_ID_TAGS[index]), + scratchPad, (short) 0); + // Return CANNOT_ATTEST_IDS if Attestation IDs are not provisioned or + // Attestation IDs are deleted. + if (storedAttIdLen == 0) { + KMException.throwIt(KMError.CANNOT_ATTEST_IDS); + } + // Return INVALID_TAG if Attestation IDs does not match. + if ((storedAttIdLen != KMByteBlob.length(attIdTagValue)) || + (0 != Util.arrayCompare(scratchPad, (short) 0, + KMByteBlob.getBuffer(attIdTagValue), + KMByteBlob.getStartOff(attIdTagValue), + storedAttIdLen))) { + KMException.throwIt(KMError.INVALID_TAG); + } + short blob = KMByteBlob.instance(scratchPad, (short) 0, storedAttIdLen); + cert.extensionTag(KMByteTag.instance(ATTEST_ID_TAGS[index], blob), true); + } + index++; + } + } + + private void addTags(short params, boolean hwEnforced, KMAttestationCert cert) { + short index = 0; + short arr = KMKeyParameters.getVals(params); + short len = KMArray.length(arr); + short tag; + while (index < len) { + tag = KMArray.get(arr, index); + cert.extensionTag(tag, hwEnforced); + index++; + } + } + + private void setUniqueId(KMAttestationCert cert, byte[] scratchPad) { + if (!KMTag.isPresent(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.INCLUDE_UNIQUE_ID)) { + return; + } + // temporal count T + short time = KMKeyParameters.findTag(data[SW_PARAMETERS], KMType.DATE_TAG, + KMType.CREATION_DATETIME); + if (time == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_TAG); + } + time = KMIntegerTag.getValue(time); + + // Application Id C + short appId = KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, + KMType.ATTESTATION_APPLICATION_ID); + if (appId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); + } + appId = KMByteTag.getValue(appId); + + // Reset After Rotation R - it will be part of HW Enforced key + // characteristics + byte resetAfterRotation = 0; + if (KMTag.isPresent(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.RESET_SINCE_ID_ROTATION)) { + resetAfterRotation = 0x01; + } + + cert.makeUniqueId(scratchPad, (short) 0, KMInteger.getBuffer(time), + KMInteger.getStartOff(time), KMInteger.length(time), + KMByteBlob.getBuffer(appId), KMByteBlob.getStartOff(appId), KMByteBlob.length(appId), + resetAfterRotation, + storeDataInst.getMasterKey()); + } + + private void deleteAttestationIds() { + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_BRAND); + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_DEVICE); + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_IMEI); + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_MEID); + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_MANUFACTURER); + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_PRODUCT); + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_MODEL); + storeDataInst.clearData(KMDataStoreConstants.ATT_ID_SERIAL); + } + + private void processDestroyAttIdsCmd(APDU apdu) { + deleteAttestationIds(); + sendError(apdu, KMError.OK); + } + + private void processVerifyAuthorizationCmd(APDU apdu) { + sendError(apdu, KMError.UNIMPLEMENTED); + } + + private short abortOperationCmd(APDU apdu) { + short cmd = KMArray.instance((short) 1); + KMArray.add(cmd, (short) 0, KMInteger.exp()); + return receiveIncoming(apdu, cmd); + } + + private void processAbortOperationCmd(APDU apdu) { + short cmd = abortOperationCmd(apdu); + data[OP_HANDLE] = KMArray.get(cmd, (short) 0); + KMOperationState op = findOperation(data[OP_HANDLE]); + if (op == null) { + sendError(apdu, KMError.INVALID_OPERATION_HANDLE); + } else { + releaseOperation(op); + sendError(apdu, KMError.OK); + } + } + + private short finishOperationCmd(APDU apdu) { + return receiveIncoming(apdu, prepareFinishExp()); + } + + private void processFinishOperationCmd(APDU apdu) { + short cmd = finishOperationCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + getFinishInputParameters(cmd, data, OP_HANDLE, KEY_PARAMETERS, INPUT_DATA, + SIGNATURE, HW_TOKEN, VERIFICATION_TOKEN, CONFIRMATION_TOKEN); + + // Check Operation Handle + KMOperationState op = findOperation(data[OP_HANDLE]); + if (op == null) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + // Authorize the finish operation + authorizeUpdateFinishOperation(op, scratchPad); + switch (op.getPurpose()) { + case KMType.SIGN: + finishTrustedConfirmationOperation(op); + case KMType.VERIFY: + finishSigningVerifyingOperation(op, scratchPad); + break; + case KMType.ENCRYPT: + finishEncryptOperation(op, scratchPad); + break; + case KMType.DECRYPT: + finishDecryptOperation(op, scratchPad); + break; + case KMType.AGREE_KEY: + finishKeyAgreementOperation(op, scratchPad); + break; + } + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + // Remove the operation handle + releaseOperation(op); + + // make response + sendOutgoing(apdu, prepareFinishResp(data[OUTPUT_DATA])); + } + + private void finishEncryptOperation(KMOperationState op, byte[] scratchPad) { + if (op.getAlgorithm() != KMType.AES && op.getAlgorithm() != KMType.DES) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + finishAesDesOperation(op); + } + + private void finishDecryptOperation(KMOperationState op, byte[] scratchPad) { + short len = KMByteBlob.length(data[INPUT_DATA]); + switch (op.getAlgorithm()) { + case KMType.RSA: + // Fill the scratch pad with zero + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + if (op.getPadding() == KMType.PADDING_NONE && len != 256) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + len = + op.getOperation().finish( + KMByteBlob.getBuffer(data[INPUT_DATA]), KMByteBlob.getStartOff(data[INPUT_DATA]), + len, scratchPad, (short) 0); + + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len); + break; + case KMType.AES: + case KMType.DES: + finishAesDesOperation(op); + break; + } + } + + private void finishAesDesOperation(KMOperationState op) { + short len = KMByteBlob.length(data[INPUT_DATA]); + short blockSize = AES_BLOCK_SIZE; + if (op.getAlgorithm() == KMType.DES) { + blockSize = DES_BLOCK_SIZE; + } + + if (op.getPurpose() == KMType.DECRYPT && len > 0 + && (op.getBlockMode() == KMType.ECB || op.getBlockMode() == KMType.CBC) + && ((short) (len % blockSize) != 0)) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + + if (op.getBlockMode() == KMType.GCM) { + if (op.getPurpose() == KMType.DECRYPT && (len < (short) (op.getMacLength() / 8))) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + // update aad if there is any + updateAAD(op, (byte) 0x01); + if (op.isAesGcmUpdateAllowed()) { + op.setAesGcmUpdateComplete(); + } + // Get the output size + len = op.getOperation().getAESGCMOutputSize(len, (short) (op.getMacLength() / 8)); + } + // If padding i.e. pkcs7 then add padding to right + // Output data can at most one block size more the input data in case of pkcs7 encryption + // In case of gcm we will allocate extra memory of the size equal to blocksize. + data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize)); + try { + len = op.getOperation().finish( + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + KMByteBlob.length(data[INPUT_DATA]), + KMByteBlob.getBuffer(data[OUTPUT_DATA]), + KMByteBlob.getStartOff(data[OUTPUT_DATA])); + } catch (CryptoException e) { + if (e.getReason() == CryptoException.ILLEGAL_USE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + } + KMByteBlob.setLength(data[OUTPUT_DATA], len); + } + + public void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + + private void finishSigningVerifyingOperation(KMOperationState op, byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + switch (op.getAlgorithm()) { + case KMType.RSA: + // If there is no padding we can treat signing as a RSA decryption operation. + try { + if (op.getPurpose() == KMType.SIGN) { + // len of signature will be 256 bytes - but it can be less then 256 bytes + short len = op.getOperation().sign( + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + KMByteBlob.length(data[INPUT_DATA]), scratchPad, + (short) 0); + // Maximum output size of signature is 256 bytes. - the signature will always be positive + data[OUTPUT_DATA] = KMByteBlob.instance((short) 256); + Util.arrayCopyNonAtomic( + scratchPad, + (short) 0, + KMByteBlob.getBuffer(data[OUTPUT_DATA]), + (short) (KMByteBlob.getStartOff(data[OUTPUT_DATA]) + 256 - len), + len); + } else { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } catch (CryptoException e) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + break; + case KMType.EC: + short len = KMByteBlob.length(data[INPUT_DATA]); + // If DIGEST NONE then truncate the input data to 32 bytes. + if (op.getDigest() == KMType.DIGEST_NONE && len > 32) { + len = 32; + } + if (op.getPurpose() == KMType.SIGN) { + // len of signature will be 512 bits i.e. 64 bytes + len = + op.getOperation() + .sign( + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + len, + scratchPad, + (short) 0); + data[OUTPUT_DATA] = KMByteBlob.instance(scratchPad, (short) 0, len); + } else { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + break; + case KMType.HMAC: + // As per Keymaster HAL documentation, the length of the Hmac output can + // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no + // such provision to control the length of the Hmac output using JavaCard + // crypto APIs and the current implementation always returns 32 bytes + // length of Hmac output. So to provide support to TAG_MAC_LENGTH + // feature, we truncate the output signature to TAG_MAC_LENGTH and return + // the truncated signature back to the caller. At the time of verfication + // we again compute the signature of the plain text input, truncate it to + // TAG_MAC_LENGTH and compare it with the input signature for + // verification. So this is the reason we are using KMType.SIGN directly + // instead of using op.getPurpose(). + op.getOperation() + .sign( + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + KMByteBlob.length(data[INPUT_DATA]), + scratchPad, + (short) 0); + if (op.getPurpose() == KMType.SIGN) { + // Copy only signature of mac length size. + data[OUTPUT_DATA] = + KMByteBlob.instance(scratchPad, (short) 0, (short) (op.getMacLength() / 8)); + } else if (op.getPurpose() == KMType.VERIFY) { + if (0 + != Util.arrayCompare( + scratchPad, (short) 0, + KMByteBlob.getBuffer(data[SIGNATURE]), + KMByteBlob.getStartOff(data[SIGNATURE]), + KMByteBlob.length(data[SIGNATURE]))) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + break; + default: // This is should never happen + KMException.throwIt(KMError.OPERATION_CANCELLED); + break; + } + } + + private void authorizeUpdateFinishOperation(KMOperationState op, byte[] scratchPad) { + // If one time user Authentication is required + if (op.isSecureUserIdReqd() && !op.isAuthTimeoutValidated()) { + // Validate Verification Token. + validateVerificationToken(data[VERIFICATION_TOKEN], scratchPad); + // validate operation handle. + short ptr = KMVerificationToken.getChallenge(data[VERIFICATION_TOKEN]); + if (KMInteger.compare(ptr, op.getHandle()) != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + tmpVariables[0] = op.getAuthTime(); + tmpVariables[2] = KMVerificationToken.getTimestamp(data[VERIFICATION_TOKEN]); + if (tmpVariables[2] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + if (KMInteger.compare(tmpVariables[0], tmpVariables[2]) < 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + op.setAuthTimeoutValidated(true); + } else if (op.isAuthPerOperationReqd()) { // If Auth per operation is required + tmpVariables[0] = KMHardwareAuthToken.getChallenge(data[HW_TOKEN]); + if (KMInteger.compare(data[OP_HANDLE], tmpVariables[0]) != 0) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + if (!authTokenMatches(op.getUserSecureId(), op.getAuthType(), scratchPad)) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + } + } + + private void authorizeKeyUsageForCount(byte[] scratchPad) { + // Allocate first 12 bytes in scratchpad required for integer + // operations. + short scratchPadOff = 0; + short requiredScratchBufLen = 12; + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, requiredScratchBufLen, (byte) 0); + + short usageLimitBufLen = KMIntegerTag.getValue(scratchPad, scratchPadOff, + KMType.UINT_TAG, KMType.MAX_USES_PER_BOOT, data[HW_PARAMETERS]); + + if (usageLimitBufLen == KMType.INVALID_VALUE) { + return; + } + + if (usageLimitBufLen > 4) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + if (storeDataInst.isAuthTagPersisted(KMByteBlob.getBuffer(data[AUTH_TAG]), + KMByteBlob.getStartOff(data[AUTH_TAG]), + KMByteBlob.length(data[AUTH_TAG]), scratchPad, requiredScratchBufLen)) { + // Get current counter, update and increment it. + short len = storeDataInst + .getRateLimitedKeyCount(KMByteBlob.getBuffer(data[AUTH_TAG]), + KMByteBlob.getStartOff(data[AUTH_TAG]), + KMByteBlob.length(data[AUTH_TAG]), scratchPad, (short) (scratchPadOff + 4)); + if (len != 4) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (0 >= KMInteger.unsignedByteArrayCompare(scratchPad, scratchPadOff, scratchPad, + (short) (scratchPadOff + 4), (short) 4)) { + KMException.throwIt(KMError.KEY_MAX_OPS_EXCEEDED); + } + // Increment the counter. + Util.arrayFillNonAtomic(scratchPad, scratchPadOff, len, (byte) 0); + Util.setShort(scratchPad, (short) (scratchPadOff + 2), (short) 1); + KMUtils.add(scratchPad, scratchPadOff, (short) (scratchPadOff + len), + (short) (scratchPadOff + len * 2)); + + storeDataInst.setRateLimitedKeyCount(KMByteBlob.getBuffer(data[AUTH_TAG]), + KMByteBlob.getStartOff(data[AUTH_TAG]), + KMByteBlob.length(data[AUTH_TAG]), + scratchPad, (short) (scratchPadOff + len * 2), len, scratchPad, + requiredScratchBufLen); + } else { + // Persist auth tag. + if (!storeDataInst.storeAuthTag(KMByteBlob.getBuffer(data[AUTH_TAG]), + KMByteBlob.getStartOff(data[AUTH_TAG]), + KMByteBlob.length(data[AUTH_TAG]), scratchPad, scratchPadOff)) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + } + } + + private void authorizeDeviceUnlock(byte[] scratchPad) { + // If device is locked and key characteristics requires unlocked device then check whether + // HW auth token has correct timestamp. + short ptr = + KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.BOOL_TAG, + KMType.UNLOCKED_DEVICE_REQUIRED); + + if (ptr != KMType.INVALID_VALUE && readBoolean(KMDataStoreConstants.DEVICE_LOCKED, scratchPad, + (short) 0)) { + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { + KMException.throwIt(KMError.DEVICE_LOCKED); + } + ptr = KMHardwareAuthToken.getTimestamp(data[HW_TOKEN]); + // Check if the current auth time stamp is greater than device locked time stamp + short ts = readInteger64(KMDataStoreConstants.DEVICE_LOCKED_TIME, scratchPad, (short) 0); + if (KMInteger.compare(ptr, ts) <= 0) { + KMException.throwIt(KMError.DEVICE_LOCKED); + } + // Now check if the device unlock requires password only authentication and whether + // auth token is generated through password authentication or not. + if (readBoolean(KMDataStoreConstants.DEVICE_LOCKED_PASSWORD_ONLY, scratchPad, (short) 0)) { + ptr = KMHardwareAuthToken.getHwAuthenticatorType(data[HW_TOKEN]); + ptr = KMEnum.getVal(ptr); + if (((byte) ptr & KMType.PASSWORD) == 0) { + KMException.throwIt(KMError.DEVICE_LOCKED); + } + } + // Unlock the device + // repository.deviceLockedFlag = false; + writeBoolean(KMDataStoreConstants.DEVICE_LOCKED, false); + storeDataInst.clearData(KMDataStoreConstants.DEVICE_LOCKED_TIME); + } + } + + private boolean verifyVerificationTokenMacInBigEndian(short verToken, byte[] scratchPad) { + // concatenation length will be 37 + length of verified parameters list - which + // is typically empty + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + // Add "Auth Verification" - 17 bytes. + Util.arrayCopyNonAtomic(authVerification, (short) 0, scratchPad, (short) 0, + (short) authVerification.length); + short len = (short) authVerification.length; + // concatenate challenge - 8 bytes + short ptr = KMVerificationToken.getChallenge(verToken); + KMInteger.value(ptr, scratchPad, (short) (len + (short) (8 - KMInteger.length(ptr)))); + len += 8; + // concatenate timestamp -8 bytes + ptr = KMVerificationToken.getTimestamp(verToken); + KMInteger.value(ptr, scratchPad, (short) (len + (short) (8 - KMInteger.length(ptr)))); + len += 8; + // concatenate security level - 4 bytes + scratchPad[(short) (len + 3)] = TRUSTED_ENVIRONMENT; + len += 4; + // hmac the data + ptr = getMacFromVerificationToken(verToken); + + return seProvider.hmacVerify( + storeDataInst.getComputedHmacKey(), + scratchPad, + (short) 0, + len, + KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), + KMByteBlob.length(ptr)); + } + + private void validateVerificationToken(short verToken, byte[] scratchPad) { + short ptr = getMacFromVerificationToken(verToken); + // If mac length is zero then token is empty. + if (KMByteBlob.length(ptr) == 0) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + if (!verifyVerificationTokenMacInBigEndian(verToken, scratchPad)) { + // Throw Exception if none of the combination works. + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + + private short updateOperationCmd(APDU apdu) { + return receiveIncoming(apdu, prepareUpdateExp()); + } + + private void processUpdateOperationCmd(APDU apdu) { + short cmd = updateOperationCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + getUpdateInputParameters(cmd, data, OP_HANDLE, KEY_PARAMETERS, + INPUT_DATA, HW_TOKEN, VERIFICATION_TOKEN); + + // Input data must be present even if it is zero length. + if (data[INPUT_DATA] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + + // Check Operation Handle and get op state + // Check Operation Handle + KMOperationState op = findOperation(data[OP_HANDLE]); + if (op == null) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + // authorize the update operation + authorizeUpdateFinishOperation(op, scratchPad); + short inputConsumed = 0; + if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) { + // update the data. + op.getOperation() + .update( + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + KMByteBlob.length(data[INPUT_DATA])); + // update trusted confirmation operation + updateTrustedConfirmationOperation(op); + + data[OUTPUT_DATA] = KMType.INVALID_VALUE; + } else if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { + // Update for encrypt/decrypt using RSA will not be supported because to do this op state + // will have to buffer the data - so reject the update if it is rsa algorithm. + if (op.getAlgorithm() == KMType.RSA) { + KMException.throwIt(KMError.OPERATION_CANCELLED); + } + short len = KMByteBlob.length(data[INPUT_DATA]); + short blockSize = DES_BLOCK_SIZE; + if (op.getAlgorithm() == KMType.AES) { + blockSize = AES_BLOCK_SIZE; + if (op.getBlockMode() == KMType.GCM) { + // data[KEY_PARAMETERS] will be invalid for keymint + if (data[KEY_PARAMETERS] != KMType.INVALID_VALUE) { + updateAAD(op, (byte) 0x00); + } + // if input data present + if (len > 0) { + // no more future updateAAD allowed if input data present. + if (op.isAesGcmUpdateAllowed()) { + op.setAesGcmUpdateComplete(); + } + } + } + } + // Allocate output buffer as input data is already block aligned + data[OUTPUT_DATA] = KMByteBlob.instance((short) (len + 2 * blockSize)); + // Otherwise just update the data. + // HAL consumes all the input and maintains a buffered data inside it. So the + // applet sends the inputConsumed length as same as the input length. + inputConsumed = len; + try { + len = + op.getOperation() + .update( + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + KMByteBlob.length(data[INPUT_DATA]), + KMByteBlob.getBuffer(data[OUTPUT_DATA]), + KMByteBlob.getStartOff(data[OUTPUT_DATA])); + } catch (CryptoException e) { + KMException.throwIt(KMError.INVALID_TAG); + } + + // Adjust the Output data if it is not equal to input data. + // This happens in case of JCardSim provider. + KMByteBlob.setLength(data[OUTPUT_DATA], len); + } + + if (data[OUTPUT_DATA] == KMType.INVALID_VALUE) { + data[OUTPUT_DATA] = KMByteBlob.instance((short) 0); + } + // Persist if there are any updates. + // make response + sendOutgoing(apdu, prepareUpdateResp(data[OUTPUT_DATA], KMInteger.uint_16(inputConsumed))); + } + + private short updateAadOperationCmd(APDU apdu) { + short cmd = KMArray.instance((short) 4); + KMArray.add(cmd, (short) 0, KMInteger.exp()); + KMArray.add(cmd, (short) 1, KMByteBlob.exp()); + short authToken = KMHardwareAuthToken.exp(); + KMArray.add(cmd, (short) 2, authToken); + short verToken = getKMVerificationTokenExp(); + KMArray.add(cmd, (short) 3, verToken); + return receiveIncoming(apdu, cmd); + } + + //update operation should send 0x00 for finish variable, where as finish operation + // should send 0x01 for finish variable. + public void updateAAD(KMOperationState op, byte finish) { + // Is input data absent + if (data[INPUT_DATA] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Update can be called either to update auth data, update input data or both. + // But if it is called for neither then return error. + short len = KMByteBlob.length(data[INPUT_DATA]); + short tag = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.ASSOCIATED_DATA); + // For Finish operation the input data can be zero length and associated data can be + // INVALID_VALUE + // For update operation either input data or associated data should be present. + if (tag == KMType.INVALID_VALUE && len <= 0 && finish == 0x00) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + // Check if associated data is present and update aad still allowed by the operation. + if (tag != KMType.INVALID_VALUE) { + // If allowed the update the aad + if (!op.isAesGcmUpdateAllowed()) { + KMException.throwIt(KMError.INVALID_TAG); + } + // If allowed the update the aad + short aData = KMByteTag.getValue(tag); + + op.getOperation() + .updateAAD( + KMByteBlob.getBuffer(aData), + KMByteBlob.getStartOff(aData), + KMByteBlob.length(aData)); + } + } + + private void processUpdateAadOperationCmd(APDU apdu) { + short cmd = updateAadOperationCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + data[OP_HANDLE] = KMArray.get(cmd, (short) 0); + data[INPUT_DATA] = KMArray.get(cmd, (short) 1); + data[HW_TOKEN] = KMArray.get(cmd, (short) 2); + data[VERIFICATION_TOKEN] = KMArray.get(cmd, (short) 3); + + // Input data must be present even if it is zero length. + if (data[INPUT_DATA] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Check Operation Handle and get op state + // Check Operation Handle + KMOperationState op = findOperation(data[OP_HANDLE]); + if (op == null) { + KMException.throwIt(KMError.INVALID_OPERATION_HANDLE); + } + if (op.getAlgorithm() != KMType.AES) { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + if (op.getBlockMode() != KMType.GCM) { + KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE); + } + if (!op.isAesGcmUpdateAllowed()) { + KMException.throwIt(KMError.INVALID_TAG); + } + if (op.getPurpose() != KMType.ENCRYPT && op.getPurpose() != KMType.DECRYPT) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + // authorize the update operation + authorizeUpdateFinishOperation(op, scratchPad); + try { + op.getOperation() + .updateAAD( + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + KMByteBlob.length(data[INPUT_DATA])); + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + + // make response + short resp = KMArray.instance((short) 1); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + sendOutgoing(apdu, resp); + } + + private short beginOperationCmd(APDU apdu) { + short cmd = KMArray.instance((short) 4); + // Arguments + short params = KMKeyParameters.expAny(); + KMArray.add(cmd, (short) 0, KMEnum.instance(KMType.PURPOSE)); + KMArray.add(cmd, (short) 1, KMByteBlob.exp()); + KMArray.add(cmd, (short) 2, params); + short authToken = KMHardwareAuthToken.exp(); + KMArray.add(cmd, (short) 3, authToken); + return receiveIncoming(apdu, cmd); + } + + private void processBeginOperationCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = beginOperationCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + short purpose = KMArray.get(cmd, (short) 0); + data[KEY_BLOB] = KMArray.get(cmd, (short) 1); + data[KEY_PARAMETERS] = KMArray.get(cmd, (short) 2); + data[HW_TOKEN] = KMArray.get(cmd, (short) 3); + purpose = KMEnum.getVal(purpose); + // Check for app id and app data. + data[APP_ID] = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_ID); + data[APP_DATA] = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_DATA); + if (data[APP_ID] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.getValue(data[APP_ID]); + } + if (data[APP_DATA] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.getValue(data[APP_DATA]); + } + // Parse the encrypted blob and decrypt it. + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + KMTag.assertPresence(data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, + KMError.UNSUPPORTED_ALGORITHM); + short algorithm = KMEnumTag.getValue(KMType.ALGORITHM, data[SB_PARAMETERS]); + + //TODO should be removed for keymint + // If Blob usage tag is present in key characteristics then it should be standalone. + if (KMTag.isPresent(data[SB_PARAMETERS], KMType.ENUM_TAG, KMType.BLOB_USAGE_REQ)) { + if (KMEnumTag.getValue(KMType.BLOB_USAGE_REQ, data[SB_PARAMETERS]) != KMType.STANDALONE) { + KMException.throwIt(KMError.UNSUPPORTED_TAG); + } + } + + // Generate a random number for operation handle + short buf = KMByteBlob.instance(KMOperationState.OPERATION_HANDLE_SIZE); + generateUniqueOperationHandle( + KMByteBlob.getBuffer(buf), + KMByteBlob.getStartOff(buf), + KMByteBlob.length(buf)); + /* opHandle is a KMInteger and is encoded as KMInteger when it is returned back. */ + short opHandle = KMInteger.instance( + KMByteBlob.getBuffer(buf), + KMByteBlob.getStartOff(buf), + KMByteBlob.length(buf)); + KMOperationState op = reserveOperation(algorithm, opHandle); + if (op == null) { + KMException.throwIt(KMError.TOO_MANY_OPERATIONS); + } + data[OP_HANDLE] = op.getHandle(); + op.setPurpose((byte) purpose); + op.setKeySize(KMByteBlob.length(data[SECRET])); + authorizeAndBeginOperation(op, scratchPad); + switch (op.getPurpose()) { + case KMType.SIGN: + beginTrustedConfirmationOperation(op); + case KMType.VERIFY: + beginSignVerifyOperation(op); + break; + case KMType.ENCRYPT: + case KMType.DECRYPT: + beginCipherOperation(op); + break; + case KMType.AGREE_KEY: + beginKeyAgreementOperation(op); + break; + default: + KMException.throwIt(KMError.UNIMPLEMENTED); + break; + } + short iv = KMType.INVALID_VALUE; + // If the data[IV] is required to be returned. + // As per VTS, for the decryption operation don't send the iv back. + if (data[IV] != KMType.INVALID_VALUE + && op.getPurpose() != KMType.DECRYPT + && op.getBlockMode() != KMType.ECB) { + iv = KMArray.instance((short) 1); + if (op.getAlgorithm() == KMType.DES && op.getBlockMode() == KMType.CBC) { + // For AES/DES we are generate an random iv of length 16 bytes. + // While sending the iv back for DES/CBC mode of opeation only send + // 8 bytes back. + short ivBlob = KMByteBlob.instance((short) 8); + Util.arrayCopy( + KMByteBlob.getBuffer(data[IV]), + KMByteBlob.getStartOff(data[IV]), + KMByteBlob.getBuffer(ivBlob), + KMByteBlob.getStartOff(ivBlob), + (short) 8); + data[IV] = ivBlob; + } + KMArray.add(iv, (short) 0, KMByteTag.instance(KMType.NONCE, data[IV])); + } else { + iv = KMArray.instance((short) 0); + } + + short params = KMKeyParameters.instance(iv); + short resp = prepareBeginResp(params, data[OP_HANDLE], KMInteger.uint_8(op.getBufferingMode()), + KMInteger.uint_16((short) (op.getMacLength() / 8))); + sendOutgoing(apdu, resp); + } + + private void authorizePurpose(KMOperationState op) { + switch (op.getAlgorithm()) { + case KMType.AES: + case KMType.DES: + if (op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY || + op.getPurpose() == KMType.AGREE_KEY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + break; + case KMType.EC: + if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + break; + case KMType.HMAC: + if (op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT || + op.getPurpose() == KMType.AGREE_KEY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + break; + case KMType.RSA: + if (op.getPurpose() == KMType.AGREE_KEY) { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + break; + default: + break; + } + if (!KMEnumArrayTag.contains(KMType.PURPOSE, op.getPurpose(), data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } + + private void authorizeDigest(KMOperationState op) { + short digests = + KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.DIGEST); + op.setDigest(KMType.DIGEST_NONE); + short param = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.DIGEST); + if (param != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.length(param) != 1) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + param = KMEnumArrayTag.get(param, (short) 0); + if (!KMEnumArrayTag.contains(digests, param)) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + op.setDigest((byte) param); + } else if (KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_PKCS1_1_5_SIGN, + data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + short paramPadding = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.PADDING); + if (paramPadding != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.length(paramPadding) != 1) { + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); + } + paramPadding = KMEnumArrayTag.get(paramPadding, (short) 0); + } + switch (op.getAlgorithm()) { + case KMType.RSA: + if ((paramPadding == KMType.RSA_OAEP || paramPadding == KMType.RSA_PSS) + && param == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + break; + case KMType.EC: + case KMType.HMAC: + if (op.getPurpose() != KMType.AGREE_KEY && param == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + break; + default: + break; + } + } + + private void authorizePadding(KMOperationState op) { + short paddings = + KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.PADDING); + op.setPadding(KMType.PADDING_NONE); + short param = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.PADDING); + if (param != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.length(param) != 1) { + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); + } + param = KMEnumArrayTag.get(param, (short) 0); + if (!KMEnumArrayTag.contains(paddings, param)) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + } + switch (op.getAlgorithm()) { + case KMType.RSA: + if (param == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); + } + if ((op.getPurpose() == KMType.SIGN || op.getPurpose() == KMType.VERIFY) + && param != KMType.PADDING_NONE + && param != KMType.RSA_PSS + && param != KMType.RSA_PKCS1_1_5_SIGN) { + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); + } + if ((op.getPurpose() == KMType.ENCRYPT || op.getPurpose() == KMType.DECRYPT) + && param != KMType.PADDING_NONE + && param != KMType.RSA_OAEP + && param != KMType.RSA_PKCS1_1_5_ENCRYPT) { + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); + } + + if (param == KMType.PADDING_NONE && op.getDigest() != KMType.DIGEST_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if ((param == KMType.RSA_OAEP || param == KMType.RSA_PSS) + && op.getDigest() == KMType.DIGEST_NONE) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if (param == KMType.RSA_OAEP) { + op.setMgfDigest( + (byte) getMgf1Digest(data[KEY_PARAMETERS], data[HW_PARAMETERS])); + } + op.setPadding((byte) param); + break; + case KMType.DES: + case KMType.AES: + if (param == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_PADDING_MODE); + } + op.setPadding((byte) param); + break; + default: + break; + } + } + + private void authorizeBlockModeAndMacLength(KMOperationState op) { + short param = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE); + if (param != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.length(param) != 1) { + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + param = KMEnumArrayTag.get(param, (short) 0); + } + if (KMType.AES == op.getAlgorithm() || KMType.DES == op.getAlgorithm()) { + if (!KMEnumArrayTag.contains(KMType.BLOCK_MODE, param, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INCOMPATIBLE_BLOCK_MODE); + } + } + short macLen = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MAC_LENGTH, data[KEY_PARAMETERS]); + switch (op.getAlgorithm()) { + case KMType.AES: + //Validate the block mode. + switch (param) { + case KMType.ECB: + case KMType.CBC: + case KMType.CTR: + case KMType.GCM: + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + if (param == KMType.GCM) { + if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + if (macLen == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + if (macLen % 8 != 0 + || macLen > 128 + || macLen + < KMIntegerTag.getShortValue( + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + op.setMacLength(macLen); + } + if (param == KMType.CTR) { + if (op.getPadding() != KMType.PADDING_NONE || op.getPadding() == KMType.PKCS7) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + } + break; + case KMType.DES: + //Validate the block mode. + switch (param) { + case KMType.ECB: + case KMType.CBC: + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_BLOCK_MODE); + } + break; + case KMType.HMAC: + if (macLen == KMType.INVALID_VALUE) { + if (op.getPurpose() == KMType.SIGN) { + KMException.throwIt(KMError.MISSING_MAC_LENGTH); + } + } else { + // MAC length may not be specified for verify. + if (op.getPurpose() == KMType.VERIFY) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + if (macLen + < KMIntegerTag.getShortValue( + KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } else if (macLen % 8 != 0 || macLen > 256) { + KMException.throwIt(KMError.UNSUPPORTED_MAC_LENGTH); + } + op.setMacLength(macLen); + } + break; + default: + break; + } + op.setBlockMode((byte) param); + } + + private void authorizeAndBeginOperation(KMOperationState op, byte[] scratchPad) { + authorizePurpose(op); + authorizeDigest(op); + authorizePadding(op); + authorizeBlockModeAndMacLength(op); + assertPrivateOperation(op.getPurpose(), op.getAlgorithm()); + authorizeUserSecureIdAuthTimeout(op, scratchPad); + authorizeDeviceUnlock(scratchPad); + authorizeKeyUsageForCount(scratchPad); + + //Validate early boot + validateEarlyBoot(data[HW_PARAMETERS], INS_BEGIN_OPERATION_CMD, scratchPad, (short) 0, + KMError.INVALID_KEY_BLOB); + + //Validate bootloader only + if (readBoolean(KMDataStoreConstants.BOOT_ENDED_STATUS, scratchPad, (short) 0)) { + 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; + + if (!KMTag.isPresent(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.CALLER_NONCE) + && KMTag.isPresent(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.NONCE) + && op.getPurpose() != KMType.DECRYPT) { + KMException.throwIt(KMError.CALLER_NONCE_PROHIBITED); + } + + short nonce = KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.NONCE); + // If Nonce is present then check whether the size of nonce is correct. + if (nonce != KMType.INVALID_VALUE) { + data[IV] = KMByteTag.getValue(nonce); + // For CBC mode - iv must be 8 bytes + if (op.getBlockMode() == KMType.CBC + && op.getAlgorithm() == KMType.DES + && KMByteBlob.length(data[IV]) != 8) { + KMException.throwIt(KMError.INVALID_NONCE); + } + + // For GCM mode - IV must be 12 bytes + if (KMByteBlob.length(data[IV]) != 12 && op.getBlockMode() == KMType.GCM) { + KMException.throwIt(KMError.INVALID_NONCE); + } + + // For AES CBC and CTR modes IV must be 16 bytes + if ((op.getBlockMode() == KMType.CBC || op.getBlockMode() == KMType.CTR) + && op.getAlgorithm() == KMType.AES + && KMByteBlob.length(data[IV]) != 16) { + KMException.throwIt(KMError.INVALID_NONCE); + } + } else if (op.getAlgorithm() == KMType.AES || op.getAlgorithm() == KMType.DES) { + + // For symmetric decryption iv is required + if (op.getPurpose() == KMType.DECRYPT + && (op.getBlockMode() == KMType.CBC + || op.getBlockMode() == KMType.GCM + || op.getBlockMode() == KMType.CTR)) { + KMException.throwIt(KMError.MISSING_NONCE); + } else if (op.getBlockMode() == KMType.ECB) { + // For ECB we create zero length nonce + data[IV] = KMByteBlob.instance((short) 0); + } else if (op.getPurpose() == KMType.ENCRYPT) { + + // For encrypt mode if nonce is absent then create random nonce of correct length + byte ivLen = 16; + if (op.getBlockMode() == KMType.GCM) { + ivLen = 12; + } else if (op.getAlgorithm() == KMType.DES) { + ivLen = 8; + } + data[IV] = KMByteBlob.instance(ivLen); + seProvider.newRandomNumber( + KMByteBlob.getBuffer(data[IV]), + KMByteBlob.getStartOff(data[IV]), + KMByteBlob.length(data[IV])); + } + } + } + + public void beginKeyAgreementOperation(KMOperationState op) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + + private void beginCipherOperation(KMOperationState op) { + switch (op.getAlgorithm()) { + case KMType.RSA: + try { + if (op.getPurpose() == KMType.DECRYPT) { + op.setOperation( + seProvider.initAsymmetricOperation( + (byte) op.getPurpose(), + (byte) op.getAlgorithm(), + (byte) op.getPadding(), + (byte) op.getDigest(), + (byte) op.getMgfDigest(), + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[PUB_KEY]), + KMByteBlob.getStartOff(data[PUB_KEY]), + KMByteBlob.length(data[PUB_KEY]))); + } else { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + case KMType.AES: + case KMType.DES: + if (op.getBlockMode() == KMType.GCM) { + op.setAesGcmUpdateStart(); + } + try { + op.setOperation( + seProvider.initSymmetricOperation( + (byte) op.getPurpose(), + (byte) op.getAlgorithm(), + (byte) op.getDigest(), + (byte) op.getPadding(), + (byte) op.getBlockMode(), + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[IV]), + KMByteBlob.getStartOff(data[IV]), + KMByteBlob.length(data[IV]), + op.getMacLength())); + } catch (CryptoException exception) { + if (exception.getReason() == CryptoException.ILLEGAL_VALUE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } else if (exception.getReason() == CryptoException.NO_SUCH_ALGORITHM) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + } + } + } + + private void beginTrustedConfirmationOperation(KMOperationState op) { + // Check for trusted confirmation - if required then set the signer in op state. + if (KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.BOOL_TAG, + KMType.TRUSTED_CONFIRMATION_REQUIRED) != KMType.INVALID_VALUE) { + + op.setTrustedConfirmationSigner( + seProvider.initTrustedConfirmationSymmetricOperation(storeDataInst.getComputedHmacKey())); + + op.getTrustedConfirmationSigner().update( + confirmationToken, + (short) 0, + (short) confirmationToken.length); + } + + } + + private void beginSignVerifyOperation(KMOperationState op) { + switch (op.getAlgorithm()) { + case KMType.RSA: + try { + if (op.getPurpose() == KMType.SIGN) { + op.setOperation( + seProvider.initAsymmetricOperation( + (byte) op.getPurpose(), + (byte) op.getAlgorithm(), + (byte) op.getPadding(), + (byte) op.getDigest(), + KMType.DIGEST_NONE, /* No MGF Digest */ + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[PUB_KEY]), + KMByteBlob.getStartOff(data[PUB_KEY]), + KMByteBlob.length(data[PUB_KEY]))); + } else { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + case KMType.EC: + try { + if (op.getPurpose() == KMType.SIGN) { + op.setOperation( + seProvider.initAsymmetricOperation( + (byte) op.getPurpose(), + (byte) op.getAlgorithm(), + (byte) op.getPadding(), + (byte) op.getDigest(), + KMType.DIGEST_NONE, /* No MGF Digest */ + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + null, + (short) 0, + (short) 0)); + } else { + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + } + } catch (CryptoException exp) { + // Javacard does not support NO digest based signing. + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + case KMType.HMAC: + // As per Keymaster HAL documentation, the length of the Hmac output can + // be decided by using TAG_MAC_LENGTH in Keyparameters. But there is no + // such provision to control the length of the Hmac output using JavaCard + // crypto APIs and the current implementation always returns 32 bytes + // length of Hmac output. So to provide support to TAG_MAC_LENGTH + // feature, we truncate the output signature to TAG_MAC_LENGTH and return + // the truncated signature back to the caller. At the time of verfication + // we again compute the signature of the plain text input, truncate it to + // TAG_MAC_LENGTH and compare it with the input signature for + // verification. So this is the reason we are using KMType.SIGN directly + // instead of using op.getPurpose(). + try { + op.setOperation( + seProvider.initSymmetricOperation( + (byte) KMType.SIGN, + (byte) op.getAlgorithm(), + (byte) op.getDigest(), + (byte) op.getPadding(), + (byte) op.getBlockMode(), + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + null, + (short) 0, + (short) 0, + (short) 0)); + } catch (CryptoException exp) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + } + + private boolean isHwAuthTokenContainsMatchingSecureId(short hwAuthToken, + short secureUserIdsObj) { + short secureUserId = KMHardwareAuthToken.getUserId(hwAuthToken); + if (!KMInteger.isZero(secureUserId)) { + if (KMIntegerArrayTag.contains(secureUserIdsObj, secureUserId)) { + return true; + } + } + + short authenticatorId = KMHardwareAuthToken.getAuthenticatorId(hwAuthToken); + if (!KMInteger.isZero(authenticatorId)) { + if (KMIntegerArrayTag.contains(secureUserIdsObj, authenticatorId)) { + return true; + } + } + return false; + } + + private boolean authTokenMatches(short userSecureIdsPtr, short authType, + byte[] scratchPad) { + if (!validateHwToken(data[HW_TOKEN], scratchPad)) { + return false; + } + if (!isHwAuthTokenContainsMatchingSecureId(data[HW_TOKEN], userSecureIdsPtr)) { + return false; + } + // check auth type + tmpVariables[2] = KMHardwareAuthToken.getHwAuthenticatorType(data[HW_TOKEN]); + tmpVariables[2] = KMEnum.getVal(tmpVariables[2]); + if (((byte) tmpVariables[2] & (byte) authType) == 0) { + return false; + } + return true; + } + + private void authorizeUserSecureIdAuthTimeout(KMOperationState op, byte[] scratchPad) { + short authTime; + short authType; + // Authorize User Secure Id and Auth timeout + short userSecureIdPtr = + KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID); + if (userSecureIdPtr != KMType.INVALID_VALUE) { + // Authentication required. + if (KMType.INVALID_VALUE != + KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.BOOL_TAG, KMType.NO_AUTH_REQUIRED)) { + // Key has both USER_SECURE_ID and NO_AUTH_REQUIRED + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // authenticator type must be provided. + if (KMType.INVALID_VALUE == + (authType = KMEnumTag.getValue(KMType.USER_AUTH_TYPE, data[HW_PARAMETERS]))) { + // Authentication required, but no auth type found. + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + + short authTimeoutTagPtr = + KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.UINT_TAG, KMType.AUTH_TIMEOUT); + if (authTimeoutTagPtr != KMType.INVALID_VALUE) { + // authenticate user + if (!authTokenMatches(userSecureIdPtr, authType, scratchPad)) { + KMException.throwIt(KMError.KEY_USER_NOT_AUTHENTICATED); + } + + authTimeoutTagPtr = + KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.ULONG_TAG, + KMType.AUTH_TIMEOUT_MILLIS); + if (authTimeoutTagPtr == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + authTime = KMIntegerTag.getValue(authTimeoutTagPtr); + // set the one time auth + op.setOneTimeAuthReqd(true); + // set the authentication time stamp in operation state + authTime = + addIntegers(authTime, + KMHardwareAuthToken.getTimestamp(data[HW_TOKEN]), scratchPad); + op.setAuthTime( + KMInteger.getBuffer(authTime), KMInteger.getStartOff(authTime)); + // auth time validation will happen in update or finish + op.setAuthTimeoutValidated(false); + } else { + // auth per operation required + // store user secure id and authType in OperationState. + op.setUserSecureId(userSecureIdPtr); + op.setAuthType((byte) authType); + // set flags + op.setOneTimeAuthReqd(false); + op.setAuthPerOperationReqd(true); + } + } + } + + private boolean verifyHwTokenMacInBigEndian(short hwToken, byte[] scratchPad) { + // The challenge, userId and authenticatorId, authenticatorType and timestamp + // are in network order (big-endian). + short len = 0; + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + short ptr = KMHardwareAuthToken.getChallenge(hwToken); + KMInteger.value(ptr, scratchPad, (short) (len + (short) (8 - KMInteger.length(ptr)))); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.getUserId(hwToken); + KMInteger.value(ptr, scratchPad, (short) (len + (short) (8 - KMInteger.length(ptr)))); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.getAuthenticatorId(hwToken); + KMInteger.value(ptr, scratchPad, (short) (len + (short) (8 - KMInteger.length(ptr)))); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.getHwAuthenticatorType(hwToken); + scratchPad[(short) (len + 3)] = KMEnum.getVal(ptr); + len += 4; + // concatenate timestamp -8 bytes + ptr = KMHardwareAuthToken.getTimestamp(hwToken); + KMInteger.value(ptr, scratchPad, (short) (len + (short) (8 - KMInteger.length(ptr)))); + len += 8; + + ptr = KMHardwareAuthToken.getMac(hwToken); + + return seProvider.hmacVerify( + storeDataInst.getComputedHmacKey(), + scratchPad, + (short) 0, + len, + KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), + KMByteBlob.length(ptr)); + } + + private boolean verifyHwTokenMacInLittleEndian(short hwToken, byte[] scratchPad) { + // The challenge, userId and authenticatorId values are in little endian order, + // but authenticatorType and timestamp are in network order (big-endian). + short len = 0; + // add 0 + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + len = 1; + // concatenate challenge - 8 bytes + short ptr = KMHardwareAuthToken.getChallenge(hwToken); + KMInteger.toLittleEndian(ptr, scratchPad, len); + len += 8; + // concatenate user id - 8 bytes + ptr = KMHardwareAuthToken.getUserId(hwToken); + KMInteger.toLittleEndian(ptr, scratchPad, len); + len += 8; + // concatenate authenticator id - 8 bytes + ptr = KMHardwareAuthToken.getAuthenticatorId(hwToken); + KMInteger.toLittleEndian(ptr, scratchPad, len); + len += 8; + // concatenate authenticator type - 4 bytes + ptr = KMHardwareAuthToken.getHwAuthenticatorType(hwToken); + scratchPad[(short) (len + 3)] = KMEnum.getVal(ptr); + len += 4; + // concatenate timestamp - 8 bytes + ptr = KMHardwareAuthToken.getTimestamp(hwToken); + KMInteger.value(ptr, scratchPad, (short) (len + (short) (8 - KMInteger.length(ptr)))); + len += 8; + + ptr = KMHardwareAuthToken.getMac(hwToken); + + return seProvider.hmacVerify( + storeDataInst.getComputedHmacKey(), + scratchPad, + (short) 0, + len, + KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), + KMByteBlob.length(ptr)); + } + + private boolean validateHwToken(short hwToken, byte[] scratchPad) { + // CBOR Encoding is always big endian + short ptr = KMHardwareAuthToken.getMac(hwToken); + // If mac length is zero then token is empty. + if (KMByteBlob.length(ptr) == 0) { + return false; + } + if (KMConfigurations.TEE_MACHINE_TYPE == KMConfigurations.LITTLE_ENDIAN) { + return verifyHwTokenMacInLittleEndian(hwToken, scratchPad); + } else { + return verifyHwTokenMacInBigEndian(hwToken, scratchPad); + } + } + + private short importKeyCmd(APDU apdu) { + short cmd = KMArray.instance((short) 3); + // Arguments + short params = KMKeyParameters.expAny(); + KMArray.add(cmd, (short) 0, params); + KMArray.add(cmd, (short) 1, KMEnum.instance(KMType.KEY_FORMAT)); + KMArray.add(cmd, (short) 2, KMByteBlob.exp()); + return receiveIncoming(apdu, cmd); + } + + private void processImportKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = importKeyCmd(apdu); + byte[] scratchPad = apdu.getBuffer(); + data[KEY_PARAMETERS] = KMArray.get(cmd, (short) 0); + short keyFmt = KMArray.get(cmd, (short) 1); + data[IMPORTED_KEY_BLOB] = KMArray.get(cmd, (short) 2); + keyFmt = KMEnum.getVal(keyFmt); + + data[CERTIFICATE] = KMArray.instance((short) 0); //by default the cert is empty. + data[ORIGIN] = KMType.IMPORTED; + importKey(apdu, keyFmt, scratchPad); + } + + private short importWrappedKeyCmd(APDU apdu) { + short cmd = KMArray.instance((short) 12); + // Arguments + short params = KMKeyParameters.exp(); + short bBlob = KMByteBlob.exp(); + KMArray.add(cmd, (short) 0, params); // Key Params of wrapped key + KMArray.add(cmd, (short) 1, KMEnum.instance(KMType.KEY_FORMAT)); // Key Format + KMArray.add(cmd, (short) 2, bBlob); // Wrapped Import Key Blob + KMArray.add(cmd, (short) 3, bBlob); // Auth Tag + KMArray.add(cmd, (short) 4, bBlob); // IV - Nonce + KMArray.add(cmd, (short) 5, bBlob); // Encrypted Transport Key + KMArray.add(cmd, (short) 6, bBlob); // Wrapping Key KeyBlob + KMArray.add(cmd, (short) 7, bBlob); // Masking Key + KMArray.add(cmd, (short) 8, params); // Un-wrapping Params + KMArray.add(cmd, (short) 9, bBlob); // Wrapped Key ASSOCIATED AUTH DATA + KMArray.add(cmd, (short) 10, KMInteger.exp()); // Password Sid + KMArray.add(cmd, (short) 11, KMInteger.exp()); // Biometric Sid + return receiveIncoming(apdu, cmd); + } + + private void processImportWrappedKeyCmd(APDU apdu) { + + byte[] scratchPad = apdu.getBuffer(); + short cmd = importWrappedKeyCmd(apdu); + + // Step -0 - check whether the key format and algorithm supported + // read algorithm + tmpVariables[0] = KMArray.get(cmd, (short) 0); + tmpVariables[1] = KMEnumTag.getValue(KMType.ALGORITHM, tmpVariables[0]); + // read key format + tmpVariables[2] = KMArray.get(cmd, (short) 1); + byte keyFormat = KMEnum.getVal(tmpVariables[2]); + if ((tmpVariables[1] == KMType.RSA || tmpVariables[1] == KMType.EC) + && (keyFormat != KMType.PKCS8)) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + + // Step -1 parse the wrapping key blob + // read wrapping key blob + data[KEY_BLOB] = KMArray.get(cmd, (short) 6); + // read un wrapping key params + data[KEY_PARAMETERS] = KMArray.get(cmd, (short) 8); + // Read App Id and App Data if any from un wrapping key params + data[APP_ID] = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_ID); + data[APP_DATA] = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_DATA); + if (data[APP_ID] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.getValue(data[APP_ID]); + } + if (data[APP_DATA] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.getValue(data[APP_DATA]); + } + // parse the wrapping key blob + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + // check whether the wrapping key is RSA with purpose KEY_WRAP, padding RSA_OAEP and Digest + // SHA2_256. + if (KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]) != KMType.RSA) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM); + } + if (!KMEnumArrayTag.contains(KMType.DIGEST, KMType.SHA2_256, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + if (!KMEnumArrayTag.contains(KMType.PADDING, KMType.RSA_OAEP, data[HW_PARAMETERS])) { + KMException.throwIt(KMError.INCOMPATIBLE_PADDING_MODE); + } + if (!KMEnumArrayTag.contains(KMType.PURPOSE, KMType.WRAP_KEY, data[HW_PARAMETERS])) { + KMException.throwIt((KMError.INCOMPATIBLE_PURPOSE)); + } + + // Step 2 - decrypt the encrypted transport key - 32 bytes AES-GCM key + // create rsa decipher + // read encrypted transport key from args + tmpVariables[0] = KMArray.get(cmd, (short) 5); + // Decrypt the transport key + tmpVariables[1] = + seProvider.rsaDecipherOAEP256( + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[PUB_KEY]), + KMByteBlob.getStartOff(data[PUB_KEY]), + KMByteBlob.length(data[PUB_KEY]), + KMByteBlob.getBuffer(tmpVariables[0]), + KMByteBlob.getStartOff(tmpVariables[0]), + KMByteBlob.length(tmpVariables[0]), + scratchPad, + (short) 0); + data[PUB_KEY] = KMType.INVALID_VALUE; + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, tmpVariables[1]); + + // Step 3 - XOR the decrypted AES-GCM key with with masking key + // read masking key + tmpVariables[0] = KMArray.get(cmd, (short) 7); + tmpVariables[1] = KMByteBlob.length(tmpVariables[0]); + // Length of masking key and transport key must be same. + if (tmpVariables[1] != KMByteBlob.length(data[SECRET])) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + tmpVariables[2] = 0; // index + // Xor every byte of masking and key and store the result in data[SECRET] + while (tmpVariables[2] < tmpVariables[1]) { + tmpVariables[3] = + (short) (((short) KMByteBlob.get(tmpVariables[0], tmpVariables[2])) & 0x00FF); + tmpVariables[4] = + (short) (((short) KMByteBlob.get(data[SECRET], tmpVariables[2])) & 0x00FF); + KMByteBlob.add(data[SECRET], tmpVariables[2], (byte) (tmpVariables[3] ^ tmpVariables[4])); + tmpVariables[2]++; + } + + // Step 4 - AES-GCM decrypt the wrapped key + data[INPUT_DATA] = KMArray.get(cmd, (short) 2); + data[AUTH_DATA] = KMArray.get(cmd, (short) 9); + data[AUTH_TAG] = KMArray.get(cmd, (short) 3); + data[NONCE] = KMArray.get(cmd, (short) 4); + Util.arrayFillNonAtomic( + scratchPad, (short) 0, KMByteBlob.length(data[INPUT_DATA]), (byte) 0); + + if (!seProvider.aesGCMDecrypt( + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), + KMByteBlob.length(data[INPUT_DATA]), + scratchPad, + (short) 0, + KMByteBlob.getBuffer(data[NONCE]), + KMByteBlob.getStartOff(data[NONCE]), + KMByteBlob.length(data[NONCE]), + KMByteBlob.getBuffer(data[AUTH_DATA]), + KMByteBlob.getStartOff(data[AUTH_DATA]), + KMByteBlob.length(data[AUTH_DATA]), + KMByteBlob.getBuffer(data[AUTH_TAG]), + KMByteBlob.getStartOff(data[AUTH_TAG]), + KMByteBlob.length(data[AUTH_TAG]))) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + + // Step 5 - Import decrypted key + data[ORIGIN] = KMType.SECURELY_IMPORTED; + data[KEY_PARAMETERS] = KMArray.get(cmd, (short) 0); + // create key blob array + data[IMPORTED_KEY_BLOB] = KMByteBlob.instance(scratchPad, (short) 0, + KMByteBlob.length(data[INPUT_DATA])); + importKey(apdu, keyFormat, scratchPad); + } + + private void validateImportKey(short params, short keyFmt) { + validatePurpose(params); + // Rollback protection not supported + KMTag.assertAbsence(params, KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + validateEarlyBoot(params, INS_IMPORT_KEY_CMD, null, (short) 0, KMError.EARLY_BOOT_ENDED); + //Check if the tags are supported. + if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_TAG); + } + // Algorithm must be present + KMTag.assertPresence(params, KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); + short alg = KMEnumTag.getValue(KMType.ALGORITHM, params); + // key format must be raw if aes, des or hmac and pkcs8 for rsa and ec. + if ((alg == KMType.AES || alg == KMType.DES || alg == KMType.HMAC) && keyFmt != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + if ((alg == KMType.RSA || alg == KMType.EC) && keyFmt != KMType.PKCS8) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + } + + public void validatePurpose(short params) { + return; + } + + private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) { + validateImportKey(data[KEY_PARAMETERS], keyFmt); + // Check algorithm and dispatch to appropriate handler. + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + switch (alg) { + case KMType.RSA: + importRSAKey(scratchPad); + break; + case KMType.AES: + importAESKey(scratchPad); + break; + case KMType.DES: + importTDESKey(scratchPad); + break; + case KMType.HMAC: + importHmacKey(scratchPad); + break; + case KMType.EC: + importECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + makeKeyCharacteristics(scratchPad); + createEncryptedKeyBlob(scratchPad); + // prepare the response + short resp = KMArray.instance((short) 3); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, data[KEY_BLOB]); + KMArray.add(resp, (short) 2, data[KEY_CHARACTERISTICS]); + sendOutgoing(apdu, resp); + } + + public short decodeRawECKey(short rawBlob) { + // Decode key material + short arrPtr = KMArray.instance((short) 2); + KMArray.add(arrPtr, (short) 0, KMByteBlob.exp()); // secret + KMArray.add(arrPtr, (short) 1, KMByteBlob.exp()); // public key + arrPtr = + decoder.decode( + arrPtr, + KMByteBlob.getBuffer(rawBlob), + KMByteBlob.getStartOff(rawBlob), + KMByteBlob.length(rawBlob)); + return arrPtr; + } + + private void importECKeys(byte[] scratchPad) { + // Decode key material + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]); + data[PUB_KEY] = KMArray.get(keyBlob, (short) 0); + data[SECRET] = KMArray.get(keyBlob, (short) 1); + // initialize 256 bit p256 key for given private key and public key. + short index = 0; + // check whether the key size tag is present in key parameters. + short keySize = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + short SecretLen = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (keySize != KMType.INVALID_VALUE) { + // As per NIST.SP.800-186 page 9, secret for 256 curve should be between + // 256-383 + if (((256 <= SecretLen) && (383 >= SecretLen)) ^ keySize == 256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + if (keySize != 256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } else { + if ((256 > SecretLen) || (383 < SecretLen)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // add the key size to scratchPad + keySize = KMInteger.uint_16((short) 256); + keySize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keySize); + Util.setShort(scratchPad, index, keySize); + index += 2; + } + // check the curve if present in key parameters. + short curve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (curve != KMType.INVALID_VALUE) { + // As per NIST.SP.800-186 page 9, secret length for 256 curve should be between + // 256-383 + if (((256 <= SecretLen) && (383 >= SecretLen)) ^ curve == KMType.P_256) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + if (curve != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_EC_CURVE); + } + } else { + if ((256 > SecretLen) || (383 < SecretLen)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // add the curve to scratchPad + curve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + Util.setShort(scratchPad, index, curve); + index += 2; + } + // Check whether key can be created + seProvider.importAsymmetricKey( + KMType.EC, + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[PUB_KEY]), + KMByteBlob.getStartOff(data[PUB_KEY]), + KMByteBlob.length(data[PUB_KEY])); + + // add scratch pad to key parameters + updateKeyParameters(scratchPad, index); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.add(data[KEY_BLOB], KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private void importHmacKey(byte[] scratchPad) { + // Get Key + data[SECRET] = data[IMPORTED_KEY_BLOB]; + // create HMAC key of up to 512 bit + short index = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + short keysize = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (keysize != KMType.INVALID_VALUE) { + if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + if (keysize != (short) (KMByteBlob.length(data[SECRET]) * 8)) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + // add the key size to scratchPad + keysize = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (!(keysize >= 64 && keysize <= 512 && keysize % 8 == 0)) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + keysize = KMInteger.uint_16(keysize); + short keySizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keySizeTag); + index += 2; + } + // Check whether key can be created + seProvider.importSymmetricKey( + KMType.HMAC, + keysize, + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET])); + + // update the key parameters list + updateKeyParameters(scratchPad, index); + // validate HMAC Key parameters + validateHmacKey(); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void importTDESKey(byte[] scratchPad) { + // Decode Key Material + data[SECRET] = data[IMPORTED_KEY_BLOB]; + short index = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + short keysize = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (keysize != KMType.INVALID_VALUE) { + if (keysize != 168) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + if (192 != (short) (8 * KMByteBlob.length(data[SECRET]))) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + keysize = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (keysize != 192) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // add the key size to scratchPad + keysize = KMInteger.uint_16((short) 168); + short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keysizeTag); + index += 2; + } + // Read Minimum Mac length - it must not be present + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMError.INVALID_TAG); + // Check whether key can be created + seProvider.importSymmetricKey( + KMType.DES, + keysize, + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET])); + // update the key parameters list + updateKeyParameters(scratchPad, index); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void validateAesKeySize(short keySizeBits) { + if (keySizeBits != 128 && keySizeBits != 256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + + private void importAESKey(byte[] scratchPad) { + // Get Key + data[SECRET] = data[IMPORTED_KEY_BLOB]; + // create 128 or 256 bit AES key + short index = 0; // index in scratchPad for update params + // check the keysize tag if present in key parameters. + short keysize = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + if (keysize != KMType.INVALID_VALUE) { + if (keysize != (short) (8 * KMByteBlob.length(data[SECRET]))) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + validateAesKeySize(keysize); + } else { + // add the key size to scratchPad + keysize = (short) (8 * KMByteBlob.length(data[SECRET])); + validateAesKeySize(keysize); + keysize = KMInteger.uint_16(keysize); + short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keysizeTag); + index += 2; + } + // Check whether key can be created + seProvider.importSymmetricKey( + KMType.AES, + keysize, + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET])); + + // update the key parameters list + updateKeyParameters(scratchPad, index); + // validate AES Key parameters + validateAESKey(); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void importRSAKey(byte[] scratchPad) { + // Decode key material + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]); + data[PUB_KEY] = KMArray.get(keyblob, (short) 0); + short pubKeyExp = KMArray.get(keyblob, (short) 1); + data[SECRET] = KMArray.get(keyblob, (short) 2); + if (F4.length != KMByteBlob.length(pubKeyExp)) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + if (Util.arrayCompare(F4, (short) 0, KMByteBlob.getBuffer(pubKeyExp), + KMByteBlob.getStartOff(pubKeyExp), (short) F4.length) != 0) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + short index = 0; // index in scratchPad for update parameters. + // validate public exponent if present in key params - it must be 0x010001 + short len = + KMIntegerTag.getValue( + scratchPad, + (short) 10, // using offset 10 as first 10 bytes reserved for update params + KMType.ULONG_TAG, + KMType.RSA_PUBLIC_EXPONENT, + data[KEY_PARAMETERS]); + if (len != KMTag.INVALID_VALUE) { + if (len != 4 + || Util.getShort(scratchPad, (short) 10) != 0x01 + || Util.getShort(scratchPad, (short) 12) != 0x01) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + // add public exponent to scratchPad + Util.setShort(scratchPad, (short) 10, (short) 0x01); + Util.setShort(scratchPad, (short) 12, (short) 0x01); + pubKeyExp = KMInteger.uint_32(scratchPad, (short) 10); + pubKeyExp = + KMIntegerTag.instance(KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT, pubKeyExp); + Util.setShort(scratchPad, index, pubKeyExp); + index += 2; + } + + // check the keysize tag if present in key parameters. + short keysize = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + short kSize = (short) (KMByteBlob.length(data[SECRET]) * 8); + if (keysize != KMType.INVALID_VALUE) { + if (keysize != 2048 + || keysize != kSize) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + } else { + if (2048 != kSize) { + KMException.throwIt(KMError.IMPORT_PARAMETER_MISMATCH); + } + // add the key size to scratchPad + keysize = KMInteger.uint_16((short) 2048); + keysize = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); + Util.setShort(scratchPad, index, keysize); + index += 2; + } + + // Check whether key can be created + seProvider.importAsymmetricKey( + KMType.RSA, + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[PUB_KEY]), + KMByteBlob.getStartOff(data[PUB_KEY]), + KMByteBlob.length(data[PUB_KEY])); + + // update the key parameters list + updateKeyParameters(scratchPad, index); + // validate RSA Key parameters + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.add(data[KEY_BLOB], KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private void updateKeyParameters(byte[] newParams, short len) { + if (len == 0) { + return; // nothing to update + } + // Create Update Param array and copy current params + short params = KMKeyParameters.getVals(data[KEY_PARAMETERS]); + len = (short) (KMArray.length(params) + (short) (len / 2)); + short updatedParams = KMArray.instance(len); // update params + + len = KMArray.length(params); + short index = 0; + + // copy the existing key parameters to updated array + while (index < len) { + short tag = KMArray.get(params, index); + KMArray.add(updatedParams, index, tag); + index++; + } + + // copy new parameters to updated array + len = KMArray.length(updatedParams); + short newParamIndex = 0; // index in ptrArr + while (index < len) { + short tag = Util.getShort(newParams, newParamIndex); + KMArray.add(updatedParams, index, tag); + index++; + newParamIndex += 2; + } + // replace with updated key parameters. + data[KEY_PARAMETERS] = KMKeyParameters.instance(updatedParams); + } + + private short initStrongBoxCmd(APDU apdu) { + short cmd = KMArray.instance((short) 3); + KMArray.add(cmd, (short) 0, KMInteger.exp()); //OS version + KMArray.add(cmd, (short) 1, KMInteger.exp()); //OS patch level + KMArray.add(cmd, (short) 2, KMInteger.exp()); //Vendor patch level + return receiveIncoming(apdu, cmd); + } + + // This command is executed to set the boot parameters. + // releaseAllOperations has to be called on every boot, so + // it is called from inside initStrongBoxCmd. Later in future if + // initStrongBoxCmd is removed, then make sure that releaseAllOperations + // is moved to a place where it is called on every boot. + private void processInitStrongBoxCmd(APDU apdu) { + short cmd = initStrongBoxCmd(apdu); + + short osVersion = KMArray.get(cmd, (short) 0); + short osPatchLevel = KMArray.get(cmd, (short) 1); + short vendorPatchLevel = KMArray.get(cmd, (short) 2); + setOsVersion(osVersion); + setOsPatchLevel(osPatchLevel); + setVendorPatchLevel(vendorPatchLevel); + } + + public void reboot(byte[] scratchPad, short offset) { + storeDataInst.clearData(KMDataStoreConstants.HMAC_NONCE); + //flag to maintain the boot state + storeDataInst.clearData(KMDataStoreConstants.BOOT_ENDED_STATUS); + //flag to maintain early boot ended state + storeDataInst.clearData(KMDataStoreConstants.EARLY_BOOT_ENDED_STATUS); + //Clear all the operation state. + releaseAllOperations(); + // Hmac is cleared, so generate a new Hmac nonce. + initHmacNonceAndSeed(scratchPad, offset); + // Clear all auth tags. + storeDataInst.clearAllAuthTags(); + } + + protected void initSystemBootParams() { + short empty = KMInteger.uint_16((short) 0); + setOsVersion(empty); + setOsPatchLevel(empty); + setVendorPatchLevel(empty); + } + + protected void setOsVersion(short version) { + writeData(KMDataStoreConstants.OS_VERSION, KMInteger.getBuffer(version), + KMInteger.getStartOff(version), + KMInteger.length(version)); + } + + protected void setOsPatchLevel(short patch) { + writeData(KMDataStoreConstants.OS_PATCH_LEVEL, KMInteger.getBuffer(patch), + KMInteger.getStartOff(patch), + KMInteger.length(patch)); + } + + protected void setVendorPatchLevel(short patch) { + writeData(KMDataStoreConstants.VENDOR_PATCH_LEVEL, KMInteger.getBuffer(patch), + KMInteger.getStartOff(patch), + KMInteger.length(patch)); + } + + private short generateKeyCmd(APDU apdu) { + short params = KMKeyParameters.expAny(); + // Array of expected arguments + short cmd = KMArray.instance((short) 1); + KMArray.add(cmd, (short) 0, params); //key params + return receiveIncoming(apdu, cmd); + } + + private void processGenerateKey(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = generateKeyCmd(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + data[KEY_PARAMETERS] = KMArray.get(cmd, (short) 0); + data[CERTIFICATE] = KMArray.instance((short) 0); //by default the cert is empty. + // ROLLBACK_RESISTANCE not supported. + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.ROLLBACK_RESISTANCE, + KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + // BOOTLOADER_ONLY keys not supported. + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.BOOTLOADER_ONLY, + KMError.INVALID_KEY_BLOB); + // Algorithm must be present + KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, + KMError.INVALID_ARGUMENT); + // As per specification Early boot keys may be created after early boot ended. + validateEarlyBoot(data[KEY_PARAMETERS], INS_GENERATE_KEY_CMD, scratchPad, (short) 0, + KMError.EARLY_BOOT_ENDED); + validatePurpose(data[KEY_PARAMETERS]); + //Check if the tags are supported. + if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_TAG); + } + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + // Check algorithm and dispatch to appropriate handler. + switch (alg) { + case KMType.RSA: + generateRSAKey(scratchPad); + break; + case KMType.AES: + generateAESKey(scratchPad); + break; + case KMType.DES: + generateTDESKey(scratchPad); + break; + case KMType.HMAC: + generateHmacKey(scratchPad); + break; + case KMType.EC: + generateECKeys(scratchPad); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + break; + } + + // create key blob and associated attestation. + data[ORIGIN] = KMType.GENERATED; + makeKeyCharacteristics(scratchPad); + createEncryptedKeyBlob(scratchPad); + // prepare the response + short resp = KMArray.instance((short) 3); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, data[KEY_BLOB]); + KMArray.add(resp, (short) 2, data[KEY_CHARACTERISTICS]); + sendOutgoing(apdu, resp); + } + + public void processAttestationCertDataCmd(APDU apdu) { + // TODO optimize this function. + 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); + } + // Buffer holds the corresponding offsets and lengths of the certChain, certIssuer and certExpiry + // in the bufferRef[0] buffer. + short var = KMByteBlob.instance((short) 12); + // These variables point to the appropriate positions in the var buffer. + short certChainPos = KMByteBlob.getStartOff(var); + short certIssuerPos = (short) (KMByteBlob.getStartOff(var) + 4); + short certExpiryPos = (short) (KMByteBlob.getStartOff(var) + 8); + decoder.decodeCertificateData((short) 3, + buffer, bufferStartOffset, bufferLength, + KMByteBlob.getBuffer(var), KMByteBlob.getStartOff(var)); + // persist data + storeDataInst.persistCertificateData( + (byte[]) buffer, + Util.getShort(KMByteBlob.getBuffer(var), certChainPos), // offset + Util.getShort(KMByteBlob.getBuffer(var), (short) (certChainPos + 2)), // length + Util.getShort(KMByteBlob.getBuffer(var), certIssuerPos), // offset + Util.getShort(KMByteBlob.getBuffer(var), (short) (certIssuerPos + 2)), // length + Util.getShort(KMByteBlob.getBuffer(var), certExpiryPos), // offset + Util.getShort(KMByteBlob.getBuffer(var), (short) (certExpiryPos + 2))); // length + + // reclaim memory + repository.reclaimMemory(bufferLength); + } + + private short generateAttestKeyCmd(APDU apdu) { + return receiveIncoming(apdu, generateAttestKeyExp()); + } + + protected void processGetCertChainCmd(APDU apdu) { + // Make the response + short certChainLen = storeDataInst.getCertificateDataLength( + KMDataStoreConstants.CERTIFICATE_CHAIN); + short int32Ptr = KMInteger.uint_16(KMError.OK); + short maxByteHeaderLen = 3; // Maximum possible ByteBlob header len. + short arrayHeaderLen = 1; + // Allocate maximum possible buffer. + // Add arrayHeader + (PowerResetStatus + KMError.OK) + Byte Header + encoder.getEncodedLength(int32Ptr); + short totalLen = (short) (arrayHeaderLen + encoder.getEncodedLength(int32Ptr) + maxByteHeaderLen + + certChainLen); + short certChain = KMByteBlob.instance(totalLen); + // copy the certificate chain to the end of the buffer. + storeDataInst.readCertificateData( + KMDataStoreConstants.CERTIFICATE_CHAIN, + KMByteBlob.getBuffer(certChain), + (short) (KMByteBlob.getStartOff(certChain) + totalLen - certChainLen)); + // Encode cert chain. + encoder.encodeCertChain( + KMByteBlob.getBuffer(certChain), + KMByteBlob.getStartOff(certChain), + KMByteBlob.length(certChain), + int32Ptr, // uint32 ptr + (short) (KMByteBlob.getStartOff(certChain) + totalLen - certChainLen), + // start pos of cert chain. + certChainLen); + apdu.setOutgoing(); + apdu.setOutgoingLength(KMByteBlob.length(certChain)); + apdu.sendBytesLong(KMByteBlob.getBuffer(certChain), + KMByteBlob.getStartOff(certChain), + KMByteBlob.length(certChain)); + } + + private void processAttestKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = generateAttestKeyCmd(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + getAttestKeyInputParameters(cmd, data, KEY_BLOB, KEY_PARAMETERS, ATTEST_KEY_BLOB, + ATTEST_KEY_PARAMS, ATTEST_KEY_ISSUER); + data[CERTIFICATE] = KMArray.instance((short) 0); //by default the cert is empty. + + // Check for app id and app data. + data[APP_ID] = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_ID); + data[APP_DATA] = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, KMType.APPLICATION_DATA); + if (data[APP_ID] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.getValue(data[APP_ID]); + } + if (data[APP_DATA] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.getValue(data[APP_DATA]); + } + // parse key blob + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + // The key which is being attested should be asymmetric i.e. RSA or EC + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); + if (alg != KMType.RSA && alg != KMType.EC) { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + // Build certificate + generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad); + + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(resp, (short) 1, data[CERTIFICATE]); + sendOutgoing(apdu, resp); + } + + private short getAttestationMode(short attKeyBlob, short attChallenge) { + short alg = KMKeyParameters.findTag(data[HW_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM); + short mode = KMType.NO_CERT; + // TODO Keymaster specification: Symmetric keys with challenge should return error. + if (KMEnumTag.getValue(alg) != KMType.RSA && + KMEnumTag.getValue(alg) != KMType.EC) { + return mode; + } + // If attestation keyblob present + if (attKeyBlob != KMType.INVALID_VALUE && KMByteBlob.length(attKeyBlob) > 0) { + // No attestation challenge present then it is an error + if (attChallenge == KMType.INVALID_VALUE || KMByteBlob.length(attChallenge) <= 0) { + KMException.throwIt(KMError.ATTESTATION_CHALLENGE_MISSING); + } else { + mode = KMType.ATTESTATION_CERT; + } + } else { + mode = getSupportedAttestationMode(attChallenge); + } + return mode; + } + + private void generateAttestation(short attKeyBlob, short attKeyParam, byte[] scratchPad) { + // Device unique attestation not supported + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.DEVICE_UNIQUE_ATTESTATION, + KMError.CANNOT_ATTEST_IDS); + // Read attestation challenge if present + short attChallenge = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.BYTES_TAG, + KMType.ATTESTATION_CHALLENGE); + if (attChallenge != KMType.INVALID_VALUE) { + attChallenge = KMByteTag.getValue(attChallenge); + } + // No attestation required for symmetric keys + short mode = getAttestationMode(attKeyBlob, attChallenge); + KMAttestationCert cert = null; + + switch (mode) { + case KMType.ATTESTATION_CERT: + cert = makeAttestationCert(attKeyBlob, attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER], + data[HW_PARAMETERS], data[SW_PARAMETERS], data[KEY_PARAMETERS], scratchPad); + break; + case KMType.SELF_SIGNED_CERT: + //cert = makeCert(attKeyBlob, attKeyParam, scratchPad); + cert = makeSelfSignedCert(data[SECRET], data[PUB_KEY], scratchPad); + break; + case KMType.FACTORY_PROVISIONED_ATTEST_CERT: + cert = makeCertWithFactoryProvisionedKey(attChallenge, scratchPad); + break; + case KMType.FAKE_CERT: + //cert = makeCert(attKeyBlob, attKeyParam, scratchPad); + cert = makeSelfSignedCert(KMType.INVALID_VALUE, data[PUB_KEY], scratchPad); + break; + default: + data[CERTIFICATE] = KMArray.instance((short) 0); + return; + } + // Allocate memory + short certData = KMByteBlob.instance(MAX_CERT_SIZE); + + cert.buffer(KMByteBlob.getBuffer(certData), + KMByteBlob.getStartOff(certData), + KMByteBlob.length(certData)); + + // Build the certificate - this will sign the cert + cert.build(); + // Adjust the start and length of the certificate in the blob + KMByteBlob.setStartOff(certData, cert.getCertStart()); + KMByteBlob.setLength(certData, cert.getCertLength()); + // Initialize the certificate as array of blob + data[CERTIFICATE] = KMArray.instance((short) 1); + KMArray.add(data[CERTIFICATE], (short) 0, certData); + } + + /** + * 1) If attestation key is present and attestation challenge is absent then it is an error. 2) If + * attestation key is absent and attestation challenge is present then it is an error as factory + * provisioned attestation key is not supported. 3) If both are present and issuer is absent or + * attest key purpose is not ATTEST_KEY then it is an error. 4) If the generated/imported keys are + * RSA or EC then validity period must be specified. Device Unique Attestation is not supported. + */ + + private static void validateRSAKey(byte[] scratchPad) { + // Read key size + if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + if (!KMTag.isValidPublicExponent(data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + + // Generate key handlers + private void generateRSAKey(byte[] scratchPad) { + // Validate RSA Key + validateRSAKey(scratchPad); + // Now generate 2048 bit RSA keypair for the given exponent + short[] lengths = tmpVariables; + data[PUB_KEY] = KMByteBlob.instance((short) 256); + data[SECRET] = KMByteBlob.instance((short) 256); + seProvider.createAsymmetricKey( + KMType.RSA, + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + KMByteBlob.getBuffer(data[PUB_KEY]), + KMByteBlob.getStartOff(data[PUB_KEY]), + KMByteBlob.length(data[PUB_KEY]), + lengths); + + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.add(data[KEY_BLOB], KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private static void validateAESKey() { + // Read key size + if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read Block mode - array of byte values + if (KMTag.isPresent(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE)) { + short blockModes = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.BLOCK_MODE); + // If it is a GCM mode + if (KMEnumArrayTag.contains(blockModes, KMType.GCM)) { + // Min mac length must be present + KMTag.assertPresence(data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMError.MISSING_MIN_MAC_LENGTH); + short macLength = + KMKeyParameters.findTag(data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH); + macLength = KMIntegerTag.getValue(macLength); + // Validate the MIN_MAC_LENGTH for AES - should be multiple of 8, less then 128 bits + // and greater the 96 bits + if (KMInteger.getSignificantShort(macLength) != 0 + || KMInteger.getShort(macLength) > 128 + || KMInteger.getShort(macLength) < 96 + || (KMInteger.getShort(macLength) % 8) != 0) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + } + } + } + + private void generateAESKey(byte[] scratchPad) { + validateAESKey(); + short keysize = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); + short len = + seProvider.createSymmetricKey(KMType.AES, keysize, scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + public void validateECKeys() { + // Read key size + short eccurve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) { + if (eccurve == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } else if (eccurve != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + } + + private void generateECKeys(byte[] scratchPad) { + validateECKeys(); + short[] lengths = tmpVariables; + seProvider.createAsymmetricKey(KMType.EC, scratchPad, (short) 0, (short) 128, scratchPad, + (short) 128, + (short) 128, lengths); + data[PUB_KEY] = KMByteBlob.instance(scratchPad, (short) 128, lengths[1]); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, lengths[0]); + data[KEY_BLOB] = KMArray.instance((short) 5); + KMArray.add(data[KEY_BLOB], KEY_BLOB_PUB_KEY, data[PUB_KEY]); + } + + private static void validateTDESKey() { + if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + // Read Minimum Mac length - it must not be present + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMError.INVALID_TAG); + } + + private void generateTDESKey(byte[] scratchPad) { + validateTDESKey(); + short len = seProvider.createSymmetricKey(KMType.DES, (short) 168, scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void validateHmacKey() { + // If params does not contain any digest throw unsupported digest error. + KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_ARRAY_TAG, KMType.DIGEST, + KMError.UNSUPPORTED_DIGEST); + + // check whether digest sizes are greater then or equal to min mac length. + // Only SHA256 digest must be supported. + if (KMEnumArrayTag.contains(KMType.DIGEST, KMType.DIGEST_NONE, data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_DIGEST); + } + // Read Minimum Mac length + KMTag.assertPresence(data[KEY_PARAMETERS], KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, + KMError.MISSING_MIN_MAC_LENGTH); + short minMacLength = + KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.MIN_MAC_LENGTH, data[KEY_PARAMETERS]); + + if (((short) (minMacLength % 8) != 0) + || minMacLength < (short) 64 + || minMacLength > (short) 256) { + KMException.throwIt(KMError.UNSUPPORTED_MIN_MAC_LENGTH); + } + // Read Keysize + if (!KMTag.isValidKeySize(data[KEY_PARAMETERS])) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + + private void generateHmacKey(byte[] scratchPad) { + validateHmacKey(); + short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, + data[KEY_PARAMETERS]); + // generate HMAC Key + short len = seProvider.createSymmetricKey(KMType.HMAC, keysize, scratchPad, (short) 0); + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); + data[KEY_BLOB] = KMArray.instance((short) 4); + } + + private void checkVersionAndPatchLevel(byte[] scratchPad) { + short len = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_VERSION, data[HW_PARAMETERS]); + if (len != KMType.INVALID_VALUE) { + short provOsVersion = readInteger32(KMDataStoreConstants.OS_VERSION, scratchPad, len); + short status = + KMInteger.unsignedByteArrayCompare( + KMInteger.getBuffer(provOsVersion), + KMInteger.getStartOff(provOsVersion), + scratchPad, + (short) 0, + len); + if (status == -1) { + // If the key characteristics has os version > current os version + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } else if (status == 1) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + len = + KMIntegerTag.getValue( + scratchPad, (short) 0, KMType.UINT_TAG, KMType.OS_PATCH_LEVEL, data[HW_PARAMETERS]); + if (len != KMType.INVALID_VALUE) { + short osPatch = readInteger32(KMDataStoreConstants.OS_PATCH_LEVEL, scratchPad, len); + short status = + KMInteger.unsignedByteArrayCompare( + KMInteger.getBuffer(osPatch), + KMInteger.getStartOff(osPatch), + scratchPad, + (short) 0, + len); + if (status == -1) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } else if (status == 1) { + KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); + } + } + } + + protected short getBootPatchLevel(byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, BOOT_PATCH_LVL_SIZE, (byte) 0); + short len = bootParamsProv.getBootPatchLevel(scratchPad, (short) 0); + if (len != BOOT_PATCH_LVL_SIZE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return KMInteger.uint_32(scratchPad, (short) 0); + } + + private void makeKeyCharacteristics(byte[] scratchPad) { + data[KEY_CHARACTERISTICS] = + makeKeyCharacteristics( + data[KEY_PARAMETERS], + readInteger32(KMDataStoreConstants.OS_VERSION, scratchPad, (short) 0), + readInteger32(KMDataStoreConstants.OS_PATCH_LEVEL, scratchPad, (short) 0), + readInteger32(KMDataStoreConstants.VENDOR_PATCH_LEVEL, scratchPad, (short) 0), + getBootPatchLevel(scratchPad), + data[ORIGIN], + scratchPad); + data[TEE_PARAMETERS] = KMKeyCharacteristics.getTeeEnforced(data[KEY_CHARACTERISTICS]); + data[SW_PARAMETERS] = KMKeyCharacteristics.getKeystoreEnforced(data[KEY_CHARACTERISTICS]); + data[SB_PARAMETERS] = KMKeyCharacteristics.getStrongboxEnforced(data[KEY_CHARACTERISTICS]); + data[HW_PARAMETERS] = getHardwareParamters(data[SB_PARAMETERS], data[TEE_PARAMETERS]); + } + + private void createEncryptedKeyBlob(byte[] scratchPad) { + // make root of trust blob + data[ROT] = readROT(scratchPad); + if (data[ROT] == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // make hidden key params list + data[HIDDEN_PARAMETERS] = + KMKeyParameters.makeHidden(data[KEY_PARAMETERS], data[ROT], scratchPad); + // make authorization data + makeAuthData(scratchPad); + // encrypt the secret and cryptographically attach that to authorization data + encryptSecret(scratchPad); + // create key blob array + KMArray.add(data[KEY_BLOB], KEY_BLOB_SECRET, data[SECRET]); + KMArray.add(data[KEY_BLOB], KEY_BLOB_AUTH_TAG, data[AUTH_TAG]); + KMArray.add(data[KEY_BLOB], KEY_BLOB_NONCE, data[NONCE]); + + //TODO remove the following temporary creation of keyblob. + /* short tempChar = KMKeyCharacteristics.instance(); + short emptyParam = KMArray.instance((short) 0); + emptyParam = KMKeyParameters.instance(emptyParam); + KMKeyCharacteristics.cast(tempChar).setStrongboxEnforced(data[SB_PARAMETERS]); + KMKeyCharacteristics.cast(tempChar).setKeystoreEnforced(emptyParam); + KMKeyCharacteristics.cast(tempChar).setTeeEnforced(data[TEE_PARAMETERS]); + KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, tempChar);*/ + short keyChars = makeKeyCharacteristicsForKeyblob(data[SW_PARAMETERS], data[SB_PARAMETERS], + data[TEE_PARAMETERS]); + KMArray.add(data[KEY_BLOB], KEY_BLOB_PARAMS, keyChars); + + // allocate reclaimable memory. + short buffer = repository.alloc((short) 1024); + short keyBlob = encoder.encode(data[KEY_BLOB], repository.getHeap(), buffer); + data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), buffer, keyBlob); + } + + private short parseEncryptedKeyBlob(short keyBlob, short appId, short appData, + byte[] scratchPad) { + short parsedBlob = KMType.INVALID_VALUE; + short rot = readROT(scratchPad); + if (rot == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + try { + parsedBlob = decoder.decodeArray(keyBlob(), + KMByteBlob.getBuffer(keyBlob), + KMByteBlob.getStartOff(keyBlob), + KMByteBlob.length(keyBlob)); + if (KMArray.length(parsedBlob) < 4) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + + // initialize data + data[SECRET] = KMArray.get(parsedBlob, KEY_BLOB_SECRET); + data[NONCE] = KMArray.get(parsedBlob, KEY_BLOB_NONCE); + data[AUTH_TAG] = KMArray.get(parsedBlob, KEY_BLOB_AUTH_TAG); + data[KEY_CHARACTERISTICS] = KMArray.get(parsedBlob, KEY_BLOB_PARAMS); + data[PUB_KEY] = KMType.INVALID_VALUE; + if (KMArray.length(parsedBlob) == 5) { + data[PUB_KEY] = KMArray.get(parsedBlob, KEY_BLOB_PUB_KEY); + } + + data[TEE_PARAMETERS] = KMKeyCharacteristics.getTeeEnforced(data[KEY_CHARACTERISTICS]); + data[SB_PARAMETERS] = KMKeyCharacteristics.getStrongboxEnforced(data[KEY_CHARACTERISTICS]); + data[SW_PARAMETERS] = KMKeyCharacteristics.getKeystoreEnforced(data[KEY_CHARACTERISTICS]); + data[HW_PARAMETERS] = getHardwareParamters(data[SB_PARAMETERS], data[TEE_PARAMETERS]); + + data[HIDDEN_PARAMETERS] = KMKeyParameters.makeHidden(appId, appData, rot, scratchPad); + data[KEY_BLOB] = parsedBlob; + // make auth data + makeAuthData(scratchPad); + // Decrypt Secret and verify auth tag + decryptSecret(scratchPad); + KMArray.add(parsedBlob, KEY_BLOB_SECRET, data[SECRET]); + } catch (Exception e) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + return parsedBlob; + } + + // Read RoT + public short readROT(byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + short len = bootParamsProv.getBootKey(scratchPad, (short) 0); + len += bootParamsProv.getVerifiedBootHash(scratchPad, (short) len); + short bootState = bootParamsProv.getBootState(); + len = Util.setShort(scratchPad, len, bootState); + if (bootParamsProv.isDeviceBootLocked()) { + scratchPad[len] = (byte) 1; + } else { + scratchPad[len] = (byte) 0; + } + len++; + return KMByteBlob.instance(scratchPad, (short) 0, len); + } + + private void decryptSecret(byte[] scratchPad) { + // derive master key - stored in derivedKey + short len = deriveKey(scratchPad); + if (!seProvider.aesGCMDecrypt( + KMByteBlob.getBuffer(data[DERIVED_KEY]), + KMByteBlob.getStartOff(data[DERIVED_KEY]), + KMByteBlob.length(data[DERIVED_KEY]), + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + scratchPad, (short) 0, + KMByteBlob.getBuffer(data[NONCE]), + KMByteBlob.getStartOff(data[NONCE]), + KMByteBlob.length(data[NONCE]), + repository.getHeap(), data[AUTH_DATA], data[AUTH_DATA_LENGTH], + KMByteBlob.getBuffer(data[AUTH_TAG]), + KMByteBlob.getStartOff(data[AUTH_TAG]), + KMByteBlob.length(data[AUTH_TAG]))) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + // Copy the decrypted secret + data[SECRET] = + KMByteBlob.instance(scratchPad, (short) 0, KMByteBlob.length(data[SECRET])); + } + + private void encryptSecret(byte[] scratchPad) { + // make nonce + data[NONCE] = KMByteBlob.instance((short) AES_GCM_NONCE_LENGTH); + data[AUTH_TAG] = KMByteBlob.instance(AES_GCM_AUTH_TAG_LENGTH); + Util.arrayCopyNonAtomic( + KMByteBlob.getBuffer(data[NONCE]), + KMByteBlob.getStartOff(data[NONCE]), + scratchPad, + (short) 0, + KMByteBlob.length(data[NONCE])); + seProvider.newRandomNumber( + KMByteBlob.getBuffer(data[NONCE]), + KMByteBlob.getStartOff(data[NONCE]), + KMByteBlob.length(data[NONCE])); + // derive master key - stored in derivedKey + short len = deriveKey(scratchPad); + len = seProvider.aesGCMEncrypt( + KMByteBlob.getBuffer(data[DERIVED_KEY]), + KMByteBlob.getStartOff(data[DERIVED_KEY]), + KMByteBlob.length(data[DERIVED_KEY]), + KMByteBlob.getBuffer(data[SECRET]), + KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), + scratchPad, + (short) 0, + KMByteBlob.getBuffer(data[NONCE]), + KMByteBlob.getStartOff(data[NONCE]), + KMByteBlob.length(data[NONCE]), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + KMByteBlob.getBuffer(data[AUTH_TAG]), + KMByteBlob.getStartOff(data[AUTH_TAG]), + KMByteBlob.length(data[AUTH_TAG])); + + if (len > 0 && len != KMByteBlob.length(data[SECRET])) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + data[SECRET] = KMByteBlob.instance(scratchPad, (short) 0, len); + } + + private void makeAuthData(byte[] scratchPad) { + /*short arrayLen = 2; + if (KMArray.cast(data[KEY_BLOB]).length() == 5) { + arrayLen = 3; + } + short params = KMArray.instance((short) arrayLen); + KMArray.cast(params).add((short) 0, KMKeyParameters.cast(data[HW_PARAMETERS]).getVals()); + // KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[SW_PARAMETERS]).getVals()); + KMArray.cast(params).add((short) 1, KMKeyParameters.cast(data[HIDDEN_PARAMETERS]).getVals()); + if (3 == arrayLen) { + KMArray.cast(params).add((short) 2, data[PUB_KEY]); + }*/ + short params = + concatParamsForAuthData(data[KEY_BLOB], data[HW_PARAMETERS], + data[SW_PARAMETERS], data[HIDDEN_PARAMETERS], data[PUB_KEY]); + + short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); + short index = 0; + short len = 0; + short paramsLen = KMArray.length(params); + Util.arrayFillNonAtomic(repository.getHeap(), authIndex, (short) MAX_AUTH_DATA_SIZE, (byte) 0); + while (index < paramsLen) { + short tag = KMArray.get(params, index); + len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); + Util.arrayCopyNonAtomic(repository.getHeap(), (short) authIndex, repository.getHeap(), + (short) (authIndex + len + 32), (short) 32); + len = seProvider.messageDigest256(repository.getHeap(), + (short) (authIndex + 32), (short) (len + 32), repository.getHeap(), (short) authIndex); + if (len != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + index++; + } + data[AUTH_DATA] = authIndex; + data[AUTH_DATA_LENGTH] = len; + } + + private short deriveKey(byte[] scratchPad) { + // KeyDerivation: + // 1. Do HMAC Sign, Auth data. + // 2. HMAC Sign generates an output of 32 bytes length. + // Consume only first 16 bytes as derived key. + // Hmac sign. + short len = seProvider.hmacKDF( + storeDataInst.getMasterKey(), + repository.getHeap(), + data[AUTH_DATA], + data[AUTH_DATA_LENGTH], + scratchPad, + (short) 0); + if (len < 16) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + len = 16; + data[DERIVED_KEY] = KMByteBlob.instance(scratchPad, (short) 0, len); + return len; + } + + public void sendError(APDU apdu, short err) { + short resp = KMArray.instance((short) 1); + err = KMError.translate(err); + short error = KMInteger.uint_16(err); + KMArray.add(resp, (short) 0, error); + sendOutgoing(apdu, resp); + } + + private short addIntegers(short authTime, short timeStamp, byte[] scratchPad) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 24, (byte) 0); + Util.arrayCopyNonAtomic( + KMInteger.getBuffer(authTime), + KMInteger.getStartOff(authTime), + scratchPad, + (short) (8 - KMInteger.length(timeStamp)), + KMInteger.length(timeStamp)); + + // Copy timestamp to scratchpad + Util.arrayCopyNonAtomic( + KMInteger.getBuffer(timeStamp), + KMInteger.getStartOff(timeStamp), + scratchPad, + (short) (16 - KMInteger.length(timeStamp)), + KMInteger.length(timeStamp)); + + // add authTime in millis to timestamp. + KMUtils.add(scratchPad, (short) 0, (short) 8, (short) 16); + return KMInteger.uint_64(scratchPad, (short) 16); + } + + public void powerReset() { + releaseAllOperations(); + resetWrappingKey(); + } + + public void generateRkpKey(byte[] scratchPad, short keyParams) { + data[KEY_PARAMETERS] = keyParams; + generateECKeys(scratchPad); + // create key blob + data[ORIGIN] = KMType.GENERATED; + makeKeyCharacteristics(scratchPad); + createEncryptedKeyBlob(scratchPad); + } + + public static short getPubKey() { + return data[PUB_KEY]; + } + + public static short getPivateKey() { + return data[KEY_BLOB]; + } + + /** + * Encodes the object to the provided apdu buffer. + * + * @param object Object to be encoded. + * @param apduBuf Buffer on which the encoded data is copied. + * @param apduOff Start offset of the buffer. + * @param maxLen Max value of the expected out length. + * @return length of the encoded buffer. + */ + public short encodeToApduBuffer(short object, byte[] apduBuf, short apduOff, + short maxLen) { + short offset = repository.allocReclaimableMemory(maxLen); + short len = encoder.encode(object, repository.getHeap(), offset); + Util.arrayCopyNonAtomic(repository.getHeap(), offset, apduBuf, apduOff, len); + //release memory + repository.reclaimMemory(maxLen); + return len; + } + + + private void updateTrustedConfirmationOperation(KMOperationState op) { + if (op.isTrustedConfirmationRequired()) { + op.getTrustedConfirmationSigner().update(KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), KMByteBlob.length(data[INPUT_DATA])); + } + } + + private void finishTrustedConfirmationOperation(KMOperationState op) { + // Perform trusted confirmation if required + if (op.isTrustedConfirmationRequired()) { + short confToken = getConfirmationToken(data[CONFIRMATION_TOKEN], data[KEY_PARAMETERS]); + boolean verified = op.getTrustedConfirmationSigner() + .verify(KMByteBlob.getBuffer(data[INPUT_DATA]), + KMByteBlob.getStartOff(data[INPUT_DATA]), KMByteBlob.length(data[INPUT_DATA]), + KMByteBlob.getBuffer(confToken), + KMByteBlob.getStartOff(confToken), + KMByteBlob.length(confToken)); + if (!verified) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + } + } + + public short getHardwareInfo() { + short respPtr = KMArray.instance((short) 4); + KMArray.add(respPtr, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(respPtr, (short) 1, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + KMArray.add(respPtr, + (short) 2, + KMByteBlob.instance( + JAVACARD_KEYMASTER_DEVICE, (short) 0, (short) JAVACARD_KEYMASTER_DEVICE.length)); + KMArray.add(respPtr, (short) 3, KMByteBlob.instance(GOOGLE, (short) 0, (short) GOOGLE.length)); + return respPtr; + } + + public short makeKeyCharacteristics(short keyParams, short osVersion, short osPatch, + short vendorPatch, short bootPatch, short origin, byte[] scratchPad) { + short strongboxParams = KMKeyParameters.makeSbEnforced( + keyParams, (byte) origin, osVersion, osPatch, vendorPatch, bootPatch, scratchPad); + short teeParams = KMKeyParameters.makeTeeEnforced(keyParams, scratchPad); + short swParams = KMKeyParameters.makeKeystoreEnforced(keyParams, scratchPad); + short hwParams = KMKeyParameters.makeHwEnforced(strongboxParams, teeParams); + short arr = KMArray.instance((short) 0); + short emptyParams = KMKeyParameters.instance(arr); + short keyCharacteristics = KMKeyCharacteristics.instance(); + KMKeyCharacteristics.setStrongboxEnforced(keyCharacteristics, hwParams); + KMKeyCharacteristics.setKeystoreEnforced(keyCharacteristics, swParams); + KMKeyCharacteristics.setTeeEnforced(keyCharacteristics, emptyParams); + return keyCharacteristics; + } + + public short makeKeyCharacteristicsForKeyblob(short swParams, short sbParams, short teeParams) { + short keyChars = KMKeyCharacteristics.instance(); + KMKeyCharacteristics.setStrongboxEnforced(keyChars, sbParams); + KMKeyCharacteristics.setKeystoreEnforced(keyChars, swParams); + KMKeyCharacteristics.setTeeEnforced(keyChars, teeParams); + return keyChars; + } + + public short getKeyCharacteristicsExp() { + return KMKeyCharacteristics.exp(); + } + + public void validateEarlyBoot(short Params, byte inst, byte[] sPad, short sPadOff, + short errorCode) { + + // As per specification, Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is + // provided to IKeyMintDevice::importKey + if (inst == INS_IMPORT_KEY_CMD || readBoolean(KMDataStoreConstants.EARLY_BOOT_ENDED_STATUS, + sPad, sPadOff)) { + // Validate early boot + KMTag.assertAbsence(Params, KMType.BOOL_TAG, KMType.EARLY_BOOT_ONLY, errorCode); + } + } + + public short getHardwareParamters(short sbParams, short teeParams) { + return sbParams; + } + + public short concatParamsForAuthData(short keyBlobPtr, short hwParams, short swParams, + short hiddenParams, short pubKey) { + short arrayLen = 3; + if (pubKey != KMType.INVALID_VALUE) { + arrayLen = 4; + } + short params = KMArray.instance((short) arrayLen); + KMArray.add(params, (short) 0, KMKeyParameters.getVals(hwParams)); + KMArray.add(params, (short) 1, KMKeyParameters.getVals(swParams)); + KMArray.add(params, (short) 2, KMKeyParameters.getVals(hiddenParams)); + if (4 == arrayLen) { + KMArray.add(params, (short) 3, pubKey); + } + return params; + } + + public short getSupportedAttestationMode(short attChallenge) { + return KMType.FACTORY_PROVISIONED_ATTEST_CERT; + } + + public KMAttestationCert makeCommonCert(short swParams, short hwParams, short keyParams, + byte[] scratchPad, KMSEProvider seProvider) { + boolean rsaCert = (KMEnumTag.getValue(KMType.ALGORITHM, hwParams) == KMType.RSA); + KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider); + // notBefore + short notBefore = + KMKeyParameters.findTag(swParams, KMType.DATE_TAG, KMType.ACTIVE_DATETIME); + if (notBefore == KMType.INVALID_VALUE) { + notBefore = + KMKeyParameters.findTag(swParams, KMType.DATE_TAG, KMType.CREATION_DATETIME); + if (notBefore == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + } + notBefore = KMIntegerTag.getValue(notBefore); + cert.notBefore(notBefore, false, scratchPad); + // notAfter + // expiry time - byte blob + boolean derEncoded = false; + short notAfter = + KMKeyParameters.findTag(swParams, KMType.DATE_TAG, KMType.USAGE_EXPIRE_DATETIME); + if (notAfter == KMType.INVALID_VALUE) { + notAfter = getProvisionedCertificateData(seProvider, KMDataStoreConstants.CERTIFICATE_EXPIRY); + derEncoded = true; + } + cert.notAfter(notAfter, derEncoded, scratchPad); + // SubjectName + cert.subjectName(KMByteBlob.instance(X509Subject, (short) 0, (short) X509Subject.length)); + // Serial + short serialNumber = KMByteBlob.instance((short) 1); + KMByteBlob.add(serialNumber, (short) 0, SERIAL_NUM); + cert.serialNumber(serialNumber); + // Issuer. + cert.issuer(getProvisionedCertificateData(seProvider, KMDataStoreConstants.CERTIFICATE_ISSUER)); + return cert; + } + + private short getProvisionedCertificateData(KMSEProvider kmseProvider, byte dataType) { + short len = storeDataInst.getCertificateDataLength(dataType); + if (len == 0) { + KMException.throwIt(KMError.INVALID_DATA); + } + short ptr = KMByteBlob.instance(len); + storeDataInst.readCertificateData( + dataType, + KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr)); + return ptr; + } + + public short getConfirmationToken(short confToken, short keyParams) { + short cToken = + KMKeyParameters.findTag(keyParams, KMType.BYTES_TAG, KMType.CONFIRMATION_TOKEN); + if (cToken == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + return KMByteTag.getValue(cToken); + } + + public short getKMVerificationTokenExp() { + return KMVerificationToken.verificationTokenExp(); + } + + public short getMacFromVerificationToken(short verToken) { + return KMVerificationToken.getMac(verToken, (short) 0x04); + } + + public short getMgf1Digest(short keyParams, short hwParams) { + return KMType.SHA1; + } + + //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. + public short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 4); + short powerResetStatus = 0; + if (seProvider.isPowerReset(true)) { + powerResetStatus = POWER_RESET_MASK_FLAG; + } + Util.setShort(KMInteger.getBuffer(int32Ptr), + KMInteger.getStartOff(int32Ptr), + powerResetStatus); + + Util.setShort(KMInteger.getBuffer(int32Ptr), + (short) (KMInteger.getStartOff(int32Ptr) + 2), + err); + return int32Ptr; + } + + public short generateAttestKeyExp() { + // Arguments + short keyParams = KMKeyParameters.expAny(); + short keyBlob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 2); + KMArray.add(argsProto, (short) 0, keyBlob); + KMArray.add(argsProto, (short) 1, keyParams); + return argsProto; + } + + public void getAttestKeyInputParameters(short arrPtr, short[] data, byte keyBlobOff, + byte keyParametersOff, + byte attestKeyBlobOff, byte attestKeyParamsOff, byte attestKeyIssuerOff) { + data[keyBlobOff] = KMArray.get(arrPtr, (short) 0); + data[keyParametersOff] = KMArray.get(arrPtr, (short) 1); + data[attestKeyBlobOff] = KMType.INVALID_VALUE; + data[attestKeyParamsOff] = KMType.INVALID_VALUE; + data[attestKeyIssuerOff] = KMType.INVALID_VALUE; + } + + public short prepareBeginResp(short paramsPtr, short opHandlePtr, short bufModPtr, + short macLengthPtr) { + short resp = KMArray.instance((short) 3); + KMArray.add(resp, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(resp, (short) 1, paramsPtr); + KMArray.add(resp, (short) 2, opHandlePtr); + return resp; + } + + public short prepareFinishExp() { + short byteBlob = KMByteBlob.exp(); + short cmd = KMArray.instance((short) 6); + KMArray.add(cmd, (short) 0, KMInteger.exp());//op handle + short keyParam = KMKeyParameters.exp(); + KMArray.add(cmd, (short) 1, keyParam);// Key Parameters + KMArray.add(cmd, (short) 2, byteBlob);// input data + KMArray.add(cmd, (short) 3, byteBlob); // signature + short authToken = KMHardwareAuthToken.exp(); + KMArray.add(cmd, (short) 4, authToken); // auth token + short verToken = getKMVerificationTokenExp(); + KMArray.add(cmd, (short) 5, verToken); // time stamp token + return cmd; + } + + public short prepareUpdateExp() { + short cmd = KMArray.instance((short) 5); + // Arguments + short keyParams = KMKeyParameters.exp(); + KMArray.add(cmd, (short) 0, KMInteger.exp()); + KMArray.add(cmd, (short) 1, keyParams); + KMArray.add(cmd, (short) 2, KMByteBlob.exp()); + short authToken = KMHardwareAuthToken.exp(); + KMArray.add(cmd, (short) 3, authToken); + short verToken = getKMVerificationTokenExp(); + KMArray.add(cmd, (short) 4, verToken); + return cmd; + } + + public void getUpdateInputParameters(short arrPtr, short[] data, byte opHandleOff, + byte keyParametersOff, byte inputDataOff, byte hwTokenOff, + byte verToken) { + data[opHandleOff] = KMArray.get(arrPtr, (short) 0); + data[keyParametersOff] = KMArray.get(arrPtr, (short) 1); + data[inputDataOff] = KMArray.get(arrPtr, (short) 2); + data[hwTokenOff] = KMArray.get(arrPtr, (short) 3); + data[verToken] = KMArray.get(arrPtr, (short) 4); + } + + public void getFinishInputParameters(short arrPtr, short[] data, byte opHandleOff, + byte keyParametersOff, byte inputDataOff, byte signDataOff, byte hwTokenOff, byte verToken, + byte confToken) { + data[opHandleOff] = KMArray.get(arrPtr, (short) 0); + data[keyParametersOff] = KMArray.get(arrPtr, (short) 1); + data[inputDataOff] = KMArray.get(arrPtr, (short) 2); + data[signDataOff] = KMArray.get(arrPtr, (short) 3); + data[hwTokenOff] = KMArray.get(arrPtr, (short) 4); + data[verToken] = KMArray.get(arrPtr, (short) 5); + data[confToken] = KMType.INVALID_VALUE; + } + + public short prepareFinishResp(short outputPtr) { + short keyParam = KMArray.instance((short) 0); + keyParam = KMKeyParameters.instance(keyParam); + short resp = KMArray.instance((short) 3); + KMArray.add(resp, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(resp, (short) 1, keyParam); + KMArray.add(resp, (short) 2, outputPtr); + return resp; + } + + public short prepareUpdateResp(short outputPtr, short inputConsumedPtr) { + short resp = KMArray.instance((short) 4); + KMArray.add(resp, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(resp, (short) 1, inputConsumedPtr); + short keyParm = KMKeyParameters.instance(KMArray.instance((short) 0)); + KMArray.add(resp, (short) 2, keyParm); + KMArray.add(resp, (short) 3, outputPtr); + return resp; + } + + public void validateP1P2(APDU apdu) { + byte[] apduBuffer = apdu.getBuffer(); + short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + // Validate P1P2. + if (P1P2 != KEYMASTER_HAL_VERSION) { + ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); + } + } + + public boolean isAssociatedDataTagSupported() { + return true; + } + + void assertPrivateOperation(short purpose, short algorithm) { + switch (algorithm) { + case KMType.RSA: + if (purpose == KMType.ENCRYPT || purpose == KMType.VERIFY) { + KMException.throwIt(KMError.PUBLIC_KEY_OPERATION); + } + break; + case KMType.EC: + if (purpose == KMType.VERIFY) { + KMException.throwIt(KMError.PUBLIC_KEY_OPERATION); + } + break; + default: + break; + } + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMKeymintDevice.java b/Applet/src/com/android/javacard/kmdevice/KMKeymintDevice.java new file mode 100644 index 00000000..cc326a64 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMKeymintDevice.java @@ -0,0 +1,613 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; +import javacard.security.CryptoException; + +public class KMKeymintDevice extends KMKeymasterDevice { + + public static byte[] JAVACARD_KEYMINT_DEVICE; + private static byte[] GOOGLE; + private static byte[] dec319999Ms; + private static byte[] dec319999; + private static byte[] jan01970; + private KMCose kmCoseInst; + private RemotelyProvisionedComponentDevice rkp; + private KMRkpDataStore rkpDataStoreInst; + + public KMKeymintDevice(KMSEProvider seImpl, KMRepository repoInst, KMEncoder encoderInst, + KMDecoder decoderInst, KMDataStore storeData, + KMBootDataStore bootParamsProvider, KMRkpDataStore rkpStore) { + super(seImpl, repoInst, encoderInst, decoderInst, storeData, bootParamsProvider); + rkpDataStoreInst = rkpStore; + rkp = new RemotelyProvisionedComponentDevice(this, encoderInst, decoderInst, repoInst, + seProvider, storeData, rkpStore, bootParamsProvider); + kmCoseInst = KMCose.getInstance(); + initStatics(); + } + + public static void initStatics() { + JAVACARD_KEYMINT_DEVICE = new byte[]{0x4a, 0x61, 0x76, 0x61, 0x63, 0x61, 0x72, 0x64, 0x4b, 0x65, + 0x79, 0x6d, 0x69, + 0x6e, 0x74, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65,}; + GOOGLE = new byte[]{0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + dec319999Ms = new byte[]{(byte) 0, (byte) 0, (byte) 0xE6, (byte) 0x77, (byte) 0xD2, (byte) 0x1F, + (byte) 0xD8, + (byte) 0x18}; + dec319999 = new byte[]{0x39, 0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, + 0x35, 0x39, + 0x5a,}; + jan01970 = new byte[]{0x37, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x5a,}; + } + + public void process(APDU apdu) { + try { + resetData(); + repository.onProcess(); + byte[] apduBuffer = apdu.getBuffer(); + byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; + + switch (apduIns) { + 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: + super.process(apdu); + } + } catch (KMException exception) { + sendError(apdu, KMException.reason()); + } catch (ISOException exp) { + sendError(apdu, mapISOErrorToKMError(exp.getReason())); + } catch (CryptoException e) { + sendError(apdu, mapCryptoErrorToKMError(e.getReason())); + } catch (Exception e) { + sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); + } finally { + repository.clean(); + } + } + + @Override + public short getHardwareInfo() { + final byte version = 1; + // Make the response + short respPtr = KMArray.instance((short) 6); + KMArray.add(respPtr, (short) 0, buildErrorStatus(KMError.OK)); + KMArray.add(respPtr, (short) 1, KMInteger.uint_8(version)); + KMArray.add(respPtr, (short) 2, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + KMArray.add(respPtr, (short) 3, + KMByteBlob.instance(JAVACARD_KEYMINT_DEVICE, (short) 0, + (short) JAVACARD_KEYMINT_DEVICE.length)); + KMArray.add(respPtr, (short) 4, KMByteBlob.instance(GOOGLE, (short) 0, (short) GOOGLE.length)); + KMArray.add(respPtr, (short) 5, KMInteger.uint_8((byte) 1)); + return respPtr; + } + + @Override + public short makeKeyCharacteristics(short keyParams, short osVersion, short osPatch, + short vendorPatch, + short bootPatch, short origin, byte[] scratchPad) { + short strongboxParams = KMKeyParameters.makeSbEnforced(keyParams, (byte) origin, osVersion, + osPatch, vendorPatch, + bootPatch, scratchPad); + short teeParams = KMKeyParameters.makeTeeEnforced(keyParams, scratchPad); + short swParams = KMKeyParameters.makeKeystoreEnforced(keyParams, scratchPad); + // short emptyParam = KMArray.instance((short) 0); + short keyCharacteristics = KMKeyCharacteristics.instance(); + KMKeyCharacteristics.setStrongboxEnforced(keyCharacteristics, strongboxParams); + KMKeyCharacteristics.setKeystoreEnforced(keyCharacteristics, swParams); + KMKeyCharacteristics.setTeeEnforced(keyCharacteristics, teeParams); + return keyCharacteristics; + } + + @Override + public short makeKeyCharacteristicsForKeyblob(short swParams, short sbParams, short teeParams) { + short keyChars = KMKeyCharacteristics.instance(); + short emptyParam = KMArray.instance((short) 0); + emptyParam = KMKeyParameters.instance(emptyParam); + KMKeyCharacteristics.setStrongboxEnforced(keyChars, sbParams); + KMKeyCharacteristics.setKeystoreEnforced(keyChars, emptyParam); + KMKeyCharacteristics.setTeeEnforced(keyChars, teeParams); + return keyChars; + } + + @Override + public short getKeyCharacteristicsExp() { + return KMKeyCharacteristics.exp(); + } + + @Override + public short getHardwareParamters(short sbParams, short teeParams) { + return KMKeyParameters.makeHwEnforced(sbParams, teeParams); + } + + @Override + public short concatParamsForAuthData(short keyBlobPtr, short hwParams, short swParams, + short hiddenParams, + short pubKey) { + short arrayLen = 2; + if (pubKey != KMType.INVALID_VALUE) { + arrayLen = 3; + } + short params = KMArray.instance((short) arrayLen); + KMArray.add(params, (short) 0, KMKeyParameters.getVals(hwParams)); + KMArray.add(params, (short) 1, KMKeyParameters.getVals(hiddenParams)); + if (3 == arrayLen) { + KMArray.add(params, (short) 2, pubKey); + } + return params; + } + + @Override + public short getSupportedAttestationMode(short attChallenge) { + // Attestation challenge present then it is an error because no factory + // provisioned attest key + short mode = KMType.NO_CERT; // TODO check what should be the default value + if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.length(attChallenge) > 0) { + KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } + if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.ATTEST_KEY, data[HW_PARAMETERS]) + || KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[HW_PARAMETERS])) { + mode = KMType.SELF_SIGNED_CERT; + } else { + mode = KMType.FAKE_CERT; + } + return mode; + } + + @Override + public KMAttestationCert makeCommonCert(short swParams, short hwParams, short keyParams, + byte[] scratchPad, + KMSEProvider seProvider) { + short alg = KMKeyParameters.findTag(keyParams, KMType.ENUM_TAG, KMType.ALGORITHM); + boolean rsaCert = KMEnumTag.getValue(alg) == KMType.RSA; + KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider); + + // Validity period must be specified + short notBefore = KMKeyParameters.findTag(keyParams, KMType.DATE_TAG, + KMType.CERTIFICATE_NOT_BEFORE); + if (notBefore == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_NOT_BEFORE); + } + notBefore = KMIntegerTag.getValue(notBefore); + short notAfter = KMKeyParameters.findTag(keyParams, KMType.DATE_TAG, + KMType.CERTIFICATE_NOT_AFTER); + if (notAfter == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.MISSING_NOT_AFTER); + } + notAfter = KMIntegerTag.getValue(notAfter); + // VTS sends notBefore == Epoch. + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 8, (byte) 0); + short epoch = KMInteger.instance(scratchPad, (short) 0, (short) 8); + short end = KMInteger.instance(dec319999Ms, (short) 0, (short) dec319999Ms.length); + if (KMInteger.compare(notBefore, epoch) == 0) { + cert.notBefore(KMByteBlob.instance(jan01970, (short) 0, (short) jan01970.length), true, + scratchPad); + } else { + cert.notBefore(notBefore, false, scratchPad); + } + // VTS sends notAfter == Dec 31st 9999 + if (KMInteger.compare(notAfter, end) == 0) { + cert.notAfter(KMByteBlob.instance(dec319999, (short) 0, (short) dec319999.length), true, + scratchPad); + } else { + cert.notAfter(notAfter, false, scratchPad); + } + // Serial number + short serialNum = KMKeyParameters.findTag(keyParams, KMType.BIGNUM_TAG, + KMType.CERTIFICATE_SERIAL_NUM); + if (serialNum != KMType.INVALID_VALUE) { + serialNum = KMBignumTag.getValue(serialNum); + } else { + serialNum = KMByteBlob.instance((short) 1); + KMByteBlob.add(serialNum, (short) 0, (byte) 1); + } + cert.serialNumber(serialNum); + return cert; + } + + @Override + public short getMgf1Digest(short keyParams, short hwParams) { + short mgfDigest = KMKeyParameters.findTag(keyParams, KMType.ENUM_ARRAY_TAG, + KMType.RSA_OAEP_MGF_DIGEST); + if (mgfDigest != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.length(mgfDigest) != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + mgfDigest = KMEnumArrayTag.get(mgfDigest, (short) 0); + if (mgfDigest == KMType.DIGEST_NONE) { + KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST); + } + if (!KMEnumArrayTag.contains(KMType.RSA_OAEP_MGF_DIGEST, mgfDigest, hwParams)) { + KMException.throwIt(KMError.INCOMPATIBLE_MGF_DIGEST); + } + if (mgfDigest != KMType.SHA1 && mgfDigest != KMType.SHA2_256) { + KMException.throwIt(KMError.UNSUPPORTED_MGF_DIGEST); + } + } + return mgfDigest; + } + + @Override + public void beginKeyAgreementOperation(KMOperationState op) { + if (op.getAlgorithm() != KMType.EC) { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + + op.setOperation( + seProvider.initAsymmetricOperation((byte) op.getPurpose(), (byte) op.getAlgorithm(), + (byte) op.getPadding(), (byte) op.getDigest(), KMType.DIGEST_NONE, /* No MGF1 Digest */ + KMByteBlob.getBuffer(data[SECRET]), KMByteBlob.getStartOff(data[SECRET]), + KMByteBlob.length(data[SECRET]), null, + (short) 0, (short) 0)); + } + + @Override + public void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) { + try { + KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + short blob = pkcs8.decodeEcSubjectPublicKeyInfo(data[INPUT_DATA]); + short len = op.getOperation().finish(KMByteBlob.getBuffer(blob), KMByteBlob.getStartOff(blob), + KMByteBlob.length(blob), scratchPad, (short) 0); + data[OUTPUT_DATA] = KMByteBlob.instance((short) 32); + Util.arrayCopyNonAtomic(scratchPad, (short) 0, KMByteBlob.getBuffer(data[OUTPUT_DATA]), + KMByteBlob.getStartOff(data[OUTPUT_DATA]), len); + } catch (CryptoException e) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + } + + @Override + public short getConfirmationToken(short confToken, short keyParams) { + if (0 == KMByteBlob.length(confToken)) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + return confToken; + } + + @Override + public short getKMVerificationTokenExp() { + return KMVerificationToken.timeStampTokenExp(); + } + + @Override + public short getMacFromVerificationToken(short verToken) { + return KMVerificationToken.getMac(verToken, (short) 0x02); + } + + @Override + public void validateECKeys() { + // Read key size + short eccurve = KMEnumTag.getValue(KMType.ECCURVE, data[KEY_PARAMETERS]); + if (eccurve == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } else { + if (eccurve != KMType.P_256) { + KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); + } + } + } + + @Override + public short buildErrorStatus(short err) { + return KMInteger.uint_16(err); + } + + @Override + public short generateAttestKeyExp() { + short params = KMKeyParameters.expAny(); + short blob = KMByteBlob.exp(); + // Array of expected arguments + short cmd = KMArray.instance((short) 5); + KMArray.add(cmd, (short) 0, blob); // key blob + KMArray.add(cmd, (short) 1, params); // keyparamters to be attested. + KMArray.add(cmd, (short) 2, blob); // attest key blob + KMArray.add(cmd, (short) 3, params); // attest key params + KMArray.add(cmd, (short) 4, blob); // attest issuer + return cmd; + } + + @Override + public void getAttestKeyInputParameters(short arrPtr, short[] data, byte keyBlobOff, + byte keyParametersOff, + byte attestKeyBlobOff, byte attestKeyParamsOff, byte attestKeyIssuerOff) { + data[keyBlobOff] = KMArray.get(arrPtr, (short) 0); + data[keyParametersOff] = KMArray.get(arrPtr, (short) 1); + data[attestKeyBlobOff] = KMArray.get(arrPtr, (short) 2); + data[attestKeyParamsOff] = KMArray.get(arrPtr, (short) 3); + data[attestKeyIssuerOff] = KMArray.get(arrPtr, (short) 4); + } + + @Override + public short prepareBeginResp(short paramsPtr, short opHandlePtr, short bufModePtr, + short macLengthPtr) { + short resp = KMArray.instance((short) 5); + KMArray.add(resp, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(resp, (short) 1, paramsPtr); + KMArray.add(resp, (short) 2, opHandlePtr); + KMArray.add(resp, (short) 3, bufModePtr); + KMArray.add(resp, (short) 4, macLengthPtr); + return resp; + } + + @Override + public short prepareFinishExp() { + short byteBlob = KMByteBlob.exp(); + short cmd = KMArray.instance((short) 6); + KMArray.add(cmd, (short) 0, KMInteger.exp());// op handle + KMArray.add(cmd, (short) 1, byteBlob);// input data + KMArray.add(cmd, (short) 2, byteBlob); // signature + short authToken = KMHardwareAuthToken.exp(); + KMArray.add(cmd, (short) 3, authToken); // auth token + short verToken = getKMVerificationTokenExp(); + KMArray.add(cmd, (short) 4, verToken); // time stamp token + KMArray.add(cmd, (short) 5, byteBlob); // confirmation token + return cmd; + } + + @Override + public short prepareUpdateExp() { + short cmd = KMArray.instance((short) 4); + // Arguments + KMArray.add(cmd, (short) 0, KMInteger.exp()); + KMArray.add(cmd, (short) 1, KMByteBlob.exp()); + short authToken = KMHardwareAuthToken.exp(); + KMArray.add(cmd, (short) 2, authToken); + short verToken = getKMVerificationTokenExp(); + KMArray.add(cmd, (short) 3, verToken); + return cmd; + } + + @Override + public void getUpdateInputParameters(short arrPtr, short[] data, byte opHandleOff, + byte keyParametersOff, + byte inputDataOff, byte hwTokenOff, byte verToken) { + data[opHandleOff] = KMArray.get(arrPtr, (short) 0); + data[inputDataOff] = KMArray.get(arrPtr, (short) 1); + data[hwTokenOff] = KMArray.get(arrPtr, (short) 2); + data[verToken] = KMArray.get(arrPtr, (short) 3); + } + + @Override + public void getFinishInputParameters(short arrPtr, short[] data, byte opHandleOff, + byte keyParametersOff, + byte inputDataOff, byte signDataOff, byte hwTokenOff, byte verToken, byte confToken) { + data[opHandleOff] = KMArray.get(arrPtr, (short) 0); + data[inputDataOff] = KMArray.get(arrPtr, (short) 1); + data[signDataOff] = KMArray.get(arrPtr, (short) 2); + data[hwTokenOff] = KMArray.get(arrPtr, (short) 3); + data[verToken] = KMArray.get(arrPtr, (short) 4); + data[confToken] = KMArray.get(arrPtr, (short) 5); + } + + @Override + public short prepareFinishResp(short outputPtr) { + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(resp, (short) 1, outputPtr); + return resp; + } + + @Override + public short prepareUpdateResp(short outputPtr, short inputConsumedPtr) { + short resp = KMArray.instance((short) 2); + KMArray.add(resp, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(resp, (short) 1, outputPtr); + return resp; + } + + @Override + public void validateP1P2(APDU apdu) { + byte[] apduBuffer = apdu.getBuffer(); + short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + if (P1P2 != KEYMINT_HAL_VERSION) { + ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); + } + } + + @Override + public void updateAAD(KMOperationState op, byte finish) { + return; + } + + @Override + public void validatePurpose(short params) { + short attKeyPurpose = KMKeyParameters.findTag(params, KMType.ENUM_ARRAY_TAG, KMType.PURPOSE); + // ATTEST_KEY purpose cannot be combined with any other purpose. + if (attKeyPurpose != KMType.INVALID_VALUE && KMEnumArrayTag.contains(attKeyPurpose, + KMType.ATTEST_KEY) + && KMEnumArrayTag.length(attKeyPurpose) > 1) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } + + public short generateBcc(boolean testMode, byte[] scratchPad) { + if (!testMode && readBoolean(KMDataStoreConstants.PROVISIONED_LOCKED, scratchPad, (short) 0)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + KMDeviceUniqueKey deviceUniqueKey = rkpDataStoreInst.getDeviceUniqueKey(testMode); + short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0); + short coseKey = kmCoseInst.constructCoseKey(KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), KMInteger.uint_8(KMCose.COSE_KEY_OP_VERIFY), + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), scratchPad, (short) 0, temp, + KMType.INVALID_VALUE, false); + temp = encodeToApduBuffer(coseKey, scratchPad, (short) 0, + RemotelyProvisionedComponentDevice.MAX_COSE_BUF_SIZE); + // Construct payload. + short payload = kmCoseInst.constructCoseCertPayload( + KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.ISSUER), + KMTextString.instance(KMCose.TEST_ISSUER_NAME, (short) 0, + (short) KMCose.TEST_ISSUER_NAME.length)), + KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.SUBJECT), + KMTextString.instance(KMCose.TEST_SUBJECT_NAME, (short) 0, + (short) KMCose.TEST_SUBJECT_NAME.length)), + KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0), + KMByteBlob.instance(scratchPad, (short) 0, temp)), + KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0), + KMByteBlob.instance(KMCose.KEY_USAGE_SIGN, (short) 0, + (short) KMCose.KEY_USAGE_SIGN.length))); + // temp temporarily holds the length of encoded cert payload. + temp = encodeToApduBuffer(payload, scratchPad, (short) 0, + RemotelyProvisionedComponentDevice.MAX_COSE_BUF_SIZE); + payload = KMByteBlob.instance(scratchPad, (short) 0, temp); + + // protected header + short protectedHeader = kmCoseInst.constructHeaders(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 = encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + RemotelyProvisionedComponentDevice.MAX_COSE_BUF_SIZE); + protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp); + + // unprotected headers. + short arr = KMArray.instance((short) 0); + short unprotectedHeader = KMCoseHeaders.instance(arr); + + // construct cose sign structure. + short coseSignStructure = kmCoseInst.constructCoseSignStructure(protectedHeader, + KMByteBlob.instance((short) 0), + payload); + // temp temporarily holds the length of encoded sign structure. + // Encode cose Sign_Structure. + temp = encodeToApduBuffer(coseSignStructure, scratchPad, (short) 0, + RemotelyProvisionedComponentDevice.MAX_COSE_BUF_SIZE); + // do sign + short len = seProvider.ecSign256(deviceUniqueKey, scratchPad, (short) 0, temp, scratchPad, + temp); + coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); + + // construct cose_sign1 + short coseSign1 = kmCoseInst.constructCoseSign1(protectedHeader, unprotectedHeader, payload, + coseSignStructure); + + // [Cose_Key, Cose_Sign1] + short bcc = KMArray.instance((short) 2); + KMArray.add(bcc, (short) 0, coseKey); + KMArray.add(bcc, (short) 1, coseSign1); + return bcc; + } + + + public short validateCertChain(boolean validateEekRoot, byte expCertAlg, + byte expLeafCertAlg, short certChainArr, byte[] scratchPad, Object[] authorizedEekRoots) { + short len = KMArray.length(certChainArr); + short coseHeadersExp = KMCoseHeaders.exp(); + //prepare exp for coseky + short coseKeyExp = KMCoseKey.exp(); + short ptr1; + short ptr2; + short signStructure; + short encodedLen; + short prevCoseKey = 0; + short keySize; + short alg = expCertAlg; + short index; + for (index = 0; index < len; index++) { + ptr1 = KMArray.get(certChainArr, index); + + // validate protected Headers + ptr2 = KMArray.get(ptr1, KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET); + ptr2 = decoder.decode(coseHeadersExp, KMByteBlob.getBuffer(ptr2), + KMByteBlob.getStartOff(ptr2), KMByteBlob.length(ptr2)); + if (!KMCoseHeaders.cast(ptr2).isDataValid(alg, KMType.INVALID_VALUE)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + + // parse and get the public key from payload. + ptr2 = KMArray.get(ptr1, KMCose.COSE_SIGN1_PAYLOAD_OFFSET); + ptr2 = decoder.decode(coseKeyExp, KMByteBlob.getBuffer(ptr2), + KMByteBlob.getStartOff(ptr2), KMByteBlob.length(ptr2)); + if ((index == (short) (len - 1)) && len > 1) { + alg = expLeafCertAlg; + } + if (!KMCoseKey.cast(ptr2).isDataValid(KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, alg, + KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + if (prevCoseKey == 0) { + prevCoseKey = ptr2; + } + // Get the public key. + keySize = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(scratchPad, (short) 0); + if (keySize != 65) { + KMException.throwIt(KMError.STATUS_FAILED); + } + if (validateEekRoot && (index == 0)) { + boolean found = false; + // In prod mode the first pubkey should match a well-known Google public key. + for (short i = 0; i < (short) authorizedEekRoots.length; i++) { + if (0 == Util.arrayCompare(scratchPad, (short) 0, (byte[]) authorizedEekRoots[i], + (short) 0, (short) ((byte[]) authorizedEekRoots[i]).length)) { + found = true; + break; + } + } + if (!found) { + KMException.throwIt(KMError.STATUS_FAILED); + } + } + // Validate signature. + signStructure = + kmCoseInst.constructCoseSignStructure( + KMArray.get(ptr1, KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET), + KMByteBlob.instance((short) 0), + KMArray.get(ptr1, KMCose.COSE_SIGN1_PAYLOAD_OFFSET)); + encodedLen = encodeToApduBuffer(signStructure, scratchPad, + keySize, RemotelyProvisionedComponentDevice.MAX_COSE_BUF_SIZE); + + if (!seProvider.ecVerify256(scratchPad, (short) 0, keySize, scratchPad, keySize, encodedLen, + KMByteBlob.getBuffer(KMArray.get(ptr1, KMCose.COSE_SIGN1_SIGNATURE_OFFSET)), + KMByteBlob.getStartOff(KMArray.get(ptr1, KMCose.COSE_SIGN1_SIGNATURE_OFFSET)), + KMByteBlob.length(KMArray.get(ptr1, KMCose.COSE_SIGN1_SIGNATURE_OFFSET)))) { + KMException.throwIt(KMError.STATUS_FAILED); + } + prevCoseKey = ptr2; + } + return prevCoseKey; + } + + @Override + protected void processGetCertChainCmd(APDU apdu) { + KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } + + @Override + public void validateEarlyBoot(short Params, byte inst, byte[] sPad, short sPadOff, + short errorCode) { + if (inst != INS_GENERATE_KEY_CMD) { + //VTS expects error code EARLY_BOOT_ONLY during begin operation if eary boot ended tag is present + if (inst == INS_BEGIN_OPERATION_CMD) { + errorCode = KMError.EARLY_BOOT_ENDED; + } + // Validate early boot + super.validateEarlyBoot(Params, inst, sPad, sPadOff, errorCode); + } + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMMap.java b/Applet/src/com/android/javacard/kmdevice/KMMap.java new file mode 100644 index 00000000..b4763688 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMMap.java @@ -0,0 +1,194 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +public class KMMap extends KMType { + + public static final short ANY_MAP_LENGTH = 0x1000; + private static final short MAP_HEADER_SIZE = 4; + private static KMMap prototype; + + private KMMap() { + } + + private static KMMap proto(short ptr) { + if (prototype == null) { + prototype = new KMMap(); + } + instanceTable[KM_MAP_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + short ptr = instance(MAP_TYPE, MAP_HEADER_SIZE); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), ANY_MAP_LENGTH); + return ptr; + } + + public static short instance(short length) { + short ptr = KMType.instance(MAP_TYPE, (short) (MAP_HEADER_SIZE + (length * 4))); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), (short) 0); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), length); + return ptr; + } + + public static short instance(short length, byte type) { + short ptr = instance(length); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), type); + return ptr; + } + + private static KMMap cast(short ptr) { + if (heap[ptr] != MAP_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public void add(short index, short keyPtr, short valPtr) { + short len = length(); + if (index >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short keyIndex = (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + + (short) (index * 4)); + Util.setShort(heap, keyIndex, keyPtr); + Util.setShort(heap, (short) (keyIndex + 2), valPtr); + } + + public short getKey(short index) { + short len = length(); + if (index >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + return Util.getShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index + * 4))); + } + + public short getKeyValue(short index) { + short len = length(); + if (index >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + return Util.getShort( + heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) ( + index * 4 + 2))); + } + + public void swap(short index1, short index2) { + short len = length(); + if (index1 >= len || index2 >= len) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + // Swap keys + short indexPtr1 = + Util.getShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) ( + index1 * 4))); + short indexPtr2 = + Util.getShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) ( + index2 * 4))); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index1 + * 4)), + indexPtr2); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) (index2 + * 4)), + indexPtr1); + + // Swap Values + indexPtr1 = + Util.getShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) ( + index1 * 4 + 2))); + indexPtr2 = + Util.getShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) ( + index2 * 4 + 2))); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) ( + index1 * 4 + 2)), + indexPtr2); + Util.setShort( + heap, + (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE + (short) ( + index2 * 4 + 2)), + indexPtr1); + } + + public void canonicalize() { + KMCoseMap.canonicalize(instanceTable[KM_MAP_OFFSET], length()); + } + + public short containedType() { + return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE)); + } + + public short getStartOff() { + return (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + MAP_HEADER_SIZE); + } + + public short length() { + return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE + 2)); + } + + public byte[] getBuffer() { + return heap; + } + + public static void add(short bPtr, short index, short keyPtr, short valPtr) { + KMMap.cast(bPtr).add(index, keyPtr, valPtr); + } + + public static short getKeyValue(short bPtr, short index) { + return KMMap.cast(bPtr).getKeyValue(index); + } + + public static void swap(short bPtr, short index1, short index2) { + KMMap.cast(bPtr).swap(index1, index2); + } + + public static short getKey(short bPtr, short index) { + return KMMap.cast(bPtr).getKey(index); + } + + public static short length(short bPtr) { + return KMMap.cast(bPtr).length(); + } + + public static void canonicalize(short bPtr) { + KMMap.cast(bPtr).canonicalize(); + } + + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMMasterKey.java b/Applet/src/com/android/javacard/kmdevice/KMMasterKey.java similarity index 88% rename from Applet/src/com/android/javacard/keymaster/KMMasterKey.java rename to Applet/src/com/android/javacard/kmdevice/KMMasterKey.java index 7a88778e..d91c0bb3 100644 --- a/Applet/src/com/android/javacard/keymaster/KMMasterKey.java +++ b/Applet/src/com/android/javacard/kmdevice/KMMasterKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; /** * KMMasterKey is a marker interface and the SE Provider has to implement this interface. Internally @@ -22,4 +22,7 @@ */ public interface KMMasterKey { + public byte getKey(byte[] keyData, short kOff); + + public short getKeySizeBits(); } diff --git a/Applet/src/com/android/javacard/kmdevice/KMNInteger.java b/Applet/src/com/android/javacard/kmdevice/KMNInteger.java new file mode 100644 index 00000000..17eb04cb --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMNInteger.java @@ -0,0 +1,186 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +public class KMNInteger extends KMType { + + private static KMNInteger prototype; + public static final byte SIGNED_MASK = (byte) 0x80; + + private KMNInteger() { + } + + private static KMNInteger proto(short ptr) { + if (prototype == null) { + prototype = new KMNInteger(); + } + instanceTable[KM_NEG_INTEGER_OFFSET] = ptr; + return prototype; + } + + public static short exp() { + return KMType.exp(NEG_INTEGER_TYPE); + } + + // return an empty integer instance + public static short instance(short length) { + if ((length <= 0) || (length > 8)) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (length > 4) { + length = KMInteger.UINT_64; + } else { + length = KMInteger.UINT_32; + } + return KMType.instance(NEG_INTEGER_TYPE, length); + } + + public static short instance(byte[] num, short srcOff, short length) { + if (length > 8) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + if (length == 1) { + return uint_8(num[srcOff]); + } else if (length == 2) { + return uint_16(Util.getShort(num, srcOff)); + } else if (length == 4) { + return uint_32(num, srcOff); + } else { + return uint_64(num, srcOff); + } + } + + private static KMNInteger cast(short ptr) { + byte[] heap = repository.getHeap(); + if (heap[ptr] != NEG_INTEGER_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + // create integer and copy byte value + public static short uint_8(byte num) { + if (num >= 0) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(KMInteger.UINT_32); + heap[(short) (ptr + TLV_HEADER_SIZE + 3)] = num; + return ptr; + } + + // create integer and copy short value + public static short uint_16(short num) { + if (num >= 0) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(KMInteger.UINT_32); + Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), num); + return ptr; + } + + // create integer and copy integer value + public static short uint_32(byte[] num, short offset) { + if (!isSignedInteger(num, offset)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(KMInteger.UINT_32); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_32); + return ptr; + } + + // create integer and copy integer value + public static short uint_64(byte[] num, short offset) { + if (!isSignedInteger(num, offset)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = instance(KMInteger.UINT_64); + Util.arrayCopy(num, offset, heap, (short) (ptr + TLV_HEADER_SIZE), KMInteger.UINT_64); + return ptr; + } + + private short getStartOff() { + return (short) (getBaseOffset() + TLV_HEADER_SIZE); + } + + private short getShort() { + return Util.getShort(heap, (short) (getStartOff() + 2)); + } + + private short getBaseOffset() { + return instanceTable[KM_NEG_INTEGER_OFFSET]; + } + + private short length() { + return Util.getShort(heap, (short) (getBaseOffset() + 1)); + } + + private short getSignificantShort() { + return Util.getShort(heap, getStartOff()); + } + + private void getValue(byte[] dest, short destOff, short length) { + if (length < length()) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + if (length > length()) { + length = length(); + destOff += length; + } + Util.arrayCopyNonAtomic(heap, getStartOff(), dest, destOff, length); + } + + private byte[] getBuffer() { + return heap; + } + + public static boolean isSignedInteger(byte[] num, short offset) { + byte val = num[offset]; + return SIGNED_MASK == (val & SIGNED_MASK); + } + + public static short getShort(short bPtr) { + return KMNInteger.cast(bPtr).getShort(); + } + + public static short getStartOff(short bPtr) { + return KMNInteger.cast(bPtr).getStartOff(); + } + + public static short getSignificantShort(short bPtr) { + return KMNInteger.cast(bPtr).getSignificantShort(); + } + + public static void getValue(short bPtr, byte[] dest, short destOff, short length) { + KMNInteger.cast(bPtr).getValue(dest, destOff, length); + } + + public static short length(short bPtr) { + return KMNInteger.cast(bPtr).length(); + } + + public static byte[] getBuffer(short bPtr) { + return KMNInteger.cast(bPtr).getBuffer(); + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMOperation.java b/Applet/src/com/android/javacard/kmdevice/KMOperation.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMOperation.java rename to Applet/src/com/android/javacard/kmdevice/KMOperation.java index 3132e4b3..b3341231 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperation.java +++ b/Applet/src/com/android/javacard/kmdevice/KMOperation.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; /** * KMOperation represents a persistent operation started by keymaster hal's beginOperation function. @@ -29,7 +29,7 @@ short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, // Used for signature operations short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength); - // Used for finishing cipher operations. + // Used for finishing cipher operations or ecdh keyAgreement. short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] outputDataBuf, short outputDataStart); diff --git a/Applet/src/com/android/javacard/kmdevice/KMOperationState.java b/Applet/src/com/android/javacard/kmdevice/KMOperationState.java new file mode 100644 index 00000000..0d0aaf2b --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMOperationState.java @@ -0,0 +1,331 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * KMOperationState is the container of an active operation started by beginOperation function. This + * operation state is persisted by the applet in non volatile memory. However, this state is not + * retained if applet is upgraded. There will be four operation state records maintained i.e. only + * four active operations are supported at any given time. + */ +public class KMOperationState { + + // byte type + private static final byte ALG = 0; + private static final byte PURPOSE = 1; + private static final byte PADDING = 2; + private static final byte BLOCK_MODE = 3; + private static final byte DIGEST = 4; + private static final byte FLAGS = 5; + private static final byte KEY_SIZE = 6; + private static final byte MAC_LENGTH = 7; + private static final byte MGF_DIGEST = 8; + private static final byte AUTH_TYPE = 9; + // sizes + public static final byte OPERATION_HANDLE_SIZE = 8; + public static final byte DATA_SIZE = 10; + public static final byte AUTH_TIME_SIZE = 8; + // Secure user ids 5 * 8 = 40 bytes ( Considering Maximum 5 SECURE USER IDs) + // First two bytes are reserved to store number of secure ids. So total 42 bytes. + public static final byte USER_SECURE_IDS_SIZE = 42; + + private static final byte OPERATION = 0; + private static final byte HMAC_SIGNER_OPERATION = 1; + // Flag masks + private static final short AUTH_PER_OP_REQD = 1; + private static final short SECURE_USER_ID_REQD = 2; + private static final short AUTH_TIMEOUT_VALIDATED = 4; + private static final short AES_GCM_UPDATE_ALLOWED = 8; + // Max user secure ids. + private static final byte MAX_SECURE_USER_IDS = 5; + + // Object References + private byte[] opHandle; + private byte[] authTime; + private byte[] userSecureIds; + private short[] data; + private Object[] operations; + + + public KMOperationState() { + opHandle = JCSystem.makeTransientByteArray(OPERATION_HANDLE_SIZE, JCSystem.CLEAR_ON_RESET); + authTime = JCSystem.makeTransientByteArray(AUTH_TIME_SIZE, JCSystem.CLEAR_ON_RESET); + data = JCSystem.makeTransientShortArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); + operations = JCSystem.makeTransientObjectArray((short) 2, JCSystem.CLEAR_ON_RESET); + userSecureIds = JCSystem.makeTransientByteArray(USER_SECURE_IDS_SIZE, JCSystem.CLEAR_ON_RESET); + reset(); + } + + public void reset() { + byte index = 0; + while (index < DATA_SIZE) { + data[index] = KMType.INVALID_VALUE; + index++; + } + Util.arrayFillNonAtomic(opHandle, (short) 0, OPERATION_HANDLE_SIZE, (byte) 0); + Util.arrayFillNonAtomic(authTime, (short) 0, AUTH_TIME_SIZE, (byte) 0); + + if (null != operations[OPERATION]) { + ((KMOperation) operations[OPERATION]).abort(); + } + operations[OPERATION] = null; + + if (null != operations[HMAC_SIGNER_OPERATION]) { + ((KMOperation) operations[HMAC_SIGNER_OPERATION]).abort(); + } + operations[HMAC_SIGNER_OPERATION] = null; + } + + public short compare(byte[] handle, short start, short len) { + return Util.arrayCompare(handle, start, opHandle, (short) 0, (short) opHandle.length); + } + + public void setKeySize(short keySize) { + data[KEY_SIZE] = keySize; + } + + public short getKeySize() { + return data[KEY_SIZE]; + } + + public short getHandle() { + return KMInteger.uint_64(opHandle, (short) 0); + } + + public void setHandle(byte[] buf, short start, short len) { + Util.arrayCopyNonAtomic(buf, start, opHandle, (short) 0, (short) opHandle.length); + } + + public short getPurpose() { + return data[PURPOSE]; + } + + public void setPurpose(short purpose) { + data[PURPOSE] = purpose; + } + + public void setOperation(KMOperation op) { + operations[OPERATION] = op; + } + + public KMOperation getOperation() { + return (KMOperation) operations[OPERATION]; + } + + public boolean isAuthPerOperationReqd() { + return (data[FLAGS] & AUTH_PER_OP_REQD) != 0; + } + + public boolean isAuthTimeoutValidated() { + return (data[FLAGS] & AUTH_TIMEOUT_VALIDATED) != 0; + } + + public boolean isSecureUserIdReqd() { + return (data[FLAGS] & SECURE_USER_ID_REQD) != 0; + } + + public short getAuthTime() { + return KMInteger.uint_64(authTime, (short) 0); + } + + public void setAuthTime(byte[] timeBuf, short start) { + Util.arrayCopyNonAtomic(timeBuf, start, authTime, (short) 0, AUTH_TIME_SIZE); + } + + public void setOneTimeAuthReqd(boolean flag) { + if (flag) { + data[FLAGS] = (short) (data[FLAGS] | SECURE_USER_ID_REQD); + } else { + data[FLAGS] = (short) (data[FLAGS] & (~SECURE_USER_ID_REQD)); + } + } + + public void setAuthTimeoutValidated(boolean flag) { + if (flag) { + data[FLAGS] = (byte) (data[FLAGS] | AUTH_TIMEOUT_VALIDATED); + } else { + data[FLAGS] = (byte) (data[FLAGS] & (~AUTH_TIMEOUT_VALIDATED)); + } + } + + public void setAuthType(byte authType) { + data[AUTH_TYPE] = authType; + } + + public short getAuthType() { + return data[AUTH_TYPE]; + } + + public short getUserSecureId() { + short offset = 0; + short length = Util.getShort(userSecureIds, offset); + offset += 2; + if (length == 0) { + return KMType.INVALID_VALUE; + } + short arrObj = KMArray.instance(length); + short index = 0; + short obj; + while (index < length) { + obj = KMInteger.instance(userSecureIds, (short) (offset + index * 8), (short) 8); + KMArray.add(arrObj, index, obj); + index++; + } + return KMIntegerArrayTag.instance(KMType.ULONG_ARRAY_TAG, KMType.USER_SECURE_ID, arrObj); + } + + public void setUserSecureId(short integerArrayPtr) { + short length = KMIntegerArrayTag.length(integerArrayPtr); + if (length > MAX_SECURE_USER_IDS) { + KMException.throwIt(KMError.INVALID_KEY_BLOB); + } + Util.arrayFillNonAtomic(userSecureIds, (short) 0, USER_SECURE_IDS_SIZE, (byte) 0); + short index = 0; + short obj; + short offset = 0; + offset = Util.setShort(userSecureIds, offset, length); + while (index < length) { + obj = KMIntegerArrayTag.get(integerArrayPtr, index); + Util.arrayCopyNonAtomic( + KMInteger.getBuffer(obj), + KMInteger.getStartOff(obj), + userSecureIds, + (short) (8 - KMInteger.length(obj) + offset + 8 * index), + KMInteger.length(obj) + ); + index++; + } + } + + public void setAuthPerOperationReqd(boolean flag) { + if (flag) { + data[FLAGS] = (short) (data[FLAGS] | AUTH_PER_OP_REQD); + } else { + data[FLAGS] = (short) (data[FLAGS] & (~AUTH_PER_OP_REQD)); + } + } + + public short getAlgorithm() { + return data[ALG]; + } + + public void setAlgorithm(short algorithm) { + data[ALG] = algorithm; + } + + public short getPadding() { + return data[PADDING]; + } + + public void setPadding(short padding) { + data[PADDING] = padding; + } + + public short getBlockMode() { + return data[BLOCK_MODE]; + } + + public void setBlockMode(short blockMode) { + data[BLOCK_MODE] = blockMode; + } + + public short getDigest() { + return data[DIGEST]; + } + + public short getMgfDigest() { + return data[MGF_DIGEST]; + } + + public void setDigest(byte digest) { + data[DIGEST] = digest; + } + + public void setMgfDigest(byte mgfDigest) { + data[MGF_DIGEST] = mgfDigest; + } + + public boolean isAesGcmUpdateAllowed() { + return (data[FLAGS] & AES_GCM_UPDATE_ALLOWED) != 0; + } + + public void setAesGcmUpdateComplete() { + data[FLAGS] = (byte) (data[FLAGS] & (~AES_GCM_UPDATE_ALLOWED)); + } + + public void setAesGcmUpdateStart() { + data[FLAGS] = (byte) (data[FLAGS] | AES_GCM_UPDATE_ALLOWED); + } + + public void setMacLength(short length) { + data[MAC_LENGTH] = length; + } + + public short getMacLength() { + return data[MAC_LENGTH]; + } + + public byte getBufferingMode() { + short alg = getAlgorithm(); + short purpose = getPurpose(); + short digest = getDigest(); + short padding = getPadding(); + short blockMode = getBlockMode(); + + if (alg == KMType.RSA && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { + return KMType.BUF_RSA_NO_DIGEST; + } + + if (alg == KMType.EC && digest == KMType.DIGEST_NONE && purpose == KMType.SIGN) { + return KMType.BUF_EC_NO_DIGEST; + } + + switch (alg) { + case KMType.AES: + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { + return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; + } + break; + case KMType.DES: + if (purpose == KMType.ENCRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN; + } + } + return KMType.BUF_NONE; + } + + public void setTrustedConfirmationSigner(KMOperation hmacSignerOp) { + operations[HMAC_SIGNER_OPERATION] = hmacSignerOp; + } + + public KMOperation getTrustedConfirmationSigner() { + return (KMOperation) operations[HMAC_SIGNER_OPERATION]; + } + + public boolean isTrustedConfirmationRequired() { + return operations[HMAC_SIGNER_OPERATION] != null; + } +} diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java b/Applet/src/com/android/javacard/kmdevice/KMPKCS8Decoder.java similarity index 61% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java rename to Applet/src/com/android/javacard/kmdevice/KMPKCS8Decoder.java index 921cae28..be94b586 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMPKCS8DecoderImpl.java +++ b/Applet/src/com/android/javacard/kmdevice/KMPKCS8Decoder.java @@ -1,8 +1,8 @@ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.Util; -public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { +public class KMPKCS8Decoder { public static final byte ASN1_OCTET_STRING = 0x04; public static final byte ASN1_SEQUENCE = 0x30; @@ -10,45 +10,76 @@ public class KMPKCS8DecoderImpl implements KMPKCS8Decoder { public static final byte ASN1_A0_TAG = (byte) 0xA0; public static final byte ASN1_A1_TAG = (byte) 0xA1; public static final byte ASN1_BIT_STRING = 0x03; - public static final byte[] EC_CURVE = { - 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, - 0x01, 0x07 - }; - public static final byte[] RSA_ALGORITHM = { - 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, - (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 - }; - public static final byte[] EC_ALGORITHM = { - 0x06, 0x07, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, - 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, - (byte) 0xce, 0x3d, 0x03, 0x01, 0x07 - }; + public static byte[] EC_CURVE; + public static byte[] RSA_ALGORITHM; + public static byte[] EC_ALGORITHM; private byte[] data; private short start; private short length; private short cur; - private static KMPKCS8DecoderImpl inst; + private static KMPKCS8Decoder inst; - private KMPKCS8DecoderImpl() { + private KMPKCS8Decoder() { start = 0; length = 0; cur = 0; } - @Override + public static void initStatics() { + EC_CURVE = new byte[]{ + 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, 0x3d, 0x03, + 0x01, 0x07 + }; + RSA_ALGORITHM = new byte[]{ + 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, + (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 + }; + EC_ALGORITHM = new byte[]{ + 0x06, 0x07, 0x2a, (byte) 0x86, 0x48, (byte) 0xce, + 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, (byte) 0x86, 0x48, + (byte) 0xce, 0x3d, 0x03, 0x01, 0x07 + }; + } + public short decodeRsa(short blob) { init(blob); decodeCommon((short) 0, RSA_ALGORITHM); return decodeRsaPrivateKey((short) 0); } - @Override public short decodeEc(short blob) { init(blob); decodeCommon((short) 0, EC_ALGORITHM); return decodeEcPrivateKey((short) 1); } + public short decodeEcSubjectPublicKeyInfo(short blob) { + init(blob); + header(ASN1_SEQUENCE); + short len = header(ASN1_SEQUENCE); + short ecPublicInfo = KMByteBlob.instance(len); + getBytes(ecPublicInfo); + if (Util.arrayCompare( + KMByteBlob.getBuffer(ecPublicInfo), + KMByteBlob.getStartOff(ecPublicInfo), + EC_ALGORITHM, + (short) 0, KMByteBlob.length(ecPublicInfo)) != 0) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + len = header(ASN1_BIT_STRING); + if (len < 1) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + // TODO need to handle if unused bits are not zero + byte unusedBits = getByte(); + if (unusedBits != 0) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + short pubKey = KMByteBlob.instance((short) (len - 1)); + getBytes(pubKey); + return pubKey; + } + //Seq[Int,Int,Int,Int,] public short decodeRsaPrivateKey(short version) { short resp = KMArray.instance((short) 3); @@ -63,19 +94,31 @@ public short decodeRsaPrivateKey(short version) { KMException.throwIt(KMError.UNKNOWN_ERROR); } len = header(ASN1_INTEGER); - short modulus = getModulus(len); + short modulus = KMByteBlob.instance(len); + getBytes(modulus); + updateModulus(modulus); len = header(ASN1_INTEGER); short pubKey = KMByteBlob.instance(len); getBytes(pubKey); len = header(ASN1_INTEGER); short privKey = KMByteBlob.instance(len); getBytes(privKey); - KMArray.cast(resp).add((short) 0, modulus); - KMArray.cast(resp).add((short) 1, pubKey); - KMArray.cast(resp).add((short) 2, privKey); + KMArray.add(resp, (short) 0, modulus); + KMArray.add(resp, (short) 1, pubKey); + KMArray.add(resp, (short) 2, privKey); return resp; } + private void updateModulus(short blob) { + byte[] buffer = KMByteBlob.getBuffer(blob); + short startOff = KMByteBlob.getStartOff(blob); + short len = KMByteBlob.length(blob); + if (0 == buffer[startOff] && len > 256) { + KMByteBlob.setStartOff(blob, ++startOff); + KMByteBlob.setLength(blob, --len); + } + } + // Seq [Int, Blob] public void decodeCommon(short version, byte[] alg) { short len = header(ASN1_SEQUENCE); @@ -91,10 +134,10 @@ public void decodeCommon(short version, byte[] alg) { short blob = KMByteBlob.instance(len); getBytes(blob); if (Util.arrayCompare( - KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), + KMByteBlob.getBuffer(blob), + KMByteBlob.getStartOff(blob), alg, - (short) 0, KMByteBlob.cast(blob).length()) != 0) { + (short) 0, KMByteBlob.length(blob)) != 0) { KMException.throwIt(KMError.UNKNOWN_ERROR); } } @@ -121,14 +164,15 @@ public short decodeEcPrivateKey(short version) { if (len < 1) { KMException.throwIt(KMError.UNKNOWN_ERROR); } + // TODO need to handle if unused bits are not zero byte unusedBits = getByte(); if (unusedBits != 0) { KMException.throwIt(KMError.UNIMPLEMENTED); } short pubKey = KMByteBlob.instance((short) (len - 1)); getBytes(pubKey); - KMArray.cast(resp).add((short) 0, pubKey); - KMArray.cast(resp).add((short) 1, privKey); + KMArray.add(resp, (short) 0, pubKey); + KMArray.add(resp, (short) 1, privKey); return resp; } @@ -167,20 +211,10 @@ private short getShort() { return d; } - private short getModulus(short modulusLen) { - if (0 == data[cur] && modulusLen == 257) { - incrementCursor((short) 1); - modulusLen--; - } - short blob = KMByteBlob.instance(modulusLen); - getBytes(blob); - return blob; - } - private void getBytes(short blob) { - short len = KMByteBlob.cast(blob).length(); - Util.arrayCopyNonAtomic(data, cur, KMByteBlob.cast(blob).getBuffer(), - KMByteBlob.cast(blob).getStartOff(), len); + short len = KMByteBlob.length(blob); + Util.arrayCopyNonAtomic(data, cur, KMByteBlob.getBuffer(blob), + KMByteBlob.getStartOff(blob), len); incrementCursor(len); } @@ -200,17 +234,17 @@ private short getLength() { return KMType.INVALID_VALUE; //should not come here } - public static KMPKCS8DecoderImpl instance() { + public static KMPKCS8Decoder instance() { if (inst == null) { - inst = new KMPKCS8DecoderImpl(); + inst = new KMPKCS8Decoder(); } return inst; } public void init(short blob) { - data = KMByteBlob.cast(blob).getBuffer(); - start = KMByteBlob.cast(blob).getStartOff(); - length = KMByteBlob.cast(blob).length(); + data = KMByteBlob.getBuffer(blob); + start = KMByteBlob.getStartOff(blob); + length = KMByteBlob.length(blob); cur = start; } diff --git a/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java b/Applet/src/com/android/javacard/kmdevice/KMPreSharedKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java rename to Applet/src/com/android/javacard/kmdevice/KMPreSharedKey.java index 273aeb4a..268f9a8e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java +++ b/Applet/src/com/android/javacard/kmdevice/KMPreSharedKey.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; /** * KMPreSharedKey is a marker interface and the SE Provider has to implement this interface. diff --git a/Applet/src/com/android/javacard/kmdevice/KMRepository.java b/Applet/src/com/android/javacard/kmdevice/KMRepository.java new file mode 100644 index 00000000..56a3cb3e --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMRepository.java @@ -0,0 +1,113 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * KMRepository class manages persistent and volatile memory usage by the applet. Note the + * repository is only used by applet and it is not intended to be used by seProvider. + */ +public class KMRepository { + + public static final short HEAP_SIZE = 15000; + + // Class Attributes + private byte[] heap; + private short[] heapIndex; + private short reclaimIndex; + + // Singleton instance + private static KMRepository repository; + + public static KMRepository instance() { + return repository; + } + + public KMRepository(boolean isUpgrading) { + heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); + heapIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); + reclaimIndex = HEAP_SIZE; + heapIndex[0] = (short) 0; + repository = this; + } + + public void onUninstall() { + // Javacard Runtime environment cleans up the data. + + } + + public void onProcess() { + } + + public void clean() { + Util.arrayFillNonAtomic(heap, (short) 0, heapIndex[0], (byte) 0); + heapIndex[0] = 0; + reclaimIndex = HEAP_SIZE; + } + + public void onDeselect() { + } + + public void onSelect() { + // If write through caching is implemented then this method will restore the data into cache + } + + // This function uses memory from the back of the heap(transient memory). Call + // reclaimMemory function immediately after the use. + public short allocReclaimableMemory(short length) { + if ((((short) (reclaimIndex - length)) <= heapIndex[0]) + || (length >= HEAP_SIZE / 2)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + reclaimIndex -= length; + return reclaimIndex; + } + + // Reclaims the memory back. + public void reclaimMemory(short length) { + if (reclaimIndex < heapIndex[0]) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + reclaimIndex += length; + } + + public short allocAvailableMemory() { + if (heapIndex[0] >= heap.length) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + short index = heapIndex[0]; + heapIndex[0] = (short) heap.length; + return index; + } + + public short alloc(short length) { + if ((((short) (heapIndex[0] + length)) > heap.length) || + (((short) (heapIndex[0] + length)) > reclaimIndex)) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + heapIndex[0] += length; + return (short) (heapIndex[0] - length); + } + + public byte[] getHeap() { + return heap; + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMRkpDataStore.java b/Applet/src/com/android/javacard/kmdevice/KMRkpDataStore.java new file mode 100644 index 00000000..bb7023b4 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMRkpDataStore.java @@ -0,0 +1,64 @@ +/* + * Copyright(C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +public interface KMRkpDataStore extends KMUpgradable { + + /** + * This function stores the data of the corresponding id into the persistent memory. + * + * @param id of the buffer to be stored. @see {@link KMDataStoreConstants} + * @param data is the buffer that contains the data to be stored. + * @param offset is the start offset of the buffer. + * @param length is the length of the buffer. + */ + void storeData(byte id, byte[] data, short offset, short length); + + /** + * This function returns the stored data of the corresponding id. + * + * @param id of the buffer to be stored.@see {@link KMDataStoreConstants} + * @param data is the buffer in which the data of the corresponding id is returned. + * @param offset is the start offset of the buffer. + * @return length of the data copied to the buffer. + */ + byte[] getData(byte id); + + // keys + + /** + * This function creates an instance device unique key and stores in persitent memory. + * + * @param testMode flag denotes if the key is used test mode or production mode. + * @param pubKey buffer containing the EC public key. + * @param pubKeyOff start offset of the public key buffer. + * @param pubKeyLen length of the public key buffer. + * @param privKey buffer containing the EC private key. + * @param privKeyOff start offset of the private key buffer. + * @param privKeyLen length of the private key buffer. + */ + void createDeviceUniqueKey(boolean testMode, byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, + short privKeyOff, short privKeyLen); + + /** + * Returns the device unique key + * + * @param testMode flag denotes if the key is used test mode or production mode. + * @return KMDeviceUniqueKey instance + */ + KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode); +} diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/src/com/android/javacard/kmdevice/KMSEProvider.java similarity index 70% rename from Applet/src/com/android/javacard/keymaster/KMSEProvider.java rename to Applet/src/com/android/javacard/kmdevice/KMSEProvider.java index dbfa3710..03dfcd50 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/src/com/android/javacard/kmdevice/KMSEProvider.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import org.globalplatform.upgrade.Element; @@ -23,12 +23,7 @@ * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there * can be only one provider in the applet package. */ -public interface KMSEProvider extends KMUpgradable { - - // Provision related constants. - public static final byte CERTIFICATE_CHAIN = 0; - public static final byte CERTIFICATE_EXPIRY = 1; - public static final byte CERTIFICATE_ISSUER = 2; +public interface KMSEProvider { /** * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it @@ -70,6 +65,14 @@ void createAsymmetricKey( short pubModMaxLength, short[] lengths); + /** + * Initializes the trusted confirmation operation. + * + * @param computedHmacKey Instance of the computed Hmac key. + * @return instance of KMOperation. + */ + KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); + /** * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then * it should throw a CryptoException. @@ -228,7 +231,7 @@ boolean aesGCMDecrypt( * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as * defined in android keymaster hal definition. * - * @param hmacKey instance of pre-shared key. + * @param hmacKey of pre-shared key. * @param label is the label to be used for ckdf. * @param labelStart is the start of label. * @param labelLen is the length of the label. @@ -277,7 +280,7 @@ short hmacSign( * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive * the key, which is used to encrypt the keyblob. * - * @param masterKey instance of masterkey. + * @param masterkey of masterkey. * @param data is the buffer containing data to be signed. * @param dataStart is the start of the data. * @param dataLength is the length of the data. @@ -286,7 +289,7 @@ short hmacSign( * @return length of the signature buffer in bytes. */ short hmacKDF( - KMMasterKey masterKey, + KMMasterKey masterkey, byte[] data, short dataStart, short dataLength, @@ -296,7 +299,7 @@ short hmacKDF( /** * This is a oneshot operation that verifies the signature using hmac algorithm. * - * @param hmacKey instance of KMComputedHmacKey. + * @param hmacKey is the computed hmac key. * @param data is the buffer containing data. * @param dataStart is the start of the data. * @param dataLength is the length of the data. @@ -348,7 +351,7 @@ short rsaDecipherOAEP256( /** * This is a oneshot operation that signs the data using EC private key. * - * @param ecPrivKey instance of KMAttestationKey. + * @param ecPrivKey of KMAttestationKey. * @param inputDataBuf is the buffer of the input data. * @param inputDataStart is the start of the input data buffer. * @param inputDataLength is the length of the inpur data buffer in bytes. @@ -364,6 +367,134 @@ short ecSign256( byte[] outputDataBuf, short outputDataStart); + /** + * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2 + * + * @param ikm is the buffer containing input key material. + * @param ikmOff is the start of the input key. + * @param ikmLen is the length of the input key. + * @param salt is the buffer containing the salt. + * @param saltOff is the start of the salt buffer. + * @param saltLen is the length of the salt buffer. + * @param info is the buffer containing the application specific information + * @param infoOff is the start of the info buffer. + * @param infoLen is the length of the info buffer. + * @param out is the output buffer. + * @param outOff is the start of the output buffer. + * @param outLen is the length of the expected out buffer. + * @return Length of the out buffer which is outLen. + */ + short hkdf( + byte[] ikm, + short ikmOff, + short ikmLen, + byte[] salt, + short saltOff, + short saltLen, + byte[] info, + short infoOff, + short infoLen, + byte[] out, + short outOff, + short outLen); + + /** + * This function performs ECDH key agreement and generates a secret. + * + * @param privKey is the buffer containing the private key from first party. + * @param privKeyOff is the offset of the private key buffer. + * @param privKeyLen is the length of the private key buffer. + * @param publicKey is the buffer containing the public key from second party. + * @param publicKeyOff is the offset of the public key buffer. + * @param publicKeyLen is the length of the public key buffer. + * @param secret is the output buffer. + * @param secretOff is the offset of the output buffer. + * @return The length of the secret. + */ + short ecdhKeyAgreement( + byte[] privKey, + short privKeyOff, + short privKeyLen, + byte[] publicKey, + short publicKeyOff, + short publicKeyLen, + byte[] secret, + short secretOff); + + /** + * This is a oneshort operation that verifies the data using EC public key + * + * @param pubKey is the public key buffer. + * @param pubKeyOffset is the start of the public key buffer. + * @param pubKeyLen is the length of the public key. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param signatureDataBuf is the buffer the signature input data. + * @param signatureDataStart is the start of the signature input data. + * @param signatureDataLen is the length of the signature input data. + * @return true if verification is successful, otherwise false. + */ + boolean ecVerify256( + byte[] pubKey, + short pubKeyOffset, + short pubKeyLen, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signatureDataBuf, + short signatureDataStart, + short signatureDataLen); + + /** + * This is a oneshot operation that signs the data using device unique key. + * + * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the signature. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short ecSign256( + KMDeviceUniqueKey ecPrivKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * This is a oneshot operation that signs the data using device unique key. + * + * @param secret is the private key buffer. + * @param secretStart is the start of the private key buffer. + * @param secretLength is the length of the private key. + * @param inputDataBuf is the input buffer. + * @param inputDataStart is the start offset of the input buffer. + * @param inputDataLength is the length of the input buffer. + * @param outputDataBuf is the output buffer. + * @param outputDataStart is the start offset of the output buffer. + * @return length of the signed data. + */ + short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + short rsaSign256Pkcs1( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuf, + short modStart, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + /** * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, * AES and DES algorithms when keymaster hal's beginOperation function is executed. The @@ -401,14 +532,6 @@ KMOperation initSymmetricOperation( short ivLength, short macLength); - /** - * Initializes the trusted confirmation operation. - * - * @param computedHmacKey Instance of the computed Hmac key. - * @return instance of KMOperation. - */ - KMOperation initTrustedConfirmationSymmetricOperation(KMComputedHmacKey computedHmacKey); - /** * 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 @@ -422,6 +545,7 @@ KMOperation initSymmetricOperation( * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT, * KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS. * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256. + * @param mgfDigest is the MGF digest. * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA. * @param privKeyStart is the start of the private key. * @param privKeyLength is the length of the private key. @@ -435,6 +559,7 @@ KMOperation initAsymmetricOperation( byte alg, byte padding, byte digest, + byte mgfDigest, byte[] privKeyBuf, short privKeyStart, short privKeyLength, @@ -443,93 +568,86 @@ KMOperation initAsymmetricOperation( short pubModLength); /** - * This operation creates the empty instance of KMAttestationCert for rsa or ec public key - * attestation certificate. It corresponds to attestKEy command from keymaster hal specifications. - * The attestation certificate implementation will comply keymaster hal specifications. - * - * @param rsaCert if true indicates that certificate will attest a rsa public key else if false it - * is for ec public key. - * @return An empty instance of KMAttestationCert implementation. - */ - KMAttestationCert getAttestationCert(boolean rsaCert); - - /** - * Returns the implementation of the PKCS8 decoder. + * This function tells if applet is upgrading or not. * - * @return Instance of PKCS8 decoder. + * @return true if upgrading, otherwise false. */ - KMPKCS8Decoder getPKCS8DecoderInstance(); + boolean isUpgrading(); /** - * This operation persists the provision data in the persistent memory. + * This function creates an HMACKey and initializes the key with the provided input key data. * - * @param buf buffer which contains all the provision data. - * @param certChainOff is the start of the cert chain. - * @param certChainLen is the length of the cert chain. - * @param certIssuerOff is the start of the cert issuer. - * @param certIssuerLen is the length of the cert issuer. - * @param certExpiryOff is the start of the cert expiry. - * @param certExpiryLen is the length of the cert expiry. + * @param keyData buffer containing the key data. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of the KMComputedHmacKey. */ - void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, - short certIssuerOff, short certIssuerLen, short certExpiryOff, short certExpiryLen); + KMComputedHmacKey createComputedHmacKey(KMComputedHmacKey createComputedHmacKey, byte[] keyData, + short offset, short length); /** - * The operation reads the provisioned data from persistent memory. + * This function generates an AES Key of keySizeBits, which is used as an master key. This + * generated key is maintained by the SEProvider. This function should be called only once at the + * time of installation. * - * @param dataType type of the provision data to read. - * @param buf is the start of data buffer. - * @param offset is the start of the data. - * @return the length of the data buffer in bytes. + * @param instance of the masterkey. + * @param keySizeBits key size in bits. + * @return An instance of KMMasterKey. */ - short readProvisionedData(byte dataType, byte[] buf, short offset); + KMMasterKey createMasterKey(KMMasterKey masterKey, byte[] key, short offset, short length); /** - * This function returns the provisioned data length. + * This function generates a HMAC key from the provided key buffers. * - * @param dataType type of the provision data to read. - * @return length of the certificate chain. + * @param presharedKey instance of the presharedkey. + * @param key buffer containing the key data. + * @param offset start offset of the buffer. + * @param length is the length of the key. + * @return instance of KMPresharedKey. */ - short getProvisionedDataLength(byte dataType); + KMPreSharedKey createPreSharedKey(KMPreSharedKey presharedKey, byte[] key, short offset, + short length); /** - * This function tells if boot signal event is supported or not. - * - * @return true if supported, false otherwise. + * Returns true if factory provisioned attestation key is supported. */ - boolean isBootSignalEventSupported(); + boolean isAttestationKeyProvisioned(); /** - * This function tells if the device is booted or not. - * - * @return true if device booted, false otherwise. + * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the + * attestation key is provisioned in the factory. */ - boolean isDeviceRebooted(); + short getAttestationKeyAlgorithm(); /** - * This function is supposed to be used to reset the device booted stated after set boot param is - * handled + * Creates an ECKey instance and sets the public and private keys to it. * - * @param resetBootFlag is false if event has been handled + * @param testMode to indicate if current execution is for test or production. + * @param pubKey buffer containing the public key. + * @param pubKeyOff public key buffer start offset. + * @param pubKeyLen public key buffer length. + * @param privKey buffer containing the private key. + * @param privKeyOff private key buffer start offset. + * @param privKeyLen private key buffer length. + * @return instance of KMDeviceUniqueKey. */ - void clearDeviceBooted(boolean resetBootFlag); + KMDeviceUniqueKey createDeviceUniqueKey(KMDeviceUniqueKey key, + byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen); /** - * This function tells if applet is upgrading or not. + * This is a one-shot operation the does digest of the input mesage. * - * @return true if upgrading, otherwise false. + * @param inBuff input buffer to be digested. + * @param inOffset start offset of the input buffer. + * @param inLength length of the input buffer. + * @param outBuff is the output buffer that contains the digested data. + * @param outOffset start offset of the digested output buffer. + * @return length of the digested data. */ - boolean isUpgrading(); + short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, + short outOffset); - /** - * This function generates an AES Key of keySizeBits, which is used as an master key. This - * generated key is maintained by the SEProvider. This function should be called only once at the - * time of installation. - * - * @param keySizeBits key size in bits. - * @return An instance of KMMasterKey. - */ - KMMasterKey createMasterKey(short keySizeBits); /** * This function creates an ECKey and initializes the ECPrivateKey with the provided input key @@ -541,74 +659,53 @@ void persistProvisionData(byte[] buf, short certChainOff, short certChainLen, * @param length length of the buffer. * @return An instance of KMAttestationKey. */ - KMAttestationKey createAttestationKey(byte[] keyData, short offset, short length); + KMAttestationKey createAttestationKey(KMAttestationKey attestationKey, byte[] keyData, + short offset, + short length); - /** - * This function creates an HMACKey and initializes the key with the provided input key data. This - * created key is maintained by the SEProvider. This function should be called only while - * provisioing the pre-shared secret. - * - * @param keyData buffer containing the key data. - * @param offset start of the buffer. - * @param length length of the buffer. - * @return An instance of KMPreSharedKey. - */ - KMPreSharedKey createPresharedKey(byte[] keyData, short offset, short length); /** - * This function creates an HMACKey and initializes the key with the provided input key data. + * This functions checks if SE power reset event occurred. * - * @param keyData buffer containing the key data. - * @param offset start of the buffer. - * @param length length of the buffer. - * @return An instance of the KMComputedHmacKey. + * @param resetFlag flag which denotes to reset the power reset event flag. + * @return true if power reset event occurrred; flase otherwise. */ - KMComputedHmacKey createComputedHmacKey(byte[] keyData, short offset, short length); + boolean isPowerReset(boolean resetFlag); /** - * Returns the master key. + * This function saves the key objects while upgrade. * - * @return Instance of the KMMasterKey + * @param element instance of the Element class where the objects to be stored. + * @param interfaceType the type interface of the parent object. + * @param object instance of the object to be saved. */ - KMMasterKey getMasterKey(); + void onSave(Element element, byte interfaceType, Object object); /** - * Returns the attestation key. - * - * @return Instance of the KMAttestationKey. + * This function restores the the object from element instance. + * + * @param element instance of the Element class. + * @return restored object. */ - KMAttestationKey getAttestationKey(); + Object onResore(Element element); /** - * Returns the preshared key. + * This function returns the count of the primitive bytes required to + * be stored by the implementation of the interface type. * - * @return Instance of the KMPreSharedKey. + * @param interfaceType type interface of the parent object. + * @return count of the primitive bytes. */ - KMPreSharedKey getPresharedKey(); + short getBackupPrimitiveByteCount(byte interfaceType); /** - * Returns the computed Hmac key. + * This function returns the object count required to be stored by the + * implementation of the interface type. * - * @return Instance of the computed hmac key. - */ - KMComputedHmacKey getComputedHmacKey(); - - /** - * Releases all the instance back to pool. Generally this is used when card is reset. + * @param interfaceType type interface of the parent object. + * @return count of the objects. */ - void releaseAllOperations(); + short getBackupObjectCount(byte interfaceType); - /** - * This is a one-shot operation the does digest of the input mesage. - * - * @param inBuff input buffer to be digested. - * @param inOffset start offset of the input buffer. - * @param inLength length of the input buffer. - * @param outBuff is the output buffer that contains the digested data. - * @param outOffset start offset of the digested output buffer. - * @return length of the digested data. - */ - short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, - short outOffset); } diff --git a/Applet/src/com/android/javacard/kmdevice/KMSimpleValue.java b/Applet/src/com/android/javacard/kmdevice/KMSimpleValue.java new file mode 100644 index 00000000..314c0670 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMSimpleValue.java @@ -0,0 +1,74 @@ +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +public class KMSimpleValue extends KMType { + + private static KMSimpleValue prototype; + + public static final byte FALSE = (byte) 20; + public static final byte TRUE = (byte) 21; + public static final byte NULL = (byte) 22; + + + private KMSimpleValue() { + } + + private static KMSimpleValue proto(short ptr) { + if (prototype == null) { + prototype = new KMSimpleValue(); + } + instanceTable[KM_SIMPLE_VALUE_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(SIMPLE_VALUE_TYPE); + } + + public short length() { + return Util.getShort(heap, (short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 1)); + } + + private static KMSimpleValue cast(short ptr) { + if (heap[ptr] != SIMPLE_VALUE_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (!isSimpleValueValid(heap[(short) (ptr + 3)])) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + public static short instance(byte value) { + if (!isSimpleValueValid(value)) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short ptr = KMType.instance(SIMPLE_VALUE_TYPE, (short) 1); + heap[(short) (ptr + 3)] = value; + return ptr; + } + + public byte getValue() { + return heap[(short) (instanceTable[KM_SIMPLE_VALUE_OFFSET] + 3)]; + } + + public static byte getValue(short bPtr) { + return KMSimpleValue.cast(bPtr).getValue(); + } + + private static boolean isSimpleValueValid(byte value) { + switch (value) { + case TRUE: + case FALSE: + case NULL: + break; + default: + return false; + } + return true; + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMTag.java b/Applet/src/com/android/javacard/kmdevice/KMTag.java new file mode 100644 index 00000000..d420b453 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMTag.java @@ -0,0 +1,102 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.Util; + +/** + * This class represents a tag as defined by keymaster hal specifications. It is composed of key + * value pair. The key consists of short tag type e.g. KMType.ENUM and short tag key e.g. + * KMType.ALGORITHM. The key is encoded as uint CBOR type with 4 bytes. This is followed by value + * which can be any CBOR type based on key. struct{byte tag=KMType.TAG_TYPE, short length, value) + * where value is subtype of KMTag i.e. struct{short tagType=one of tag types declared in KMType , + * short tagKey=one of the tag keys declared in KMType, value} where value is one of the sub-types + * of KMType. + */ +public class KMTag extends KMType { + + public static short getKMTagType(short ptr) { + return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + } + + public static short getKMTagKey(short ptr) { + return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2)); + } + + public static void assertPresence(short params, short tagType, short tagKey, short error) { + if (!isPresent(params, tagType, tagKey)) { + KMException.throwIt(error); + } + } + + public static void assertAbsence(short params, short tagType, short tagKey, short error) { + if (isPresent(params, tagType, tagKey)) { + KMException.throwIt(error); + } + } + + public static boolean isPresent(short params, short tagType, short tagKey) { + short tag = KMKeyParameters.findTag(params, tagType, tagKey); + return tag != KMType.INVALID_VALUE; + } + + public static boolean isEqual(short params, short tagType, short tagKey, short value) { + switch (tagType) { + case KMType.ENUM_TAG: + return KMEnumTag.getValue(tagKey, params) == value; + case KMType.UINT_TAG: + case KMType.DATE_TAG: + case KMType.ULONG_TAG: + return KMIntegerTag.isEqual(params, tagType, tagKey, value); + case KMType.ENUM_ARRAY_TAG: + return KMEnumArrayTag.contains(tagKey, value, params); + case KMType.UINT_ARRAY_TAG: + case KMType.ULONG_ARRAY_TAG: + return KMIntegerArrayTag.contains(tagKey, value, params); + } + return false; + } + + public static void assertTrue(boolean condition, short error) { + if (!condition) { + KMException.throwIt(error); + } + } + + public static boolean isValidPublicExponent(short params) { + short pubExp = KMKeyParameters.findTag(params, KMType.ULONG_TAG, KMType.RSA_PUBLIC_EXPONENT); + if (pubExp == KMType.INVALID_VALUE) { + return false; + } + // Only exponent support is F4 - 65537 which is 0x00010001. + pubExp = KMIntegerTag.getValue(pubExp); + if (!(KMInteger.getShort(pubExp) == 0x01 && + KMInteger.getSignificantShort(pubExp) == 0x01)) { + return false; + } + return true; + } + + public static boolean isValidKeySize(short params) { + short keysize = KMKeyParameters.findTag(params, KMType.UINT_TAG, KMType.KEYSIZE); + if (keysize == KMType.INVALID_VALUE) { + return false; + } + short alg = KMEnumTag.getValue(KMType.ALGORITHM, params); + return KMIntegerTag.isValidKeySize(keysize, (byte) alg); + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMTextString.java b/Applet/src/com/android/javacard/kmdevice/KMTextString.java new file mode 100644 index 00000000..e6bf4cc5 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMTextString.java @@ -0,0 +1,103 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.Util; + +/** + * KMTextString represents contiguous block of bytes. It corresponds to CBOR type of Text String. It + * extends KMByteBlob by specifying value field as zero or more sequence of bytes. struct{ byte + * TEXT_STR_TYPE; short length; sequence of bytes} + */ +public class KMTextString extends KMType { + + private static KMTextString prototype; + + private KMTextString() { + } + + private static KMTextString proto(short ptr) { + if (prototype == null) { + prototype = new KMTextString(); + } + instanceTable[KM_TEXT_STRING_OFFSET] = ptr; + return prototype; + } + + // pointer to an empty instance used as expression + public static short exp() { + return KMType.exp(TEXT_STRING_TYPE); + } + + // return an empty byte blob instance + public static short instance(short length) { + short ptr = KMType.instance(TEXT_STRING_TYPE, (short) (length)); + Util.setShort(heap, (short) (ptr + 1), length); + return ptr; + } + + // byte blob from existing buf + public static short instance(byte[] buf, short startOff, short length) { + short ptr = instance(length); + Util.arrayCopyNonAtomic(buf, startOff, heap, + (short) (ptr + TLV_HEADER_SIZE), length); + return ptr; + } + + // cast the ptr to KMTextString + private static KMTextString cast(short ptr) { + if (heap[ptr] != TEXT_STRING_TYPE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + if (Util.getShort(heap, (short) (ptr + 1)) == INVALID_VALUE) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + return proto(ptr); + } + + protected short getBaseOffset() { + return instanceTable[KM_TEXT_STRING_OFFSET]; + } + + // Get the length of the blob + private short length() { + return Util.getShort(heap, (short) (getBaseOffset() + 1)); + } + + private byte[] getBuffer() { + return heap; + } + + // Get the start of blob + public short getStartOff() { + return (short) (getBaseOffset() + TLV_HEADER_SIZE); + } + + public static short length(short bPtr) { + return cast(bPtr).length(); + } + + public static byte[] getBuffer(short bPtr) { + return cast(bPtr).getBuffer(); + } + + public static short getStartOff(short bPtr) { + return cast(bPtr).getStartOff(); + } +} diff --git a/Applet/src/com/android/javacard/kmdevice/KMType.java b/Applet/src/com/android/javacard/kmdevice/KMType.java new file mode 100644 index 00000000..0650b775 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/KMType.java @@ -0,0 +1,403 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.kmdevice; + +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/** + * This class declares all types, tag types, and tag keys. It also establishes basic structure of + * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also, + * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton + * prototype objects which just cast the structure over contiguous memory buffer. + */ +public abstract class KMType { + + public static final short INVALID_VALUE = (short) 0x8000; + protected static final byte TLV_HEADER_SIZE = 3; + + // Types + public static final byte BYTE_BLOB_TYPE = 0x01; + public static final byte INTEGER_TYPE = 0x02; + public static final byte ENUM_TYPE = 0x03; + public static final byte TAG_TYPE = 0x04; + public static final byte ARRAY_TYPE = 0x05; + public static final byte KEY_PARAM_TYPE = 0x06; + public static final byte KEY_CHAR_TYPE = 0x07; + public static final byte HW_AUTH_TOKEN_TYPE = 0x08; + public static final byte VERIFICATION_TOKEN_TYPE = 0x09; + public static final byte HMAC_SHARING_PARAM_TYPE = 0x0A; + public static final byte X509_CERT = 0x0B; + public static final byte NEG_INTEGER_TYPE = 0x0C; + public static final byte TEXT_STRING_TYPE = 0x0D; + public static final byte MAP_TYPE = 0x0E; + public static final byte COSE_KEY_TYPE = 0x0F; + public static final byte COSE_PAIR_TAG_TYPE = 0x10; + public static final byte COSE_PAIR_INT_TAG_TYPE = 0x20; + public static final byte COSE_PAIR_NEG_INT_TAG_TYPE = 0x30; + public static final byte COSE_PAIR_BYTE_BLOB_TAG_TYPE = 0x40; + public static final byte COSE_PAIR_COSE_KEY_TAG_TYPE = 0x60; + public static final byte COSE_PAIR_SIMPLE_VALUE_TAG_TYPE = 0x70; + public static final byte COSE_PAIR_TEXT_STR_TAG_TYPE = (byte) 0x80; + public static final byte SIMPLE_VALUE_TYPE = (byte) 0x90; + public static final byte COSE_HEADERS_TYPE = (byte) 0xA0; + public static final byte COSE_CERT_PAYLOAD_TYPE = (byte) 0xB0; + // Tag Types + public static final short INVALID_TAG = 0x0000; + public static final short ENUM_TAG = 0x1000; + public static final short ENUM_ARRAY_TAG = 0x2000; + public static final short UINT_TAG = 0x3000; + public static final short UINT_ARRAY_TAG = 0x4000; + public static final short ULONG_TAG = 0x5000; + public static final short DATE_TAG = 0x6000; + public static final short BOOL_TAG = 0x7000; + public static final short BIGNUM_TAG = (short) 0x8000; + public static final short BYTES_TAG = (short) 0x9000; + public static final short ULONG_ARRAY_TAG = (short) 0xA000; + public static final short TAG_TYPE_MASK = (short) 0xF000; + + // Enum Tag + // Internal tags + public static final short RULE = 0x7FFF; + public static final byte IGNORE_INVALID_TAGS = 0x00; + public static final byte FAIL_ON_INVALID_TAGS = 0x01; + + // Algorithm Enum Tag key and values + public static final short ALGORITHM = 0x0002; + public static final byte RSA = 0x01; + public static final byte DES = 0x21; + public static final byte EC = 0x03; + public static final byte AES = 0x20; + public static final byte HMAC = (byte) 0x80; + + // EcCurve Enum Tag key and values. + public static final short ECCURVE = 0x000A; + public static final byte P_224 = 0x00; + public static final byte P_256 = 0x01; + public static final byte P_384 = 0x02; + public static final byte P_521 = 0x03; + + // KeyBlobUsageRequirements Enum Tag key and values. + public static final short BLOB_USAGE_REQ = 0x012D; + public static final byte STANDALONE = 0x00; + public static final byte REQUIRES_FILE_SYSTEM = 0x01; + + // HardwareAuthenticatorType Enum Tag key and values. + public static final short USER_AUTH_TYPE = 0x01F8; + public static final byte USER_AUTH_NONE = 0x00; + public static final byte PASSWORD = 0x01; + public static final byte FINGERPRINT = 0x02; + public static final byte BOTH = 0x03; + // have to be power of 2 + public static final byte ANY = (byte) 0xFF; + + // Origin Enum Tag key and values. + public static final short ORIGIN = 0x02BE; + public static final byte GENERATED = 0x00; + public static final byte DERIVED = 0x01; + public static final byte IMPORTED = 0x02; + public static final byte UNKNOWN = 0x03; + public static final byte SECURELY_IMPORTED = 0x04; + + // Hardware Type tag key and values + public static final short HARDWARE_TYPE = 0x0130; + public static final byte SOFTWARE = 0x00; + public static final byte TRUSTED_ENVIRONMENT = 0x01; + public static final byte STRONGBOX = 0x02; + + // No Tag + // Derivation Function - No Tag defined + public static final short KEY_DERIVATION_FUNCTION = (short) 0xF001; + public static final byte DERIVATION_NONE = 0x00; + public static final byte RFC5869_SHA256 = 0x01; + public static final byte ISO18033_2_KDF1_SHA1 = 0x02; + public static final byte ISO18033_2_KDF1_SHA256 = 0x03; + public static final byte ISO18033_2_KDF2_SHA1 = 0x04; + public static final byte ISO18033_2_KDF2_SHA256 = 0x05; + + // KeyFormat - No Tag defined. + public static final short KEY_FORMAT = (short) 0xF002; + public static final byte X509 = 0x00; + public static final byte PKCS8 = 0x01; + public static final byte RAW = 0x03; + + // Verified Boot State + public static final short VERIFIED_BOOT_STATE = (short) 0xF003; + public static final byte VERIFIED_BOOT = 0x00; + public static final byte SELF_SIGNED_BOOT = 0x01; + public static final byte UNVERIFIED_BOOT = 0x02; + public static final byte FAILED_BOOT = 0x03; + + // Verified Boot Key + public static final short VERIFIED_BOOT_KEY = (short) 0xF004; + + // Verified Boot Hash + public static final short VERIFIED_BOOT_HASH = (short) 0xF005; + + // Device Locked + public static final short DEVICE_LOCKED = (short) 0xF006; + public static final byte DEVICE_LOCKED_TRUE = 0x01; + public static final byte DEVICE_LOCKED_FALSE = 0x00; + + // Enum Array Tag + // Purpose + public static final short PURPOSE = 0x0001; + public static final byte ENCRYPT = 0x00; + public static final byte DECRYPT = 0x01; + public static final byte SIGN = 0x02; + public static final byte VERIFY = 0x03; + public static final byte DERIVE_KEY = 0x04; + public static final byte WRAP_KEY = 0x05; + public static final byte AGREE_KEY = 0x06; + public static final byte ATTEST_KEY = (byte) 0x07; + // Block mode + public static final short BLOCK_MODE = 0x0004; + public static final byte ECB = 0x01; + public static final byte CBC = 0x02; + public static final byte CTR = 0x03; + public static final byte GCM = 0x20; + + // Digest + public static final short DIGEST = 0x0005; + public static final byte DIGEST_NONE = 0x00; + public static final byte MD5 = 0x01; + public static final byte SHA1 = 0x02; + public static final byte SHA2_224 = 0x03; + public static final byte SHA2_256 = 0x04; + public static final byte SHA2_384 = 0x05; + public static final byte SHA2_512 = 0x06; + + // Padding mode + public static final short PADDING = 0x0006; + public static final byte PADDING_NONE = 0x01; + public static final byte RSA_OAEP = 0x02; + public static final byte RSA_PSS = 0x03; + public static final byte RSA_PKCS1_1_5_ENCRYPT = 0x04; + public static final byte RSA_PKCS1_1_5_SIGN = 0x05; + public static final byte PKCS7 = 0x40; + + // OAEP MGF Digests - only SHA-1 is supported in Javacard + public static final short RSA_OAEP_MGF_DIGEST = 0xCB; + + // Integer Tag - UINT, ULONG and DATE + // UINT tags + // Keysize + public static final short KEYSIZE = 0x0003; + // Min Mac Length + public static final short MIN_MAC_LENGTH = 0x0008; + // Min Seconds between OPS + public static final short MIN_SEC_BETWEEN_OPS = 0x0193; + // Max Uses per Boot + public static final short MAX_USES_PER_BOOT = 0x0194; + // UserId + public static final short USERID = 0x01F5; + // Auth Timeout + public static final short AUTH_TIMEOUT = 0x01F9; + // Auth Timeout in Milliseconds + public static final short AUTH_TIMEOUT_MILLIS = 0x7FFF; + // OS Version + public static final short OS_VERSION = 0x02C1; + // OS Patch Level + public static final short OS_PATCH_LEVEL = 0x02C2; + // Vendor Patch Level + public static final short VENDOR_PATCH_LEVEL = 0x02CE; + // Boot Patch Level + public static final short BOOT_PATCH_LEVEL = 0x02CF; + // Mac Length + public static final short MAC_LENGTH = 0x03EB; + // Usage Count Limit + public static final short USAGE_COUNT_LIMIT = 0x195; + + // ULONG tags + // RSA Public Exponent + public static final short RSA_PUBLIC_EXPONENT = 0x00C8; + + // DATE tags + public static final short ACTIVE_DATETIME = 0x0190; + public static final short ORIGINATION_EXPIRE_DATETIME = 0x0191; + public static final short USAGE_EXPIRE_DATETIME = 0x0192; + public static final short CREATION_DATETIME = 0x02BD; + ; + public static final short CERTIFICATE_NOT_BEFORE = 0x03F0; + public static final short CERTIFICATE_NOT_AFTER = 0x03F1; + // Integer Array Tags - ULONG_REP and UINT_REP. + // User Secure Id + public static final short USER_SECURE_ID = (short) 0x01F6; + + // Boolean Tag + // Caller Nonce + public static final short CALLER_NONCE = (short) 0x0007; + // Include Unique Id + public static final short INCLUDE_UNIQUE_ID = (short) 0x00CA; + // Bootloader Only + public static final short BOOTLOADER_ONLY = (short) 0x012E; + // Rollback Resistance + public static final short ROLLBACK_RESISTANCE = (short) 0x012F; + // No Auth Required + public static final short NO_AUTH_REQUIRED = (short) 0x01F7; + // Allow While On Body + public static final short ALLOW_WHILE_ON_BODY = (short) 0x01FA; + // Trusted User Presence Required + public static final short TRUSTED_USER_PRESENCE_REQUIRED = (short) 0x01FB; + // Trusted Confirmation Required + public static final short TRUSTED_CONFIRMATION_REQUIRED = (short) 0x01FC; + // Unlocked Device Required + public static final short UNLOCKED_DEVICE_REQUIRED = (short) 0x01FD; + // Reset Since Id Rotation + public static final short RESET_SINCE_ID_ROTATION = (short) 0x03EC; + //Early boot ended. + public static final short EARLY_BOOT_ONLY = (short) 0x0131; + //Device unique attestation. + public static final short DEVICE_UNIQUE_ATTESTATION = (short) 0x02D0; + + // Byte Tag + // Application Id + public static final short APPLICATION_ID = (short) 0x0259; + // Application Data + public static final short APPLICATION_DATA = (short) 0x02BC; + // Root Of Trust + public static final short ROOT_OF_TRUST = (short) 0x02C0; + // Unique Id + public static final short UNIQUE_ID = (short) 0x02C3; + // Attestation Challenge + public static final short ATTESTATION_CHALLENGE = (short) 0x02C4; + // Attestation Application Id + public static final short ATTESTATION_APPLICATION_ID = (short) 0x02C5; + // Attestation Id Brand + public static final short ATTESTATION_ID_BRAND = (short) 0x02C6; + // Attestation Id Device + public static final short ATTESTATION_ID_DEVICE = (short) 0x02C7; + // Attestation Id Product + public static final short ATTESTATION_ID_PRODUCT = (short) 0x02C8; + // Attestation Id Serial + public static final short ATTESTATION_ID_SERIAL = (short) 0x02C9; + // Attestation Id IMEI + public static final short ATTESTATION_ID_IMEI = (short) 0x02CA; + // Attestation Id MEID + public static final short ATTESTATION_ID_MEID = (short) 0x02CB; + // Attestation Id Manufacturer + public static final short ATTESTATION_ID_MANUFACTURER = (short) 0x02CC; + // Attestation Id Model + public static final short ATTESTATION_ID_MODEL = (short) 0x02CD; + // Associated Data + public static final short ASSOCIATED_DATA = (short) 0x03E8; + // Nonce + public static final short NONCE = (short) 0x03E9; + // Confirmation Token + public static final short CONFIRMATION_TOKEN = (short) 0x03ED; + // Serial Number - this is a big num but in applet we handle it as byte blob + public static final short CERTIFICATE_SERIAL_NUM = (short) 0x03EE; + // Subject Name + public static final short CERTIFICATE_SUBJECT_NAME = (short) 0x03EF; + + public static final short LENGTH_FROM_PDU = (short) 0xFFFF; + + public static final byte NO_VALUE = (byte) 0xff; + // Support Curves for Eek Chain validation. + public static final byte RKP_CURVE_P256 = 1; + // Type offsets. + public static final byte KM_TYPE_BASE_OFFSET = 0; + public static final byte KM_ARRAY_OFFSET = KM_TYPE_BASE_OFFSET; + public static final byte KM_BOOL_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 1; + public static final byte KM_BYTE_BLOB_OFFSET = KM_TYPE_BASE_OFFSET + 2; + public static final byte KM_BYTE_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 3; + public static final byte KM_ENUM_OFFSET = KM_TYPE_BASE_OFFSET + 4; + public static final byte KM_ENUM_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 5; + public static final byte KM_ENUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 6; + public static final byte KM_HARDWARE_AUTH_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 7; + public static final byte KM_HMAC_SHARING_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 8; + public static final byte KM_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 9; + public static final byte KM_INTEGER_ARRAY_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 10; + public static final byte KM_INTEGER_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 11; + public static final byte KM_KEY_CHARACTERISTICS_OFFSET = KM_TYPE_BASE_OFFSET + 12; + public static final byte KM_KEY_PARAMETERS_OFFSET = KM_TYPE_BASE_OFFSET + 13; + public static final byte KM_VERIFICATION_TOKEN_OFFSET = KM_TYPE_BASE_OFFSET + 14; + public static final byte KM_NEG_INTEGER_OFFSET = KM_TYPE_BASE_OFFSET + 15; + public static final byte KM_TEXT_STRING_OFFSET = KM_TYPE_BASE_OFFSET + 16; + public static final byte KM_MAP_OFFSET = KM_TYPE_BASE_OFFSET + 17; + public static final byte KM_COSE_KEY_OFFSET = KM_TYPE_BASE_OFFSET + 18; + public static final byte KM_COSE_KEY_INT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 19; + public static final byte KM_COSE_KEY_NINT_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 20; + public static final byte KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 21; + public static final byte KM_COSE_KEY_COSE_KEY_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 22; + public static final byte KM_COSE_KEY_SIMPLE_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 23; + public static final byte KM_SIMPLE_VALUE_OFFSET = KM_TYPE_BASE_OFFSET + 24; + public static final byte KM_COSE_HEADERS_OFFSET = KM_TYPE_BASE_OFFSET + 25; + public static final byte KM_COSE_KEY_TXT_STR_VAL_OFFSET = KM_TYPE_BASE_OFFSET + 26; + public static final byte KM_COSE_CERT_PAYLOAD_OFFSET = KM_TYPE_BASE_OFFSET + 27; + public static final byte KM_BIGNUM_TAG_OFFSET = KM_TYPE_BASE_OFFSET + 28; + + // Attestation types + public static final byte NO_CERT = 0; + public static final byte ATTESTATION_CERT = 1; + public static final byte SELF_SIGNED_CERT = 2; + public static final byte FAKE_CERT = 3; + public static final byte FACTORY_PROVISIONED_ATTEST_CERT = 4; + // Buffering Mode + public static final byte BUF_NONE = 0; + public static final byte BUF_RSA_NO_DIGEST = 1; + public static final byte BUF_EC_NO_DIGEST = 2; + public static final byte BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGN = 3; + public static final byte BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGN = 4; + public static final byte BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGN = 5; + public static final byte BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGN = 6; + public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7; + + protected static KMRepository repository; + protected static byte[] heap; + // Instance table + public static final byte INSTANCE_TABLE_SIZE = 29; + protected static short[] instanceTable; + + public static void initialize() { + instanceTable = JCSystem.makeTransientShortArray(INSTANCE_TABLE_SIZE, JCSystem.CLEAR_ON_RESET); + KMType.repository = KMRepository.instance(); + KMType.heap = repository.getHeap(); + } + + public static byte getKMType(short ptr) { + return heap[ptr]; + } + + public static short getKMTypeLength(short ptr) { + return Util.getShort(heap, (short) (ptr + 1)); + } + + public static short getKMTypeValue(short ptr) { + return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); + } + + protected static short instance(byte type, short length) { + if (length < 0) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + short ptr = repository.alloc((short) (length + TLV_HEADER_SIZE)); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), length); + return ptr; + } + + protected static short exp(byte type) { + short ptr = repository.alloc(TLV_HEADER_SIZE); + heap[ptr] = type; + Util.setShort(heap, (short) (ptr + 1), INVALID_VALUE); + return ptr; + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/src/com/android/javacard/kmdevice/KMUpgradable.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMUpgradable.java rename to Applet/src/com/android/javacard/kmdevice/KMUpgradable.java index 87204a06..d19fc3be 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/src/com/android/javacard/kmdevice/KMUpgradable.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import org.globalplatform.upgrade.Element; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/src/com/android/javacard/kmdevice/KMUtils.java similarity index 80% rename from Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java rename to Applet/src/com/android/javacard/kmdevice/KMUtils.java index e41663ec..62677a35 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/src/com/android/javacard/kmdevice/KMUtils.java @@ -13,49 +13,71 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.Util; public class KMUtils { // 64 bit unsigned calculations for time - public static final byte[] oneSecMsec = { - 0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec - public static final byte[] oneMinMsec = { - 0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec - public static final byte[] oneHourMsec = { - 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80}; // 3600000 msec - public static final byte[] oneDayMsec = { - 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec - public static final byte[] oneMonthMsec = { - 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec - public static final byte[] leapYearMsec = { - 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; - public static final byte[] yearMsec = { - 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 + public static byte[] oneSecMsec; // 1000 msec + public static byte[] oneMinMsec; // 60000 msec + public static byte[] oneHourMsec; // 3600000 msec + public static byte[] oneDayMsec; // 86400000 msec + public static byte[] oneMonthMsec; // 2629746000 msec + public static byte[] leapYearMsec; //31622400000; + public static byte[] yearMsec; //31536000000 //Leap year(366) + 3 * 365 - public static final byte[] fourYrsMsec = { - 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 - public static final byte[] firstJan2020 = { - 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00}; // 1577836800000 msec - public static final byte[] firstJan2051 = { - 0, 0, 0x02, 0x53, 0x26, (byte) 0x0E, (byte) 0x1C, 0x00}; // 2556144000000 + public static byte[] fourYrsMsec;//126230400000 + public static byte[] firstJan2020; // 1577836800000 msec + public static byte[] firstJan2051; // 2556144000000 // msec - public static final byte[] febMonthLeapMSec = { - 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00}; //2505600000 - public static final byte[] febMonthMsec = { - 0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00}; //2419200000 - public static final byte[] ThirtyOneDaysMonthMsec = { - 0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00};//2678400000 - public static final byte[] ThirtDaysMonthMsec = { - 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00};//2592000000 + public static byte[] febMonthLeapMSec; //2505600000 + public static byte[] febMonthMsec; //2419200000 + public static byte[] ThirtyOneDaysMonthMsec;//2678400000 + public static byte[] ThirtDaysMonthMsec;//2592000000 public static final short year2051 = 2051; public static final short year2020 = 2020; // Convert to milliseconds constants - public static final byte[] SEC_TO_MILLIS_SHIFT_POS = {9, 8, 7, 6, 5, 3}; + public static byte[] SEC_TO_MILLIS_SHIFT_POS; // -------------------------------------- + public static void initStatics() { + oneSecMsec = new byte[]{ + 0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec + oneMinMsec = new byte[]{ + 0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec + oneHourMsec = new byte[]{ + 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80}; // 3600000 msec + oneDayMsec = new byte[]{ + 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec + oneMonthMsec = new byte[]{ + 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec + leapYearMsec = new byte[]{ + 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; + yearMsec = new byte[]{ + 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 + //Leap year(366) + 3 * 365 + fourYrsMsec = new byte[]{ + 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 + firstJan2020 = new byte[]{ + 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00}; // 1577836800000 msec + firstJan2051 = new byte[]{ + 0, 0, 0x02, 0x53, 0x26, (byte) 0x0E, (byte) 0x1C, 0x00}; // 2556144000000 + // msec + febMonthLeapMSec = new byte[]{ + 0, 0, 0, 0, (byte) 0x95, 0x58, 0x6C, 0x00}; //2505600000 + febMonthMsec = new byte[]{ + 0, 0, 0, 0, (byte) 0x90, 0x32, 0x10, 0x00}; //2419200000 + ThirtyOneDaysMonthMsec = new byte[]{ + 0, 0, 0, 0, (byte) 0x9F, (byte) 0xA5, 0x24, 0x00};//2678400000 + ThirtDaysMonthMsec = new byte[]{ + 0, 0, 0, 0, (byte) 0x9A, 0x7E, (byte) 0xC8, 0x00};//2592000000 + // Convert to milliseconds constants + SEC_TO_MILLIS_SHIFT_POS = new byte[]{9, 8, 7, 6, 5, 3}; + + } + public static short convertToDate(short time, byte[] scratchPad, boolean utcFlag) { @@ -68,10 +90,9 @@ public static short convertToDate(short time, byte[] scratchPad, byte Z = 0x5A; boolean from2020 = true; Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); - Util.arrayCopyNonAtomic(KMInteger.cast(time).getBuffer(), - KMInteger.cast(time).getStartOff(), scratchPad, - (short) (8 - KMInteger.cast(time).length()), KMInteger.cast(time) - .length()); + Util.arrayCopyNonAtomic(KMInteger.getBuffer(time), + KMInteger.getStartOff(time), scratchPad, + (short) (8 - KMInteger.length(time)), KMInteger.length(time)); // If the time is less then 1 Jan 2020 then it is an error if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, firstJan2020, (short) 0, (short) 8) < 0) { @@ -87,14 +108,14 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) < 0) { Util.arrayCopyNonAtomic(firstJan2020, (short) 0, scratchPad, (short) 8, (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } else { from2020 = false; Util.arrayCopyNonAtomic(firstJan2051, (short) 0, scratchPad, (short) 8, (short) 8); - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } @@ -103,10 +124,8 @@ public static short convertToDate(short time, byte[] scratchPad, (short) 8) >= 0) { Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, (short) 8); - yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); // quotient - // is - // multiple - // of 4 + // quotient is multiple of 4 + yrsCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); yrsCount = (short) (yrsCount * 4); // number of yrs. // copy reminder as new dividend Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, @@ -135,7 +154,7 @@ public static short convertToDate(short time, byte[] scratchPad, Util.arrayCopyNonAtomic(yearMsec, (short) 0, scratchPad, (short) 8, (short) 8); } - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); if (((short) (i + 1) == leapYrIdx)) { @@ -185,7 +204,7 @@ public static short convertToDate(short time, byte[] scratchPad, if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, scratchPad, (short) 8, (short) 8) >= 0) { - subtract(scratchPad, (short) 0, (short) 8, (short) 16); + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, (short) 8); } else { @@ -287,7 +306,7 @@ public static short divide(byte[] buf, short dividend, short divisor, // Copy remainder in the dividend and repeat. while (expCnt != 0) { if (compare(buf, dividend, divisor) >= 0) { - subtract(buf, dividend, divisor, remainder); + subtract(buf, dividend, divisor, remainder, (byte) 8); copy(buf, remainder, dividend); q = (short) (q + expCnt); } @@ -353,9 +372,7 @@ public static void add(byte[] buf, short op1, short op2, short result) { byte carry = 0; short tmp; while (index >= 0) { - tmp = - (short) ((buf[(short) (op1 + index)] & 0xFF) + - (buf[(short) (op2 + index)] & 0xFF) + carry); + tmp = (short) (buf[(short) (op1 + index)] + buf[(short) (op2 + index)] + carry); carry = 0; if (tmp > 255) { carry = 1; // max unsigned byte value is 255 @@ -366,9 +383,9 @@ public static void add(byte[] buf, short op1, short op2, short result) { } // subtraction by borrowing. - public static void subtract(byte[] buf, short op1, short op2, short result) { + public static void subtract(byte[] buf, short op1, short op2, short result, byte sizeBytes) { byte borrow = 0; - byte index = 7; + byte index = (byte) (sizeBytes - 1); short r; short x; short y; @@ -421,6 +438,15 @@ public static short getLeapYrIndex(boolean from2020, short yrsCount) { return -1; } + public static void computeOnesCompliment(byte[] buf, short offset, short len) { + short index = offset; + // Compute 1s compliment + while (index < (short) (len + offset)) { + buf[index] = (byte) ~buf[index]; + index++; + } + } + // i * 1000 = (i << 9) + (i << 8) + (i << 7) + (i << 6) + (i << 5) + ( i << 3) public static void convertToMilliseconds(byte[] buf, short inputOff, short outputOff, short scratchPadOff) { @@ -436,5 +462,4 @@ public static void convertToMilliseconds(byte[] buf, short inputOff, short outpu index++; } } - } diff --git a/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java b/Applet/src/com/android/javacard/kmdevice/KMVerificationToken.java similarity index 50% rename from Applet/src/com/android/javacard/keymaster/KMVerificationToken.java rename to Applet/src/com/android/javacard/kmdevice/KMVerificationToken.java index 1be88ded..f553d5ce 100644 --- a/Applet/src/com/android/javacard/keymaster/KMVerificationToken.java +++ b/Applet/src/com/android/javacard/kmdevice/KMVerificationToken.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.kmdevice; import javacard.framework.ISO7816; import javacard.framework.ISOException; @@ -33,22 +33,30 @@ public class KMVerificationToken extends KMType { public static final byte TIMESTAMP = 0x01; public static final byte PARAMETERS_VERIFIED = 0x02; public static final byte SECURITY_LEVEL = 0x03; - public static final byte MAC = 0x04; + public static final byte MAC1 = 0x02; + public static final byte MAC2 = 0x04; private static KMVerificationToken prototype; private KMVerificationToken() { } - public static short exp() { + public static short timeStampTokenExp() { + short arrPtr = KMArray.instance((short) 3); + KMArray.add(arrPtr, CHALLENGE, KMInteger.exp()); + KMArray.add(arrPtr, TIMESTAMP, KMInteger.exp()); + KMArray.add(arrPtr, MAC1, KMByteBlob.exp()); + return instance(arrPtr); + } + + public static short verificationTokenExp() { short arrPtr = KMArray.instance((short) 5); - KMArray arr = KMArray.cast(arrPtr); - arr.add(CHALLENGE, KMInteger.exp()); - arr.add(TIMESTAMP, KMInteger.exp()); + KMArray.add(arrPtr, CHALLENGE, KMInteger.exp()); + KMArray.add(arrPtr, TIMESTAMP, KMInteger.exp()); //arr.add(PARAMETERS_VERIFIED, KMKeyParameters.exp()); - arr.add(PARAMETERS_VERIFIED, KMByteBlob.exp()); - arr.add(SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE)); - arr.add(MAC, KMByteBlob.exp()); + KMArray.add(arrPtr, PARAMETERS_VERIFIED, KMByteBlob.exp()); + KMArray.add(arrPtr, SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE)); + KMArray.add(arrPtr, MAC2, KMByteBlob.exp()); return instance(arrPtr); } @@ -56,24 +64,30 @@ private static KMVerificationToken proto(short ptr) { if (prototype == null) { prototype = new KMVerificationToken(); } - instanceTable[KM_VERIFICATION_TOKEN_OFFSET] = ptr; + KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] = ptr; return prototype; } - public static short instance() { + public static short instance1() { + short arrPtr = KMArray.instance((short) 3); + KMArray.add(arrPtr, CHALLENGE, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, TIMESTAMP, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, MAC1, KMByteBlob.instance((short) 0)); + return instance(arrPtr); + } + + public static short instance2() { short arrPtr = KMArray.instance((short) 5); - KMArray arr = KMArray.cast(arrPtr); - arr.add(CHALLENGE, KMInteger.uint_16((short) 0)); - arr.add(TIMESTAMP, KMInteger.uint_16((short) 0)); - arr.add(PARAMETERS_VERIFIED, KMByteBlob.instance((short) 0)); - arr.add(SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); - arr.add(MAC, KMByteBlob.instance((short) 0)); + KMArray.add(arrPtr, CHALLENGE, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, TIMESTAMP, KMInteger.uint_16((short) 0)); + KMArray.add(arrPtr, PARAMETERS_VERIFIED, KMByteBlob.instance((short) 0)); + KMArray.add(arrPtr, SECURITY_LEVEL, KMEnum.instance(KMType.HARDWARE_TYPE, KMType.STRONGBOX)); + KMArray.add(arrPtr, MAC2, KMByteBlob.instance((short) 0)); return instance(arrPtr); } public static short instance(short vals) { - KMArray arr = KMArray.cast(vals); - if (arr.length() != 5) { + if (KMArray.length(vals) != 3 && KMArray.length(vals) != 5) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } short ptr = KMType.instance(VERIFICATION_TOKEN_TYPE, (short) 2); @@ -81,7 +95,7 @@ public static short instance(short vals) { return ptr; } - public static KMVerificationToken cast(short ptr) { + private static KMVerificationToken cast(short ptr) { if (heap[ptr] != VERIFICATION_TOKEN_TYPE) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -93,68 +107,69 @@ public static KMVerificationToken cast(short ptr) { } public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_VERIFICATION_TOKEN_OFFSET] + TLV_HEADER_SIZE)); + return Util.getShort(heap, + (short) (KMType.instanceTable[KM_VERIFICATION_TOKEN_OFFSET] + TLV_HEADER_SIZE)); } public short length() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); + return KMArray.length(arrPtr); } public short getChallenge() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(CHALLENGE); + return KMArray.get(arrPtr, CHALLENGE); } public void setChallenge(short vals) { - KMInteger.cast(vals); + KMInteger.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(CHALLENGE, vals); + KMArray.add(arrPtr, CHALLENGE, vals); } public short getTimestamp() { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(TIMESTAMP); + return KMArray.get(arrPtr, TIMESTAMP); } public void setTimestamp(short vals) { - KMInteger.cast(vals); + KMInteger.validate(vals); short arrPtr = getVals(); - KMArray.cast(arrPtr).add(TIMESTAMP, vals); + KMArray.add(arrPtr, TIMESTAMP, vals); } - public short getMac() { + public short getMac(short macIndex) { short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(MAC); + return KMArray.get(arrPtr, macIndex); } - public void setMac(short vals) { - KMByteBlob.cast(vals); - short arrPtr = getVals(); - KMArray.cast(arrPtr).add(MAC, vals); + + public static short getVals(short bPtr) { + return KMVerificationToken.cast(bPtr).getVals(); } - public short getParametersVerified() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(PARAMETERS_VERIFIED); + public static short length(short bPtr) { + return KMVerificationToken.cast(bPtr).length(); } - public void setParametersVerified(short vals) { - // KMKeyParameters.cast(vals); - KMByteBlob.cast(vals); - short arrPtr = getVals(); - KMArray.cast(arrPtr).add(PARAMETERS_VERIFIED, vals); + public static short getChallenge(short bPtr) { + return KMVerificationToken.cast(bPtr).getChallenge(); } - public short getSecurityLevel() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).get(SECURITY_LEVEL); + public static void setChallenge(short bPtr, short vals) { + KMVerificationToken.cast(bPtr).setChallenge(vals); } - public void setSecurityLevel(short vals) { - KMEnum.cast(vals); - short arrPtr = getVals(); - KMArray.cast(arrPtr).add(SECURITY_LEVEL, vals); + public static short getTimestamp(short bPtr) { + return KMVerificationToken.cast(bPtr).getTimestamp(); + } + + public static void setTimestamp(short bPtr, short vals) { + KMVerificationToken.cast(bPtr).setTimestamp(vals); + } + + public static short getMac(short bPtr, short macIndex) { + return KMVerificationToken.cast(bPtr).getMac(macIndex); } } diff --git a/Applet/src/com/android/javacard/kmdevice/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/kmdevice/RemotelyProvisionedComponentDevice.java new file mode 100644 index 00000000..814cb8f0 --- /dev/null +++ b/Applet/src/com/android/javacard/kmdevice/RemotelyProvisionedComponentDevice.java @@ -0,0 +1,1450 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.kmdevice; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; + +/* + * This class handles the remote key provisioning. Generates an RKP key and generates a certificate signing + * request(CSR). The generation of CSR is divided amoung multiple functions to the save the memory inside + * the Applet. The set of functions to be called sequentially in the order to complete the process of + * generating the CSR are processBeginSendData, processUpdateKey, processUpdateEekChain, + * processUpdateChallenge, processFinishSendData and getResponse. ProcessUpdateKey is called N times, where + * N is the number of keys. Similarly getResponse is called is multiple times till the client receives the + * response completely. + */ +public class RemotelyProvisionedComponentDevice { + + private static final byte TRUE = 0x01; + private static final byte FALSE = 0x00; + // RKP Version + private static final short RKP_VERSION = (short) 0x01; + // Boot params + private static final byte OS_VERSION_ID = 0x00; + private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01; + private static final byte BOOT_PATCH_LEVEL_ID = 0x02; + private static final byte VENDOR_PATCH_LEVEL_ID = 0x03; + public static final short MAX_COSE_BUF_SIZE = (short) 1024; + // Device Info labels + public static byte[] BRAND; + public static byte[] MANUFACTURER; + public static byte[] PRODUCT; + public static byte[] MODEL; + public static byte[] BOARD; + public static byte[] VB_STATE; + public static byte[] BOOTLOADER_STATE; + public static byte[] VB_META_DIGEST; + public static byte[] OS_VERSION; + public static byte[] SYSTEM_PATCH_LEVEL; + public static byte[] BOOT_PATCH_LEVEL; + public static byte[] VENDOR_PATCH_LEVEL; + public static byte[] DEVICE_INFO_VERSION; + public static byte[] SECURITY_LEVEL; + public static byte[] ATTEST_ID_STATE; + // Verified boot state values + public static byte[] VB_STATE_GREEN; + public static byte[] VB_STATE_YELLOW; + public static byte[] VB_STATE_ORANGE; + public static byte[] VB_STATE_RED; + // Boot loader state values + public static byte[] UNLOCKED; + public static byte[] LOCKED; + // Device info CDDL schema version + public static final byte DI_SCHEMA_VERSION = 1; + public static byte[] DI_SECURITY_LEVEL; + public static byte[] ATTEST_ID_LOCKED; + public static byte[] ATTEST_ID_OPEN; + private static final short MAX_SEND_DATA = 1024; + // more data or no data + private static final byte MORE_DATA = 0x01; // flag to denote more data to retrieve + private static final byte NO_DATA = 0x00; + // Response processing states + private static final byte START_PROCESSING = 0x00; + private static final byte PROCESSING_BCC_IN_PROGRESS = 0x02; + private static final byte PROCESSING_BCC_COMPLETE = 0x04; + private static final byte PROCESSING_ACC_IN_PROGRESS = 0x08; // Additional certificate chain. + private static final byte PROCESSING_ACC_COMPLETE = 0x0A; + // data table + private static final short DATA_SIZE = 512; + private static final short DATA_INDEX_SIZE = 11; + public static final short DATA_INDEX_ENTRY_SIZE = 4; + public static final short DATA_INDEX_ENTRY_LENGTH = 0; + public static final short DATA_INDEX_ENTRY_OFFSET = 2; + // data offsets + private static final short EPHEMERAL_MAC_KEY = 0; + private static final short TOTAL_KEYS_TO_SIGN = 1; + private static final short KEYS_TO_SIGN_COUNT = 2; + private static final short TEST_MODE = 3; + private static final short EEK_KEY = 4; + private static final short EEK_KEY_ID = 5; + private static final short CHALLENGE = 6; + private static final short GENERATE_CSR_PHASE = 7; + private static final short EPHEMERAL_PUB_KEY = 8; + private static final short RESPONSE_PROCESSING_STATE = 9; + private static final short ACC_PROCESSED_LENGTH = 10; + + // data item sizes + private static final short MAC_KEY_SIZE = 32; + private static final short SHORT_SIZE = 2; + private static final short BYTE_SIZE = 1; + private static final short TEST_MODE_SIZE = 1; + // generate csr states + private static final byte BEGIN = 0x01; + private static final byte UPDATE = 0x02; + private static final byte FINISH = 0x04; + private static final byte GET_RESPONSE = 0x06; + // variables + private byte[] data; + private KMEncoder encoder; + private KMDecoder decoder; + private KMRepository repository; + private KMSEProvider seProvider; + private Object[] operation; + private short[] dataIndex; + private Object[] authorizedEekRoots; + private KMKeymintDevice KMAppletInst; + private KMDataStore storeDataInst; + private KMRkpDataStore rkpStoreDataInst; + private KMBootDataStore bootParamsProv; + private KMCose kmCoseInst; + private short[] deviceIds; + + public RemotelyProvisionedComponentDevice(KMKeymintDevice KMApplet, KMEncoder encoder, + KMDecoder decoder, + KMRepository repository, KMSEProvider seProvider, KMDataStore storeData, + KMRkpDataStore rkpStore, + KMBootDataStore bootParamsProvider) { + initStatics(); + this.encoder = encoder; + this.decoder = decoder; + this.repository = repository; + this.seProvider = seProvider; + this.KMAppletInst = KMApplet; + storeDataInst = storeData; + rkpStoreDataInst = rkpStore; + bootParamsProv = bootParamsProvider; + deviceIds = JCSystem.makeTransientShortArray((short) 30, 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); + operation[0] = null; + createAuthorizedEEKRoot(); + kmCoseInst = KMCose.getInstance(); + } + + public static void initStatics() { + // Device Info labels + BRAND = new byte[]{0x62, 0x72, 0x61, 0x6E, 0x64}; + MANUFACTURER = new byte[]{0x6D, 0x61, 0x6E, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, + 0x72, 0x65, 0x72}; + PRODUCT = new byte[]{0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74}; + MODEL = new byte[]{0x6D, 0x6F, 0x64, 0x65, 0x6C}; + BOARD = new byte[]{0x62, 0x6F, 0x61, 0x72, 0x64}; + VB_STATE = new byte[]{0x76, 0x62, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65}; + BOOTLOADER_STATE = new byte[] + {0x62, 0x6F, 0x6F, 0x74, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x61, 0x74, + 0x65}; + VB_META_DIGEST = new byte[] + {0X76, 0X62, 0X6D, 0X65, 0X74, 0X61, 0X5F, 0X64, 0X69, 0X67, 0X65, 0X73, 0X74}; + OS_VERSION = new byte[]{0x6F, 0x73, 0x5F, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, + 0x6E}; + SYSTEM_PATCH_LEVEL = new byte[] + {0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, + 0x76, 0x65, 0x6C}; + BOOT_PATCH_LEVEL = new byte[] + {0x62, 0x6F, 0x6F, 0x74, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 0x65, + 0x6C}; + VENDOR_PATCH_LEVEL = new byte[] + {0x76, 0x65, 0x6E, 0x64, 0x6F, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, + 0x76, 0x65, 0x6C}; + DEVICE_INFO_VERSION = new byte[] + {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E}; + SECURITY_LEVEL = new byte[] + {0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C}; + ATTEST_ID_STATE = new byte[] + {0x61, 0x74, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65}; + // Verified boot state values + VB_STATE_GREEN = new byte[]{0x67, 0x72, 0x65, 0x65, 0x6E}; + VB_STATE_YELLOW = new byte[]{0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77}; + VB_STATE_ORANGE = new byte[]{0x6F, 0x72, 0x61, 0x6E, 0x67, 0x65}; + VB_STATE_RED = new byte[]{0x72, 0x65, 0x64}; + // Boot loader state values + UNLOCKED = new byte[]{0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; + LOCKED = new byte[]{0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; + + DI_SECURITY_LEVEL = new byte[]{0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, + 0x78}; + ATTEST_ID_LOCKED = new byte[]{0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64}; + ATTEST_ID_OPEN = new byte[]{0x6f, 0x70, 0x65, 0x6e}; + } + + private void createAuthorizedEEKRoot() { + if (authorizedEekRoots == null) { + authorizedEekRoots = + new Object[] + { + new byte[]{ + 0x04, + (byte) 0xf7, (byte) 0x14, (byte) 0x8a, (byte) 0xdb, (byte) 0x97, (byte) 0xf4, + (byte) 0xcc, (byte) 0x53, (byte) 0xef, (byte) 0xd2, (byte) 0x64, (byte) 0x11, + (byte) 0xc4, (byte) 0xe3, (byte) 0x75, (byte) 0x1f, (byte) 0x66, (byte) 0x1f, + (byte) 0xa4, (byte) 0x71, (byte) 0x0c, (byte) 0x6c, (byte) 0xcf, (byte) 0xfa, + (byte) 0x09, (byte) 0x46, (byte) 0x80, (byte) 0x74, (byte) 0x87, (byte) 0x54, + (byte) 0xf2, (byte) 0xad, + (byte) 0x5e, (byte) 0x7f, (byte) 0x5b, (byte) 0xf6, (byte) 0xec, (byte) 0xe4, + (byte) 0xf6, (byte) 0x19, (byte) 0xcc, (byte) 0xff, (byte) 0x13, (byte) 0x37, + (byte) 0xfd, (byte) 0x0f, (byte) 0xa1, (byte) 0xc8, (byte) 0x93, (byte) 0xdb, + (byte) 0x18, (byte) 0x06, (byte) 0x76, (byte) 0xc4, (byte) 0x5d, (byte) 0xe6, + (byte) 0xd7, (byte) 0x6a, (byte) 0x77, (byte) 0x86, (byte) 0xc3, (byte) 0x2d, + (byte) 0xaf, (byte) 0x8f + }, + }; + } + } + + private void initializeDataTable() { + if (dataIndex[0] != 0) { + KMException.throwIt(KMError.INVALID_STATE); + } + dataIndex[0] = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); + } + + private short dataAlloc(short length) { + if ((short) (dataIndex[0] + length) > (short) data.length) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + dataIndex[0] += length; + return (short) (dataIndex[0] - length); + } + + private void clearDataTable() { + Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0x00); + dataIndex[0] = 0x00; + } + + private void releaseOperation() { + if (operation[0] != null) { + ((KMOperation) operation[0]).abort(); + operation[0] = null; + } + } + + private short createEntry(short index, short length) { + index = (short) (index * DATA_INDEX_ENTRY_SIZE); + short ptr = dataAlloc(length); + Util.setShort(data, index, length); + Util.setShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET), ptr); + return ptr; + } + + private short getEntry(short index) { + index = (short) (index * DATA_INDEX_ENTRY_SIZE); + return Util.getShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET)); + } + + private short getEntryLength(short index) { + index = (short) (index * DATA_INDEX_ENTRY_SIZE); + return Util.getShort(data, index); + } + + private void processGetRkpHwInfoCmd(APDU apdu) { + // Make the response + // Author name - Google. + final byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; + short respPtr = KMArray.instance((short) 4); + KMArray.add(respPtr, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(respPtr, (short) 1, KMInteger.uint_16(RKP_VERSION)); + KMArray.add(respPtr, (short) 2, KMByteBlob.instance(google, (short) 0, (short) google.length)); + KMArray.add(respPtr, (short) 3, KMInteger.uint_8(KMType.RKP_CURVE_P256)); + KMAppletInst.sendOutgoing(apdu, respPtr); + } + + /** + * This function generates an EC key pair with attest key as purpose and creates an encrypted key + * blob. It then generates a COSEMac message which includes the ECDSA public key. + */ + public void processGenerateRkpKey(APDU apdu) { + short arr = KMArray.instance((short) 1); + KMArray.add(arr, (short) 0, KMSimpleValue.exp()); + arr = KMAppletInst.receiveIncoming(apdu, arr); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // test mode flag. + boolean testMode = + (KMSimpleValue.TRUE == KMSimpleValue.getValue(KMArray.get(arr, (short) 0))); + KMAppletInst.generateRkpKey(scratchPad, getEcAttestKeyParameters()); + short pubKey = KMKeymasterDevice.getPubKey(); + short coseMac0 = constructCoseMacForRkpKey(testMode, scratchPad, pubKey); + // Encode the COSE_MAC0 object + arr = KMArray.instance((short) 3); + KMArray.add(arr, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(arr, (short) 1, coseMac0); + KMArray.add(arr, (short) 2, KMKeymasterDevice.getPivateKey()); + KMAppletInst.sendOutgoing(apdu, arr); + } + + public void processBeginSendData(APDU apdu) throws Exception { + try { + initializeDataTable(); + short arr = KMArray.instance((short) 3); + KMArray.add(arr, (short) 0, KMInteger.exp()); // Array length + KMArray.add(arr, (short) 1, KMInteger.exp()); // Total length of the encoded CoseKeys. + KMArray.add(arr, (short) 2, KMSimpleValue.exp()); + arr = KMAppletInst.receiveIncoming(apdu, arr); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Generate ephemeral mac key. + short dataEntryIndex = createEntry(EPHEMERAL_MAC_KEY, MAC_KEY_SIZE); + seProvider.newRandomNumber(data, dataEntryIndex, MAC_KEY_SIZE); + // Initialize hmac operation. + initHmacOperation(); + // Partially encode CoseMac structure with partial payload. + constructPartialPubKeysToSignMac(scratchPad, + KMInteger.getShort(KMArray.get(arr, (short) 0)), + KMInteger.getShort(KMArray.get(arr, (short) 1))); + // Store the total keys in data table. + dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE); + Util.setShort(data, dataEntryIndex, + KMInteger.getShort(KMArray.get(arr, (short) 0))); + // Store the test mode value in data table. + dataEntryIndex = createEntry(TEST_MODE, TEST_MODE_SIZE); + data[dataEntryIndex] = + (KMSimpleValue.TRUE == KMSimpleValue.getValue(KMArray.get(arr, (short) 2))) ? + TRUE : FALSE; + // Store the current csr status, which is BEGIN. + createEntry(GENERATE_CSR_PHASE, BYTE_SIZE); + updateState(BEGIN); + // Send response. + KMAppletInst.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processUpdateKey(APDU apdu) throws Exception { + try { + // The prior state can be BEGIN or UPDATE + validateState((byte) (BEGIN | UPDATE)); + validateKeysToSignCount(); + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.add(arrInst, (short) 0, KMByteBlob.exp()); + KMArray.add(arrInst, (short) 1, headers); + KMArray.add(arrInst, (short) 2, KMByteBlob.exp()); + KMArray.add(arrInst, (short) 3, KMByteBlob.exp()); + short arr = KMArray.exp(arrInst); + arr = KMAppletInst.receiveIncoming(apdu, arr); + arrInst = KMArray.get(arr, (short) 0); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + + // Validate and extract the CoseKey from CoseMac0 message. + short coseKey = validateAndExtractPublicKey(arrInst, scratchPad); + // Encode CoseKey + short length = KMAppletInst.encodeToApduBuffer(coseKey, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + // Do Hmac update with input as encoded CoseKey. + ((KMOperation) operation[0]).update(scratchPad, (short) 0, length); + // Increment the count each time this function gets executed. + // Store the count in data table. + short dataEntryIndex = getEntry(KEYS_TO_SIGN_COUNT); + if (dataEntryIndex == 0) { + dataEntryIndex = createEntry(KEYS_TO_SIGN_COUNT, SHORT_SIZE); + } + length = Util.getShort(data, dataEntryIndex); + Util.setShort(data, dataEntryIndex, ++length); + // Update the csr state + updateState(UPDATE); + // Send response. + KMAppletInst.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processUpdateEekChain(APDU apdu) throws Exception { + try { + // The prior state can be BEGIN or UPDATE + validateState((byte) (BEGIN | UPDATE)); + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.add(arrInst, (short) 0, KMByteBlob.exp()); + KMArray.add(arrInst, (short) 1, headers); + KMArray.add(arrInst, (short) 2, KMByteBlob.exp()); + KMArray.add(arrInst, (short) 3, KMByteBlob.exp()); + short arrSignPtr = KMArray.exp(arrInst); + arrInst = KMAppletInst.receiveIncoming(apdu, arrSignPtr); + if (KMArray.length(arrInst) == 0) { + KMException.throwIt(KMError.STATUS_INVALID_EEK); + } + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Validate eek chain. + short eekKey = validateAndExtractEekPub(arrInst, scratchPad); + // Store eek public key and eek id in the data table. + short eekKeyId = KMCoseKey.cast(eekKey).getKeyIdentifier(); + short dataEntryIndex = createEntry(EEK_KEY_ID, KMByteBlob.length(eekKeyId)); + Util.arrayCopyNonAtomic( + KMByteBlob.getBuffer(eekKeyId), + KMByteBlob.getStartOff(eekKeyId), + data, + dataEntryIndex, + KMByteBlob.length(eekKeyId) + ); + // Convert the coseKey to a public key. + short len = KMCoseKey.cast(eekKey).getEcdsa256PublicKey(scratchPad, (short) 0); + dataEntryIndex = createEntry(EEK_KEY, len); + Util.arrayCopyNonAtomic(scratchPad, (short) 0, data, dataEntryIndex, len); + // Update the state + updateState(UPDATE); + KMAppletInst.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processUpdateChallenge(APDU apdu) throws Exception { + try { + // The prior state can be BEGIN or UPDATE + validateState((byte) (BEGIN | UPDATE)); + short arr = KMArray.instance((short) 1); + KMArray.add(arr, (short) 0, KMByteBlob.exp()); + arr = KMAppletInst.receiveIncoming(apdu, arr); + // Store the challenge in the data table. + short challenge = KMArray.get(arr, (short) 0); + short dataEntryIndex = createEntry(CHALLENGE, KMByteBlob.length(challenge)); + Util.arrayCopyNonAtomic( + KMByteBlob.getBuffer(challenge), + KMByteBlob.getStartOff(challenge), + data, + dataEntryIndex, + KMByteBlob.length(challenge) + ); + // Update the state + updateState(UPDATE); + KMAppletInst.sendError(apdu, KMError.OK); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + // This function returns pubKeysToSignMac, deviceInfo and partially constructed protected data + // wrapped inside byte blob. The partial protected data contains Headers and encrypted signedMac. + public void processFinishSendData(APDU apdu) throws Exception { + try { + // The prior state should be UPDATE. + validateState(UPDATE); + byte[] scratchPad = apdu.getBuffer(); + if (data[getEntry(TOTAL_KEYS_TO_SIGN)] != data[getEntry(KEYS_TO_SIGN_COUNT)]) { + // Mismatch in the number of keys sent. + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + // PubKeysToSignMac + byte[] empty = {}; + short len = + ((KMOperation) operation[0]).sign(empty, (short) 0, + (short) 0, scratchPad, (short) 0); + // release operation + releaseOperation(); + short pubKeysToSignMac = KMByteBlob.instance(scratchPad, (short) 0, len); + // Create DeviceInfo + short deviceInfo = createDeviceInfo(scratchPad); + // Generate Nonce for AES-GCM + seProvider.newRandomNumber(scratchPad, (short) 0, + KMKeymasterDevice.AES_GCM_NONCE_LENGTH); + short nonce = KMByteBlob.instance(scratchPad, (short) 0, + KMKeymasterDevice.AES_GCM_NONCE_LENGTH); + // Initializes cipher instance. + initAesGcmOperation(scratchPad, nonce); + // Encode Enc_Structure as additional data for AES-GCM. + processAesGcmUpdateAad(scratchPad); + short partialPayloadLen = processSignedMac(scratchPad, pubKeysToSignMac, deviceInfo); + short partialCipherText = KMByteBlob.instance(scratchPad, (short) 0, partialPayloadLen); + short coseEncryptProtectedHeader = getCoseEncryptProtectedHeader(scratchPad); + short coseEncryptUnProtectedHeader = getCoseEncryptUnprotectedHeader(scratchPad, nonce); + len = KMAppletInst.encodeToApduBuffer(deviceInfo, scratchPad, + (short) 0, MAX_COSE_BUF_SIZE); + short encodedDeviceInfo = KMByteBlob.instance(scratchPad, (short) 0, len); + updateState(FINISH); + short arr = KMArray.instance((short) 7); + KMArray.add(arr, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(arr, (short) 1, pubKeysToSignMac); + KMArray.add(arr, (short) 2, encodedDeviceInfo); + KMArray.add(arr, (short) 3, coseEncryptProtectedHeader); + KMArray.add(arr, (short) 4, coseEncryptUnProtectedHeader); + KMArray.add(arr, (short) 5, partialCipherText); + KMArray.add(arr, (short) 6, KMInteger.uint_8(MORE_DATA)); + KMAppletInst.sendOutgoing(apdu, arr); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void processGetResponse(APDU apdu) throws Exception { + try { + // The prior state should be FINISH. + validateState((byte) (FINISH | GET_RESPONSE)); + byte[] scratchPad = apdu.getBuffer(); + short len = 0; + short recipientStructure = KMArray.instance((short) 0); + byte moreData = MORE_DATA; + byte state = getCurrentOutputProcessingState(); + switch (state) { + case START_PROCESSING: + case PROCESSING_BCC_IN_PROGRESS: + len = processBcc(scratchPad); + updateState(GET_RESPONSE); + break; + case PROCESSING_BCC_COMPLETE: + case PROCESSING_ACC_IN_PROGRESS: + len = processAdditionalCertificateChain(scratchPad); + updateState(GET_RESPONSE); + break; + case PROCESSING_ACC_COMPLETE: + recipientStructure = processRecipientStructure(scratchPad); + len = processFinalData(scratchPad); + moreData = NO_DATA; + releaseOperation(); + clearDataTable(); + break; + default: + KMException.throwIt(KMError.INVALID_STATE); + } + short data = KMByteBlob.instance(scratchPad, (short) 0, len); + short arr = KMArray.instance((short) 4); + KMArray.add(arr, (short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.add(arr, (short) 1, data); + KMArray.add(arr, (short) 2, recipientStructure); + // represents there is more output to retrieve + KMArray.add(arr, (short) 3, KMInteger.uint_8(moreData)); + KMAppletInst.sendOutgoing(apdu, arr); + } catch (Exception e) { + clearDataTable(); + releaseOperation(); + throw e; + } + } + + public void process(short ins, APDU apdu) throws Exception { + switch (ins) { + case KMKeymasterDevice.INS_GET_RKP_HARDWARE_INFO: + processGetRkpHwInfoCmd(apdu); + break; + case KMKeymasterDevice.INS_GENERATE_RKP_KEY_CMD: + processGenerateRkpKey(apdu); + break; + case KMKeymasterDevice.INS_BEGIN_SEND_DATA_CMD: + processBeginSendData(apdu); + break; + case KMKeymasterDevice.INS_UPDATE_KEY_CMD: + processUpdateKey(apdu); + break; + case KMKeymasterDevice.INS_UPDATE_EEK_CHAIN_CMD: + processUpdateEekChain(apdu); + break; + case KMKeymasterDevice.INS_UPDATE_CHALLENGE_CMD: + processUpdateChallenge(apdu); + break; + case KMKeymasterDevice.INS_FINISH_SEND_DATA_CMD: + processFinishSendData(apdu); + break; + case KMKeymasterDevice.INS_GET_RESPONSE_CMD: + processGetResponse(apdu); + break; + default: + ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); + } + } + + private boolean isAdditionalCertificateChainPresent() { + byte[] data = rkpStoreDataInst.getData(KMDataStoreConstants.ADDITIONAL_CERT_CHAIN); + return (Util.getShort(data, (short) 0) == 0 ? false : true); + } + + private short processFinalData(byte[] scratchPad) { + // Call finish on AES GCM Cipher + byte[] empty = {}; + short len = + ((KMOperation) operation[0]).finish(empty, (short) 0, (short) 0, scratchPad, (short) 0); + return len; + } + + private byte getCurrentOutputProcessingState() { + short index = getEntry(RESPONSE_PROCESSING_STATE); + if (index == 0) { + return START_PROCESSING; + } + return data[index]; + } + + private void updateOutputProcessingState(byte state) { + short dataEntryIndex = getEntry(RESPONSE_PROCESSING_STATE); + data[dataEntryIndex] = state; + } + + + private short getHmacKey(boolean testMode, byte[] scratchPad) { + short macKey = KMByteBlob.instance(MAC_KEY_SIZE); + Util.arrayFillNonAtomic(KMByteBlob.getBuffer(macKey), + KMByteBlob.getStartOff(macKey), MAC_KEY_SIZE, (byte) 0); + if (!testMode) { + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) (2 * MAC_KEY_SIZE), (byte) 0); + short len = + seProvider.hkdf( + scratchPad, //ikm + (short) 0, // ikm offset + MAC_KEY_SIZE, // ikm size + scratchPad, // salt + MAC_KEY_SIZE, // salt offset + MAC_KEY_SIZE, // salt length + KMCose.MAC_DERIVE_KEY_CTX, + (short) 0, + (short) KMCose.MAC_DERIVE_KEY_CTX.length, + KMByteBlob.getBuffer(macKey), + KMByteBlob.getStartOff(macKey), + MAC_KEY_SIZE + ); + if (len != MAC_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_MAC_LENGTH); + } + } + return macKey; + } + + /** + * Validates the CoseMac message and extracts the CoseKey from it. + * + * @param coseMacPtr CoseMac instance to be validated. + * @param scratchPad Scratch buffer used to store temp results. + * @return CoseKey instance. + */ + private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { + boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; + // Exp for KMCoseHeaders + short coseHeadersExp = KMCoseHeaders.exp(); + // Exp for coseky + short coseKeyExp = KMCoseKey.exp(); + // Get the mackey. + short macKey = getHmacKey(testMode, scratchPad); + + // validate protected Headers + short ptr = KMArray.get(coseMacPtr, KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET); + ptr = decoder.decode(coseHeadersExp, KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), KMByteBlob.length(ptr)); + + if (!KMCoseHeaders.cast(ptr).isDataValid(KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + + // Validate payload. + ptr = KMArray.get(coseMacPtr, KMCose.COSE_MAC0_PAYLOAD_OFFSET); + ptr = decoder.decode(coseKeyExp, KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), KMByteBlob.length(ptr)); + + if (!KMCoseKey.cast(ptr).isDataValid(KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, + KMCose.COSE_ALG_ES256, KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)) { + KMException.throwIt(KMError.STATUS_FAILED); + } + + boolean isTestKey = KMCoseKey.cast(ptr).isTestKey(); + if (isTestKey && !testMode) { + KMException.throwIt(KMError.STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); + } else if (!isTestKey && testMode) { + KMException.throwIt(KMError.STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); + } + + // Compute CoseMac Structure and compare the macs. + short macStructure = + kmCoseInst.constructCoseMacStructure(KMArray.get(coseMacPtr, + KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET), + KMByteBlob.instance((short) 0), + KMArray.get(coseMacPtr, KMCose.COSE_MAC0_PAYLOAD_OFFSET)); + short encodedLen = KMAppletInst.encodeToApduBuffer(macStructure, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + + short hmacLen = seProvider.hmacSign(KMByteBlob.getBuffer(macKey), + KMByteBlob.getStartOff(macKey), + (short) 32, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen); + + if (hmacLen != KMByteBlob.length(KMArray.get(coseMacPtr, KMCose.COSE_MAC0_TAG_OFFSET))) { + KMException.throwIt(KMError.STATUS_INVALID_MAC); + } + + if (0 != Util.arrayCompare(scratchPad, encodedLen, + KMByteBlob.getBuffer(KMArray.get(coseMacPtr, KMCose.COSE_MAC0_TAG_OFFSET)), + KMByteBlob.getStartOff(KMArray.get(coseMacPtr, KMCose.COSE_MAC0_TAG_OFFSET)), + hmacLen)) { + KMException.throwIt(KMError.STATUS_INVALID_MAC); + } + return ptr; + } + + + /** + * This function validates the EEK Chain and extracts the leaf public key, which is used to + * generate shared secret using ECDH. + * + * @param eekArr EEK cert chain array pointer. + * @param scratchPad Scratch buffer used to store temp results. + * @return CoseKey instance. + */ + private short validateAndExtractEekPub(short eekArr, byte[] scratchPad) { + short leafPubKey = 0; + try { + leafPubKey = KMAppletInst.validateCertChain( + (TRUE == data[getEntry(TEST_MODE)]) ? false : true, // validate EEK root + KMCose.COSE_ALG_ES256, + KMCose.COSE_ALG_ECDH_ES_HKDF_256, + eekArr, + scratchPad, + authorizedEekRoots + ); + } catch (KMException e) { + KMException.throwIt(KMError.STATUS_INVALID_EEK); + } + return leafPubKey; + } + + private void validateKeysToSignCount() { + short index = getEntry(KEYS_TO_SIGN_COUNT); + short keysToSignCount = 0; + if (index != 0) { + keysToSignCount = Util.getShort(data, index); + } + if (Util.getShort(data, getEntry(TOTAL_KEYS_TO_SIGN)) <= keysToSignCount) { + // Mismatch in the number of keys sent. + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + } + + private void validateState(byte expectedState) { + short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); + if (0 == (data[dataEntryIndex] & expectedState)) { + KMException.throwIt(KMError.INVALID_STATE); + } + } + + private void updateState(byte state) { + short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); + if (dataEntryIndex == 0) { + KMException.throwIt(KMError.INVALID_STATE); + } + data[dataEntryIndex] = state; + } + + + /** + * This function constructs a Mac Structure, encode it and signs the encoded buffer with the + * ephemeral mac key. + */ + private void constructPartialPubKeysToSignMac(byte[] scratchPad, short arrayLength, + short encodedCoseKeysLen) { + short ptr; + short len; + short headerPtr = kmCoseInst.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + len = KMAppletInst.encodeToApduBuffer(headerPtr, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); + // create MAC_Structure + ptr = + kmCoseInst.constructCoseMacStructure(protectedHeader, + KMByteBlob.instance((short) 0), KMType.INVALID_VALUE); + // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 + len = KMAppletInst.encodeToApduBuffer(ptr, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + // Construct partial payload - Bstr Header + Array Header + // The maximum combined length of bstr header and array header length is 6 bytes. + // The lengths will never exceed Max SHORT value. + short arrPtr = KMArray.instance(arrayLength); + for (short i = 0; i < arrayLength; i++) { + KMArray.add(arrPtr, i, KMType.INVALID_VALUE); + } + arrayLength = encoder.getEncodedLength(arrPtr); + short bufIndex = repository.alloc((short) 6); + short partialPayloadLen = + encoder.encodeByteBlobHeader((short) (arrayLength + encodedCoseKeysLen), + repository.getHeap(), + bufIndex, (short) 3); + + partialPayloadLen += + encoder.encode(arrPtr, repository.getHeap(), (short) (bufIndex + partialPayloadLen)); + Util.arrayCopyNonAtomic(repository.getHeap(), bufIndex, scratchPad, len, partialPayloadLen); + ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen)); + } + + private short createSignedMac(KMDeviceUniqueKey deviceUniqueKey, byte[] scratchPad, + short deviceMapPtr, short pubKeysToSign) { + // Challenge + short dataEntryIndex = getEntry(CHALLENGE); + short challengePtr = KMByteBlob.instance(data, dataEntryIndex, getEntryLength(CHALLENGE)); + // Ephemeral mac key + dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); + short ephmeralMacKey = + KMByteBlob.instance(data, dataEntryIndex, getEntryLength(EPHEMERAL_MAC_KEY)); + + /* Prepare AAD */ + short aad = KMArray.instance((short) 3); + KMArray.add(aad, (short) 0, challengePtr); + KMArray.add(aad, (short) 1, deviceMapPtr); + KMArray.add(aad, (short) 2, pubKeysToSign); + aad = KMAppletInst.encodeToApduBuffer(aad, scratchPad, + (short) 0, MAX_COSE_BUF_SIZE); + aad = KMByteBlob.instance(scratchPad, (short) 0, aad); + + /* construct protected header */ + short protectedHeaders = kmCoseInst.constructHeaders( + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + protectedHeaders = KMAppletInst.encodeToApduBuffer(protectedHeaders, scratchPad, + (short) 0, MAX_COSE_BUF_SIZE); + protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders); + + /* construct cose sign structure */ + short signStructure = + kmCoseInst.constructCoseSignStructure(protectedHeaders, aad, ephmeralMacKey); + signStructure = KMAppletInst.encodeToApduBuffer(signStructure, scratchPad, + (short) 0, MAX_COSE_BUF_SIZE); + short len = + seProvider.ecSign256( + deviceUniqueKey, + scratchPad, + (short) 0, + signStructure, + scratchPad, + signStructure + ); + signStructure = KMByteBlob.instance(scratchPad, signStructure, len); + + /* Construct unprotected headers */ + short unprotectedHeader = KMArray.instance((short) 0); + unprotectedHeader = KMCoseHeaders.instance(unprotectedHeader); + + /* construct Cose_Sign1 */ + return kmCoseInst.constructCoseSign1(protectedHeaders, unprotectedHeader, + ephmeralMacKey, signStructure); + } + + + private KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, byte[] scratchPad) { + KMDeviceUniqueKey deviceUniqueKey; + short[] lengths = {0, 0}; + if (testMode) { + seProvider.createAsymmetricKey( + KMType.EC, + scratchPad, + (short) 0, + (short) 128, + scratchPad, + (short) 128, + (short) 128, + lengths); + rkpStoreDataInst.createDeviceUniqueKey(true, scratchPad, (short) 128, lengths[1], + scratchPad, (short) 0, lengths[0]); + deviceUniqueKey = + rkpStoreDataInst.getDeviceUniqueKey(true); + } else { + deviceUniqueKey = rkpStoreDataInst.getDeviceUniqueKey(false); + } + return deviceUniqueKey; + } + + /** + * DeviceInfo is a CBOR Map structure described by the following CDDL. + *

+ * DeviceInfo = { ? "brand" : tstr, ? "manufacturer" : tstr, ? "product" : tstr, ? "model" : tstr, + * ? "board" : tstr, ? "vb_state" : "green" / "yellow" / "orange", // Taken from the AVB values + * ? "bootloader_state" : "locked" / "unlocked", // Taken from the AVB values ? + * "vbmeta_digest": bstr, // Taken from the AVB values ? "os_version" : + * tstr, // Same as android.os.Build.VERSION.release ? "system_patch_level" : + * uint, // YYYYMMDD ? "boot_patch_level" : uint, //YYYYMMDD + * ? "vendor_patch_level" : uint, // YYYYMMDD "version" : 1, // TheCDDL schema + * version "security_level" : "tee" / "strongbox" "att_id_state": "locked" / "open" } + */ + private short createDeviceInfo(byte[] scratchpad) { + // Device Info Key Value pairs. + for (short i = 0; i < 30; i++) { + deviceIds[i] = KMType.INVALID_VALUE; + } + short[] out = {0/* index */, 0 /* length */}; + updateItem(deviceIds, out, BRAND, getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad)); + updateItem(deviceIds, out, MANUFACTURER, + getAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, scratchpad)); + updateItem(deviceIds, out, 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, + getBootParams(SYSTEM_PATCH_LEVEL_ID, scratchpad)); + updateItem(deviceIds, out, BOOT_PATCH_LEVEL, getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad)); + updateItem(deviceIds, out, 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, + KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length)); + //TODO Add attest_id_state + // Create device info map. + short map = KMMap.instance(out[1]); + short mapIndex = 0; + short index = 0; + while (index < (short) deviceIds.length) { + if (deviceIds[index] != KMType.INVALID_VALUE) { + KMMap.add(map, mapIndex++, deviceIds[index], deviceIds[(short) (index + 1)]); + } + index += 2; + } + KMMap.canonicalize(map); + return map; + } + + // Below 6 methods are helper methods to create device info structure. + //---------------------------------------------------------------------------- + + /** + * Update the item inside the device info structure. + * + * @param deviceIds Device Info structure to be updated. + * @param meta Out parameter meta information. Offset 0 is index and Offset 1 is length. + * @param item Key info to be updated. + * @param value value to be updated. + */ + private void updateItem(short[] deviceIds, short[] meta, byte[] item, short value) { + if (KMType.INVALID_VALUE != value) { + deviceIds[meta[0]++] = + KMTextString.instance(item, (short) 0, (short) item.length); + deviceIds[meta[0]++] = value; + meta[1]++; + } + } + + public short mapAttestIdToStoreId(short tag) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + return KMDataStoreConstants.ATT_ID_BRAND; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + return KMDataStoreConstants.ATT_ID_DEVICE; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + return KMDataStoreConstants.ATT_ID_PRODUCT; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + return KMDataStoreConstants.ATT_ID_SERIAL; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + return KMDataStoreConstants.ATT_ID_IMEI; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + return KMDataStoreConstants.ATT_ID_MEID; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + return KMDataStoreConstants.ATT_ID_MANUFACTURER; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + return KMDataStoreConstants.ATT_ID_MODEL; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + return KMType.INVALID_VALUE; + } + + private short getAttestationId(short attestId, byte[] scratchpad) { + short attIdTagLen = storeDataInst.getData((byte) mapAttestIdToStoreId(attestId), + scratchpad, (short) 0); + if (attIdTagLen != 0) { + return KMTextString.instance(scratchpad, (short) 0, attIdTagLen); + } + return KMType.INVALID_VALUE; + } + + private short getVerifiedBootHash(byte[] scratchPad) { + short len = bootParamsProv.getVerifiedBootHash(scratchPad, (short) 0); + if (len != 0) { + return KMByteBlob.instance(scratchPad, (short) 0, len); + } + return KMType.INVALID_VALUE; + } + + private short getBootloaderState() { + short bootloaderState; + if (bootParamsProv.isDeviceBootLocked()) { + bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length); + } else { + bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length); + } + return bootloaderState; + } + + private short getVbState() { + short state = bootParamsProv.getBootState(); + short vbState = KMType.INVALID_VALUE; + if (state == KMType.VERIFIED_BOOT) { + vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length); + } else if (state == KMType.SELF_SIGNED_BOOT) { + vbState = KMTextString.instance(VB_STATE_YELLOW, (short) 0, (short) VB_STATE_YELLOW.length); + } else if (state == KMType.UNVERIFIED_BOOT) { + vbState = KMTextString.instance(VB_STATE_ORANGE, (short) 0, (short) VB_STATE_ORANGE.length); + } else if (state == KMType.FAILED_BOOT) { + vbState = KMTextString.instance(VB_STATE_RED, (short) 0, (short) VB_STATE_RED.length); + } + return vbState; + } + + private short readData(byte storeDataId, byte[] scratchPad, short offset) { + short len = storeDataInst.getData(storeDataId, scratchPad, offset); + if (len == 0) { + KMException.throwIt(KMError.INVALID_DATA); + } + return len; + } + + private short readInteger32(byte storeDataId, byte[] scratchPad, short offset) { + readData(storeDataId, scratchPad, offset); + return KMInteger.uint_32(scratchPad, offset); + } + + private short getBootParams(byte bootParam, byte[] scratchPad) { + short value = KMType.INVALID_VALUE; + switch (bootParam) { + case OS_VERSION_ID: + value = readInteger32(KMDataStoreConstants.OS_VERSION, scratchPad, (short) 0); + break; + case SYSTEM_PATCH_LEVEL_ID: + value = readInteger32(KMDataStoreConstants.OS_PATCH_LEVEL, scratchPad, (short) 0); + break; + case BOOT_PATCH_LEVEL_ID: + short len = bootParamsProv.getBootPatchLevel(scratchPad, (short) 0); + value = KMByteBlob.instance(scratchPad, (short) 0, len); + break; + case VENDOR_PATCH_LEVEL_ID: + value = readInteger32(KMDataStoreConstants.VENDOR_PATCH_LEVEL, scratchPad, (short) 0); + break; + default: + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Convert Integer to Text String for OS_VERSION. + if (bootParam == OS_VERSION_ID) { + value = + KMTextString + .instance(KMInteger.getBuffer(value), KMInteger.getStartOff(value), + KMInteger.length(value)); + } + return value; + } + //---------------------------------------------------------------------------- + + //---------------------------------------------------------------------------- + // ECDH HKDF + private short ecdhHkdfDeriveKey(byte[] privKeyA, short privKeyAOff, short privKeyALen, + byte[] pubKeyA, + short pubKeyAOff, short pubKeyALen, byte[] pubKeyB, short pubKeyBOff, + short pubKeyBLen, byte[] scratchPad) { + short key = + seProvider.ecdhKeyAgreement(privKeyA, privKeyAOff, privKeyALen, pubKeyB, pubKeyBOff, + pubKeyBLen, scratchPad, (short) 0); + key = KMByteBlob.instance(scratchPad, (short) 0, key); + + short kdfContext = + kmCoseInst.constructKdfContext(pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, + pubKeyBLen, + true); + kdfContext = KMAppletInst + .encodeToApduBuffer(kdfContext, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); + kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext); + + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0); + seProvider.hkdf( + KMByteBlob.getBuffer(key), + KMByteBlob.getStartOff(key), + KMByteBlob.length(key), + scratchPad, + (short) 0, + (short) 32, + KMByteBlob.getBuffer(kdfContext), + KMByteBlob.getStartOff(kdfContext), + KMByteBlob.length(kdfContext), + scratchPad, + (short) 32, // offset + (short) 32 // Length of expected output. + ); + Util.arrayCopy(scratchPad, (short) 32, scratchPad, (short) 0, (short) 32); + return (short) 32; + } + + //---------------------------------------------------------------------------- + // This function returns the instance of private key and It stores the public key in the + // data table for later usage. + private short generateEphemeralEcKey(byte[] scratchPad) { + // Generate ephemeral ec key. + short[] lengths = {0/* Private key Length*/, 0 /* Public key length*/}; + seProvider.createAsymmetricKey( + KMType.EC, + scratchPad, + (short) 0, + (short) 128, + scratchPad, + (short) 128, + (short) 128, + lengths); + // Copy the ephemeral private key from scratch pad + short ptr = KMByteBlob.instance(lengths[0]); + Util.arrayCopyNonAtomic( + scratchPad, + (short) 0, + KMByteBlob.getBuffer(ptr), + KMByteBlob.getStartOff(ptr), + lengths[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]); + return ptr; + } + + private void initHmacOperation() { + short dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); + operation[0] = + seProvider.initSymmetricOperation( + KMType.SIGN, + KMType.HMAC, + KMType.SHA2_256, + KMType.PADDING_NONE, + (byte) 0, + data, + dataEntryIndex, + getEntryLength(EPHEMERAL_MAC_KEY), + null, + (short) 0, + (short) 0, + (short) 0 + ); + if (operation[0] == null) { + KMException.throwIt(KMError.STATUS_FAILED); + } + } + + private void initAesGcmOperation(byte[] scratchPad, short nonce) { + // Generate Ephemeral mac key + short privKey = generateEphemeralEcKey(scratchPad); + short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); + // Generate session key + short eekIndex = getEntry(EEK_KEY); + // Generate session key + short sessionKeyLen = + ecdhHkdfDeriveKey( + KMByteBlob.getBuffer(privKey), /* Ephemeral Private Key */ + KMByteBlob.getStartOff(privKey), + KMByteBlob.length(privKey), + data, /* Ephemeral Public key */ + pubKeyIndex, + getEntryLength(EPHEMERAL_PUB_KEY), + data, /* EEK Public key */ + eekIndex, + getEntryLength(EEK_KEY), + scratchPad /* scratchpad */ + ); + // Initialize the Cipher object. + operation[0] = + seProvider.initSymmetricOperation( + KMType.ENCRYPT, + KMType.AES, + (byte) 0, + KMType.PADDING_NONE, + KMType.GCM, + scratchPad, /* key */ + (short) 0, + sessionKeyLen, + KMByteBlob.getBuffer(nonce), /* nonce */ + KMByteBlob.getStartOff(nonce), + KMByteBlob.length(nonce), + (short) (KMKeymasterDevice.AES_GCM_AUTH_TAG_LENGTH * 8) + ); + if (operation[0] == null) { + KMException.throwIt(KMError.STATUS_FAILED); + } + } + + private short processRecipientStructure(byte[] scratchPad) { + short protectedHeaderRecipient = kmCoseInst.constructHeaders( + KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + protectedHeaderRecipient = KMAppletInst + .encodeToApduBuffer(protectedHeaderRecipient, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + protectedHeaderRecipient = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaderRecipient); + + /* Construct unprotected headers */ + short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); + // prepare cosekey + short coseKey = + kmCoseInst.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + data, + pubKeyIndex, + getEntryLength(EPHEMERAL_PUB_KEY), + KMType.INVALID_VALUE, + false + ); + short keyIdentifierPtr = KMByteBlob + .instance(data, getEntry(EEK_KEY_ID), getEntryLength(EEK_KEY_ID)); + short unprotectedHeaderRecipient = + kmCoseInst.constructHeaders(KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, + coseKey); + + // Construct recipients structure. + return kmCoseInst.constructRecipientsStructure(protectedHeaderRecipient, + unprotectedHeaderRecipient, + KMSimpleValue.instance(KMSimpleValue.NULL)); + } + + private short getAdditionalCertChainProcessedLength() { + short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); + if (dataEntryIndex == 0) { + dataEntryIndex = createEntry(ACC_PROCESSED_LENGTH, SHORT_SIZE); + Util.setShort(data, dataEntryIndex, (short) 0); + return (short) 0; + } + return Util.getShort(data, dataEntryIndex); + } + + private void updateAdditionalCertChainProcessedLength(short processedLen) { + short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); + Util.setShort(data, dataEntryIndex, processedLen); + } + + private short processAdditionalCertificateChain(byte[] scratchPad) { + byte[] persistedData = rkpStoreDataInst.getData(KMDataStoreConstants.ADDITIONAL_CERT_CHAIN); + short totalAccLen = Util.getShort(persistedData, (short) 0); + if (totalAccLen == 0) { + // No Additional certificate chain present. + return 0; + } + short processedLen = getAdditionalCertChainProcessedLength(); + short lengthToSend = (short) (totalAccLen - processedLen); + if (lengthToSend > MAX_SEND_DATA) { + lengthToSend = MAX_SEND_DATA; + } + short cipherTextLen = + ((KMOperation) operation[0]).update(persistedData, (short) (2 + processedLen), lengthToSend, + scratchPad, (short) 0); + processedLen += lengthToSend; + updateAdditionalCertChainProcessedLength(processedLen); + // Update the output processing state. + updateOutputProcessingState( + (processedLen == totalAccLen) ? PROCESSING_ACC_COMPLETE : PROCESSING_ACC_IN_PROGRESS); + return cipherTextLen; + } + + // BCC for STRONGBOX has chain length of 2. So it can be returned in a single go. + private short processBcc(byte[] scratchPad) { + // Construct BCC + boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; + short len; + if (testMode) { + short bcc = KMAppletInst.generateBcc(true, scratchPad); + len = KMAppletInst + .encodeToApduBuffer(bcc, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); + } else { + byte[] bcc = rkpStoreDataInst.getData(KMDataStoreConstants.BOOT_CERT_CHAIN); + len = Util.getShort(bcc, (short) 0); + Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len); + } + short cipherTextLen = ((KMOperation) operation[0]) + .update(scratchPad, (short) 0, len, scratchPad, len); + // move cipher text on scratch pad from starting position. + Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen); + createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE); + // If there is no additional certificate chain present then put the state to + // PROCESSING_ACC_COMPLETE. + updateOutputProcessingState( + isAdditionalCertificateChainPresent() ? PROCESSING_BCC_COMPLETE : PROCESSING_ACC_COMPLETE); + return cipherTextLen; + } + + // AAD is the CoseEncrypt structure + private void processAesGcmUpdateAad(byte[] scratchPad) { + short protectedHeader = kmCoseInst.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + protectedHeader = KMAppletInst.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); + short coseEncryptStr = + kmCoseInst.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0)); + coseEncryptStr = KMAppletInst.encodeToApduBuffer(coseEncryptStr, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + ((KMOperation) operation[0]).updateAAD(scratchPad, (short) 0, coseEncryptStr); + } + + private short processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short deviceInfo) { + // Construct SignedMac + KMDeviceUniqueKey deviceUniqueKey = + createDeviceUniqueKey((TRUE == data[getEntry(TEST_MODE)]) ? true : false, scratchPad); + // Create signedMac + short signedMac = createSignedMac(deviceUniqueKey, scratchPad, deviceInfo, pubKeysToSignMac); + //Prepare partial data for encryption. + short arrLength = (short) (isAdditionalCertificateChainPresent() ? 3 : 2); + short arr = KMArray.instance(arrLength); + KMArray.add(arr, (short) 0, signedMac); + KMArray.add(arr, (short) 1, KMType.INVALID_VALUE); + if (arrLength == 3) { + KMArray.add(arr, (short) 2, KMType.INVALID_VALUE); + } + short len = KMAppletInst + .encodeToApduBuffer(arr, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); + short cipherTextLen = ((KMOperation) operation[0]) + .update(scratchPad, (short) 0, len, scratchPad, len); + Util.arrayCopyNonAtomic( + scratchPad, + len, + scratchPad, + (short) 0, + cipherTextLen + ); + return cipherTextLen; + } + + private short getCoseEncryptProtectedHeader(byte[] scratchPad) { + // CoseEncrypt protected headers. + short protectedHeader = kmCoseInst.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + protectedHeader = KMAppletInst.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + return KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); + } + + private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) { + /* CoseEncrypt unprotected headers */ + return kmCoseInst + .constructHeaders(KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE); + } + + private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) { + // prepare cosekey + short coseKey = + kmCoseInst.constructCoseKey( + KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), + KMType.INVALID_VALUE, + KMNInteger.uint_8(KMCose.COSE_ALG_ES256), + KMType.INVALID_VALUE, + KMInteger.uint_8(KMCose.COSE_ECCURVE_256), + KMByteBlob.getBuffer(pubKey), + KMByteBlob.getStartOff(pubKey), + KMByteBlob.length(pubKey), + KMType.INVALID_VALUE, + testMode); + // Encode the cose key and make it as payload. + short len = KMAppletInst + .encodeToApduBuffer(coseKey, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); + short payload = KMByteBlob.instance(scratchPad, (short) 0, len); + // Get the mackey. + short macKey = getHmacKey(testMode, scratchPad); + // Prepare protected header, which is required to construct the COSE_MAC0 + short headerPtr = kmCoseInst.constructHeaders( + KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), + KMType.INVALID_VALUE, + KMType.INVALID_VALUE, + KMType.INVALID_VALUE); + // Encode the protected header as byte blob. + len = KMAppletInst + .encodeToApduBuffer(headerPtr, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); + short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); + // create MAC_Structure + short macStructure = + kmCoseInst.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), + payload); + // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 + len = KMAppletInst.encodeToApduBuffer(macStructure, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + // HMAC Sign. + short hmacLen = seProvider + .hmacSign(KMByteBlob.getBuffer(macKey), KMByteBlob.getStartOff(macKey), + (short) 32, scratchPad, (short) 0, len, scratchPad, len); + // Create COSE_MAC0 object + short coseMac0 = + kmCoseInst + .constructCoseMac0(protectedHeader, KMCoseHeaders.instance(KMArray.instance((short) 0)), + payload, + KMByteBlob.instance(scratchPad, len, hmacLen)); + len = KMAppletInst + .encodeToApduBuffer(coseMac0, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); + return KMByteBlob.instance(scratchPad, (short) 0, len); + } + + private short getEcAttestKeyParameters() { + short tagIndex = 0; + short arrPtr = KMArray.instance((short) 6); + // Key size - 256 + short keySize = KMIntegerTag + .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); + // Digest - SHA256 + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.add(byteBlob, (short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + // Purpose - Attest + byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.add(byteBlob, (short) 0, KMType.ATTEST_KEY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); + + KMArray.add(arrPtr, tagIndex++, purpose); + // Algorithm - EC + KMArray.add(arrPtr, tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + KMArray.add(arrPtr, tagIndex++, keySize); + KMArray.add(arrPtr, tagIndex++, digest); + // Curve - P256 + KMArray.add(arrPtr, tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); + // No Authentication is required to use this key. + KMArray.add(arrPtr, tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); + return KMKeyParameters.instance(arrPtr); + } +} diff --git a/HAL/.clang-format b/HAL/.clang-format new file mode 100644 index 00000000..b0dc94c1 --- /dev/null +++ b/HAL/.clang-format @@ -0,0 +1,10 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +UseTab: Never +BreakBeforeBraces: Attach +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: true +IndentCaseLabels: false +ColumnLimit: 100 +PointerBindsToType: true +SpacesBeforeTrailingComments: 2 diff --git a/HAL/Android.bp b/HAL/Android.bp new file mode 100644 index 00000000..83cb013c --- /dev/null +++ b/HAL/Android.bp @@ -0,0 +1,222 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_library { + name: "libjc_keymint", + defaults: [ + "keymaster_defaults", + ], + srcs: [ + "JavacardKeyMintDevice.cpp", + "JavacardKeyMintOperation.cpp", + "JavacardKeyMintUtils.cpp", + "JavacardRemotelyProvisionedComponentDevice.cpp", + "JavacardSharedSecret.cpp", + ], + cflags:["-O0",], + shared_libs: [ + "android.hardware.security.keymint-V1-ndk", + "android.hardware.security.secureclock-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", + "lib_android_keymaster_keymint_utils", + "libbase", + "libcppbor_external", + "libkeymaster_portable", + "libkeymaster_messages", + "libsoft_attestation_cert", + "liblog", + "libcrypto", + "libcutils", + "libjc_km_transport", + "libbinder_ndk", + "libjc_keymaster_portable", + ], + export_include_dirs: [ + ".", + ], + vendor_available: true, +} + +cc_library { + name: "libjc_keymaster_portable", + defaults: [ + "keymaster_defaults", + ], + srcs: [ + "JavacardKeymaster.cpp", + "CborConverter.cpp", + "KMUtils.cpp", + "JavacardSecureElement.cpp", + "JavacardKeymasterOperation.cpp", + ], + cflags:["-O0",], + shared_libs: [ + "libbase", + "libcppbor_external", + "libkeymaster_portable", + "libkeymaster_messages", + "libpuresoftkeymasterdevice", + "libsoft_attestation_cert", + "liblog", + "libcrypto", + "libcutils", + "libjc_km_transport", + "libbinder_ndk", + ], + export_include_dirs: [ + ".", + ], + vendor_available: true, +} + +cc_library { + name: "libjc_km_transport", + vendor_available: true, + srcs: [ + "SocketTransport.cpp", + ], + export_include_dirs: [ + "." + ], + shared_libs: [ + "libbase", + "liblog", + ], +} + +cc_library { + name: "libjc_keymaster", + defaults: [ + "keymaster_defaults", + ], + srcs: [ + "JavacardKeymaster4Device.cpp", + "JavacardSoftKeymasterContext.cpp", + ], + cflags:["-O0",], + shared_libs: [ + "liblog", + "libcutils", + "libdl", + "libbase", + "libutils", + "libhardware", + "libhidlbase", + "libsoftkeymasterdevice", + "libsoft_attestation_cert", + "libkeymaster_messages", + "libkeymaster_portable", + "libcppbor_external", + "android.hardware.keymaster@4.1", + "android.hardware.keymaster@4.0", + "libjc_km_transport", + "libjc_keymaster_portable", + "libcrypto", + "libkeymaster4support", + ], + export_include_dirs: [ + ".", + ], + vendor_available: true, +} + +cc_binary { + name: "android.hardware.keymaster@4.1-strongbox.service", + relative_install_path: "hw", + vendor: true, + init_rc: ["android.hardware.keymaster@4.1-strongbox.service.rc"], + vintf_fragments: ["android.hardware.keymaster@4.1-strongbox.service.xml"], + cflags: [ + "-Wall", + "-Wextra", + ], + srcs: [ + "keymasterService.cpp", + ], + shared_libs: [ + "liblog", + "libcutils", + "libdl", + "libbase", + "libutils", + "libhardware", + "libhidlbase", + "libsoftkeymasterdevice", + "libsoft_attestation_cert", + "libkeymaster_messages", + "libkeymaster_portable", + "libcppbor_external", + "android.hardware.keymaster@4.1", + "android.hardware.keymaster@4.0", + "libjc_km_transport", + "libcrypto", + "libjc_keymaster", + "libjc_keymaster_portable", + ], + required: [ + "android.hardware.keymaster_strongbox_keystore.xml", + ], +} + +cc_binary { + name: "android.hardware.security.keymint-service.strongbox", + relative_install_path: "hw", + init_rc: ["android.hardware.security.keymint-service.strongbox.rc"], + vintf_fragments: [ + "android.hardware.security.keymint-service.strongbox.xml", + "android.hardware.security.sharedsecret-service.strongbox.xml", + ], + vendor: true, + cflags: [ + "-Wall", + "-Wextra", + ], + shared_libs: [ + "android.hardware.security.keymint-V1-ndk", + "android.hardware.security.sharedsecret-V1-ndk", + "libbase", + "libbinder_ndk", + "libcppbor_external", + "libcrypto", + "libkeymaster_portable", + "libjc_keymint", + "libjc_km_transport", + "liblog", + "libutils", + "libjc_keymaster_portable", + ], + srcs: [ + "keymintService.cpp", + ], + required: [ + "RemoteProvisioner", + "android.hardware.strongbox_keystore.xml", + ], +} + +prebuilt_etc { + name: "android.hardware.strongbox_keystore.xml", + sub_dir: "permissions", + vendor: true, + src: "android.hardware.strongbox_keystore.xml", +} + +prebuilt_etc { + name: "android.hardware.keymaster_strongbox_keystore.xml", + sub_dir: "permissions", + vendor: true, + src: "android.hardware.keymaster_strongbox_keystore.xml", +} + diff --git a/HAL/CborConverter.cpp b/HAL/CborConverter.cpp new file mode 100644 index 00000000..66d538b2 --- /dev/null +++ b/HAL/CborConverter.cpp @@ -0,0 +1,454 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#include "CborConverter.h" +#include +#include +#include +#include +#include +#include + +namespace javacard_keymaster { +using namespace cppbor; +using ::keymaster::KeymasterBlob; +using std::string; +using std::unique_ptr; +using std::vector; + +inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) { + return keymaster_tag_get_type(tag); +} + +inline vector kmBlob2vector(const keymaster_blob_t& blob) { + vector result(blob.data, blob.data + blob.data_length); + return result; +} + +bool CborConverter::addKeyparameters(Array& array, const keymaster_key_param_set_t& paramSet) { + Map map; + std::map> enum_repetition; + std::map uint_repetition; + for (size_t i = 0; i < paramSet.length; i++) { + const auto& param = paramSet.params[i]; + switch (typeFromTag(param.tag)) { + case KM_ENUM: + map.add(static_cast(param.tag), param.enumerated); + break; + case KM_UINT: + map.add(static_cast(param.tag), param.integer); + break; + case KM_UINT_REP: + uint_repetition[static_cast(param.tag)].add(param.integer); + break; + case KM_ENUM_REP: + enum_repetition[static_cast(param.tag)].push_back( + static_cast(param.enumerated)); + break; + case KM_ULONG: + map.add(static_cast(param.tag), param.long_integer); + break; + case KM_ULONG_REP: + uint_repetition[static_cast(param.tag & 0x00000000ffffffff)].add( + param.long_integer); + break; + case KM_DATE: + map.add(static_cast(param.tag), param.date_time); + break; + case KM_BOOL: + map.add(static_cast(param.tag), static_cast(param.boolean)); + break; + case KM_BIGNUM: + case KM_BYTES: + map.add(static_cast(param.tag & 0x00000000ffffffff), + kmBlob2vector(param.blob)); + break; + default: + /* Invalid skip */ + break; + } + } + if (0 < enum_repetition.size()) { + for (auto const& [key, val] : enum_repetition) { + Bstr bstr(val); + map.add(key, std::move(bstr)); + } + } + if (0 < uint_repetition.size()) { + for (auto& [key, val] : uint_repetition) { + map.add(key, std::move(val)); + } + } + array.add(std::move(map)); + return true; +} + +bool CborConverter::getKeyCharacteristics(const std::unique_ptr& item, const uint32_t pos, + AuthorizationSet& swEnforced, + AuthorizationSet& hwEnforced, + AuthorizationSet& teeEnforced) { + unique_ptr arrayItem(nullptr); + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return false; + + if (!getKeyParameters(arrayItem, 0, swEnforced) || + !getKeyParameters(arrayItem, 1, hwEnforced) || + !getKeyParameters(arrayItem, 2, teeEnforced)) { + return false; + } + return true; +} + +bool CborConverter::getKeyParameter( + const std::pair&, const unique_ptr&> pair, + AuthorizationSet& keyParams) { + uint64_t key; + uint64_t value; + if (!getUint64(pair.first, key)) { + return false; + } + switch (keymaster_tag_get_type(static_cast(key))) { + case KM_ENUM_REP: { + /* ENUM_REP contains values encoded in a Binary string */ + const Bstr* bstr = pair.second.get()->asBstr(); + if (bstr == nullptr) return false; + for (auto bchar : bstr->value()) { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + keyParam.enumerated = bchar; + keyParams.push_back(keyParam); + } + } break; + case KM_ENUM: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.enumerated = static_cast(value); + keyParams.push_back(keyParam); + } break; + case KM_UINT: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.integer = static_cast(value); + keyParams.push_back(keyParam); + } break; + case KM_ULONG: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.long_integer = value; + keyParams.push_back(keyParam); + } break; + case KM_UINT_REP: { + /* UINT_REP contains values encoded in a Array */ + Array* array = const_cast(pair.second.get()->asArray()); + if (array == nullptr) return false; + for (int i = 0; i < array->size(); i++) { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + std::unique_ptr item = std::move((*array)[i]); + if (!getUint64(item, value)) { + return false; + } + keyParam.integer = static_cast(value); + keyParams.push_back(keyParam); + } + } break; + case KM_ULONG_REP: { + /* ULONG_REP contains values encoded in a Array */ + Array* array = const_cast(pair.second.get()->asArray()); + if (array == nullptr) return false; + for (int i = 0; i < array->size(); i++) { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + std::unique_ptr item = std::move((*array)[i]); + if (!getUint64(item, keyParam.long_integer)) { + return false; + } + keyParams.push_back(keyParam); + } + } break; + case KM_DATE: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + keyParam.date_time = value; + keyParams.push_back(keyParam); + } break; + case KM_BOOL: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + if (!getUint64(pair.second, value)) { + return false; + } + // TODO re-check the logic below + keyParam.boolean = static_cast(value); + keyParams.push_back(keyParam); + } break; + case KM_BYTES: { + keymaster_key_param_t keyParam; + keyParam.tag = static_cast(key); + const Bstr* bstr = pair.second.get()->asBstr(); + if (bstr == nullptr) return false; + size_t blobSize = bstr->value().size(); + keyParam.blob.data = keymaster::dup_buffer(bstr->value().data(), blobSize); + keyParam.blob.data_length = blobSize; + keyParams.push_back(keyParam); + } break; + default: + /* Invalid - return error */ + return false; + break; + } + return true; +} + +bool CborConverter::getMultiBinaryArray(const unique_ptr& item, const uint32_t pos, + vector>& data) { + bool ret = false; + std::unique_ptr arrayItem(nullptr); + + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return ret; + const Array* arr = arrayItem.get()->asArray(); + size_t arrSize = arr->size(); + for (int i = 0; i < arrSize; i++) { + std::vector temp; + if (!getBinaryArray(arrayItem, i, temp)) return ret; + data.push_back(std::move(temp)); + } + ret = true; // success + return ret; +} + +bool CborConverter::getBinaryArray(const unique_ptr& item, const uint32_t pos, + string& value) { + vector vec; + string str; + if (!getBinaryArray(item, pos, vec)) { + return false; + } + for (auto ch : vec) { + str += ch; + } + value = str; + return true; +} + +bool CborConverter::getBinaryArray(const unique_ptr& item, const uint32_t pos, + vector& value) { + bool ret = false; + unique_ptr strItem(nullptr); + getItemAtPos(item, pos, strItem); + if ((strItem == nullptr) || (MajorType::BSTR != getType(strItem))) return ret; + + const Bstr* bstr = strItem.get()->asBstr(); + for (auto bchar : bstr->value()) { + value.push_back(bchar); + } + ret = true; + return ret; +} + +bool CborConverter::getSharedSecretParameters(const unique_ptr& item, const uint32_t pos, + vector& seed, vector& nonce) { + std::unique_ptr arrayItem(nullptr); + // Array [seed, nonce] + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem)) || + !getBinaryArray(arrayItem, 0, seed) || !getBinaryArray(arrayItem, 1, nonce)) { + return false; + } + return true; +} + +bool CborConverter::addSharedSecretParameters(Array& array, vector params) { + Array cborParamsVec; + for (auto param : params) { + Array cborParam; + cborParam.add(param.seed); + cborParam.add(param.nonce); + cborParamsVec.add(std::move(cborParam)); + } + array.add(std::move(cborParamsVec)); + return true; +} + +bool CborConverter::addTimeStampToken(Array& array, const TimestampToken& token) { + vector mac(token.mac.begin(), token.mac.end()); + Array vToken; + vToken.add(static_cast(token.challenge)); + vToken.add(static_cast(token.timestamp)); + vToken.add(mac); + array.add(std::move(vToken)); + return true; +} + +bool CborConverter::addVerificationToken(Array& vToken, const VerificationToken& token, + const vector& encodedParamsVerified) { + vector mac(token.mac.begin(), token.mac.end()); + vToken.add(token.challenge); + vToken.add(token.timestamp); + vToken.add(std::move(encodedParamsVerified)); + vToken.add(static_cast(token.security_level)); + vToken.add(mac); + return true; +} + +bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken) { + vector mac(authToken.mac.begin(), authToken.mac.end()); + Array hwAuthToken; + hwAuthToken.add(static_cast(authToken.challenge)); + hwAuthToken.add(static_cast(authToken.user_id)); + hwAuthToken.add(static_cast(authToken.authenticator_id)); + hwAuthToken.add(static_cast(authToken.authenticator_type)); + hwAuthToken.add(static_cast(authToken.timestamp)); + hwAuthToken.add(mac); + array.add(std::move(hwAuthToken)); + return true; +} + +bool CborConverter::getHardwareAuthToken(const unique_ptr& item, const uint32_t pos, + HardwareAuthToken& token) { + uint64_t authType; + std::vector mac; + // challenge, userId, AuthenticatorId, AuthType, Timestamp, MAC + if (!getUint64(item, pos, token.challenge) || + !getUint64(item, pos + 1, token.user_id) || + !getUint64(item, pos + 2, token.authenticator_id) || + !getUint64(item, pos + 3, authType) || + !getUint64(item, pos + 4, token.timestamp) || + !getBinaryArray(item, pos + 5, mac)) { + return false; + } + token.authenticator_type = static_cast(authType); + token.mac = KeymasterBlob(mac.data(), mac.size()); + return true; +} + +bool CborConverter::getTimeStampToken(const unique_ptr& item, const uint32_t pos, + TimestampToken& token) { + // {challenge, timestamp, Mac} + std::vector mac; + if (!getUint64(item, pos, token.challenge) || + !getUint64(item, pos + 1, token.timestamp) || + !getBinaryArray(item, pos + 2, mac)) { + return false; + } + token.mac = KeymasterBlob(mac.data(), mac.size()); + return true; +} + +bool CborConverter::getVerificationToken(const unique_ptr& item, const uint32_t pos, + VerificationToken& token) { + // {challenge, timestamp, parametersVerified, securityLevel, Mac} + std::vector mac; + uint64_t securityLevel; + if (!getUint64(item, pos, token.challenge) || + !getUint64(item, pos + 1, token.timestamp) || + !getKeyParameters(item, pos + 2, token.parameters_verified) || + !getUint64(item, pos + 3, securityLevel) || !getBinaryArray(item, pos + 4, mac)) { + return false; + } + token.security_level = static_cast(securityLevel); + token.mac = KeymasterBlob(mac.data(), mac.size()); + return true; +} + +bool CborConverter::getArrayItem(const std::unique_ptr& item, const uint32_t pos, + Array& array) { + unique_ptr arrayItem(nullptr); + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return false; + array = std::move(*arrayItem.get()->asArray()); + return true; +} + +bool CborConverter::getMapItem(const std::unique_ptr& item, const uint32_t pos, Map& map) { + unique_ptr mapItem(nullptr); + getItemAtPos(item, pos, mapItem); + if ((mapItem == nullptr) || (MajorType::MAP != getType(mapItem))) return false; + map = std::move(*mapItem.get()->asMap()); + return true; +} + +bool CborConverter::getKeyParameters(const unique_ptr& item, const uint32_t pos, + AuthorizationSet& keyParams) { + bool ret = false; + unique_ptr mapItem(nullptr); + getItemAtPos(item, pos, mapItem); + if ((mapItem == nullptr) || (MajorType::MAP != getType(mapItem))) return ret; + const Map* map = mapItem.get()->asMap(); + size_t mapSize = map->size(); + for (int i = 0; i < mapSize; i++) { + if (!getKeyParameter((*map)[i], keyParams)) { + return ret; + } + } + ret = true; + return ret; +} + +// array of a blobs +bool CborConverter::getCertificateChain(const std::unique_ptr& item, const uint32_t pos, + CertificateChain& certChain) { + std::unique_ptr arrayItem(nullptr); + std::vector cert; + getItemAtPos(item, pos, arrayItem); + if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) return false; + + const Array* arr = arrayItem.get()->asArray(); + size_t arrSize = arr->size(); + for (int i = (arrSize - 1); i >= 0; i--) { + if (!getBinaryArray(arrayItem, i, cert)) return false; + uint8_t* blob = new (std::nothrow) uint8_t[cert.size()]; + memcpy(blob, cert.data(), cert.size()); + certChain.push_front({blob, cert.size()}); + cert.clear(); + } + return true; +} + +std::tuple, keymaster_error_t> +CborConverter::decodeData(const std::vector& response) { + keymaster_error_t errorCode = KM_ERROR_OK; + auto [item, pos, message] = parse(response); + if (!item || MajorType::ARRAY != getType(item) || !getErrorCode(item, 0, errorCode)) { + return {nullptr, KM_ERROR_UNKNOWN_ERROR}; + } + return {std::move(item), errorCode}; +} + +std::tuple, keymaster_error_t> +CborConverter::decodeKeyblob(const vector& keyblob) { + auto [item, pos, message] = parse(keyblob); + if (!item || MajorType::ARRAY != getType(item)) { + return {nullptr, KM_ERROR_UNKNOWN_ERROR}; + } + return {std::move(item), KM_ERROR_OK}; +} + +} // namespace javacard_keymaster diff --git a/HAL/CborConverter.h b/HAL/CborConverter.h new file mode 100644 index 00000000..df946658 --- /dev/null +++ b/HAL/CborConverter.h @@ -0,0 +1,194 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#pragma once +//#include +//#include +//#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace javacard_keymaster { +using namespace cppbor; +// using namespace aidl::android::hardware::security::keymint; +// using namespace aidl::android::hardware::security::secureclock; +// using namespace aidl::android::hardware::security::sharedsecret; +using ::keymaster::AuthorizationSet; +using ::keymaster::CertificateChain; +using ::keymaster::HardwareAuthToken; +using ::keymaster::KeymasterKeyBlob; +using ::keymaster::TimestampToken; +using ::keymaster::VerificationToken; +using std::string; +using std::unique_ptr; +using std::vector; + +struct HmacSharingParameters { + vector seed; + vector nonce; +}; + +class CborConverter { + public: + CborConverter() = default; + ~CborConverter() = default; + std::tuple, keymaster_error_t> + decodeData(const std::vector& response); + std::tuple, keymaster_error_t> + decodeKeyblob(const vector& keyblob); + + template + bool getUint64(const std::unique_ptr& item, const uint32_t pos, T& value); + + template bool getUint64(const std::unique_ptr& item, T& value); + + bool getSharedSecretParameters(const std::unique_ptr& item, const uint32_t pos, + vector& seed, vector& nonce); + bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, string& value); + + bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, + vector& value); + + bool getHardwareAuthToken(const std::unique_ptr& item, const uint32_t pos, + HardwareAuthToken& authType); + + bool getKeyParameters(const std::unique_ptr& item, const uint32_t pos, + AuthorizationSet& keyParams); + + bool addKeyparameters(Array& array, const keymaster_key_param_set_t& keyParams); + + bool addHardwareAuthToken(Array& array, const HardwareAuthToken& authToken); + + bool addSharedSecretParameters(Array& array, vector params); + + bool getTimeStampToken(const std::unique_ptr& item, const uint32_t pos, + TimestampToken& token); + + bool getVerificationToken(const std::unique_ptr& item, const uint32_t pos, + VerificationToken& token); + + bool getKeyCharacteristics(const std::unique_ptr& item, const uint32_t pos, + AuthorizationSet& swEnforced, AuthorizationSet& hwEnforced, + AuthorizationSet& teeEnforced); + + bool getMultiBinaryArray(const std::unique_ptr& item, const uint32_t pos, + vector>& data); + + bool addTimeStampToken(Array& array, const TimestampToken& token); + + bool addVerificationToken(Array& array, const VerificationToken& token, + const vector& encodedParamsVerified); + + bool getMapItem(const std::unique_ptr& item, const uint32_t pos, Map& map); + + bool getArrayItem(const std::unique_ptr& item, const uint32_t pos, Array& array); + + bool getCertificateChain(const std::unique_ptr& item, const uint32_t pos, + CertificateChain& certChain); + + inline bool getErrorCode(const std::unique_ptr& item, const uint32_t pos, + keymaster_error_t& errorCode) { + uint64_t errorVal; + if (!getUint64(item, pos, errorVal)) { + return false; + } + errorCode = static_cast(0 - errorVal); + return true; + } + + inline keymaster_error_t getArraySize(const unique_ptr& item, size_t& size) { + Array* arr = nullptr; + + if (MajorType::ARRAY != getType(item)) { + return KM_ERROR_UNKNOWN_ERROR; + } + arr = const_cast(item.get()->asArray()); + size = arr->size(); + return KM_ERROR_OK; + } + + private: + /** + * Returns the negative value of the same number. + */ + inline int32_t get2sCompliment(uint32_t value) { return static_cast(~value + 1); } + + /** + * Get the type of the Item pointer. + */ + inline MajorType getType(const unique_ptr& item) { return item.get()->type(); } + + /** + * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the + * value contains binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of + * unsigned integers. + */ + // bool getKeyParameter(const std::pair&, const unique_ptr&> pair, + // vector& keyParam); + + bool getKeyParameter(const std::pair&, const unique_ptr&> pair, + AuthorizationSet& keyParam); + + /** + * Get the sub item pointer from the root item pointer at the given position. + */ + inline void getItemAtPos(const unique_ptr& item, const uint32_t pos, + unique_ptr& subItem) { + Array* arr = nullptr; + + if (MajorType::ARRAY != getType(item)) { + return; + } + arr = const_cast(item.get()->asArray()); + if (arr->size() < (pos + 1)) { + return; + } + subItem = std::move((*arr)[pos]); + } +}; + +template bool CborConverter::getUint64(const unique_ptr& item, T& value) { + bool ret = false; + if ((item == nullptr) || (std::is_unsigned::value && (MajorType::UINT != getType(item))) || + ((std::is_signed::value && (MajorType::NINT != getType(item))))) { + return ret; + } + + if (std::is_unsigned::value) { + const Uint* uintVal = item.get()->asUint(); + value = static_cast(uintVal->value()); + } else { + const Nint* nintVal = item.get()->asNint(); + value = static_cast(nintVal->value()); + } + ret = true; + return ret; // success +} + +template +bool CborConverter::getUint64(const unique_ptr& item, const uint32_t pos, T& value) { + unique_ptr intItem(nullptr); + getItemAtPos(item, pos, intItem); + return getUint64(intItem, value); +} +} // namespace javacard_keymaster diff --git a/HAL/ITransport.h b/HAL/ITransport.h new file mode 100644 index 00000000..0d816a73 --- /dev/null +++ b/HAL/ITransport.h @@ -0,0 +1,51 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#pragma once +#include +#include + +namespace javacard_keymaster { +using std::shared_ptr; +using std::vector; + +/** + * ITransport is an interface with a set of virtual methods that allow communication between the + * HAL and the applet on the secure element. + */ +class ITransport { + public: + virtual ~ITransport() {} + + /** + * Opens connection. + */ + virtual bool openConnection() = 0; + /** + * Send data over communication channel and receives data back from the remote end. + */ + virtual bool sendData(const vector& inData, vector& output) = 0; + /** + * Closes the connection. + */ + virtual bool closeConnection() = 0; + /** + * Returns the state of the connection status. Returns true if the connection is active, false + * if connection is broken. + */ + virtual bool isConnected() = 0; +}; +} // namespace javacard_keymaster diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp new file mode 100644 index 00000000..a0850591 --- /dev/null +++ b/HAL/JavacardKeyMintDevice.cpp @@ -0,0 +1,323 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "javacard.keymint.device.strongbox-impl" +#include "JavacardKeyMintDevice.h" +#include "JavacardKeyMintOperation.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using km_utils::KmParamSet; +using km_utils::kmParamSet2Aidl; +using std::nullopt; + +namespace { + +Certificate convertCertificate(const keymaster_blob_t& cert) { + return {std::vector(cert.data, cert.data + cert.data_length)}; +} + +vector convertCertificateChain(const CertificateChain& chain) { + vector retval; + retval.reserve(chain.entry_count); + std::transform(chain.begin(), chain.end(), std::back_inserter(retval), convertCertificate); + return retval; +} + +vector convertKeyCharacteristics(AuthorizationSet& keystoreEnforced, + AuthorizationSet& sbEnforced, + AuthorizationSet& teeEnforced) { + vector retval; + // VTS will fail if the authorizations list is empty. + if (!sbEnforced.empty()) + retval.push_back({SecurityLevel::STRONGBOX, kmParamSet2Aidl(sbEnforced)}); + if (!teeEnforced.empty()) + retval.push_back({SecurityLevel::TRUSTED_ENVIRONMENT, kmParamSet2Aidl(teeEnforced)}); + if (!keystoreEnforced.empty()) + retval.push_back({SecurityLevel::KEYSTORE, kmParamSet2Aidl(keystoreEnforced)}); + return retval; +} + +std::optional +convertAttestationKey(const std::optional& attestationKey) { + JCKMAttestationKey key; + if (attestationKey.has_value()) { + key.params.Reinitialize(KmParamSet(attestationKey->attestKeyParams)); + key.keyBlob = attestationKey->keyBlob; + key.issuerSubject = attestationKey->issuerSubjectName; + } + return std::move(key); +} +#if 0 +inline void Vec2KmBlob(const vector& input, KeymasterBlob* blob) { + blob->Reset(input.size()); + memcpy(blob->writable_data(), input.data(), input.size()); +} + +void legacyHardwareAuthToken(const std::optional& aidlToken, ::keymaster::HardwareAuthToken* legacyToken) { + if (aidlToken.has_value()) { + legacyToken->challenge = aidlToken->challenge; + legacyToken->user_id = aidlToken->userId; + legacyToken->authenticator_id = aidlToken->authenticatorId; + legacyToken->authenticator_type = static_cast(aidlToken->authenticatorType); + legacyToken->timestamp = aidlToken->timestamp.milliSeconds; + Vec2KmBlob(aidlToken->mac, &legacyToken->mac); + } +} +#endif +} // anonymous namespace + +ScopedAStatus JavacardKeyMintDevice::defaultHwInfo(KeyMintHardwareInfo* info) { + info->versionNumber = 1; + info->keyMintAuthorName = "Google"; + info->keyMintName = "JavacardKeymintDevice"; + info->securityLevel = securitylevel_; + info->timestampTokenRequired = true; + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) { + uint64_t tsRequired = 1; + auto [item, err] = jcImpl_->getHardwareInfo(); + uint32_t secLevel; + uint32_t version; + if (err != KM_ERROR_OK || !cbor_.getUint64(item, 1, version) || + !cbor_.getUint64(item, 2, secLevel) || + !cbor_.getBinaryArray(item, 3, info->keyMintName) || + !cbor_.getBinaryArray(item, 4, info->keyMintAuthorName) || + !cbor_.getUint64(item, 5, tsRequired)) { + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + return defaultHwInfo(info); + } + info->timestampTokenRequired = (tsRequired == 1); + info->securityLevel = static_cast(secLevel); + info->versionNumber = static_cast(version); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::generateKey(const vector& keyParams, + const optional& attestationKey, + KeyCreationResult* creationResult) { + AuthorizationSet paramSet; + std::optional jcAttestationKey = nullopt; + AuthorizationSet swEnforced; + AuthorizationSet sbEnforced; + AuthorizationSet teeEnforced; + paramSet.Reinitialize(KmParamSet(keyParams)); + + auto err = jcImpl_->generateKey(paramSet, &creationResult->keyBlob, &swEnforced, &sbEnforced, + &teeEnforced); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Failed in generateKey err: " << (int32_t)err; + return km_utils::kmError2ScopedAStatus(err); + } + // Call attestKey only Asymmetric algorithms. + keymaster_algorithm_t algorithm; + paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); + if (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC) { + err = attestKey(creationResult->keyBlob, paramSet, convertAttestationKey(attestationKey), + &creationResult->certificateChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Failed in attestKey err: " << (int32_t)err; + return km_utils::kmError2ScopedAStatus(err); + } + } + creationResult->keyCharacteristics = + convertKeyCharacteristics(swEnforced, sbEnforced, teeEnforced); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::addRngEntropy(const vector& data) { + auto err = jcImpl_->addRngEntropy(data); + return km_utils::kmError2ScopedAStatus(err); +} + +keymaster_error_t +JavacardKeyMintDevice::attestKey(const vector& keyblob, const AuthorizationSet& keyParams, + const optional& attestationKey, + vector* certificateChain) { + ::keymaster::CertificateChain certChain; + auto err = jcImpl_->attestKey(keyblob, keyParams, attestationKey, &certChain); + if (err != KM_ERROR_OK) { + return err; + } + *certificateChain = convertCertificateChain(certChain); + return KM_ERROR_OK; +} + +ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyParams, + KeyFormat keyFormat, const vector& keyData, + const optional& attestationKey, + KeyCreationResult* creationResult) { + AuthorizationSet paramSet; + std::optional jcAttestationKey = nullopt; + AuthorizationSet swEnforced; + AuthorizationSet sbEnforced; + AuthorizationSet teeEnforced; + paramSet.Reinitialize(KmParamSet(keyParams)); + // Add CREATION_DATETIME if required, as secure element is not having clock. + addCreationTime(paramSet); + auto err = jcImpl_->importKey(paramSet, static_cast(keyFormat), keyData, + &creationResult->keyBlob, &swEnforced, &sbEnforced, &teeEnforced); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Failed in importKey" << (int32_t)err; + return km_utils::kmError2ScopedAStatus(err); + } + // Call attestKey only Asymmetric algorithms. + keymaster_algorithm_t algorithm; + paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); + if (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC) { + err = attestKey(creationResult->keyBlob, paramSet, convertAttestationKey(attestationKey), + &creationResult->certificateChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Failed in attestKey" << (int32_t)err; + return km_utils::kmError2ScopedAStatus(err); + } + } + creationResult->keyCharacteristics = + convertKeyCharacteristics(swEnforced, sbEnforced, teeEnforced); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector& wrappedKeyData, + const vector& wrappingKeyBlob, + const vector& maskingKey, + const vector& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, + KeyCreationResult* creationResult) { + AuthorizationSet paramSet; + AuthorizationSet swEnforced; + AuthorizationSet sbEnforced; + AuthorizationSet teeEnforced; + vector retKeyblob; + paramSet.Reinitialize(KmParamSet(unwrappingParams)); + auto err = jcImpl_->importWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey, paramSet, + passwordSid, biometricSid, &creationResult->keyBlob, + &swEnforced, &sbEnforced, &teeEnforced); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Failed in attestKey" << (int32_t)err; + return km_utils::kmError2ScopedAStatus(err); + } + creationResult->keyCharacteristics = + convertKeyCharacteristics(swEnforced, sbEnforced, teeEnforced); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::upgradeKey(const vector& keyBlobToUpgrade, + const vector& upgradeParams, + vector* keyBlob) { + AuthorizationSet paramSet; + paramSet.Reinitialize(KmParamSet(upgradeParams)); + auto err = jcImpl_->upgradeKey(keyBlobToUpgrade, paramSet, keyBlob); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintDevice::deleteKey(const vector& keyBlob) { + auto err = jcImpl_->deleteKey(keyBlob); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintDevice::deleteAllKeys() { + auto err = jcImpl_->deleteAllKeys(); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintDevice::destroyAttestationIds() { + auto err = jcImpl_->destroyAttestationIds(); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector& keyBlob, + const std::vector& params, + const std::optional& authToken, + BeginResult* result) { + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + AuthorizationSet paramSet; + AuthorizationSet outParams; + paramSet.Reinitialize(KmParamSet(params)); + ::keymaster::HardwareAuthToken legacyToken; + std::unique_ptr operation; + legacyHardwareAuthToken(aToken, &legacyToken); + auto err = jcImpl_->begin(static_cast(purpose), keyBlob, paramSet, + legacyToken, &outParams, operation); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Failed in begin" << (int32_t)err; + return km_utils::kmError2ScopedAStatus(err); + } + result->challenge = operation->getOpertionHandle(); + result->operation = ndk::SharedRefBase::make(std::move(operation)); + result->params = kmParamSet2Aidl(outParams); + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardKeyMintDevice::deviceLocked(bool passwordOnly, + const std::optional& timestampToken) { + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + vector encodedTimestampToken; + auto err = encodeTimestampToken(tToken, &encodedTimestampToken); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "In deviceLocked failed to encode TimeStampToken" << (int32_t)err; + return km_utils::kmError2ScopedAStatus(err); + } + err = jcImpl_->deviceLocked(passwordOnly, encodedTimestampToken); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() { + auto err = jcImpl_->earlyBootEnded(); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintDevice::getKeyCharacteristics( + const std::vector& keyBlob, const std::vector& appId, + const std::vector& appData, std::vector* result) { + + AuthorizationSet swEnforced; + AuthorizationSet sbEnforced; + AuthorizationSet teeEnforced; + auto err = jcImpl_->getKeyCharacteristics(keyBlob, appId, appData, &swEnforced, &sbEnforced, + &teeEnforced); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending in getKeyCharacteristics."; + return km_utils::kmError2ScopedAStatus(err); + } + *result = convertKeyCharacteristics(swEnforced, sbEnforced, teeEnforced); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardKeyMintDevice::convertStorageKeyToEphemeral( + const std::vector& /* storageKeyBlob */, + std::vector* /* ephemeralKeyBlob */) { + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); +} +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintDevice.h b/HAL/JavacardKeyMintDevice.h new file mode 100644 index 00000000..df30db24 --- /dev/null +++ b/HAL/JavacardKeyMintDevice.h @@ -0,0 +1,106 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CborConverter.h" +#include "JavacardSecureElement.h" +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace aidl::android::hardware::security::sharedsecret; +using namespace aidl::android::hardware::security::secureclock; +using namespace ::keymaster; +using namespace ::javacard_keymaster; +using ndk::ScopedAStatus; +using std::optional; +using std::shared_ptr; +using std::vector; +using JCKMAttestationKey = ::javacard_keymaster::AttestationKey; + +class JavacardKeyMintDevice : public BnKeyMintDevice { + public: + explicit JavacardKeyMintDevice(shared_ptr jcImpl) + : securitylevel_(SecurityLevel::STRONGBOX), jcImpl_(jcImpl), + isEarlyBootEventPending(false) {} + virtual ~JavacardKeyMintDevice() {} + + ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override; + + ScopedAStatus addRngEntropy(const vector& data) override; + + ScopedAStatus generateKey(const vector& keyParams, + const optional& attestationKey, + KeyCreationResult* creationResult) override; + + ScopedAStatus importKey(const vector& keyParams, KeyFormat keyFormat, + const vector& keyData, + const optional& attestationKey, + KeyCreationResult* creationResult) override; + + ScopedAStatus importWrappedKey(const vector& wrappedKeyData, + const vector& wrappingKeyBlob, + const vector& maskingKey, + const vector& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, + KeyCreationResult* creationResult) override; + + ScopedAStatus upgradeKey(const vector& keyBlobToUpgrade, + const vector& upgradeParams, + vector* keyBlob) override; + + ScopedAStatus deleteKey(const vector& keyBlob) override; + ScopedAStatus deleteAllKeys() override; + ScopedAStatus destroyAttestationIds() override; + + virtual ScopedAStatus begin(KeyPurpose in_purpose, const std::vector& in_keyBlob, + const std::vector& in_params, + const std::optional& in_authToken, + BeginResult* _aidl_return) override; + + ScopedAStatus deviceLocked(bool passwordOnly, + const optional& timestampToken) override; + + ScopedAStatus earlyBootEnded() override; + + ScopedAStatus getKeyCharacteristics(const std::vector& in_keyBlob, + const std::vector& in_appId, + const std::vector& in_appData, + std::vector* _aidl_return) override; + + ScopedAStatus convertStorageKeyToEphemeral(const std::vector& storageKeyBlob, + std::vector* ephemeralKeyBlob) override; + + private: + keymaster_error_t attestKey(const vector& keyblob, const AuthorizationSet& keyParams, + const optional& attestationKey, + vector* certificateChain); + + ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info); + + void handleSendEarlyBootEndedEvent(); + + const SecurityLevel securitylevel_; + const shared_ptr jcImpl_; + CborConverter cbor_; + bool isEarlyBootEventPending; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp new file mode 100644 index 00000000..82d741b0 --- /dev/null +++ b/HAL/JavacardKeyMintOperation.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "javacard.strongbox.keymint.operation-impl" + +#include "JavacardKeyMintOperation.h" +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace ::keymaster; +using secureclock::TimeStampToken; +using std::nullopt; + +ScopedAStatus JavacardKeyMintOperation::updateAad(const vector& input, + const optional& authToken, + const optional& timestampToken) { + ::keymaster::HardwareAuthToken legacyToken; + vector encodedTimestampToken; + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + legacyHardwareAuthToken(aToken, &legacyToken); + encodeTimestampToken(tToken, &encodedTimestampToken); + auto err = jcKmOprImpl_->updateAad(input, legacyToken, encodedTimestampToken); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintOperation::update(const vector& input, + const optional& authToken, + const optional& timestampToken, + vector* output) { + ::keymaster::HardwareAuthToken legacyToken; + vector encodedTimestampToken; + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + legacyHardwareAuthToken(aToken, &legacyToken); + encodeTimestampToken(tToken, &encodedTimestampToken); + auto err = jcKmOprImpl_->update(input, nullopt, legacyToken, encodedTimestampToken, nullptr, + nullptr, output); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintOperation::finish(const optional>& input, + const optional>& signature, + const optional& authToken, + const optional& timestampToken, + const optional>& confirmationToken, + vector* output) { + ::keymaster::HardwareAuthToken legacyToken; + vector encodedTimestampToken; + HardwareAuthToken aToken = authToken.value_or(HardwareAuthToken()); + TimeStampToken tToken = timestampToken.value_or(TimeStampToken()); + vector inputData = input.value_or(vector()); + vector signatureData = signature.value_or(vector()); + // If confirmation token is empty, then create empty vector. This is to + // differentiate between the keymaster and keymint. + std::optional> confToken = confirmationToken.value_or(vector()); + legacyHardwareAuthToken(aToken, &legacyToken); + encodeTimestampToken(tToken, &encodedTimestampToken); + auto err = jcKmOprImpl_->finish(inputData, nullopt, signatureData, legacyToken, + encodedTimestampToken, confToken, nullptr, output); + return km_utils::kmError2ScopedAStatus(err); +} + +ScopedAStatus JavacardKeyMintOperation::abort() { + return km_utils::kmError2ScopedAStatus(jcKmOprImpl_->abort()); +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintOperation.h b/HAL/JavacardKeyMintOperation.h new file mode 100644 index 00000000..3cbb0406 --- /dev/null +++ b/HAL/JavacardKeyMintOperation.h @@ -0,0 +1,69 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +#include +#include +#include +#include +#include + +#define AES_BLOCK_SIZE 16 +#define DES_BLOCK_SIZE 8 +#define RSA_BUFFER_SIZE 256 +#define EC_BUFFER_SIZE 32 +#define MAX_CHUNK_SIZE 256 +namespace aidl::android::hardware::security::keymint { +using namespace ::javacard_keymaster; +using ::ndk::ScopedAStatus; +using secureclock::TimeStampToken; +using std::optional; +using std::shared_ptr; +using std::string; +using std::vector; + +class JavacardKeyMintOperation : public BnKeyMintOperation { + public: + explicit JavacardKeyMintOperation(std::shared_ptr jcKmOprImpl) + : jcKmOprImpl_(std::move(jcKmOprImpl)) {} + virtual ~JavacardKeyMintOperation() {} + + ScopedAStatus updateAad(const vector& input, + const optional& authToken, + const optional& timestampToken) override; + + ScopedAStatus update(const vector& input, const optional& authToken, + const optional& timestampToken, + vector* output) override; + + ScopedAStatus finish(const optional>& input, + const optional>& signature, + const optional& authToken, + const optional& timestampToken, + const optional>& confirmationToken, + vector* output) override; + + ScopedAStatus abort() override; + + private: + std::shared_ptr jcKmOprImpl_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintUtils.cpp b/HAL/JavacardKeyMintUtils.cpp new file mode 100644 index 00000000..33392e76 --- /dev/null +++ b/HAL/JavacardKeyMintUtils.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JavacardKeyMintUtils.h" +#include + +namespace aidl::android::hardware::security::keymint { + +keymaster_error_t legacyHardwareAuthToken(const HardwareAuthToken& aidlToken, + LegacyHardwareAuthToken* legacyToken) { + legacyToken->challenge = aidlToken.challenge; + legacyToken->user_id = aidlToken.userId; + legacyToken->authenticator_id = aidlToken.authenticatorId; + legacyToken->authenticator_type = + static_cast(aidlToken.authenticatorType); + legacyToken->timestamp = aidlToken.timestamp.milliSeconds; + Vec2KmBlob(aidlToken.mac, &legacyToken->mac); + return KM_ERROR_OK; +} + +keymaster_error_t encodeTimestampToken(const TimeStampToken& timestampToken, + vector* encodedToken) { + cppbor::Array array; + ::keymaster::TimestampToken token; + array.add(static_cast(timestampToken.challenge)); + array.add(static_cast(timestampToken.timestamp.milliSeconds)); + array.add(timestampToken.mac); + *encodedToken = array.encode(); + return KM_ERROR_OK; +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintUtils.h b/HAL/JavacardKeyMintUtils.h new file mode 100644 index 00000000..ca269a5e --- /dev/null +++ b/HAL/JavacardKeyMintUtils.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace ::keymaster; +using secureclock::TimeStampToken; +using std::vector; +using LegacyHardwareAuthToken = ::keymaster::HardwareAuthToken; + +inline void Vec2KmBlob(const vector& input, KeymasterBlob* blob) { + blob->Reset(input.size()); + memcpy(blob->writable_data(), input.data(), input.size()); +} + +keymaster_error_t legacyHardwareAuthToken(const HardwareAuthToken& aidlToken, + LegacyHardwareAuthToken* legacyToken); + +keymaster_error_t encodeTimestampToken(const TimeStampToken& timestampToken, + vector* encodedToken); + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeymaster.cpp b/HAL/JavacardKeymaster.cpp new file mode 100644 index 00000000..2f20b32e --- /dev/null +++ b/HAL/JavacardKeymaster.cpp @@ -0,0 +1,514 @@ +#include +#include +#include +#include + +namespace javacard_keymaster { +using cppbor::Array; +using cppbor::EncodedItem; +using keymaster::KeymasterBlob; +using keymaster::KeymasterKeyBlob; + +namespace { + +keymaster_error_t parseWrappedKey(const std::vector& wrappedKeyData, + std::vector& iv, std::vector& transitKey, + std::vector& secureKey, std::vector& tag, + AuthorizationSet& authList, keymaster_key_format_t& keyFormat, + std::vector& wrappedKeyDescription) { + KeymasterBlob kmIv; + KeymasterKeyBlob kmTransitKey; + KeymasterKeyBlob kmSecureKey; + KeymasterBlob kmTag; + KeymasterBlob kmWrappedKeyDescription; + + size_t keyDataLen = wrappedKeyData.size(); + uint8_t* keyData = keymaster::dup_buffer(wrappedKeyData.data(), keyDataLen); + keymaster_key_blob_t keyMaterial = {keyData, keyDataLen}; + + keymaster_error_t error = + parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, &kmSecureKey, &kmTag, + &authList, &keyFormat, &kmWrappedKeyDescription); + if (error != KM_ERROR_OK) return error; + blob2Vec(kmIv.data, kmIv.data_length, iv); + blob2Vec(kmTransitKey.key_material, kmTransitKey.key_material_size, transitKey); + blob2Vec(kmSecureKey.key_material, kmSecureKey.key_material_size, secureKey); + blob2Vec(kmTag.data, kmTag.data_length, tag); + blob2Vec(kmWrappedKeyDescription.data, kmWrappedKeyDescription.data_length, + wrappedKeyDescription); + + return KM_ERROR_OK; +} + +} // anonymous namespace + +keymaster_error_t JavacardKeymaster::handleErrorCode(keymaster_error_t err) { + // Check if secure element is reset + uint32_t errorCode = static_cast(0 - err); + bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); + + if (isSeResetOccurred) { + // Clear the operation table for Strongbox operations entries. + if (seResetListener_) { + seResetListener_->seResetEvent(); + } + // Unmask the power reset status flag. + errorCode &= ~SE_POWER_RESET_STATUS_FLAG; + } + return translateExtendedErrorsToHalErrors(static_cast(0 - errorCode)); +} + +std::tuple, keymaster_error_t> +JavacardKeymaster::sendRequest(Instruction ins) { + auto [item, err] = card_->sendRequest(ins); + return {std::move(item), handleErrorCode(err)}; +} + +std::tuple, keymaster_error_t> +JavacardKeymaster::sendRequest(Instruction ins, Array& request) { + auto [item, err] = card_->sendRequest(ins, request); + return {std::move(item), handleErrorCode(err)}; +} + +std::tuple, keymaster_error_t> JavacardKeymaster::getHardwareInfo() { + card_->initializeJavacard(); + return card_->sendRequest(Instruction::INS_GET_HW_INFO_CMD); +} + +keymaster_error_t JavacardKeymaster::addRngEntropy(const vector& data) { + cppbor::Array request; + // add key data + request.add(data); + auto [item, err] = sendRequest(Instruction::INS_ADD_RNG_ENTROPY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending addRngEntropy."; + } + return err; +} + +keymaster_error_t JavacardKeymaster::getHmacSharingParameters(vector* seed, + vector* nonce) { + card_->initializeJavacard(); + auto [item, err] = sendRequest(Instruction::INS_GET_SHARED_SECRET_PARAM_CMD); + if (err == KM_ERROR_OK && !cbor_.getSharedSecretParameters(item, 1, *seed, *nonce)) { + LOG(ERROR) << "Error in sending in getSharedSecretParameters."; + return KM_ERROR_UNKNOWN_ERROR; + } + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + return err; +} + +keymaster_error_t JavacardKeymaster::computeSharedHmac(const vector& params, + vector* secret) { + card_->initializeJavacard(); + cppbor::Array request; + cbor_.addSharedSecretParameters(request, params); + auto [item, err] = sendRequest(Instruction::INS_COMPUTE_SHARED_SECRET_CMD, request); + if (err == KM_ERROR_OK && !cbor_.getBinaryArray(item, 1, *secret)) { + LOG(ERROR) << "Error in sending in computeSharedHmac."; + return KM_ERROR_UNKNOWN_ERROR; + } + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + return err; +} + +keymaster_error_t JavacardKeymaster::generateKey(const AuthorizationSet& keyParams, + vector* retKeyblob, + AuthorizationSet* swEnforced, + AuthorizationSet* hwEnforced, + AuthorizationSet* teeEnforced) { + cppbor::Array array; + // add key params + cbor_.addKeyparameters(array, keyParams); + + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + + auto [item, err] = sendRequest(Instruction::INS_GENERATE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending generateKey."; + return err; + } + if (!cbor_.getBinaryArray(item, 1, *retKeyblob) || + !cbor_.getKeyCharacteristics(item, 2, *swEnforced, *hwEnforced, *teeEnforced)) { + LOG(ERROR) << "Error in decoding cbor response in generateKey."; + return KM_ERROR_UNKNOWN_ERROR; + } + return err; +} + +keymaster_error_t JavacardKeymaster::attestKey(Array& request, vector>* certChain) { + auto [item, err] = sendRequest(Instruction::INS_ATTEST_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending attestKey."; + return err; + } + if (!cbor_.getMultiBinaryArray(item, 1, *certChain)) { + LOG(ERROR) << "Error in decoding og response in attestKey."; + return KM_ERROR_UNKNOWN_ERROR; + } + return err; +} + +keymaster_error_t JavacardKeymaster::attestKey(Array& request, CertificateChain* certChain) { + auto [item, err] = sendRequest(Instruction::INS_ATTEST_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending attestKey."; + return err; + } + if (!cbor_.getCertificateChain(item, 1, *certChain)) { + LOG(ERROR) << "Error in decoding response in attestKey."; + return KM_ERROR_UNKNOWN_ERROR; + } + return err; +} + +keymaster_error_t JavacardKeymaster::attestKey(const vector& keyblob, + const AuthorizationSet& keyParams, + vector>* certChain) { + cppbor::Array array; + array.add(keyblob); + cbor_.addKeyparameters(array, keyParams); + return attestKey(array, certChain); +} + +keymaster_error_t JavacardKeymaster::attestKey(const vector& keyblob, + const AuthorizationSet& keyParams, + const optional& attestationKey, + CertificateChain* certChain) { + cppbor::Array array; + array.add(keyblob); + cbor_.addKeyparameters(array, keyParams); + if (attestationKey.has_value()) { + array.add(attestationKey->keyBlob); + cbor_.addKeyparameters(array, attestationKey->params); + array.add(attestationKey->issuerSubject); + } + return attestKey(array, certChain); +} + +keymaster_error_t JavacardKeymaster::getCertChain(vector>* certChain) { + vector certChainData; + auto [item, err] = sendRequest(Instruction::INS_GET_CERT_CHAIN_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending getCertChain."; + return err; + } + if (!cbor_.getBinaryArray(item, 1, certChainData)) { + LOG(ERROR) << "Error in decoding og response in getCertChain."; + return KM_ERROR_UNKNOWN_ERROR; + } + err = getCertificateChain(certChainData, *certChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getCertificateChain: " << (int32_t)err; + } + return err; +} + +keymaster_error_t +JavacardKeymaster::importKey(const AuthorizationSet& keyParams, + const keymaster_key_format_t keyFormat, const vector& keyData, + vector* retKeyblob, AuthorizationSet* swEnforced, + AuthorizationSet* hwEnforced, AuthorizationSet* teeEnforced) { + cppbor::Array array; + cbor_.addKeyparameters(array, keyParams); + array.add(static_cast(keyFormat)); + array.add(keyData); + + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + + auto [item, err] = sendRequest(Instruction::INS_IMPORT_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending importKey."; + return err; + } + if (!cbor_.getBinaryArray(item, 1, *retKeyblob) || + !cbor_.getKeyCharacteristics(item, 2, *swEnforced, *hwEnforced, *teeEnforced)) { + LOG(ERROR) << "Error in decoding the response in importKey."; + return KM_ERROR_UNKNOWN_ERROR; + } + return err; +} + +keymaster_error_t JavacardKeymaster::sendBeginImportWrappedKeyCmd( + const std::vector& transitKey, const std::vector& wrappingKeyBlob, + const std::vector& maskingKey, const AuthorizationSet& unwrappingParams) { + Array request; + request.add(std::vector(transitKey)); + request.add(std::vector(wrappingKeyBlob)); + request.add(std::vector(maskingKey)); + cbor_.addKeyparameters(request, unwrappingParams); + auto [item, err] = sendRequest(Instruction::INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending sendBeginImportWrappedKeyCmd err: " << (int32_t)err; + } + return err; +} + +std::tuple, keymaster_error_t> +JavacardKeymaster::sendFinishImportWrappedKeyCmd(const AuthorizationSet& keyParams, + const keymaster_key_format_t keyFormat, + const std::vector& secureKey, + const std::vector& tag, + const std::vector& iv, + const std::vector& wrappedKeyDescription, + int64_t passwordSid, int64_t biometricSid) { + Array request; + cbor_.addKeyparameters(request, keyParams); + request.add(static_cast(keyFormat)); + request.add(std::vector(secureKey)); + request.add(std::vector(tag)); + request.add(std::vector(iv)); + request.add(std::vector(wrappedKeyDescription)); + request.add(Uint(passwordSid)); + request.add(Uint(biometricSid)); + auto [item, err] = sendRequest(Instruction::INS_FINISH_IMPORT_WRAPPED_KEY_CMD, request); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending sendFinishImportWrappedKeyCmd err: " << (int32_t)err; + return {nullptr, err}; + } + return {std::move(item), err}; +} + +keymaster_error_t JavacardKeymaster::keymasterImportWrappedKey( + const vector& wrappedKeyData, const vector& wrappingKeyBlob, + const vector& maskingKey, const AuthorizationSet& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, vector* retKeyblob, + AuthorizationSet* swEnforced, AuthorizationSet* hwEnforced, AuthorizationSet* teeEnforced) { + cppbor::Array array; + std::vector iv; + std::vector transitKey; + std::vector secureKey; + std::vector tag; + AuthorizationSet authList; + keymaster_key_format_t keyFormat; + std::vector wrappedKeyDescription; + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + auto error = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, authList, + keyFormat, wrappedKeyDescription); + if (error != KM_ERROR_OK) { + LOG(ERROR) << "INS_IMPORT_WRAPPED_KEY_CMD error while parsing wrapped key status: " + << (int32_t)error; + return error; + } + cbor_.addKeyparameters(array, authList); + array.add(static_cast(keyFormat)); + array.add(secureKey); + array.add(tag); + array.add(iv); + array.add(transitKey); + array.add(std::vector(wrappingKeyBlob)); + array.add(std::vector(maskingKey)); + cbor_.addKeyparameters(array, unwrappingParams); + array.add(std::vector(wrappedKeyDescription)); + array.add(passwordSid); + array.add(biometricSid); + std::vector cborData = array.encode(); + + auto [item, err] = sendRequest(Instruction::INS_IMPORT_WRAPPED_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending importWrappedKey err: " << (int32_t)err; + return err; + } + if (!cbor_.getBinaryArray(item, 1, *retKeyblob) || + !cbor_.getKeyCharacteristics(item, 2, *swEnforced, *hwEnforced, *teeEnforced)) { + LOG(ERROR) << "Error in decoding the response in importWrappedKey."; + return KM_ERROR_UNKNOWN_ERROR; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymaster::importWrappedKey( + const vector& wrappedKeyData, const vector& wrappingKeyBlob, + const vector& maskingKey, const AuthorizationSet& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, vector* retKeyblob, + AuthorizationSet* swEnforced, AuthorizationSet* hwEnforced, AuthorizationSet* teeEnforced) { + cppbor::Array array; + std::unique_ptr item; + std::vector iv; + std::vector transitKey; + std::vector secureKey; + std::vector tag; + AuthorizationSet authList; + keymaster_key_format_t keyFormat; + std::vector wrappedKeyDescription; + auto err = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, authList, keyFormat, + wrappedKeyDescription); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "INS_IMPORT_WRAPPED_KEY_CMD error while parsing wrapped key status: " + << (int32_t)err; + return err; + } + // begin import + err = sendBeginImportWrappedKeyCmd(transitKey, wrappingKeyBlob, maskingKey, unwrappingParams); + if (err != KM_ERROR_OK) { + return err; + } + // Finish the import + std::tie(item, err) = sendFinishImportWrappedKeyCmd( + authList, keyFormat, secureKey, tag, iv, wrappedKeyDescription, passwordSid, biometricSid); + if (err != KM_ERROR_OK) { + return err; + } + if (!cbor_.getBinaryArray(item, 1, *retKeyblob) || + !cbor_.getKeyCharacteristics(item, 2, *swEnforced, *hwEnforced, *teeEnforced)) { + LOG(ERROR) << "Error in decoding the response in importWrappedKey."; + return KM_ERROR_UNKNOWN_ERROR; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymaster::upgradeKey(const vector& keyBlobToUpgrade, + const AuthorizationSet& upgradeParams, + vector* retKeyBlob) { + cppbor::Array array; + array.add(keyBlobToUpgrade); + cbor_.addKeyparameters(array, upgradeParams); + auto [item, err] = sendRequest(Instruction::INS_UPGRADE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending upgradeKey err: " << (int32_t)err; + return err; + } + if (!cbor_.getBinaryArray(item, 1, *retKeyBlob)) { + LOG(ERROR) << "Error in decoding the response in upgradeKey."; + return KM_ERROR_UNKNOWN_ERROR; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymaster::deleteKey(const vector& keyBlob) { + cppbor::Array array; + array.add(keyBlob); + auto [_, err] = sendRequest(Instruction::INS_DELETE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending deleteKey err: " << (int32_t)err; + return err; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymaster::deleteAllKeys() { + auto [_, err] = sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending deleteAllKeys err: " << (int32_t)err; + return err; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymaster::destroyAttestationIds() { + auto [_, err] = sendRequest(Instruction::INS_DESTROY_ATT_IDS_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending destroyAttestationIds err: " << (int32_t)err; + return err; + } + return KM_ERROR_OK; +} + +keymaster_error_t +JavacardKeymaster::deviceLocked(bool passwordOnly, + const vector& cborEncodedVerificationToken) { + Array array; + array.add(passwordOnly); + array.add(EncodedItem(cborEncodedVerificationToken)); + auto [_, err] = sendRequest(Instruction::INS_DEVICE_LOCKED_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending deviceLocked err: " << (int32_t)err; + return err; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymaster::earlyBootEnded() { + auto [_, err] = sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); + if (err != KM_ERROR_OK) { + // Incase of failure cache the event and send in the next immediate request to Applet. + isEarlyBootEventPending = true; + LOG(ERROR) << "Error in sending earlyBootEnded err: " << (int32_t)err; + return err; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymaster::getKeyCharacteristics(const std::vector& in_keyBlob, + const std::vector& in_appId, + const std::vector& in_appData, + AuthorizationSet* swEnforced, + AuthorizationSet* hwEnforced, + AuthorizationSet* teeEnforced) { + Array array; + array.add(in_keyBlob); + array.add(in_appId); + array.add(in_appData); + auto [item, err] = sendRequest(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending getKeyCharacteristics err: " << (int32_t)err; + return err; + } + if (!cbor_.getKeyCharacteristics(item, 1, *swEnforced, *hwEnforced, *teeEnforced)) { + LOG(ERROR) << "Error in decoding the response in getKeyCharacteristics."; + return KM_ERROR_UNKNOWN_ERROR; + } + return KM_ERROR_OK; +} + +keymaster_error_t +JavacardKeymaster::begin(keymaster_purpose_t purpose, const vector& keyBlob, + const AuthorizationSet& inParams, const HardwareAuthToken& hwAuthToken, + AuthorizationSet* outParams, + std::unique_ptr& outOperation) { + uint64_t operationHandle; + uint64_t bufMode = static_cast(BufferingMode::NONE); + uint64_t macLength = 0; + size_t size; + Array array; + + // Send earlyBootEnded if there is any pending earlybootEnded event. + handleSendEarlyBootEndedEvent(); + + // Encode input paramters into cbor array. + array.add(static_cast(purpose)); + array.add(std::vector(keyBlob)); + cbor_.addKeyparameters(array, inParams); + cbor_.addHardwareAuthToken(array, hwAuthToken); + auto [item, err] = sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending begin err: " << (int32_t)err; + return err; + } + if (!cbor_.getKeyParameters(item, 1, *outParams) || + !cbor_.getUint64(item, 2, operationHandle)) { + LOG(ERROR) << "Error in decoding the response in begin."; + return KM_ERROR_UNKNOWN_ERROR; + } + // Keymint Applet sends buffering mode and macLength parameters. + err = cbor_.getArraySize(item, size); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getting cbor array size "; + return err; + } + if ((size > 3) && (!cbor_.getUint64(item, 3, bufMode) || + !cbor_.getUint64(item, 4, macLength))) { + LOG(ERROR) << "Error in decoding the response in begin."; + return KM_ERROR_UNKNOWN_ERROR; + } + outOperation = std::make_unique( + operationHandle, static_cast(bufMode), macLength, card_, + static_cast(OperationType::PRIVATE_OPERATION), seResetListener_); + return KM_ERROR_OK; +} + +void JavacardKeymaster::handleSendEarlyBootEndedEvent() { + if (isEarlyBootEventPending) { + LOG(INFO) + << "JavacardKeymaster4Device::handleSendEarlyBootEndedEvent send earlyBootEnded Event."; + if (KM_ERROR_OK == earlyBootEnded()) { + isEarlyBootEventPending = false; + } + } +} +} // namespace javacard_keymaster diff --git a/HAL/JavacardKeymaster.h b/HAL/JavacardKeymaster.h new file mode 100644 index 00000000..adc9b936 --- /dev/null +++ b/HAL/JavacardKeymaster.h @@ -0,0 +1,127 @@ +#pragma once +#include "CborConverter.h" +#include "JavacardSecureElement.h" +#include +#include +#include + +namespace javacard_keymaster { +using ::javacard_keymaster::HmacSharingParameters; +using ::keymaster::AuthorizationSet; +using ::keymaster::HardwareAuthToken; +using std::optional; +using std::shared_ptr; +using std::vector; + +struct AttestationKey { + std::vector keyBlob; + AuthorizationSet params; + std::vector issuerSubject; +}; + +class JavacardKeymaster { + public: + explicit JavacardKeymaster(shared_ptr card) + : card_(card), seResetListener_(nullptr), isEarlyBootEventPending(false) { + card_->initializeJavacard(); + } + virtual ~JavacardKeymaster() {} + + std::tuple, keymaster_error_t> getHardwareInfo(); + + keymaster_error_t addRngEntropy(const vector& data); + + keymaster_error_t getHmacSharingParameters(vector* seed, vector* nonce); + + keymaster_error_t computeSharedHmac(const vector& params, + vector* secret); + + keymaster_error_t generateKey(const AuthorizationSet& keyParams, vector* retKeyblob, + AuthorizationSet* swEnforced, AuthorizationSet* hwEnforced, + AuthorizationSet* teeEnforced); + + keymaster_error_t attestKey(const vector& keyblob, const AuthorizationSet& keyParams, + const optional& attestationKey, + CertificateChain* certChain); + + keymaster_error_t attestKey(const vector& keyblob, const AuthorizationSet& keyParams, + vector>* certChain); + + keymaster_error_t getCertChain(vector>* certChain); + + keymaster_error_t importKey(const AuthorizationSet& keyParams, + const keymaster_key_format_t keyFormat, + const vector& keyData, vector* retKeyblob, + AuthorizationSet* swEnforced, AuthorizationSet* hwEnforced, + AuthorizationSet* teeEnforced); + + keymaster_error_t importWrappedKey(const vector& wrappedKeyData, + const vector& wrappingKeyBlob, + const vector& maskingKey, + const AuthorizationSet& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, + vector* retKeyblob, AuthorizationSet* swEnforced, + AuthorizationSet* hwEnforced, AuthorizationSet* teeEnforced); + + keymaster_error_t keymasterImportWrappedKey( + const vector& wrappedKeyData, const vector& wrappingKeyBlob, + const vector& maskingKey, const AuthorizationSet& unwrappingParams, + int64_t passwordSid, int64_t biometricSid, vector* retKeyblob, + AuthorizationSet* swEnforced, AuthorizationSet* hwEnforced, AuthorizationSet* teeEnforced); + + keymaster_error_t upgradeKey(const vector& keyBlobToUpgrade, + const AuthorizationSet& upgradeParams, + vector* retKeyBlob); + + keymaster_error_t deleteKey(const vector& keyBlob); + + keymaster_error_t deleteAllKeys(); + + keymaster_error_t destroyAttestationIds(); + + keymaster_error_t deviceLocked(bool passwordOnly, + const vector& cborEncodedVerificationToken); + + keymaster_error_t earlyBootEnded(); + + keymaster_error_t + getKeyCharacteristics(const vector& in_keyBlob, const vector& in_appId, + const vector& in_appData, AuthorizationSet* swEnforced, + AuthorizationSet* hwEnforced, AuthorizationSet* teeEnforced); + + keymaster_error_t begin(keymaster_purpose_t purpose, const vector& keyBlob, + const AuthorizationSet& inParams, const HardwareAuthToken& hwAuthToken, + AuthorizationSet* outParams, + std::unique_ptr& operation); + + void registerSeResetEventListener(shared_ptr listener) { + seResetListener_ = listener; + } + + private: + keymaster_error_t attestKey(Array& request, vector>* certChain); + keymaster_error_t attestKey(Array& request, CertificateChain* certChain); + keymaster_error_t handleErrorCode(keymaster_error_t err); + std::tuple, keymaster_error_t> sendRequest(Instruction ins); + std::tuple, keymaster_error_t> sendRequest(Instruction ins, + Array& request); + void handleSendEarlyBootEndedEvent(); + + keymaster_error_t sendBeginImportWrappedKeyCmd(const std::vector& transitKey, + const std::vector& wrappingKeyBlob, + const std::vector& maskingKey, + const AuthorizationSet& unwrappingParams); + + std::tuple, keymaster_error_t> sendFinishImportWrappedKeyCmd( + const AuthorizationSet& keyParams, const keymaster_key_format_t keyFormat, + const std::vector& secureKey, const std::vector& tag, + const std::vector& iv, const std::vector& wrappedKeyDescription, + int64_t passwordSid, int64_t biometricSid); + + const shared_ptr card_; + CborConverter cbor_; + shared_ptr seResetListener_; + bool isEarlyBootEventPending; +}; + +} // namespace javacard_keymaster diff --git a/HAL/JavacardKeymaster4Device.cpp b/HAL/JavacardKeymaster4Device.cpp new file mode 100644 index 00000000..0bbe0763 --- /dev/null +++ b/HAL/JavacardKeymaster4Device.cpp @@ -0,0 +1,839 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define JAVACARD_KEYMASTER_NAME "JavacardKeymaster4.1Device v1.0" +#define JAVACARD_KEYMASTER_AUTHOR "Android Open Source Project" +#define PROP_BUILD_QEMU "ro.kernel.qemu" +#define PROP_BUILD_FINGERPRINT "ro.build.fingerprint" + +namespace keymaster { +namespace V4_1 { +namespace javacard { +using namespace ::javacard_keymaster; +using android::hardware::keymaster::V4_0::support::authToken2HidlVec; +using std::string; +using std::vector; + +constexpr size_t kOperationTableSize = 4; +constexpr int kKeyblobKeyCharsOffset = 3; + +struct KM_AUTH_LIST_Delete { + void operator()(KM_AUTH_LIST* p) { KM_AUTH_LIST_free(p); } +}; + +namespace { + +inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) { + return static_cast(value); +} + +inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) { + return static_cast(value); +} + +inline keymaster_tag_t legacy_enum_conversion(const Tag value) { + return keymaster_tag_t(value); +} + +inline Tag legacy_enum_conversion(const keymaster_tag_t value) { + return Tag(value); +} + +inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) { + return keymaster_tag_get_type(tag); +} + +inline keymaster_security_level_t legacy_enum_conversion(const SecurityLevel value) { + return static_cast(value); +} + +inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) { + return static_cast(value); +} + +inline void hidlVec2KmBlob(const hidl_vec& input, KeymasterBlob* blob) { + blob->Reset(input.size()); + memcpy(blob->writable_data(), input.data(), input.size()); +} + +void legacyHardwareAuthToken(const HardwareAuthToken& hidlToken, + ::keymaster::HardwareAuthToken* legacyToken) { + legacyToken->challenge = hidlToken.challenge; + legacyToken->user_id = hidlToken.userId; + legacyToken->authenticator_id = hidlToken.authenticatorId; + legacyToken->authenticator_type = + static_cast(hidlToken.authenticatorType); + legacyToken->timestamp = hidlToken.timestamp; + hidlVec2KmBlob(hidlToken.mac, &legacyToken->mac); +} + +keymaster_key_param_set_t hidlKeyParams2Km(const hidl_vec& keyParams) { + keymaster_key_param_set_t set; + + set.params = new keymaster_key_param_t[keyParams.size()]; + set.length = keyParams.size(); + + for (size_t i = 0; i < keyParams.size(); ++i) { + auto tag = legacy_enum_conversion(keyParams[i].tag); + switch (typeFromTag(tag)) { + case KM_ENUM: + case KM_ENUM_REP: + set.params[i] = keymaster_param_enum(tag, keyParams[i].f.integer); + break; + case KM_UINT: + case KM_UINT_REP: + set.params[i] = keymaster_param_int(tag, keyParams[i].f.integer); + break; + case KM_ULONG: + case KM_ULONG_REP: + set.params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger); + break; + case KM_DATE: + set.params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime); + break; + case KM_BOOL: + if (keyParams[i].f.boolValue) + set.params[i] = keymaster_param_bool(tag); + else + set.params[i].tag = KM_TAG_INVALID; + break; + case KM_BIGNUM: + case KM_BYTES: + set.params[i] = + keymaster_param_blob(tag, &keyParams[i].blob[0], keyParams[i].blob.size()); + break; + case KM_INVALID: + default: + set.params[i].tag = KM_TAG_INVALID; + /* just skip */ + break; + } + } + + return set; +} + +static inline hidl_vec kmParamSet2Hidl(const keymaster_key_param_set_t& set) { + hidl_vec result; + if (set.length == 0 || set.params == nullptr) return result; + + result.resize(set.length); + keymaster_key_param_t* params = set.params; + for (size_t i = 0; i < set.length; ++i) { + auto tag = params[i].tag; + result[i].tag = legacy_enum_conversion(tag); + switch (typeFromTag(tag)) { + case KM_ENUM: + case KM_ENUM_REP: + result[i].f.integer = params[i].enumerated; + break; + case KM_UINT: + case KM_UINT_REP: + result[i].f.integer = params[i].integer; + break; + case KM_ULONG: + case KM_ULONG_REP: + result[i].f.longInteger = params[i].long_integer; + break; + case KM_DATE: + result[i].f.dateTime = params[i].date_time; + break; + case KM_BOOL: + result[i].f.boolValue = params[i].boolean; + break; + case KM_BIGNUM: + case KM_BYTES: + result[i].blob = std::vector(params[i].blob.data, + params[i].blob.data + params[i].blob.data_length); + break; + case KM_INVALID: + default: + params[i].tag = KM_TAG_INVALID; + /* just skip */ + break; + } + } + return result; +} + +class KmParamSet : public keymaster_key_param_set_t { + public: + explicit KmParamSet(const hidl_vec& keyParams) + : keymaster_key_param_set_t(hidlKeyParams2Km(keyParams)) {} + KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} { + other.length = 0; + other.params = nullptr; + } + KmParamSet(const KmParamSet&) = delete; + ~KmParamSet() { delete[] params; } +}; + +static keymaster_error_t encodeParametersVerified(const VerificationToken& verificationToken, + std::vector& asn1ParamsVerified) { + if (verificationToken.parametersVerified.size() > 0) { + AuthorizationSet paramSet; + KeymasterBlob derBlob; + UniquePtr kmAuthList(KM_AUTH_LIST_new()); + + paramSet.Reinitialize(KmParamSet(verificationToken.parametersVerified)); + + auto err = build_auth_list(paramSet, kmAuthList.get()); + if (err != KM_ERROR_OK) { + return err; + } + int len = i2d_KM_AUTH_LIST(kmAuthList.get(), nullptr); + if (len < 0) { + return TranslateLastOpenSslError(); + } + + if (!derBlob.Reset(len)) { + return KM_ERROR_MEMORY_ALLOCATION_FAILED; + } + + uint8_t* p = derBlob.writable_data(); + len = i2d_KM_AUTH_LIST(kmAuthList.get(), &p); + if (len < 0) { + return TranslateLastOpenSslError(); + } + asn1ParamsVerified.insert(asn1ParamsVerified.begin(), p, p + len); + derBlob.release(); + } + return KM_ERROR_OK; +} + +keymaster_error_t getOperationInfo(keymaster_purpose_t purpose, const AuthorizationSet& inParams, + const AuthorizationSet& keyBlobParams, uint32_t& buferingMode, + uint32_t& macLength) { + BufferingMode bufMode = BufferingMode::NONE; + keymaster_algorithm_t keyAlgo; + keymaster_digest_t digest = KM_DIGEST_NONE; + keymaster_padding_t padding = KM_PAD_NONE; + keymaster_block_mode_t blockMode = KM_MODE_ECB; + macLength = 0; + if (!keyBlobParams.GetTagValue(TAG_ALGORITHM, &keyAlgo)) { + return KM_ERROR_UNKNOWN_ERROR; + } + inParams.GetTagValue(TAG_DIGEST, &digest); + inParams.GetTagValue(TAG_PADDING, &padding); + inParams.GetTagValue(TAG_BLOCK_MODE, &blockMode); + inParams.GetTagValue(TAG_MAC_LENGTH, &macLength); + macLength = (macLength / 8); + switch (keyAlgo) { + case KM_ALGORITHM_AES: + if (purpose == KM_PURPOSE_ENCRYPT && padding == KM_PAD_PKCS7) { + bufMode = BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED; + } else if (purpose == KM_PURPOSE_DECRYPT && padding == KM_PAD_PKCS7) { + bufMode = BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED; + } else if (purpose == KM_PURPOSE_DECRYPT && blockMode == KM_MODE_GCM) { + bufMode = BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED; + } + break; + case KM_ALGORITHM_TRIPLE_DES: + if (purpose == KM_PURPOSE_ENCRYPT && padding == KM_PAD_PKCS7) { + bufMode = BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED; + } else if (purpose == KM_PURPOSE_DECRYPT && padding == KM_PAD_PKCS7) { + bufMode = BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED; + } + break; + case KM_ALGORITHM_RSA: + if (purpose == KM_PURPOSE_DECRYPT || digest == KM_DIGEST_NONE) { + bufMode = BufferingMode::RSA_NO_DIGEST; + } + break; + case KM_ALGORITHM_EC: + if (digest == KM_DIGEST_NONE && purpose == KM_PURPOSE_SIGN) { + bufMode = BufferingMode::EC_NO_DIGEST; + } + break; + default: + break; + } + buferingMode = static_cast(bufMode); + return KM_ERROR_OK; +} + +} // anonymous namespace + +JavacardKeymaster4Device::JavacardKeymaster4Device(shared_ptr jcImpl) + : softKm_(new ::keymaster::AndroidKeymaster( + []() -> auto{ + auto context = new JavaCardSoftKeymasterContext(); + context->SetSystemVersion(getOsVersion(), getOsPatchlevel()); + return context; + }(), + kOperationTableSize, + keymaster::MessageVersion(keymaster::KmVersion::KEYMASTER_4_1, 0 /* km_date */))), + jcImpl_(jcImpl) { + std::shared_ptr listener( + dynamic_cast(this)); + jcImpl_->registerSeResetEventListener(listener); +} + +JavacardKeymaster4Device::~JavacardKeymaster4Device() {} + +// Methods from IKeymasterDevice follow. +Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) { + uint64_t securityLevel = static_cast(SecurityLevel::STRONGBOX); + hidl_string jcKeymasterName; + hidl_string jcKeymasterAuthor; + string name; + string author; + auto [item, err] = jcImpl_->getHardwareInfo(); + if (err != KM_ERROR_OK || !cbor_.getUint64(item, 1, securityLevel) || + !cbor_.getBinaryArray(item, 2, name) || !cbor_.getBinaryArray(item, 3, author)) { + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + _hidl_cb(SecurityLevel::STRONGBOX, JAVACARD_KEYMASTER_NAME, JAVACARD_KEYMASTER_AUTHOR); + return Void(); + } + jcKeymasterName = name; + jcKeymasterAuthor = author; + _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); + return Void(); +} + +Return +JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) { + HmacSharingParameters hmacSharingParameters; + vector nonce; + vector seed; + auto err = jcImpl_->getHmacSharingParameters(&seed, &nonce); + hmacSharingParameters.seed = seed; + memcpy(hmacSharingParameters.nonce.data(), nonce.data(), nonce.size()); + // TODO + // Send earlyBootEnded if there is any pending earlybootEnded event. + // handleSendEarlyBootEndedEvent(); + _hidl_cb(legacy_enum_conversion(err), hmacSharingParameters); + return Void(); +} + +Return +JavacardKeymaster4Device::computeSharedHmac(const hidl_vec& params, + computeSharedHmac_cb _hidl_cb) { + std::vector secret; + vector<::javacard_keymaster::HmacSharingParameters> reqParams(params.size()); + for (size_t i = 0; i < params.size(); i++) { + reqParams[i].seed = params[i].seed; + reqParams[i].nonce.insert(reqParams[i].nonce.end(), params[i].nonce.data(), + params[i].nonce.data() + params[i].nonce.elementCount()); + } + auto err = jcImpl_->computeSharedHmac(reqParams, &secret); + // TODO + // Send earlyBootEnded if there is any pending earlybootEnded event. + // handleSendEarlyBootEndedEvent(); + _hidl_cb(legacy_enum_conversion(err), secret); + return Void(); +} + +Return JavacardKeymaster4Device::addRngEntropy(const hidl_vec& data) { + auto err = jcImpl_->addRngEntropy(data); + return legacy_enum_conversion(err); +} + +Return JavacardKeymaster4Device::generateKey(const hidl_vec& keyParams, + generateKey_cb _hidl_cb) { + AuthorizationSet paramSet; + AuthorizationSet swEnforced; + AuthorizationSet hwEnforced; + AuthorizationSet teeEnforced; + vector retKeyblob; + paramSet.Reinitialize(KmParamSet(keyParams)); + if (!paramSet.Contains(KM_TAG_CREATION_DATETIME) && + !paramSet.Contains(KM_TAG_ACTIVE_DATETIME)) { + keymaster_key_param_t dateTime; + dateTime.tag = KM_TAG_CREATION_DATETIME; + dateTime.date_time = java_time(time(nullptr)); + paramSet.push_back(dateTime); + } + auto err = jcImpl_->generateKey(paramSet, &retKeyblob, &swEnforced, &hwEnforced, &teeEnforced); + KeyCharacteristics keyCharacteristics; + keyCharacteristics.softwareEnforced = kmParamSet2Hidl(swEnforced); + keyCharacteristics.hardwareEnforced = kmParamSet2Hidl(hwEnforced); + _hidl_cb(legacy_enum_conversion(err), retKeyblob, keyCharacteristics); + return Void(); +} + +Return JavacardKeymaster4Device::importKey(const hidl_vec& keyParams, + KeyFormat keyFormat, + const hidl_vec& keyData, + importKey_cb _hidl_cb) { + AuthorizationSet paramSet; + AuthorizationSet swEnforced; + AuthorizationSet hwEnforced; + AuthorizationSet teeEnforced; + vector retKeyblob; + paramSet.Reinitialize(KmParamSet(keyParams)); + if (!paramSet.Contains(KM_TAG_CREATION_DATETIME) && + !paramSet.Contains(KM_TAG_ACTIVE_DATETIME)) { + keymaster_key_param_t dateTime; + dateTime.tag = KM_TAG_CREATION_DATETIME; + dateTime.date_time = java_time(time(nullptr)); + paramSet.push_back(dateTime); + } + auto err = jcImpl_->importKey(paramSet, legacy_enum_conversion(keyFormat), keyData, &retKeyblob, + &swEnforced, &hwEnforced, &teeEnforced); + KeyCharacteristics keyCharacteristics; + keyCharacteristics.softwareEnforced = kmParamSet2Hidl(swEnforced); + keyCharacteristics.hardwareEnforced = kmParamSet2Hidl(hwEnforced); + _hidl_cb(legacy_enum_conversion(err), retKeyblob, keyCharacteristics); + return Void(); +} + +Return JavacardKeymaster4Device::importWrappedKey( + const hidl_vec& wrappedKeyData, const hidl_vec& wrappingKeyBlob, + const hidl_vec& maskingKey, const hidl_vec& unwrappingParams, + uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) { + AuthorizationSet paramSet; + AuthorizationSet swEnforced; + AuthorizationSet hwEnforced; + AuthorizationSet teeEnforced; + vector retKeyblob; + paramSet.Reinitialize(KmParamSet(unwrappingParams)); + auto err = jcImpl_->keymasterImportWrappedKey(wrappedKeyData, wrappingKeyBlob, maskingKey, + paramSet, passwordSid, biometricSid, &retKeyblob, + &swEnforced, &hwEnforced, &teeEnforced); + KeyCharacteristics keyCharacteristics; + keyCharacteristics.softwareEnforced = kmParamSet2Hidl(swEnforced); + keyCharacteristics.hardwareEnforced = kmParamSet2Hidl(hwEnforced); + _hidl_cb(legacy_enum_conversion(err), retKeyblob, keyCharacteristics); + return Void(); +} + +Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToAttest, + const hidl_vec& attestParams, + attestKey_cb _hidl_cb) { + AuthorizationSet paramSet; + vector> certChain; + hidl_vec> outCertChain; + paramSet.Reinitialize(KmParamSet(attestParams)); + auto err = jcImpl_->attestKey(keyToAttest, paramSet, &certChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "JavacardKeymaster4Device attestKey Failed in attestKey err: " + << (int32_t)err; + _hidl_cb(legacy_enum_conversion(err), outCertChain); + return Void(); + } + err = jcImpl_->getCertChain(&certChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "JavacardKeymaster4Device attestKey Failed in getCertChain err: " + << (int32_t)err; + _hidl_cb(legacy_enum_conversion(err), outCertChain); + return Void(); + } + outCertChain.resize(certChain.size()); + for (int i = 0; i < certChain.size(); i++) { + outCertChain[i] = certChain[i]; + } + _hidl_cb(legacy_enum_conversion(err), outCertChain); + return Void(); +} + +Return JavacardKeymaster4Device::upgradeKey(const hidl_vec& keyBlobToUpgrade, + const hidl_vec& upgradeParams, + upgradeKey_cb _hidl_cb) { + AuthorizationSet paramSet; + paramSet.Reinitialize(KmParamSet(upgradeParams)); + vector upgradedKeyBlob; + auto err = jcImpl_->upgradeKey(keyBlobToUpgrade, paramSet, &upgradedKeyBlob); + _hidl_cb(legacy_enum_conversion(err), upgradedKeyBlob); + return Void(); +} + +Return JavacardKeymaster4Device::deleteKey(const hidl_vec& keyBlob) { + auto err = jcImpl_->deleteKey(keyBlob); + return legacy_enum_conversion(err); +} + +Return JavacardKeymaster4Device::deleteAllKeys() { + auto err = jcImpl_->deleteAllKeys(); + return legacy_enum_conversion(err); +} + +Return JavacardKeymaster4Device::destroyAttestationIds() { + auto err = jcImpl_->destroyAttestationIds(); + return legacy_enum_conversion(err); +} + +Return JavacardKeymaster4Device::getKeyCharacteristics(const hidl_vec& keyBlob, + const hidl_vec& clientId, + const hidl_vec& appData, + getKeyCharacteristics_cb _hidl_cb) { + AuthorizationSet swEnforced; + AuthorizationSet hwEnforced; + AuthorizationSet teeEnforced; + auto err = jcImpl_->getKeyCharacteristics(keyBlob, clientId, appData, &swEnforced, &hwEnforced, + &teeEnforced); + KeyCharacteristics keyCharacteristics; + keyCharacteristics.softwareEnforced = kmParamSet2Hidl(swEnforced); + keyCharacteristics.hardwareEnforced = kmParamSet2Hidl(hwEnforced); + _hidl_cb(legacy_enum_conversion(err), keyCharacteristics); + return Void(); +} + +Return JavacardKeymaster4Device::verifyAuthorization(uint64_t, const hidl_vec&, + const HardwareAuthToken&, + verifyAuthorization_cb _hidl_cb) { + VerificationToken verificationToken; + LOG(DEBUG) << "Verify authorizations UNIMPLEMENTED"; + _hidl_cb(ErrorCode::UNIMPLEMENTED, verificationToken); + return Void(); +} + +Return JavacardKeymaster4Device::exportKey(KeyFormat exportFormat, + const hidl_vec& keyBlob, + const hidl_vec& clientId, + const hidl_vec& appData, + exportKey_cb _hidl_cb) { + ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; + hidl_vec resultKeyBlob; + + // Check if keyblob is corrupted + getKeyCharacteristics( + keyBlob, clientId, appData, + [&](ErrorCode error, KeyCharacteristics /*keyCharacteristics*/) { errorCode = error; }); + + if (errorCode != ErrorCode::OK) { + LOG(ERROR) << "Error in exportKey: " << (int32_t)errorCode; + _hidl_cb(errorCode, resultKeyBlob); + return Void(); + } + + ExportKeyRequest request(softKm_->message_version()); + request.key_format = legacy_enum_conversion(exportFormat); + request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); + + ExportKeyResponse response(softKm_->message_version()); + softKm_->ExportKey(request, &response); + + if (response.error == KM_ERROR_INCOMPATIBLE_ALGORITHM) { + // Symmetric Keys cannot be exported. + response.error = KM_ERROR_UNSUPPORTED_KEY_FORMAT; + LOG(ERROR) << "error in exportKey: unsupported algorithm or key format"; + } + if (response.error == KM_ERROR_OK) { + resultKeyBlob.setToExternal(response.key_data, response.key_data_length); + } + errorCode = legacy_enum_conversion(response.error); + LOG(DEBUG) << "exportKey status: " << (int32_t)errorCode; + _hidl_cb(errorCode, resultKeyBlob); + return Void(); +} + +keymaster_error_t JavacardKeymaster4Device::handleBeginPublicKeyOperation( + KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, + const HardwareAuthToken& authToken, hidl_vec& outParams, + uint64_t& operationHandle, std::unique_ptr& operation) { + BeginOperationRequest request(softKm_->message_version()); + request.purpose = legacy_enum_conversion(purpose); + request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); + request.additional_params.Reinitialize(KmParamSet(inParams)); + hidl_vec hidl_vec_token = authToken2HidlVec(authToken); + request.additional_params.push_back( + TAG_AUTH_TOKEN, reinterpret_cast(hidl_vec_token.data()), hidl_vec_token.size()); + + BeginOperationResponse response(softKm_->message_version()); + softKm_->BeginOperation(request, &response); + LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD softkm BeginOperation status: " + << (int32_t)response.error; + if (response.error == KM_ERROR_OK) { + outParams = kmParamSet2Hidl(response.output_params); + operationHandle = response.op_handle; + operation = std::make_unique( + operationHandle, BufferingMode::NONE, 0, nullptr, OperationType::PUBLIC_OPERATION, + softKm_); + } else { + LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in softkm BeginOperation status: " + << (int32_t)response.error; + } + return response.error; +} + +keymaster_error_t JavacardKeymaster4Device::handleBeginPrivateKeyOperation( + KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, + const HardwareAuthToken& authToken, hidl_vec& outParams, + uint64_t& operationHandle, std::unique_ptr& operation) { + AuthorizationSet paramSet; + AuthorizationSet authSetParams; + paramSet.Reinitialize(KmParamSet(inParams)); + ::keymaster::HardwareAuthToken legacyToken; + legacyHardwareAuthToken(authToken, &legacyToken); + auto err = jcImpl_->begin(legacy_enum_conversion(purpose), keyBlob, paramSet, legacyToken, + &authSetParams, operation); + if (err == KM_ERROR_OK) { + // Decode keyblob to get the BufferingMode and macLength properties. + AuthorizationSet swEnforced; + AuthorizationSet teeEnforced; + AuthorizationSet hwEnforced; + uint32_t bufMode; + uint32_t macLength; + auto [item, _] = cbor_.decodeKeyblob(keyBlob); + if (item == nullptr) { + return KM_ERROR_UNKNOWN_ERROR; + } + if (!cbor_.getKeyCharacteristics(item, kKeyblobKeyCharsOffset, swEnforced, hwEnforced, + teeEnforced)) { + return KM_ERROR_INVALID_KEY_BLOB; + } + err = getOperationInfo(static_cast(purpose), paramSet, hwEnforced, + bufMode, macLength); + if (err != KM_ERROR_OK) { + return err; + } + operation->setBufferingMode(static_cast(bufMode)); + operation->setMacLength(macLength); + // Get the operation handle from the Operation. + operationHandle = operation->getOpertionHandle(); + outParams = kmParamSet2Hidl(authSetParams); + } + return err; +} + +keymaster_error_t JavacardKeymaster4Device::handleBeginOperation( + KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, + const HardwareAuthToken& authToken, hidl_vec& outParams, + uint64_t& operationHandle, OperationType& operType, + std::unique_ptr& operation) { + keymaster_error_t err = KM_ERROR_UNKNOWN_ERROR; + if (operType == OperationType::PRIVATE_OPERATION) { + err = handleBeginPrivateKeyOperation(purpose, keyBlob, inParams, authToken, outParams, + operationHandle, operation); + if (err == ExtendedErrors::PUBLIC_KEY_OPERATION) { + // Handle public key operation. + operType = OperationType::PUBLIC_OPERATION; + } + } + + if (operType == OperationType::PUBLIC_OPERATION) { + err = handleBeginPublicKeyOperation(purpose, keyBlob, inParams, authToken, outParams, + operationHandle, operation); + } + return err; +} + +bool JavacardKeymaster4Device::isOperationHandleExists(uint64_t opHandle) { + if (operationTable_.end() == operationTable_.find(opHandle)) { + return false; + } + return true; +} + +Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, + const HardwareAuthToken& authToken, + begin_cb _hidl_cb) { + uint64_t operationHandle = 0; + OperationType operType = OperationType::PRIVATE_OPERATION; + std::unique_ptr operation; + hidl_vec outParams; + LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD purpose: " << (int32_t)purpose; + auto err = handleBeginOperation(purpose, keyBlob, inParams, authToken, outParams, + operationHandle, operType, operation); + if (err == KM_ERROR_OK && isOperationHandleExists(operationHandle)) { + LOG(DEBUG) << "Operation handle " << operationHandle + << "already exists" + "in the opertion table. so aborting this opertaion."; + // abort the operation. + err = abortOperation(operationHandle); + if (err == KM_ERROR_OK) { + // retry begin to get an another operation handle. + err = handleBeginOperation(purpose, keyBlob, inParams, authToken, outParams, + operationHandle, operType, operation); + if (err == KM_ERROR_OK && isOperationHandleExists(operationHandle)) { + err = KM_ERROR_UNKNOWN_ERROR; + LOG(ERROR) << "INS_BEGIN_OPERATION_CMD: Failed in begin operation as the" + "operation handle already exists in the operation table." + << (int32_t)err; + // abort the operation. + auto abortErr = abortOperation(operationHandle); + if (abortErr != KM_ERROR_OK) { + LOG(ERROR) << "Fail to abort the operation."; + err = abortErr; + } + } + } + } + if (err == KM_ERROR_OK) { + operationTable_[operationHandle] = std::move(operation); + } + _hidl_cb(legacy_enum_conversion(err), outParams, operationHandle); + return Void(); +} + +keymaster_error_t JavacardKeymaster4Device::abortOperation(uint64_t operationHandle) { + auto it = operationTable_.find(operationHandle); + if (it == operationTable_.end()) { + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid " + "operation handle is passed or if" + << " secure element reset occurred."; + return KM_ERROR_INVALID_OPERATION_HANDLE; + } + auto err = it->second->abort(); + if (err == KM_ERROR_OK) { + /* Delete the entry on this operationHandle */ + operationTable_.erase(operationHandle); + } + return err; +} + +Return JavacardKeymaster4Device::abort(uint64_t operationHandle) { + return legacy_enum_conversion(abortOperation(operationHandle)); +} + +Return +JavacardKeymaster4Device::update(uint64_t operationHandle, const hidl_vec& inParams, + const hidl_vec& input, const HardwareAuthToken& authToken, + const VerificationToken& verificationToken, update_cb _hidl_cb) { + hidl_vec outParams; + AuthorizationSet authSetOutParams; + uint32_t inputConsumed = 0; + vector output; + vector encodedVerificationToken; + auto it = operationTable_.find(operationHandle); + if (it == operationTable_.end()) { + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle " + "is passed or if" + << " secure element reset occurred."; + _hidl_cb(ErrorCode::INVALID_OPERATION_HANDLE, inputConsumed, outParams, output); + return Void(); + } + AuthorizationSet paramSet; + paramSet.Reinitialize(KmParamSet(inParams)); + ::keymaster::HardwareAuthToken legacyHwToken; + legacyHardwareAuthToken(authToken, &legacyHwToken); + auto err = encodeVerificationToken(verificationToken, &encodedVerificationToken); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "In update failed to encode VerificationToken" << (int32_t)err; + _hidl_cb(legacy_enum_conversion(err), inputConsumed, outParams, output); + return Void(); + } + err = it->second->update(input, std::optional(paramSet), legacyHwToken, + encodedVerificationToken, &authSetOutParams, &inputConsumed, &output); + if (err != KM_ERROR_OK) { + /* Delete the entry on this operationHandle */ + operationTable_.erase(operationHandle); + } + outParams = kmParamSet2Hidl(authSetOutParams); + _hidl_cb(legacy_enum_conversion(err), input.size(), outParams, output); + return Void(); +} + +Return +JavacardKeymaster4Device::finish(uint64_t operationHandle, const hidl_vec& inParams, + const hidl_vec& input, const hidl_vec& signature, + const HardwareAuthToken& authToken, + const VerificationToken& verificationToken, finish_cb _hidl_cb) { + hidl_vec outParams; + AuthorizationSet authSetOutParams; + vector output; + vector encodedVerificationToken; + auto it = operationTable_.find(operationHandle); + if (it == operationTable_.end()) { + LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle " + "is passed or if" + << " secure element reset occurred."; + _hidl_cb(ErrorCode::INVALID_OPERATION_HANDLE, outParams, output); + return Void(); + } + AuthorizationSet paramSet; + paramSet.Reinitialize(KmParamSet(inParams)); + ::keymaster::HardwareAuthToken legacyHwToken; + legacyHardwareAuthToken(authToken, &legacyHwToken); + auto err = encodeVerificationToken(verificationToken, &encodedVerificationToken); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "In finish failed to encode VerificationToken" << (int32_t)err; + _hidl_cb(legacy_enum_conversion(err), outParams, output); + return Void(); + } + err = + it->second->finish(input, std::optional(paramSet), signature, + legacyHwToken, encodedVerificationToken, {}, &authSetOutParams, &output); + /* Delete the entry on this operationHandle */ + operationTable_.erase(operationHandle); + outParams = kmParamSet2Hidl(authSetOutParams); + _hidl_cb(legacy_enum_conversion(err), outParams, output); + return Void(); +} + +// Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. +Return<::android::hardware::keymaster::V4_1::ErrorCode> +JavacardKeymaster4Device::deviceLocked(bool passwordOnly, + const VerificationToken& verificationToken) { + vector encodedVerificationToken; + auto err = encodeVerificationToken(verificationToken, &encodedVerificationToken); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "In deviceLocked failed to encode VerificationToken" << (int32_t)err; + return static_cast(err); + } + err = jcImpl_->deviceLocked(passwordOnly, encodedVerificationToken); + return static_cast(err); +} + +Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::earlyBootEnded() { + auto err = jcImpl_->earlyBootEnded(); + return static_cast(err); +} + +keymaster_error_t +JavacardKeymaster4Device::encodeVerificationToken(const VerificationToken& verificationToken, + vector* encodedToken) { + vector asn1ParamsVerified; + auto err = encodeParametersVerified(verificationToken, asn1ParamsVerified); + if (err != KM_ERROR_OK) { + LOG(DEBUG) << "INS_DEVICE_LOCKED_CMD: Error in encodeParametersVerified, status: " + << (int32_t)err; + return err; + } + cppbor::Array array; + ::keymaster::VerificationToken token; + token.challenge = verificationToken.challenge; + token.timestamp = verificationToken.timestamp; + token.security_level = legacy_enum_conversion(verificationToken.securityLevel); + hidlVec2KmBlob(verificationToken.mac, &token.mac); + cbor_.addVerificationToken(array, token, asn1ParamsVerified); + *encodedToken = array.encode(); + return KM_ERROR_OK; +} + +void JavacardKeymaster4Device::seResetEvent() { + // clear strongbox entires. + LOG(INFO) + << "Secure Element reset or applet upgrade detected. Removing existing operation handles"; + auto it = operationTable_.begin(); + while (it != operationTable_.end()) { + if (it->second->getOperationType() == + ::javacard_keymaster::OperationType::PRIVATE_OPERATION) { // Strongbox operation + LOG(INFO) << "operation handle: " << it->first << " is removed"; + it = operationTable_.erase(it); + } else { + ++it; + } + } +} + +} // namespace javacard +} // namespace V4_1 +} // namespace keymaster diff --git a/HAL/JavacardKeymaster4Device.h b/HAL/JavacardKeymaster4Device.h new file mode 100644 index 00000000..54da83f3 --- /dev/null +++ b/HAL/JavacardKeymaster4Device.h @@ -0,0 +1,158 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ + +#pragma once + +#include "CborConverter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace keymaster { +namespace V4_1 { +namespace javacard { +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::javacard_keymaster::CborConverter; +using ::javacard_keymaster::IJavacardSeResetListener; +using ::javacard_keymaster::JavacardKeymaster; +using ::javacard_keymaster::JavacardKeymasterOperation; +using ::javacard_keymaster::OperationType; +using std::shared_ptr; + +using ::android::hardware::keymaster::V4_0::ErrorCode; +using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType; +using ::android::hardware::keymaster::V4_0::HardwareAuthToken; +using ::android::hardware::keymaster::V4_0::HmacSharingParameters; +using ::android::hardware::keymaster::V4_0::KeyCharacteristics; +using ::android::hardware::keymaster::V4_0::KeyFormat; +using ::android::hardware::keymaster::V4_0::KeyParameter; +using ::android::hardware::keymaster::V4_0::KeyPurpose; +using ::android::hardware::keymaster::V4_0::OperationHandle; +using ::android::hardware::keymaster::V4_0::SecurityLevel; +using ::android::hardware::keymaster::V4_0::Tag; +using ::android::hardware::keymaster::V4_0::VerificationToken; +using ::android::hardware::keymaster::V4_1::IKeymasterDevice; + +using V41ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode; + +class JavacardKeymaster4Device : public IKeymasterDevice, public IJavacardSeResetListener { + public: + JavacardKeymaster4Device(shared_ptr jcImpl); + virtual ~JavacardKeymaster4Device(); + + // Methods from ::android::hardware::keymaster::V4_0::IKeymasterDevice follow. + Return getHardwareInfo(getHardwareInfo_cb _hidl_cb) override; + Return getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override; + Return computeSharedHmac(const hidl_vec& params, + computeSharedHmac_cb _hidl_cb) override; + Return verifyAuthorization(uint64_t operationHandle, + const hidl_vec& parametersToVerify, + const HardwareAuthToken& authToken, + verifyAuthorization_cb _hidl_cb) override; + Return addRngEntropy(const hidl_vec& data) override; + Return generateKey(const hidl_vec& keyParams, + generateKey_cb _hidl_cb) override; + Return importKey(const hidl_vec& keyParams, KeyFormat keyFormat, + const hidl_vec& keyData, importKey_cb _hidl_cb) override; + Return importWrappedKey(const hidl_vec& wrappedKeyData, + const hidl_vec& wrappingKeyBlob, + const hidl_vec& maskingKey, + const hidl_vec& unwrappingParams, + uint64_t passwordSid, uint64_t biometricSid, + importWrappedKey_cb _hidl_cb) override; + Return getKeyCharacteristics(const hidl_vec& keyBlob, + const hidl_vec& clientId, + const hidl_vec& appData, + getKeyCharacteristics_cb _hidl_cb) override; + Return exportKey(KeyFormat keyFormat, const hidl_vec& keyBlob, + const hidl_vec& clientId, const hidl_vec& appData, + exportKey_cb _hidl_cb) override; + Return attestKey(const hidl_vec& keyToAttest, + const hidl_vec& attestParams, + attestKey_cb _hidl_cb) override; + Return upgradeKey(const hidl_vec& keyBlobToUpgrade, + const hidl_vec& upgradeParams, + upgradeKey_cb _hidl_cb) override; + Return deleteKey(const hidl_vec& keyBlob) override; + Return deleteAllKeys() override; + Return destroyAttestationIds() override; + Return begin(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, const HardwareAuthToken& authToken, + begin_cb _hidl_cb) override; + Return update(uint64_t operationHandle, const hidl_vec& inParams, + const hidl_vec& input, const HardwareAuthToken& authToken, + const VerificationToken& verificationToken, update_cb _hidl_cb) override; + Return finish(uint64_t operationHandle, const hidl_vec& inParams, + const hidl_vec& input, const hidl_vec& signature, + const HardwareAuthToken& authToken, + const VerificationToken& verificationToken, finish_cb _hidl_cb) override; + Return abort(uint64_t operationHandle) override; + + // Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. + Return deviceLocked(bool passwordOnly, + const VerificationToken& verificationToken) override; + Return earlyBootEnded() override; + void seResetEvent() override; + + private: + keymaster_error_t encodeVerificationToken(const VerificationToken& token, + std::vector* encodedToken); + keymaster_error_t handleBeginOperation(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, + const HardwareAuthToken& authToken, + hidl_vec& outParams, + uint64_t& operationHandle, OperationType& operType, + std::unique_ptr& operation); + keymaster_error_t + handleBeginPrivateKeyOperation(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, + const HardwareAuthToken& authToken, + hidl_vec& outParams, uint64_t& operationHandle, + std::unique_ptr& operation); + ; + + keymaster_error_t + handleBeginPublicKeyOperation(KeyPurpose purpose, const hidl_vec& keyBlob, + const hidl_vec& inParams, + const HardwareAuthToken& authToken, + hidl_vec& outParams, uint64_t& operationHandle, + std::unique_ptr& operation); + bool isOperationHandleExists(uint64_t opHandle); + keymaster_error_t abortOperation(uint64_t operationHandle); + + private: + CborConverter cbor_; + std::shared_ptr<::keymaster::AndroidKeymaster> softKm_; + const shared_ptr jcImpl_; + std::map> operationTable_; +}; + +} // namespace javacard +} // namespace V4_1 +} // namespace keymaster diff --git a/HAL/JavacardKeymasterOperation.cpp b/HAL/JavacardKeymasterOperation.cpp new file mode 100644 index 00000000..cf62de42 --- /dev/null +++ b/HAL/JavacardKeymasterOperation.cpp @@ -0,0 +1,368 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JavacardKeymasterOperation.h" +#include +#include + +namespace javacard_keymaster { + +keymaster_error_t JavacardKeymasterOperation::handleErrorCode(keymaster_error_t err) { + // Check if secure element is reset + uint32_t errorCode = static_cast(0 - err); + bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); + + if (isSeResetOccurred) { + // Clear the operation table for Strongbox operations entries. + if (seResetListener_) { + seResetListener_->seResetEvent(); + } + // Unmask the power reset status flag. + errorCode &= ~SE_POWER_RESET_STATUS_FLAG; + } + return translateExtendedErrorsToHalErrors(static_cast(0 - errorCode)); +} + +std::tuple, keymaster_error_t> +JavacardKeymasterOperation::sendRequest(Instruction ins) { + auto [item, err] = card_->sendRequest(ins); + return {std::move(item), handleErrorCode(err)}; +} + +std::tuple, keymaster_error_t> +JavacardKeymasterOperation::sendRequest(Instruction ins, Array& request) { + auto [item, err] = card_->sendRequest(ins, request); + return {std::move(item), handleErrorCode(err)}; +} + +JavacardKeymasterOperation::~JavacardKeymasterOperation() { + if (opHandle_ != 0) { + abort(); + } +} + +keymaster_error_t +JavacardKeymasterOperation::updateAad(const vector& input, + const HardwareAuthToken& authToken, + const vector& encodedVerificationToken) { + cppbor::Array request; + request.add(Uint(opHandle_)); + request.add(Bstr(input)); + cbor_.addHardwareAuthToken(request, authToken); + request.add(EncodedItem(encodedVerificationToken)); + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_AAD_OPERATION_CMD, request); + return err; +} + +keymaster_error_t JavacardKeymasterOperation::update( + const vector& input, const std::optional& inParams, + const HardwareAuthToken& authToken, const vector& encodedVerificationToken, + AuthorizationSet* outParams, uint32_t* inputConsumed, vector* output) { + if (operType_ == OperationType::PUBLIC_OPERATION) { + /* SW keymaster (Public key operation) */ + LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm operation "; + UpdateOperationResponse response(softKm_->message_version()); + UpdateOperationRequest request(softKm_->message_version()); + request.op_handle = opHandle_; + request.input.Reinitialize(input.data(), input.size()); + request.additional_params.Reinitialize(inParams.value()); + + softKm_->UpdateOperation(request, &response); + LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm update operation status: " + << (int32_t)response.error; + if (response.error == KM_ERROR_OK) { + *inputConsumed = response.input_consumed; + *outParams = response.output_params; + output->insert(output->end(), response.output.begin(), response.output.end()); + } else { + LOG(ERROR) << "INS_UPDATE_OPERATION_CMD - error swkm update operation status: " + << (int32_t)response.error; + } + return response.error; + } else { + DataView view = {.buffer = {}, .data = input, .start = 0, .length = input.size()}; + keymaster_error_t err = bufferData(view); + if (err != KM_ERROR_OK) { + return err; + } + if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || + bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { + if (view.length > MAX_CHUNK_SIZE) { + err = updateInChunks(view, inParams, authToken, encodedVerificationToken, output); + if (err != KM_ERROR_OK) { + return err; + } + } + vector remaining = popNextChunk(view, view.length); + err = sendUpdate(remaining, inParams, authToken, encodedVerificationToken, *output); + } + return err; + } +} + +keymaster_error_t JavacardKeymasterOperation::finish( + const vector& inData, const std::optional& inParams, + const vector& signature, const HardwareAuthToken& authToken, + const vector& encodedVerificationToken, + const std::optional>& confToken, AuthorizationSet* outParams, + vector* output) { + if (operType_ == OperationType::PUBLIC_OPERATION) { + FinishOperationResponse response(softKm_->message_version()); + /* SW keymaster (Public key operation) */ + LOG(DEBUG) << "FINISH - swkm operation "; + FinishOperationRequest request(softKm_->message_version()); + request.op_handle = opHandle_; + request.input.Reinitialize(inData.data(), inData.size()); + request.signature.Reinitialize(signature.data(), signature.size()); + request.additional_params.Reinitialize(inParams.value()); + softKm_->FinishOperation(request, &response); + LOG(DEBUG) << "FINISH - swkm operation, status: " << (int32_t)response.error; + ; + + if (response.error == KM_ERROR_OK) { + *outParams = response.output_params; + output->insert(output->end(), response.output.begin(), response.output.end()); + } else { + LOG(ERROR) << "Error in finish operation, status: " << (int32_t)response.error; + } + return response.error; + } else { + DataView view = {.buffer = {}, .data = inData, .start = 0, .length = inData.size()}; + appendBufferedData(view); + if (!(bufferingMode_ == BufferingMode::EC_NO_DIGEST || + bufferingMode_ == BufferingMode::RSA_NO_DIGEST)) { + if (view.length > MAX_CHUNK_SIZE) { + auto err = + updateInChunks(view, inParams, authToken, encodedVerificationToken, output); + if (err != KM_ERROR_OK) { + return err; + } + } + } + vector remaining = popNextChunk(view, view.length); + return sendFinish(remaining, inParams, signature, authToken, encodedVerificationToken, + confToken, *output); + } +} + +keymaster_error_t JavacardKeymasterOperation::abort() { + if (operType_ == OperationType::PUBLIC_OPERATION) { + AbortOperationRequest request(softKm_->message_version()); + request.op_handle = opHandle_; + + AbortOperationResponse response(softKm_->message_version()); + softKm_->AbortOperation(request, &response); + return response.error; + ; + } else { + Array request; + request.add(Uint(opHandle_)); + auto [item, err] = sendRequest(Instruction::INS_ABORT_OPERATION_CMD, request); + opHandle_ = 0; + buffer_.clear(); + return err; + } +} + +void JavacardKeymasterOperation::blockAlign(DataView& view, uint16_t blockSize) { + appendBufferedData(view); + uint16_t offset = getDataViewOffset(view, blockSize); + if (view.buffer.empty() && view.data.empty()) { + offset = 0; + } else if (view.buffer.empty()) { + buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); + } else if (view.data.empty()) { + buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); + } else { + if (offset < view.buffer.size()) { + buffer_.insert(buffer_.end(), view.buffer.begin() + offset, view.buffer.end()); + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + } else { + offset = offset - view.buffer.size(); + buffer_.insert(buffer_.end(), view.data.begin() + offset, view.data.end()); + } + } + // adjust the view length by removing the buffered data size from it. + view.length = view.length - buffer_.size(); +} + +uint16_t JavacardKeymasterOperation::getDataViewOffset(DataView& view, uint16_t blockSize) { + uint16_t offset = 0; + uint16_t remaining = 0; + switch (bufferingMode_) { + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + remaining = (view.length % blockSize); + if (offset >= blockSize && remaining == 0) { + offset -= blockSize; + } + break; + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + break; + case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: + if (view.length > macLength_) { + offset = (view.length - macLength_); + } + break; + default: + break; + } + return offset; +} + +keymaster_error_t JavacardKeymasterOperation::bufferData(DataView& view) { + if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer + switch (bufferingMode_) { + case BufferingMode::RSA_NO_DIGEST: + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + if (buffer_.size() > RSA_BUFFER_SIZE) { + abort(); + return KM_ERROR_INVALID_INPUT_LENGTH; + } + view.start = 0; + view.length = 0; + break; + case BufferingMode::EC_NO_DIGEST: + if (buffer_.size() < EC_BUFFER_SIZE) { + buffer_.insert(buffer_.end(), view.data.begin(), view.data.end()); + // Truncate the buffered data if greater then allowed EC buffer size. + if (buffer_.size() > EC_BUFFER_SIZE) { + buffer_.erase(buffer_.begin() + EC_BUFFER_SIZE, buffer_.end()); + } + } + view.start = 0; + view.length = 0; + break; + case BufferingMode::BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: + blockAlign(view, AES_BLOCK_SIZE); + break; + case BufferingMode::BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED: + blockAlign(view, macLength_); + break; + case BufferingMode::BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + blockAlign(view, DES_BLOCK_SIZE); + break; + case BufferingMode::NONE: + break; + } + return KM_ERROR_OK; +} + +// Incrementally send the request using multiple updates. +keymaster_error_t JavacardKeymasterOperation::updateInChunks( + DataView& view, const std::optional& inParams, + const HardwareAuthToken& authToken, const vector& encodedVerificationToken, + vector* output) { + keymaster_error_t sendError = KM_ERROR_UNKNOWN_ERROR; + while (view.length > MAX_CHUNK_SIZE) { + vector chunk = popNextChunk(view, MAX_CHUNK_SIZE); + sendError = sendUpdate(chunk, inParams, authToken, encodedVerificationToken, *output); + if (sendError != KM_ERROR_OK) { + return sendError; + } + // TODO Is it ok we clear tokens here.? + // Clear tokens + // if (!authToken.mac.empty()) authToken = HardwareAuthToken(); + // if (!timestampToken.mac.empty()) timestampToken = TimeStampToken(); + } + return KM_ERROR_OK; +} + +vector JavacardKeymasterOperation::popNextChunk(DataView& view, uint32_t chunkSize) { + uint32_t start = view.start; + uint32_t end = start + ((view.length < chunkSize) ? view.length : chunkSize); + vector chunk; + if (start < view.buffer.size()) { + if (end < view.buffer.size()) { + chunk = {view.buffer.begin() + start, view.buffer.begin() + end}; + } else { + end = end - view.buffer.size(); + chunk = {view.buffer.begin() + start, view.buffer.end()}; + chunk.insert(chunk.end(), view.data.begin(), view.data.begin() + end); + } + } else { + start = start - view.buffer.size(); + end = end - view.buffer.size(); + chunk = {view.data.begin() + start, view.data.begin() + end}; + } + view.start = view.start + chunk.size(); + view.length = view.length - chunk.size(); + return chunk; +} + +keymaster_error_t JavacardKeymasterOperation::sendUpdate( + const vector& input, const std::optional& inParams, + const HardwareAuthToken& authToken, const vector& encodedVerificationToken, + vector& output) { + if (input.empty() && (!inParams.has_value() || !inParams->Contains(KM_TAG_ASSOCIATED_DATA))) { + LOG(ERROR) << "JavacardKeymasterOperation::sendUpdate return no input to send"; + return KM_ERROR_OK; + } + cppbor::Array request; + request.add(Uint(opHandle_)); + if (inParams.has_value()) cbor_.addKeyparameters(request, inParams.value()); + request.add(Bstr(input)); + cbor_.addHardwareAuthToken(request, authToken); + request.add(EncodedItem(encodedVerificationToken)); + auto [item, error] = sendRequest(Instruction::INS_UPDATE_OPERATION_CMD, request); + if (error != KM_ERROR_OK) { + return error; + } + vector respData; + size_t size; + error = cbor_.getArraySize(item, size); + if ((error != KM_ERROR_OK) || !cbor_.getBinaryArray(item, size - 1, respData)) { + return KM_ERROR_UNKNOWN_ERROR; + } + output.insert(output.end(), respData.begin(), respData.end()); + return KM_ERROR_OK; +} + +keymaster_error_t JavacardKeymasterOperation::sendFinish( + const vector& data, const std::optional& inParams, + const vector& sign, const HardwareAuthToken& authToken, + const vector& encodedVerificationToken, + const std::optional>& confToken, vector& output) { + cppbor::Array request; + request.add(Uint(opHandle_)); + if (inParams.has_value()) cbor_.addKeyparameters(request, inParams.value()); + request.add(Bstr(data)); + request.add(Bstr(sign)); + cbor_.addHardwareAuthToken(request, authToken); + request.add(EncodedItem(encodedVerificationToken)); + if (confToken.has_value()) request.add(Bstr(confToken.value())); + LOG(ERROR) << "JavacardKeymasterOperation::sendFinish step2"; + auto [item, err] = sendRequest(Instruction::INS_FINISH_OPERATION_CMD, request); + if (err != KM_ERROR_OK) { + return err; + } + vector respData; + size_t size; + err = cbor_.getArraySize(item, size); + if ((err != KM_ERROR_OK) || !cbor_.getBinaryArray(item, size - 1, respData)) { + return KM_ERROR_UNKNOWN_ERROR; + } + opHandle_ = 0; + output.insert(output.end(), respData.begin(), respData.end()); + return KM_ERROR_OK; +} + +} // namespace javacard_keymaster diff --git a/HAL/JavacardKeymasterOperation.h b/HAL/JavacardKeymasterOperation.h new file mode 100644 index 00000000..514d9482 --- /dev/null +++ b/HAL/JavacardKeymasterOperation.h @@ -0,0 +1,164 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CborConverter.h" +#include "JavacardSecureElement.h" +#include +#include +#include + +#define AES_BLOCK_SIZE 16 +#define DES_BLOCK_SIZE 8 +#define RSA_BUFFER_SIZE 256 +#define EC_BUFFER_SIZE 32 +#define MAX_CHUNK_SIZE 256 +namespace javacard_keymaster { +using ::keymaster::HardwareAuthToken; +using ::keymaster::TimestampToken; +using std::shared_ptr; +using std::string; +using std::vector; + +enum class OperationType { + /* Public operations are processed inside softkeymaster */ + PUBLIC_OPERATION = 0, + /* Private operations are processed inside strongbox */ + PRIVATE_OPERATION = 1, + UNKNOWN = 2, +}; + +// Bufferig modes for update +enum class BufferingMode : int32_t { + NONE = 0, // Send everything to javacard - most of the assymteric operations + RSA_NO_DIGEST = 1, // Buffer everything in update upto 256 bytes and send in finish. If + // input data is greater then 256 bytes then it is an error. Javacard + // will further check according to exact key size and crypto provider. + EC_NO_DIGEST = 2, // Buffer upto 65 bytes and then truncate. Javacard will further truncate + // upto exact keysize. + BUF_AES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 3, // Buffer 16 bytes. + BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes. + BUF_DES_ENCRYPT_PKCS7_BLOCK_ALIGNED = 5, // Buffer 8 bytes. + BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED = 6, // Buffer 8 bytes. + BUF_AES_GCM_DECRYPT_BLOCK_ALIGNED = 7, // Buffer 16 bytes. + +}; + +// The is the view in the input data being processed by update/finish funcion. + +struct DataView { + vector buffer; // previously buffered data from cycle n-1 + const vector& data; // current data in cycle n. + uint32_t start; // start of the view + size_t length; // length of the view +}; + +class JavacardKeymasterOperation { + public: + explicit JavacardKeymasterOperation(uint64_t opHandle, BufferingMode bufferingMode, + uint16_t macLength, shared_ptr card, + OperationType operType, + shared_ptr seResetListener) + : buffer_(vector()), bufferingMode_(bufferingMode), macLength_(macLength), + card_(card), opHandle_(opHandle), operType_(operType), seResetListener_(seResetListener), + softKm_(nullptr) {} + explicit JavacardKeymasterOperation(uint64_t opHandle, BufferingMode bufferingMode, + uint16_t macLength, shared_ptr card, + OperationType operType, + std::shared_ptr<::keymaster::AndroidKeymaster> softKm) + : buffer_(vector()), bufferingMode_(bufferingMode), macLength_(macLength), + card_(card), opHandle_(opHandle), operType_(operType), seResetListener_(nullptr), + softKm_(softKm) {} + virtual ~JavacardKeymasterOperation(); + + uint64_t getOpertionHandle() { return opHandle_; } + + OperationType getOperationType() { return operType_; } + + keymaster_error_t + update(const vector& input, const std::optional& inParams, + const HardwareAuthToken& authToken, const vector& encodedVerificationToken, + AuthorizationSet* outParams, uint32_t* inputConsumed, vector* output); + + keymaster_error_t updateAad(const vector& input, const HardwareAuthToken& authToken, + const vector& encodedVerificationToken); + + keymaster_error_t finish(const vector& input, + const std::optional& inParams, + const vector& signature, const HardwareAuthToken& authToken, + const vector& encodedVerificationToken, + const std::optional>& confirmationToken, + AuthorizationSet* outParams, vector* output); + + void setBufferingMode(BufferingMode bufMode) { bufferingMode_ = bufMode; } + + void setMacLength(uint32_t macLength) { macLength_ = macLength; } + + keymaster_error_t abort(); + + private: + keymaster_error_t handleErrorCode(keymaster_error_t err); + std::tuple, keymaster_error_t> sendRequest(Instruction ins); + + std::tuple, keymaster_error_t> sendRequest(Instruction ins, + Array& request); + vector popNextChunk(DataView& view, uint32_t chunkSize); + + keymaster_error_t updateInChunks(DataView& view, + const std::optional& inParams, + const HardwareAuthToken& authToken, + const vector& encodedVerificationToken, + vector* output); + + keymaster_error_t + sendFinish(const vector& data, const std::optional& inParams, + const vector& signature, const HardwareAuthToken& authToken, + const vector& encodedVerificationToken, + const std::optional>& confToken, vector& output); + + keymaster_error_t sendUpdate(const vector& data, + const std::optional& inParams, + const HardwareAuthToken& authToken, + const vector& encodedVerificationToken, + vector& output); + + inline void appendBufferedData(DataView& view) { + if (!buffer_.empty()) { + view.buffer = buffer_; + view.length = view.length + buffer_.size(); + view.start = 0; + // view.buffer = insert(data.begin(), buffer_.begin(), buffer_.end()); + buffer_.clear(); + } + } + keymaster_error_t bufferData(DataView& data); + void blockAlign(DataView& data, uint16_t blockSize); + uint16_t getDataViewOffset(DataView& view, uint16_t blockSize); + + private: + vector buffer_; + BufferingMode bufferingMode_; + uint16_t macLength_; + const shared_ptr card_; + uint64_t opHandle_; + CborConverter cbor_; + OperationType operType_; + shared_ptr seResetListener_; + shared_ptr<::keymaster::AndroidKeymaster> softKm_; +}; + +} // namespace javacard_keymaster diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp new file mode 100644 index 00000000..847fbebb --- /dev/null +++ b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp @@ -0,0 +1,252 @@ +/* + * Copyright 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "javacard.keymint.device.rkp.strongbox-impl" +#include +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::keymint { +using namespace cppcose; +using namespace keymaster; +using namespace cppbor; +// RKP error codes defined in keymint applet. +constexpr keymaster_error_t kStatusFailed = static_cast(32000); +constexpr keymaster_error_t kStatusInvalidMac = static_cast(32001); +constexpr keymaster_error_t kStatusProductionKeyInTestRequest = + static_cast(32002); +constexpr keymaster_error_t kStatusTestKeyInProductionRequest = + static_cast(32003); +constexpr keymaster_error_t kStatusInvalidEek = static_cast(32004); +constexpr keymaster_error_t kStatusInvalidState = static_cast(32005); + +namespace { + +keymaster_error_t translateRkpErrorCode(keymaster_error_t error) { + switch (static_cast(-error)) { + case kStatusFailed: + case kStatusInvalidState: + return static_cast(BnRemotelyProvisionedComponent::STATUS_FAILED); + case kStatusInvalidMac: + return static_cast(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); + case kStatusProductionKeyInTestRequest: + return static_cast( + BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); + case kStatusTestKeyInProductionRequest: + return static_cast( + BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); + case kStatusInvalidEek: + return static_cast(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK); + } + return error; +} + +ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) { + info->versionNumber = 1; + info->rpcAuthorName = "Google"; + info->supportedEekCurve = RpcHardwareInfo::CURVE_P256; + return ScopedAStatus::ok(); +} + +uint32_t coseKeyEncodedSize(const std::vector& keysToSign) { + uint32_t size = 0; + for (auto& macKey : keysToSign) { + auto [macedKeyItem, _, coseMacErrMsg] = cppbor::parse(macKey.macedKey); + if (!macedKeyItem || !macedKeyItem->asArray() || + macedKeyItem->asArray()->size() != kCoseMac0EntryCount) { + LOG(ERROR) << "Invalid COSE_Mac0 structure"; + return 0; + } + auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr(); + if (!payload) return 0; + size += payload->value().size(); + } + return size; +} + +} // namespace + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_RKP_HARDWARE_INFO); + uint32_t versionNumber; + uint32_t supportedEekCurve; + if (err != KM_ERROR_OK || !cbor_.getUint64(item, 1, versionNumber) || + !cbor_.getBinaryArray(item, 2, info->rpcAuthorName) || + !cbor_.getUint64(item, 3, supportedEekCurve)) { + LOG(ERROR) << "Error in response of getHardwareInfo."; + LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; + return defaultHwInfo(info); + } + info->versionNumber = static_cast(versionNumber); + info->supportedEekCurve = static_cast(supportedEekCurve); + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair( + bool testMode, MacedPublicKey* macedPublicKey, std::vector* privateKeyHandle) { + cppbor::Array array; + array.add(testMode); + auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_RKP_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in sending generateEcdsaP256KeyPair."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + if (!cbor_.getBinaryArray(item, 1, macedPublicKey->macedKey) || + !cbor_.getBinaryArray(item, 2, *privateKeyHandle)) { + LOG(ERROR) << "Error in decoding og response in generateEcdsaP256KeyPair."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::beginSendData( + bool testMode, const std::vector& keysToSign) { + uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign); + cppbor::Array array; + array.add(keysToSign.size()); + array.add(totalEncodedSize); + array.add(testMode); + auto [_, err] = card_->sendRequest(Instruction::INS_BEGIN_SEND_DATA_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in beginSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateMacedKey( + const std::vector& keysToSign) { + for (auto& macedPublicKey : keysToSign) { + cppbor::Array array; + array.add(EncodedItem(macedPublicKey.macedKey)); + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_KEY_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateMacedKey."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + } + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::updateChallenge(const std::vector& challenge) { + Array array; + array.add(challenge); + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_CHALLENGE_CMD, array); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateChallenge."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::updateEEK( + const std::vector& endpointEncCertChain) { + std::vector eekChain = endpointEncCertChain; + auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_EEK_CHAIN_CMD, eekChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in updateEEK."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::finishSendData( + std::vector* keysToSignMac, DeviceInfo* deviceInfo, + std::vector& coseEncryptProtectedHeader, cppbor::Map& coseEncryptUnProtectedHeader, + std::vector& partialCipheredData, uint32_t& respFlag) { + + std::vector decodedKeysToSignMac; + std::vector decodedDeviceInfo; + auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_SEND_DATA_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in finishSendData."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + if (!cbor_.getBinaryArray(item, 1, decodedKeysToSignMac) || + !cbor_.getBinaryArray(item, 2, decodedDeviceInfo) || + !cbor_.getBinaryArray(item, 3, coseEncryptProtectedHeader) || + !cbor_.getMapItem(item, 4, coseEncryptUnProtectedHeader) || + !cbor_.getBinaryArray(item, 5, partialCipheredData) || + !cbor_.getUint64(item, 6, respFlag)) { + LOG(ERROR) << "Error in decoding og response in finishSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *keysToSignMac = decodedKeysToSignMac; + deviceInfo->deviceInfo = decodedDeviceInfo; + return ScopedAStatus::ok(); +} + +ScopedAStatus +JavacardRemotelyProvisionedComponentDevice::getResponse(std::vector& partialCipheredData, + cppbor::Array& recepientStructure, + uint32_t& respFlag) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_RESPONSE_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getResponse."; + return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); + } + if (!cbor_.getBinaryArray(item, 1, partialCipheredData) || + !cbor_.getArrayItem(item, 2, recepientStructure) || !cbor_.getUint64(item, 3, respFlag)) { + LOG(ERROR) << "Error in decoding og response in getResponse."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest( + bool testMode, const std::vector& keysToSign, + const std::vector& endpointEncCertChain, const std::vector& challenge, + DeviceInfo* deviceInfo, ProtectedData* protectedData, std::vector* keysToSignMac) { + std::vector coseEncryptProtectedHeader; + cppbor::Map coseEncryptUnProtectedHeader; + cppbor::Array recipients; + std::vector cipheredData; + uint32_t respFlag; + auto ret = beginSendData(testMode, keysToSign); + if (!ret.isOk()) return ret; + + ret = updateMacedKey(keysToSign); + if (!ret.isOk()) return ret; + + ret = updateChallenge(challenge); + if (!ret.isOk()) return ret; + + ret = updateEEK(endpointEncCertChain); + if (!ret.isOk()) return ret; + + ret = finishSendData(keysToSignMac, deviceInfo, coseEncryptProtectedHeader, + coseEncryptUnProtectedHeader, cipheredData, respFlag); + if (!ret.isOk()) return ret; + + while (respFlag != 0) { // more data is pending to receive + ret = getResponse(cipheredData, recipients, respFlag); + if (!ret.isOk()) return ret; + } + // Create ConseEncrypt structure. + protectedData->protectedData = cppbor::Array() + .add(coseEncryptProtectedHeader) // Protected + .add(std::move(coseEncryptUnProtectedHeader)) // Unprotected + .add(cipheredData) // Payload + .add(std::move(recipients)) + .encode(); + return ScopedAStatus::ok(); +} + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.h b/HAL/JavacardRemotelyProvisionedComponentDevice.h new file mode 100644 index 00000000..8e8ed6f1 --- /dev/null +++ b/HAL/JavacardRemotelyProvisionedComponentDevice.h @@ -0,0 +1,72 @@ +/* + * Copyright 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "CborConverter.h" +#include "JavacardSecureElement.h" + +namespace aidl::android::hardware::security::keymint { +using namespace ::javacard_keymaster; +using ndk::ScopedAStatus; + +class JavacardRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent { + public: + explicit JavacardRemotelyProvisionedComponentDevice(shared_ptr card) + : card_(card) {} + + virtual ~JavacardRemotelyProvisionedComponentDevice() = default; + + ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override; + + ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey, + std::vector* privateKeyHandle) override; + + ScopedAStatus generateCertificateRequest(bool testMode, + const std::vector& keysToSign, + const std::vector& endpointEncCertChain, + const std::vector& challenge, + DeviceInfo* deviceInfo, ProtectedData* protectedData, + std::vector* keysToSignMac) override; + + private: + ScopedAStatus beginSendData(bool testMode, const std::vector& keysToSign); + + ScopedAStatus updateMacedKey(const std::vector& keysToSign); + + ScopedAStatus updateChallenge(const std::vector& challenge); + + ScopedAStatus updateEEK(const std::vector& endpointEncCertChain); + + ScopedAStatus finishSendData(std::vector* keysToSignMac, DeviceInfo* deviceInfo, + std::vector& coseEncryptProtectedHeader, + cppbor::Map& coseEncryptUnProtectedHeader, + std::vector& partialCipheredData, uint32_t& respFlag); + + ScopedAStatus getResponse(std::vector& partialCipheredData, + cppbor::Array& recepientStructure, uint32_t& respFlag); + std::shared_ptr card_; + CborConverter cbor_; +}; + +} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardSecureElement.cpp b/HAL/JavacardSecureElement.cpp new file mode 100644 index 00000000..6059acfb --- /dev/null +++ b/HAL/JavacardSecureElement.cpp @@ -0,0 +1,166 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "JavacardSecureElement" +#include "JavacardSecureElement.h" +#include "KMUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace javacard_keymaster { + +using namespace ::keymaster; + +keymaster_error_t JavacardSecureElement::getP1(uint8_t* p1) { + switch (version_) { + case KmVersion::KEYMASTER_4: + case KmVersion::KEYMASTER_4_1: + *p1 = APDU_KEYMASTER_P1; + break; + case KmVersion::KEYMINT_1: + *p1 = APDU_KEYMINT_P1; + break; + default: + return KM_ERROR_UNIMPLEMENTED; + } + return KM_ERROR_OK; +} +keymaster_error_t JavacardSecureElement::initializeJavacard() { + if (!cardInitialized_) { + Array request; + request.add(Uint(osVersion_)); + request.add(Uint(osPatchLevel_)); + request.add(Uint(vendorPatchLevel_)); + auto [item, err] = sendRequest(Instruction::INS_INIT_STRONGBOX_CMD, request); + if (err != KM_ERROR_OK) { + return err; + } + cardInitialized_ = true; + } + return KM_ERROR_OK; +} + +keymaster_error_t JavacardSecureElement::constructApduMessage(Instruction& ins, + std::vector& inputData, + std::vector& apduOut) { + uint8_t p1; + auto err = getP1(&p1); + if (KM_ERROR_OK != err) { + return err; + } + apduOut.push_back(static_cast(APDU_CLS)); // CLS + apduOut.push_back(static_cast(ins)); // INS + apduOut.push_back(p1); // P1 + apduOut.push_back(static_cast(APDU_P2)); // P2 + + if (USHRT_MAX >= inputData.size()) { + // Send extended length APDU always as response size is not known to HAL. + // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le + // all set to 00. Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. + // Extended length 3 bytes, starts with 0x00 + apduOut.push_back(static_cast(0x00)); + if (inputData.size() > 0) { + apduOut.push_back(static_cast(inputData.size() >> 8)); + apduOut.push_back(static_cast(inputData.size() & 0xFF)); + // Data + apduOut.insert(apduOut.end(), inputData.begin(), inputData.end()); + } + // Expected length of output. + // Accepting complete length of output every time. + apduOut.push_back(static_cast(0x00)); + apduOut.push_back(static_cast(0x00)); + } else { + LOG(ERROR) << "Error in constructApduMessage."; + return (KM_ERROR_INVALID_INPUT_LENGTH); + } + return (KM_ERROR_OK); // success +} + +keymaster_error_t JavacardSecureElement::sendData(Instruction ins, std::vector& inData, + std::vector& response) { + keymaster_error_t ret = KM_ERROR_UNKNOWN_ERROR; + std::vector apdu; + + ret = constructApduMessage(ins, inData, apdu); + + if (ret != KM_ERROR_OK) { + return ret; + } + + if (!transport_->sendData(apdu, response)) { + LOG(ERROR) << "Error in sending data in sendData."; + return (KM_ERROR_SECURE_HW_COMMUNICATION_FAILED); + } + + // Response size should be greater than 2. Cbor output data followed by two bytes of APDU + // status. + if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) { + LOG(ERROR) << "Response of the sendData is wrong: response size = " << response.size() + << " apdu status = " << getApduStatus(response); + return (KM_ERROR_UNKNOWN_ERROR); + } + // remove the status bytes + response.pop_back(); + response.pop_back(); + return (KM_ERROR_OK); // success +} + +std::tuple, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins, Array& request) { + vector response; + // encode request + std::vector command = request.encode(); + auto sendError = sendData(ins, command, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +std::tuple, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins, std::vector& command) { + vector response; + auto sendError = sendData(ins, command, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +std::tuple, keymaster_error_t> +JavacardSecureElement::sendRequest(Instruction ins) { + vector response; + vector emptyRequest; + auto sendError = sendData(ins, emptyRequest, response); + if (sendError != KM_ERROR_OK) { + return {unique_ptr(nullptr), sendError}; + } + // decode the response and send that back + return cbor_.decodeData(response); +} + +} // namespace javacard_keymaster diff --git a/HAL/JavacardSecureElement.h b/HAL/JavacardSecureElement.h new file mode 100644 index 00000000..eb0cf4d6 --- /dev/null +++ b/HAL/JavacardSecureElement.h @@ -0,0 +1,127 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include "CborConverter.h" +#include +#include +#include +#include + +#define APDU_CLS 0x80 +#define APDU_KEYMINT_P1 0x50 +#define APDU_KEYMASTER_P1 0x40 +#define APDU_P2 0x00 +#define APDU_RESP_STATUS_OK 0x9000 + +#define SE_POWER_RESET_STATUS_FLAG (1 << 30) + +#define KEYMINT_CMD_APDU_START 0x20 + +namespace javacard_keymaster { +using keymaster::KmVersion; +using std::optional; +using std::shared_ptr; +using std::vector; + +enum class Instruction { + // Keymaster commands + INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1, + INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2, + INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3, + INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4, + INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5, + INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6, + INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7, + INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8, + INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9, + INS_COMPUTE_SHARED_SECRET_CMD = KEYMINT_CMD_APDU_START + 10, + INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11, + INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12, + INS_GET_SHARED_SECRET_PARAM_CMD = KEYMINT_CMD_APDU_START + 13, + INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14, + INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15, + INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16, + INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17, + INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18, + INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19, + INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20, + INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21, + INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22, + 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_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, + INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29, + INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30, + INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31, + INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32, + INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33, + INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34, +}; + +class IJavacardSeResetListener { + public: + virtual ~IJavacardSeResetListener(){}; + virtual void seResetEvent() = 0; +}; + +class JavacardSecureElement { + public: + explicit JavacardSecureElement(KmVersion version, shared_ptr transport, + uint32_t osVersion, uint32_t osPatchLevel, + uint32_t vendorPatchLevel) + : version_(version), transport_(transport), osVersion_(osVersion), + osPatchLevel_(osPatchLevel), vendorPatchLevel_(vendorPatchLevel), + cardInitialized_(false) { + transport_->openConnection(); + } + virtual ~JavacardSecureElement() { transport_->closeConnection(); } + + std::tuple, keymaster_error_t> sendRequest(Instruction ins, + Array& request); + std::tuple, keymaster_error_t> sendRequest(Instruction ins); + std::tuple, keymaster_error_t> sendRequest(Instruction ins, + std::vector& command); + + keymaster_error_t sendData(Instruction ins, std::vector& inData, + std::vector& response); + + keymaster_error_t constructApduMessage(Instruction& ins, std::vector& inputData, + std::vector& apduOut); + keymaster_error_t initializeJavacard(); + inline uint16_t getApduStatus(std::vector& inputData) { + // Last two bytes are the status SW0SW1 + uint8_t SW0 = inputData.at(inputData.size() - 2); + uint8_t SW1 = inputData.at(inputData.size() - 1); + return (SW0 << 8 | SW1); + } + + private: + keymaster_error_t getP1(uint8_t* p1); + + KmVersion version_; + shared_ptr transport_; + uint32_t osVersion_; + uint32_t osPatchLevel_; + uint32_t vendorPatchLevel_; + bool cardInitialized_; + CborConverter cbor_; +}; +} // namespace javacard_keymaster diff --git a/HAL/JavacardSharedSecret.cpp b/HAL/JavacardSharedSecret.cpp new file mode 100644 index 00000000..f2555a80 --- /dev/null +++ b/HAL/JavacardSharedSecret.cpp @@ -0,0 +1,31 @@ +#define LOG_TAG "javacard.strongbox.keymint.operation-impl" +#include "JavacardSharedSecret.h" +#include +#include + +namespace aidl::android::hardware::security::sharedsecret { +using aidl::android::hardware::security::keymint::km_utils::kmError2ScopedAStatus; +using ndk::ScopedAStatus; +using std::optional; +using std::shared_ptr; +using std::vector; + +ScopedAStatus JavacardSharedSecret::getSharedSecretParameters(SharedSecretParameters* params) { + auto err = jcImpl_->getHmacSharingParameters(¶ms->seed, ¶ms->nonce); + return kmError2ScopedAStatus(err); +} + +ScopedAStatus +JavacardSharedSecret::computeSharedSecret(const std::vector& params, + std::vector* secret) { + + vector<::javacard_keymaster::HmacSharingParameters> reqParams(params.size()); + for (size_t i = 0; i < params.size(); i++) { + reqParams[i].seed = params[i].seed; + reqParams[i].nonce = params[i].nonce; + } + auto err = jcImpl_->computeSharedHmac(reqParams, secret); + return kmError2ScopedAStatus(err); +} + +} // namespace aidl::android::hardware::security::sharedsecret diff --git a/HAL/JavacardSharedSecret.h b/HAL/JavacardSharedSecret.h new file mode 100644 index 00000000..de965732 --- /dev/null +++ b/HAL/JavacardSharedSecret.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::security::sharedsecret { +using namespace ::javacard_keymaster; +using ndk::ScopedAStatus; +using std::optional; +using std::shared_ptr; +using std::vector; + +class JavacardSharedSecret : public BnSharedSecret { + public: + explicit JavacardSharedSecret(shared_ptr jcImpl) : jcImpl_(jcImpl) {} + virtual ~JavacardSharedSecret() {} + + ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override; + + ScopedAStatus computeSharedSecret(const std::vector& params, + std::vector* secret) override; + + private: + const shared_ptr jcImpl_; +}; + +} // namespace aidl::android::hardware::security::sharedsecret diff --git a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp b/HAL/JavacardSoftKeymasterContext.cpp similarity index 58% rename from HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp rename to HAL/JavacardSoftKeymasterContext.cpp index bbdfba28..c1abf46f 100644 --- a/HAL/keymaster/4.1/JavacardSoftKeymasterContext.cpp +++ b/HAL/JavacardSoftKeymasterContext.cpp @@ -14,52 +14,59 @@ * limitations under the License. */ -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include -#include -#include #include +#include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include using std::unique_ptr; -using ::keymaster::V4_1::javacard::KmParamSet; namespace keymaster { +namespace V4_1 { +namespace javacard { + +using namespace ::javacard_keymaster; +constexpr int kKeyblobPubKeyOffset = 4; +constexpr int kKeyblobKeyCharsOffset = 3; +using ::keymaster::PureSoftKeymasterContext; -JavaCardSoftKeymasterContext::JavaCardSoftKeymasterContext(keymaster_security_level_t security_level) +JavaCardSoftKeymasterContext::JavaCardSoftKeymasterContext( + keymaster_security_level_t security_level) : PureSoftKeymasterContext(KmVersion::KEYMASTER_4_1, security_level) {} JavaCardSoftKeymasterContext::~JavaCardSoftKeymasterContext() {} EVP_PKEY* RSA_fromMaterial(const uint8_t* modulus, size_t mod_size) { - BIGNUM *n = BN_bin2bn(modulus, mod_size, NULL); - BIGNUM *e = BN_new();//bignum_decode(exp, 5); + BIGNUM* n = BN_bin2bn(modulus, mod_size, NULL); + BIGNUM* e = BN_new(); // bignum_decode(exp, 5); char exp[] = "65537"; BN_dec2bn(&e, exp); - if (!n || !e) - return NULL; - - if (e && n) { - EVP_PKEY* pRsaKey = EVP_PKEY_new(); - RSA* rsa = RSA_new(); - rsa->e = e; - rsa->n = n; - EVP_PKEY_assign_RSA(pRsaKey, rsa); - return pRsaKey; - } else { - if (n) BN_free(n); - if (e) BN_free(e); - return NULL; - } + if (!n || !e) return NULL; + + if (e && n) { + EVP_PKEY* pRsaKey = EVP_PKEY_new(); + RSA* rsa = RSA_new(); + rsa->e = e; + rsa->n = n; + EVP_PKEY_assign_RSA(pRsaKey, rsa); + return pRsaKey; + } else { + if (n) BN_free(n); + if (e) BN_free(e); + return NULL; + } } EC_GROUP* ChooseGroup(keymaster_ec_curve_t ec_curve) { @@ -83,24 +90,26 @@ EC_GROUP* ChooseGroup(keymaster_ec_curve_t ec_curve) { } EVP_PKEY* EC_fromMaterial(const uint8_t* pub_key, size_t key_size, keymaster_ec_curve_t ec_curve) { - - EC_GROUP *ec_group = ChooseGroup(ec_curve); - EC_POINT *p = EC_POINT_new(ec_group); - EC_KEY *ec_key = EC_KEY_new(); - EVP_PKEY *pEcKey = EVP_PKEY_new(); - - if((EC_KEY_set_group(ec_key, ec_group) != 1) || (EC_POINT_oct2point(ec_group, p, pub_key, key_size, NULL) != 1) - || (EC_KEY_set_public_key(ec_key, p) != 1) || (EVP_PKEY_set1_EC_KEY(pEcKey, ec_key) != 1)) { + + EC_GROUP* ec_group = ChooseGroup(ec_curve); + EC_POINT* p = EC_POINT_new(ec_group); + EC_KEY* ec_key = EC_KEY_new(); + EVP_PKEY* pEcKey = EVP_PKEY_new(); + + if ((EC_KEY_set_group(ec_key, ec_group) != 1) || + (EC_POINT_oct2point(ec_group, p, pub_key, key_size, NULL) != 1) || + (EC_KEY_set_public_key(ec_key, p) != 1) || (EVP_PKEY_set1_EC_KEY(pEcKey, ec_key) != 1)) { return NULL; } return pEcKey; } -keymaster_error_t JavaCardSoftKeymasterContext::LoadKey(const keymaster_algorithm_t algorithm, KeymasterKeyBlob&& key_material, - AuthorizationSet&& hw_enforced, - AuthorizationSet&& sw_enforced, - UniquePtr* key) const { +keymaster_error_t JavaCardSoftKeymasterContext::LoadKey(const keymaster_algorithm_t algorithm, + KeymasterKeyBlob&& key_material, + AuthorizationSet&& hw_enforced, + AuthorizationSet&& sw_enforced, + UniquePtr* key) const { auto factory = (AsymmetricKeyFactory*)GetKeyFactory(algorithm); UniquePtr asym_key; keymaster_error_t error = KM_ERROR_OK; @@ -108,30 +117,27 @@ keymaster_error_t JavaCardSoftKeymasterContext::LoadKey(const keymaster_algorith const size_t temp_size = key_material.key_material_size; EVP_PKEY* pkey = NULL; - if(algorithm == KM_ALGORITHM_RSA) { + if (algorithm == KM_ALGORITHM_RSA) { pkey = RSA_fromMaterial(tmp, temp_size); - } else if(algorithm == KM_ALGORITHM_EC) { + } else if (algorithm == KM_ALGORITHM_EC) { keymaster_ec_curve_t ec_curve; uint32_t keySize; if (!hw_enforced.GetTagValue(TAG_EC_CURVE, &ec_curve) && !sw_enforced.GetTagValue(TAG_EC_CURVE, &ec_curve)) { - if(!hw_enforced.GetTagValue(TAG_KEY_SIZE, &keySize) && + if (!hw_enforced.GetTagValue(TAG_KEY_SIZE, &keySize) && !sw_enforced.GetTagValue(TAG_KEY_SIZE, &keySize)) { return KM_ERROR_INVALID_ARGUMENT; } error = EcKeySizeToCurve(keySize, &ec_curve); - if(error != KM_ERROR_OK) - return error; + if (error != KM_ERROR_OK) return error; } pkey = EC_fromMaterial(tmp, temp_size, ec_curve); } - if (!pkey) - return TranslateLastOpenSslError(); + if (!pkey) return TranslateLastOpenSslError(); UniquePtr pkey_deleter(pkey); error = factory->CreateEmptyKey(move(hw_enforced), move(sw_enforced), &asym_key); - if (error != KM_ERROR_OK) - return error; + if (error != KM_ERROR_OK) return error; asym_key->key_material() = move(key_material); if (!asym_key->EvpToInternal(pkey)) @@ -142,25 +148,27 @@ keymaster_error_t JavaCardSoftKeymasterContext::LoadKey(const keymaster_algorith return error; } -keymaster_error_t JavaCardSoftKeymasterContext::ParseKeyBlob(const KeymasterKeyBlob& blob, - const AuthorizationSet& /*additional_params*/, - UniquePtr* key) const { +keymaster_error_t +JavaCardSoftKeymasterContext::ParseKeyBlob(const KeymasterKeyBlob& blob, + const AuthorizationSet& /*additional_params*/, + UniquePtr* key) const { - // The JavaCardSoftKeymasterContext handle a key blob generated by JavaCard keymaster for public key operations. + // The JavaCardSoftKeymasterContext handle a key blob generated by JavaCard keymaster for public + // key operations. // - // 1. A JavaCard keymaster key blob is a CborEncoded data of Secret, Nonce, AuthTag, KeyCharectristics and Public key. + // 1. A JavaCard keymaster key blob is a CborEncoded data of Secret, Nonce, AuthTag, + // KeyCharectristics and Public key. // Here in public key operation we need only KeyCharectristics and Public key. // Once these values extracted Public key is created based on parameters and returned. // - AuthorizationSet hw_enforced; AuthorizationSet sw_enforced; KeymasterKeyBlob key_material; keymaster_error_t error = KM_ERROR_OK; - auto constructKey = [&, this] () mutable -> keymaster_error_t { + auto constructKey = [&, this]() mutable -> keymaster_error_t { keymaster_algorithm_t algorithm; - if(error != KM_ERROR_OK) { + if (error != KM_ERROR_OK) { return error; } if (!hw_enforced.GetTagValue(TAG_ALGORITHM, &algorithm) && @@ -171,34 +179,28 @@ keymaster_error_t JavaCardSoftKeymasterContext::ParseKeyBlob(const KeymasterKeyB if (algorithm != KM_ALGORITHM_RSA && algorithm != KM_ALGORITHM_EC) { return KM_ERROR_INCOMPATIBLE_ALGORITHM; } - error = LoadKey(algorithm, move(key_material), move(hw_enforced), - move(sw_enforced), key); + error = LoadKey(algorithm, move(key_material), move(hw_enforced), move(sw_enforced), key); return error; }; - - CborConverter cc; - std::unique_ptr item; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - std::vector cborKey(blob.key_material_size); - - for(size_t i = 0; i < blob.key_material_size; i++) { - cborKey[i] = blob.key_material[i]; - } - std::tie(item, errorCode) = cc.decodeData(cborKey, false); + CborConverter cbor_; + auto [item, _] = cbor_.decodeKeyblob(std::vector(blob.begin(), blob.end())); if (item != nullptr) { - std::vector temp(0); - if(cc.getBinaryArray(item, 4, temp)) { - key_material = {temp.data(), temp.size()}; - temp.clear(); + std::vector pubKey; + AuthorizationSet _; + // Read public key from keyblob. For symmetric keys the data + // will be empty so ignore the error. + if (cbor_.getBinaryArray(item, kKeyblobPubKeyOffset, pubKey)) { + key_material = {pubKey.data(), pubKey.size()}; } - KeyCharacteristics keyCharacteristics; - cc.getKeyCharacteristics(item, 3, keyCharacteristics); - - sw_enforced.Reinitialize(KmParamSet(keyCharacteristics.softwareEnforced)); - hw_enforced.Reinitialize(KmParamSet(keyCharacteristics.hardwareEnforced)); - } else { - error = KM_ERROR_INVALID_KEY_BLOB; + if (!cbor_.getKeyCharacteristics(item, kKeyblobKeyCharsOffset, sw_enforced, hw_enforced, + _)) { + return KM_ERROR_INVALID_KEY_BLOB; + } + return constructKey(); } - return constructKey(); + return KM_ERROR_INVALID_KEY_BLOB; } + +} // namespace javacard +} // namespace V4_1 } // namespace keymaster diff --git a/HAL/keymaster/include/JavacardSoftKeymasterContext.h b/HAL/JavacardSoftKeymasterContext.h similarity index 68% rename from HAL/keymaster/include/JavacardSoftKeymasterContext.h rename to HAL/JavacardSoftKeymasterContext.h index 8cdeab92..4655a121 100644 --- a/HAL/keymaster/include/JavacardSoftKeymasterContext.h +++ b/HAL/JavacardSoftKeymasterContext.h @@ -13,27 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#ifndef SYSTEM_KEYMASTER_JAVA_CARD_SOFT_KEYMASTER_CONTEXT_H_ -#define SYSTEM_KEYMASTER_JAVA_CARD_SOFT_KEYMASTER_CONTEXT_H_ - +#pragma once #include #include + namespace keymaster { +namespace V4_1 { +namespace javacard { -class SoftKeymasterKeyRegistrations; -class Keymaster0Engine; -class Keymaster1Engine; -class Key; +using namespace ::keymaster; /** * SoftKeymasterContext provides the context for a non-secure implementation of AndroidKeymaster. */ -class JavaCardSoftKeymasterContext : public keymaster::PureSoftKeymasterContext { - keymaster_error_t LoadKey(const keymaster_algorithm_t algorithm, KeymasterKeyBlob&& key_material, - AuthorizationSet&& hw_enforced, - AuthorizationSet&& sw_enforced, - UniquePtr* key) const; +class JavaCardSoftKeymasterContext : public ::keymaster::PureSoftKeymasterContext { + keymaster_error_t LoadKey(const keymaster_algorithm_t algorithm, + KeymasterKeyBlob&& key_material, AuthorizationSet&& hw_enforced, + AuthorizationSet&& sw_enforced, UniquePtr* key) const; + public: // Security level must only be used for testing. explicit JavaCardSoftKeymasterContext( @@ -43,9 +40,8 @@ class JavaCardSoftKeymasterContext : public keymaster::PureSoftKeymasterContext keymaster_error_t ParseKeyBlob(const KeymasterKeyBlob& blob, const AuthorizationSet& additional_params, UniquePtr* key) const override; - }; +} // namespace javacard +} // namespace V4_1 } // namespace keymaster - -#endif // SYSTEM_KEYMASTER_PURE_SOFT_KEYMASTER_CONTEXT_H_ diff --git a/HAL/KMUtils.cpp b/HAL/KMUtils.cpp new file mode 100644 index 00000000..a219c412 --- /dev/null +++ b/HAL/KMUtils.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#define TAG_SEQUENCE 0x30 +#define LENGTH_MASK 0x80 +#define LENGTH_VALUE_MASK 0x7F + +namespace javacard_keymaster { +namespace { + +constexpr char kPlatformVersionProp[] = "ro.build.version.release"; +constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?"; +constexpr size_t kMajorVersionMatch = 1; +constexpr size_t kMinorVersionMatch = 3; +constexpr size_t kSubminorVersionMatch = 5; +constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1; + +constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch"; +constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch"; +constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$"; +constexpr size_t kYearMatch = 1; +constexpr size_t kMonthMatch = 2; +constexpr size_t kDayMatch = 3; +constexpr size_t kPatchlevelMatchCount = kDayMatch + 1; + +uint32_t match_to_uint32(const char* expression, const regmatch_t& match) { + if (match.rm_so == -1) return 0; + + size_t len = match.rm_eo - match.rm_so; + std::string s(expression + match.rm_so, len); + return std::stoul(s); +} + +std::string wait_and_get_property(const char* prop) { + std::string prop_value; + while (!::android::base::WaitForPropertyCreation(prop)) + ; + prop_value = ::android::base::GetProperty(prop, "" /* default */); + return prop_value; +} + +uint32_t getOsVersion(const char* version_str) { + regex_t regex; + if (regcomp(®ex, kPlatformVersionRegex, REG_EXTENDED)) { + return 0; + } + + regmatch_t matches[kPlatformVersionMatchCount]; + int not_match = + regexec(®ex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */); + regfree(®ex); + if (not_match) { + return 0; + } + + uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]); + uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]); + uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]); + + return (major * 100 + minor) * 100 + subminor; +} + +enum class PatchlevelOutput { kYearMonthDay, kYearMonth }; + +uint32_t getPatchlevel(const char* patchlevel_str, PatchlevelOutput detail) { + regex_t regex; + if (regcomp(®ex, kPatchlevelRegex, REG_EXTENDED) != 0) { + return 0; + } + + regmatch_t matches[kPatchlevelMatchCount]; + int not_match = regexec(®ex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */); + regfree(®ex); + if (not_match) { + return 0; + } + + uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]); + uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]); + + if (month < 1 || month > 12) { + return 0; + } + + switch (detail) { + case PatchlevelOutput::kYearMonthDay: { + uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]); + if (day < 1 || day > 31) { + return 0; + } + return year * 10000 + month * 100 + day; + } + case PatchlevelOutput::kYearMonth: + return year * 100 + month; + } +} + +} // anonymous namespace + +// TODO Can we move it to JavacardSecureElement class +keymaster_error_t translateExtendedErrorsToHalErrors(keymaster_error_t errorCode) { + keymaster_error_t err = errorCode; + switch (static_cast(errorCode)) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + LOG(ERROR) << "translateExtendedErrorsToHalErrors SE error: " << (int32_t)errorCode; + err = KM_ERROR_UNKNOWN_ERROR; + break; + case CRYPTO_NO_SUCH_ALGORITHM: + LOG(ERROR) << "translateExtendedErrorsToHalErrors SE error: " << (int32_t)errorCode; + err = KM_ERROR_UNSUPPORTED_ALGORITHM; + break; + case UNSUPPORTED_INSTRUCTION: + case CMD_NOT_ALLOWED: + case SW_WRONG_LENGTH: + LOG(ERROR) << "translateExtendedErrorsToHalErrors SE error: " << (int32_t)errorCode; + err = KM_ERROR_UNIMPLEMENTED; + break; + case PUBLIC_KEY_OPERATION: + // This error is handled inside keymaster + LOG(ERROR) << "translateExtendedErrorsToHalErrors SE error: " << (int32_t)errorCode; + break; + default: + break; + } + return err; +} + +uint32_t getOsVersion() { + std::string version = wait_and_get_property(kPlatformVersionProp); + return getOsVersion(version.c_str()); +} + +uint32_t getOsPatchlevel() { + std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonth); +} + +uint32_t getVendorPatchlevel() { + std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonthDay); +} + +keymaster_error_t getCertificateChain(std::vector& chainBuffer, + std::vector>& certChain) { + uint8_t* data = chainBuffer.data(); + int index = 0; + uint32_t length = 0; + while (index < chainBuffer.size()) { + std::vector temp; + if (data[index] == TAG_SEQUENCE) { + // read next byte + if (0 == (data[index + 1] & LENGTH_MASK)) { + length = (uint32_t)data[index]; + // Add SEQ and Length fields + length += 2; + } else { + int additionalBytes = data[index + 1] & LENGTH_VALUE_MASK; + if (additionalBytes == 0x01) { + length = data[index + 2]; + // Add SEQ and Length fields + length += 3; + } else if (additionalBytes == 0x02) { + length = (data[index + 2] << 8 | data[index + 3]); + // Add SEQ and Length fields + length += 4; + } else if (additionalBytes == 0x04) { + length = data[index + 2] << 24; + length |= data[index + 3] << 16; + length |= data[index + 4] << 8; + length |= data[index + 5]; + // Add SEQ and Length fields + length += 6; + } else { + // Length is larger than uint32_t max limit. + return KM_ERROR_UNKNOWN_ERROR; + } + } + temp.insert(temp.end(), (data + index), (data + index + length)); + index += length; + + certChain.push_back(std::move(temp)); + } else { + // SEQUENCE TAG MISSING. + return KM_ERROR_UNKNOWN_ERROR; + } + } + return KM_ERROR_OK; +} + +void addCreationTime(AuthorizationSet& paramSet) { + if (!paramSet.Contains(KM_TAG_CREATION_DATETIME) && + !paramSet.Contains(KM_TAG_ACTIVE_DATETIME)) { + keymaster_key_param_t dateTime; + dateTime.tag = KM_TAG_CREATION_DATETIME; + dateTime.date_time = java_time(time(nullptr)); + paramSet.push_back(dateTime); + } +} + +} // namespace javacard_keymaster diff --git a/HAL/KMUtils.h b/HAL/KMUtils.h new file mode 100644 index 00000000..05b7502f --- /dev/null +++ b/HAL/KMUtils.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace javacard_keymaster { +using namespace ::keymaster; +using std::vector; + +// Extended error codes +enum ExtendedErrors { + SW_CONDITIONS_NOT_SATISFIED = -10001, + UNSUPPORTED_CLA = -10002, + INVALID_P1P2 = -10003, + UNSUPPORTED_INSTRUCTION = -10004, + CMD_NOT_ALLOWED = -10005, + SW_WRONG_LENGTH = -10006, + INVALID_DATA = -10007, + CRYPTO_ILLEGAL_USE = -10008, + CRYPTO_ILLEGAL_VALUE = -10009, + CRYPTO_INVALID_INIT = -10010, + CRYPTO_NO_SUCH_ALGORITHM = -10011, + CRYPTO_UNINITIALIZED_KEY = -10012, + GENERIC_UNKNOWN_ERROR = -10013, + PUBLIC_KEY_OPERATION = -10014, +}; + +inline static std::vector blob2vector(const uint8_t* data, const size_t length) { + std::vector result(data, data + length); + return result; +} + +inline static std::vector blob2vector(const std::string& value) { + vector result(reinterpret_cast(value.data()), + reinterpret_cast(value.data()) + value.size()); + return result; +} + +inline void blob2Vec(const uint8_t* from, size_t size, std::vector& to) { + for (size_t i = 0; i < size; ++i) { + to.push_back(from[i]); + } +} + +// HardwareAuthToken vector2AuthToken(const vector& buffer); +// vector authToken2vector(const HardwareAuthToken& token); +keymaster_error_t translateExtendedErrorsToHalErrors(keymaster_error_t errorCode); +uint32_t getOsVersion(); +uint32_t getOsPatchlevel(); +uint32_t getVendorPatchlevel(); +void addCreationTime(AuthorizationSet& paramSet); + +keymaster_error_t getCertificateChain(std::vector& chainBuffer, + std::vector>& certChain); +} // namespace javacard_keymaster diff --git a/HAL/README.md b/HAL/README.md deleted file mode 100644 index 38e3309a..00000000 --- a/HAL/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# JavaCardKeymaster HAL - -This directory contains the implementation of the Keymaster 4.1 -Hardware Abstraction Layer (HAL) interface, implemented as a Linux -binary which runs as a standalone process mediating between Keystore -(the Keymaster client) and the Applet running on a JavaCard secure -element. diff --git a/HAL/keymaster/4.1/SocketTransport.cpp b/HAL/SocketTransport.cpp similarity index 50% rename from HAL/keymaster/4.1/SocketTransport.cpp rename to HAL/SocketTransport.cpp index e060262f..599dad39 100644 --- a/HAL/keymaster/4.1/SocketTransport.cpp +++ b/HAL/SocketTransport.cpp @@ -14,102 +14,94 @@ ** See the License for the specific language governing permissions and ** limitations under the License. */ -#include -#include +#include "SocketTransport.h" +#include "ITransport.h" #include -#include -#include "Transport.h" +#include #include +#include +#include +#include -#define PORT 8080 -#define IPADDR "192.168.0.29" -//#define IPADDR "192.168.0.5" +#define PORT 8080 +#define IPADDR "192.168.7.239" #define MAX_RECV_BUFFER_SIZE 2500 -namespace se_transport { +namespace javacard_keymaster { +using std::shared_ptr; +using std::vector; bool SocketTransport::openConnection() { - struct sockaddr_in serv_addr; - - if(mSocketStatus){ - closeConnection(); - } - - if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) - { - LOG(ERROR) << "Socket creation failed" << " Error: "<& output) { +bool SocketTransport::sendData(const vector& inData, vector& output) { uint8_t buffer[MAX_RECV_BUFFER_SIZE]; int count = 1; - bool sendStatus = false; - while(!mSocketStatus && count++ < 5 ) { + while (!socketStatus && count++ < 5) { sleep(1); LOG(ERROR) << "Trying to open socket connection... count: " << count; openConnection(); } - if(count >= 5) { + if (count >= 5) { LOG(ERROR) << "Failed to open socket connection"; - closeConnection(); return false; } - if (send(mSocket, inData, inLen , 0)< 0) { + if (0 > send(mSocket, inData.data(), inData.size(), 0)) { static int connectionResetCnt = 0; /* To avoid loop */ if (ECONNRESET == errno && connectionResetCnt == 0) { - //Connection reset. Try open socket and then sendData. - closeConnection(); + // Connection reset. Try open socket and then sendData. + socketStatus = false; connectionResetCnt++; - sendStatus = sendData(inData, inLen, output); - return sendStatus; + return sendData(inData, output); } LOG(ERROR) << "Failed to send data over socket err: " << errno; connectionResetCnt = 0; return false; } - ssize_t valRead = read( mSocket , buffer, MAX_RECV_BUFFER_SIZE); - if(0 > valRead) { + + ssize_t valRead = read(mSocket, buffer, MAX_RECV_BUFFER_SIZE); + if (0 > valRead) { LOG(ERROR) << "Failed to read data from socket."; } - for(size_t i = 0; i < valRead; i++) { + for (size_t i = 0; i < valRead; i++) { output.push_back(buffer[i]); } return true; } bool SocketTransport::closeConnection() { - if(mSocketStatus) - close(mSocket); - mSocketStatus = false; + close(mSocket); + socketStatus = false; return true; } bool SocketTransport::isConnected() { - return mSocketStatus; + return socketStatus; } -} +} // namespace javacard_keymaster diff --git a/HAL/SocketTransport.h b/HAL/SocketTransport.h new file mode 100644 index 00000000..9b9db63b --- /dev/null +++ b/HAL/SocketTransport.h @@ -0,0 +1,55 @@ +/* + ** + ** Copyright 2020, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#pragma once +#include "ITransport.h" +#include +#include + +namespace javacard_keymaster { +using std::shared_ptr; +using std::vector; + +class SocketTransport : public ITransport { + + public: + SocketTransport() : mSocket(-1), socketStatus(false) {} + /** + * Creates a socket instance and connects to the provided server IP and port. + */ + bool openConnection() override; + /** + * Sends data over socket and receives data back. + */ + bool sendData(const vector& inData, vector& output) override; + /** + * Closes the connection. + */ + bool closeConnection() override; + /** + * Returns the state of the connection status. Returns true if the connection is active, + * false if connection is broken. + */ + bool isConnected() override; + + private: + /** + * Socket instance. + */ + int mSocket; + bool socketStatus; +}; +} // namespace javacard_keymaster diff --git a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.rc b/HAL/android.hardware.keymaster@4.1-strongbox.service.rc similarity index 100% rename from HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.rc rename to HAL/android.hardware.keymaster@4.1-strongbox.service.rc diff --git a/HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.xml b/HAL/android.hardware.keymaster@4.1-strongbox.service.xml similarity index 100% rename from HAL/keymaster/4.1/android.hardware.keymaster@4.1-strongbox.service.xml rename to HAL/android.hardware.keymaster@4.1-strongbox.service.xml diff --git a/HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml b/HAL/android.hardware.keymaster_strongbox_keystore.xml similarity index 100% rename from HAL/keymaster/4.1/android.hardware.strongbox_keystore.xml rename to HAL/android.hardware.keymaster_strongbox_keystore.xml diff --git a/HAL/android.hardware.security.keymint-service.strongbox.rc b/HAL/android.hardware.security.keymint-service.strongbox.rc new file mode 100644 index 00000000..9419c350 --- /dev/null +++ b/HAL/android.hardware.security.keymint-service.strongbox.rc @@ -0,0 +1,3 @@ +service vendor.keymint-strongbox /vendor/bin/hw/android.hardware.security.keymint-service.strongbox + class early_hal + user nobody diff --git a/HAL/android.hardware.security.keymint-service.strongbox.xml b/HAL/android.hardware.security.keymint-service.strongbox.xml new file mode 100644 index 00000000..0631f129 --- /dev/null +++ b/HAL/android.hardware.security.keymint-service.strongbox.xml @@ -0,0 +1,10 @@ + + + android.hardware.security.keymint + IKeyMintDevice/strongbox + + + android.hardware.security.keymint + IRemotelyProvisionedComponent/strongbox + + diff --git a/HAL/android.hardware.security.sharedsecret-service.strongbox.xml b/HAL/android.hardware.security.sharedsecret-service.strongbox.xml new file mode 100644 index 00000000..5492100e --- /dev/null +++ b/HAL/android.hardware.security.sharedsecret-service.strongbox.xml @@ -0,0 +1,6 @@ + + + android.hardware.security.sharedsecret + ISharedSecret/strongbox + + diff --git a/HAL/android.hardware.strongbox_keystore.xml b/HAL/android.hardware.strongbox_keystore.xml new file mode 100644 index 00000000..d92d6059 --- /dev/null +++ b/HAL/android.hardware.strongbox_keystore.xml @@ -0,0 +1,17 @@ + + + + + + + diff --git a/HAL/keymaster/4.1/CborConverter.cpp b/HAL/keymaster/4.1/CborConverter.cpp deleted file mode 100644 index 4d7041fd..00000000 --- a/HAL/keymaster/4.1/CborConverter.cpp +++ /dev/null @@ -1,412 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include -#include - -bool CborConverter::addKeyparameters(Array& array, const android::hardware::hidl_vec& keyParams) { - Map map; - std::map> enum_repetition; - std::map uint_repetition; - for(size_t i = 0; i < keyParams.size(); i++) { - KeyParameter param = keyParams[i]; - TagType tagType = static_cast(param.tag & (0xF << 28)); - switch(tagType) { - case TagType::ENUM: - case TagType::UINT: - map.add(static_cast(param.tag), param.f.integer); - break; - case TagType::UINT_REP: - uint_repetition[static_cast(param.tag)].add(param.f.integer); - break; - case TagType::ENUM_REP: - enum_repetition[static_cast(param.tag)].push_back(static_cast(param.f.integer)); - break; - case TagType::ULONG: - map.add(static_cast(param.tag), param.f.longInteger); - break; - case TagType::ULONG_REP: - uint_repetition[static_cast(param.tag)].add(param.f.longInteger); - break; - case TagType::DATE: - map.add(static_cast(param.tag), param.f.dateTime); - break; - case TagType::BOOL: - map.add(static_cast(param.tag), static_cast(param.f.boolValue)); - break; - case TagType::BIGNUM: - case TagType::BYTES: - map.add(static_cast(param.tag), (std::vector(param.blob))); - break; - default: - /* Invalid skip */ - break; - } - } - if(0 < enum_repetition.size()) { - for( auto const& [key, val] : enum_repetition ) { - Bstr bstr(val); - map.add(key, std::move(bstr)); - } - } - if(0 < uint_repetition.size()) { - for( auto & [key, val] : uint_repetition ) { - map.add(key, std::move(val)); - } - } - array.add(std::move(map)); - return true; -} - -bool CborConverter::getKeyCharacteristics(const std::unique_ptr &item, const uint32_t pos, - KeyCharacteristics& keyCharacteristics) { - bool ret = false; - std::unique_ptr arrayItem(nullptr); - getItemAtPos(item, pos, arrayItem); - if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) - return ret; - - if (!getKeyParameters(arrayItem, 0, keyCharacteristics.softwareEnforced)) { - return ret; - } - - if (!getKeyParameters(arrayItem, 1, keyCharacteristics.hardwareEnforced)) { - return ret; - } - //success - ret = true; - return ret; -} - -bool CborConverter::getKeyParameter(const std::pair&, - const std::unique_ptr&> pair, std::vector& keyParams) { - bool ret = false; - uint64_t key; - uint64_t value; - - if(!getUint64(pair.first, key)) { - return ret; - } - - /* Get the TagType from the Tag */ - TagType tagType = static_cast(key & (0xF << 28)); - switch(tagType) { - case TagType::ENUM_REP: - { - /* ENUM_REP contains values encoded in a Binary string */ - const Bstr* bstr = pair.second.get()->asBstr(); - if(bstr == nullptr) return ret; - for (auto bchar : bstr->value()) { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - keyParam.f.integer = bchar; - keyParams.push_back(std::move(keyParam)); - } - return true; - } - break; - case TagType::ENUM: - case TagType::UINT: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.integer = static_cast(value); - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::ULONG: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.longInteger = value; - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::UINT_REP: - { - /* UINT_REP contains values encoded in a Array */ - Array* array = const_cast(pair.second.get()->asArray()); - if(array == nullptr) return ret; - for(int i = 0; i < array->size(); i++) { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - std::unique_ptr item = std::move((*array)[i]); - if(!getUint64(item, value)) { - return ret; - } - keyParam.f.integer = static_cast(value); - keyParams.push_back(std::move(keyParam)); - - } - return true; - } - break; - case TagType::ULONG_REP: - { - /* ULONG_REP contains values encoded in a Array */ - Array* array = const_cast(pair.second.get()->asArray()); - if(array == nullptr) return ret; - for(int i = 0; i < array->size(); i++) { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - std::unique_ptr item = std::move((*array)[i]); - if(!getUint64(item, keyParam.f.longInteger)) { - return ret; - } - keyParams.push_back(std::move(keyParam)); - - } - return true; - } - break; - case TagType::DATE: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.dateTime = value; - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::BOOL: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - if(!getUint64(pair.second, value)) { - return ret; - } - keyParam.f.boolValue = static_cast(value); - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - case TagType::BYTES: - { - KeyParameter keyParam; - keyParam.tag = static_cast(key); - const Bstr* bstr = pair.second.get()->asBstr(); - if(bstr == nullptr) return ret; - keyParam.blob = bstr->value(); - keyParams.push_back(std::move(keyParam)); - return true; - } - break; - default: - /* Invalid skip */ - break; - } - return ret; -} - - -bool CborConverter::getMultiBinaryArray(const std::unique_ptr& item, const uint32_t pos, - std::vector>& data) { - bool ret = false; - std::unique_ptr arrayItem(nullptr); - - getItemAtPos(item, pos, arrayItem); - if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) - return ret; - const Array* arr = arrayItem.get()->asArray(); - size_t arrSize = arr->size(); - for (int i = 0; i < arrSize; i++) { - std::vector temp; - if (!getBinaryArray(arrayItem, i, temp)) - return ret; - data.push_back(std::move(temp)); - } - ret = true; // success - return ret; -} - -bool CborConverter::getBinaryArray(const std::unique_ptr& item, const uint32_t pos, - ::android::hardware::hidl_vec& value) { - bool ret = false; - std::unique_ptr strItem(nullptr); - getItemAtPos(item, pos, strItem); - if ((strItem == nullptr) || (MajorType::BSTR != getType(strItem))) - return ret; - - const Bstr* bstr = strItem.get()->asBstr(); - value = bstr->value(); - ret = true; - return ret; -} - -bool CborConverter::getBinaryArray(const std::unique_ptr& item, const uint32_t pos, - ::android::hardware::hidl_string& value) { - std::vector vec; - std::string str; - if(!getBinaryArray(item, pos, vec)) { - return false; - } - for(auto ch : vec) { - str += ch; - } - value = str; - return true; -} - -bool CborConverter::getBinaryArray(const std::unique_ptr& item, const uint32_t pos, std::vector& value) { - bool ret = false; - std::unique_ptr strItem(nullptr); - getItemAtPos(item, pos, strItem); - if ((strItem == nullptr) || (MajorType::BSTR != getType(strItem))) - return ret; - - const Bstr* bstr = strItem.get()->asBstr(); - for (auto bchar : bstr->value()) { - value.push_back(bchar); - } - ret = true; - return ret; -} - -bool CborConverter::getHmacSharingParameters(const std::unique_ptr& item, const uint32_t pos, HmacSharingParameters& params) { - std::vector paramValue; - bool ret = false; - std::unique_ptr arrayItem(nullptr); - - //1. Get ArrayItem - //2. First item in the array seed; second item in the array is nonce. - - getItemAtPos(item, pos, arrayItem); - if ((arrayItem == nullptr) || (MajorType::ARRAY != getType(arrayItem))) - return ret; - - //Seed - if (!getBinaryArray(arrayItem, 0, params.seed)) - return ret; - - //nonce - if (!getBinaryArray(arrayItem, 1, paramValue)) - return ret; - memcpy(params.nonce.data(), paramValue.data(), paramValue.size()); - ret = true; - return ret; -} - -bool CborConverter::addVerificationToken(Array& array, const VerificationToken& - verificationToken, std::vector& encodedParamsVerified) { - Array vToken; - vToken.add(verificationToken.challenge); - vToken.add(verificationToken.timestamp); - vToken.add(std::move(encodedParamsVerified)); - vToken.add(static_cast(verificationToken.securityLevel)); - vToken.add((std::vector(verificationToken.mac))); - array.add(std::move(vToken)); - return true; -} - -bool CborConverter::addHardwareAuthToken(Array& array, const HardwareAuthToken& - authToken) { - Array hwAuthToken; - hwAuthToken.add(authToken.challenge); - hwAuthToken.add(authToken.userId); - hwAuthToken.add(authToken.authenticatorId); - hwAuthToken.add(static_cast(authToken.authenticatorType)); - hwAuthToken.add(authToken.timestamp); - hwAuthToken.add((std::vector(authToken.mac))); - array.add(std::move(hwAuthToken)); - return true; -} - -bool CborConverter::getHardwareAuthToken(const std::unique_ptr& item, const uint32_t pos, HardwareAuthToken& token) { - bool ret = false; - //challenge - if (!getUint64(item, pos, token.challenge)) - return ret; - //userId - if (!getUint64(item, pos+1, token.userId)) - return ret; - //AuthenticatorId - if (!getUint64(item, pos+2, token.authenticatorId)) - return ret; - //AuthType - uint64_t authType; - if (!getUint64(item, pos+3, authType)) - return ret; - token.authenticatorType = static_cast(authType); - //Timestamp - if (!getUint64(item, pos+4, token.timestamp)) - return ret; - //MAC - if (!getBinaryArray(item, pos+5, token.mac)) - return ret; - ret = true; - return ret; -} - -bool CborConverter::getVerificationToken(const std::unique_ptr& item, const uint32_t pos, VerificationToken& - token) { - bool ret = false; - //challenge - if (!getUint64(item, pos, token.challenge)) - return ret; - - //timestamp - if (!getUint64(item, pos+1, token.timestamp)) - return ret; - - //List of KeyParameters - if (!getKeyParameters(item, pos+2, token.parametersVerified)) - return ret; - - //AuthenticatorId - uint64_t val; - if (!getUint64(item, pos+3, val)) - return ret; - token.securityLevel = static_cast(val); - - //MAC - if (!getBinaryArray(item, pos+4, token.mac)) - return ret; - ret = true; - return ret; - -} - -bool CborConverter::getKeyParameters(const std::unique_ptr& item, const uint32_t pos, android::hardware::hidl_vec& keyParams) { - bool ret = false; - std::unique_ptr mapItem(nullptr); - std::vector params; - getItemAtPos(item, pos, mapItem); - if ((mapItem == nullptr) || (MajorType::MAP != getType(mapItem))) - return ret; - const Map* map = mapItem.get()->asMap(); - size_t mapSize = map->size(); - for (int i = 0; i < mapSize; i++) { - if (!getKeyParameter((*map)[i], params)) { - return ret; - } - } - keyParams.resize(params.size()); - keyParams = params; - ret = true; - return ret; -} diff --git a/HAL/keymaster/4.1/CommonUtils.cpp b/HAL/keymaster/4.1/CommonUtils.cpp deleted file mode 100644 index 476fe68e..00000000 --- a/HAL/keymaster/4.1/CommonUtils.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TAG_SEQUENCE 0x30 -#define LENGTH_MASK 0x80 -#define LENGTH_VALUE_MASK 0x7F - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch"; -constexpr char kVendorPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$"; -constexpr size_t kYearMatch = 1; -constexpr size_t kMonthMatch = 2; -constexpr size_t kDayMatch = 3; -constexpr size_t kVendorPatchlevelMatchCount = kDayMatch + 1; - -hidl_vec kmParamSet2Hidl(const keymaster_key_param_set_t& set) { - hidl_vec result; - if (set.length == 0 || set.params == nullptr) - return result; - - result.resize(set.length); - keymaster_key_param_t* params = set.params; - for (size_t i = 0; i < set.length; ++i) { - auto tag = params[i].tag; - result[i].tag = legacy_enum_conversion(tag); - switch (typeFromTag(tag)) { - case KM_ENUM: - case KM_ENUM_REP: - result[i].f.integer = params[i].enumerated; - break; - case KM_UINT: - case KM_UINT_REP: - result[i].f.integer = params[i].integer; - break; - case KM_ULONG: - case KM_ULONG_REP: - result[i].f.longInteger = params[i].long_integer; - break; - case KM_DATE: - result[i].f.dateTime = params[i].date_time; - break; - case KM_BOOL: - result[i].f.boolValue = params[i].boolean; - break; - case KM_BIGNUM: - case KM_BYTES: - result[i].blob.setToExternal(const_cast(params[i].blob.data), - params[i].blob.data_length); - break; - case KM_INVALID: - default: - params[i].tag = KM_TAG_INVALID; - /* just skip */ - break; - } - } - return result; -} - -keymaster_key_param_set_t hidlKeyParams2Km(const hidl_vec& keyParams) { - keymaster_key_param_set_t set; - - set.params = new keymaster_key_param_t[keyParams.size()]; - set.length = keyParams.size(); - - for (size_t i = 0; i < keyParams.size(); ++i) { - auto tag = legacy_enum_conversion(keyParams[i].tag); - switch (typeFromTag(tag)) { - case KM_ENUM: - case KM_ENUM_REP: - set.params[i] = keymaster_param_enum(tag, keyParams[i].f.integer); - break; - case KM_UINT: - case KM_UINT_REP: - set.params[i] = keymaster_param_int(tag, keyParams[i].f.integer); - break; - case KM_ULONG: - case KM_ULONG_REP: - set.params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger); - break; - case KM_DATE: - set.params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime); - break; - case KM_BOOL: - if (keyParams[i].f.boolValue) - set.params[i] = keymaster_param_bool(tag); - else - set.params[i].tag = KM_TAG_INVALID; - break; - case KM_BIGNUM: - case KM_BYTES: - set.params[i] = - keymaster_param_blob(tag, &keyParams[i].blob[0], keyParams[i].blob.size()); - break; - case KM_INVALID: - default: - set.params[i].tag = KM_TAG_INVALID; - /* just skip */ - break; - } - } - - return set; -} - -ErrorCode getEcCurve(const EC_GROUP *group, EcCurve& ecCurve) { - int curve = EC_GROUP_get_curve_name(group); - switch(curve) { - case NID_secp224r1: - ecCurve = EcCurve::P_224; - break; - case NID_X9_62_prime256v1: - ecCurve = EcCurve::P_256; - break; - case NID_secp384r1: - ecCurve = EcCurve::P_384; - break; - case NID_secp521r1: - ecCurve = EcCurve::P_521; - break; - default: - return ErrorCode::UNSUPPORTED_EC_CURVE; - } - return ErrorCode::OK; -} - -ErrorCode ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& -publicKey, EcCurve& ecCurve) { - ErrorCode errorCode = ErrorCode::INVALID_KEY_BLOB; - EVP_PKEY *pkey = nullptr; - const uint8_t *data = pkcs8Blob.data(); - - d2i_PrivateKey(EVP_PKEY_EC, &pkey, &data, pkcs8Blob.size()); - if(!pkey) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - UniquePtr ec_key(EVP_PKEY_get1_EC_KEY(pkey)); - if(!ec_key.get()) - return legacy_enum_conversion(TranslateLastOpenSslError()); - - //Get EC Group - const EC_GROUP *group = EC_KEY_get0_group(ec_key.get()); - if(group == NULL) - return errorCode; - - if(ErrorCode::OK != (errorCode = getEcCurve(group, ecCurve))) { - return errorCode; - } - - //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); - - EVP_PKEY_free(pkey); - return ErrorCode::OK; -} - -ErrorCode rsaRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& privateExp, std::vector& -pubModulus) { - ErrorCode errorCode = ErrorCode::INVALID_KEY_BLOB; - const BIGNUM *n=NULL, *e=NULL, *d=NULL; - EVP_PKEY *pkey = nullptr; - const uint8_t *data = pkcs8Blob.data(); - - d2i_PrivateKey(EVP_PKEY_RSA, &pkey, &data, pkcs8Blob.size()); - if(!pkey) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - UniquePtr rsa_key(EVP_PKEY_get1_RSA(pkey)); - if(!rsa_key.get()) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - RSA_get0_key(rsa_key.get(), &n, &e, &d); - if(d != NULL && n != NULL) { - /*private exponent */ - int privExpLen = BN_num_bytes(d); - std::unique_ptr privExp(new uint8_t[privExpLen]); - BN_bn2bin(d, privExp.get()); - /* public modulus */ - int pubModLen = BN_num_bytes(n); - std::unique_ptr pubMod(new uint8_t[pubModLen]); - BN_bn2bin(n, pubMod.get()); - - privateExp.insert(privateExp.begin(), privExp.get(), privExp.get()+privExpLen); - pubModulus.insert(pubModulus.begin(), pubMod.get(), pubMod.get()+pubModLen); - } else { - return errorCode; - } - EVP_PKEY_free(pkey); - return ErrorCode::OK; -} - -ErrorCode getCertificateChain(std::vector& chainBuffer, std::vector>& certChain) { - uint8_t *data = chainBuffer.data(); - int index = 0; - uint32_t length = 0; - while (index < chainBuffer.size()) { - std::vector temp; - if(data[index] == TAG_SEQUENCE) { - //read next byte - if (0 == (data[index+1] & LENGTH_MASK)) { - length = (uint32_t)data[index]; - //Add SEQ and Length fields - length += 2; - } else { - int additionalBytes = data[index+1] & LENGTH_VALUE_MASK; - if (additionalBytes == 0x01) { - length = data[index+2]; - //Add SEQ and Length fields - length += 3; - } else if (additionalBytes == 0x02) { - length = (data[index+2] << 8 | data[index+3]); - //Add SEQ and Length fields - length += 4; - } else if (additionalBytes == 0x04) { - length = data[index+2] << 24; - length |= data[index+3] << 16; - length |= data[index+4] << 8; - length |= data[index+5]; - //Add SEQ and Length fields - length += 6; - } else { - //Length is larger than uint32_t max limit. - return ErrorCode::UNKNOWN_ERROR; - } - } - temp.insert(temp.end(), (data+index), (data+index+length)); - index += length; - - certChain.push_back(std::move(temp)); - } else { - //SEQUENCE TAG MISSING. - return ErrorCode::UNKNOWN_ERROR; - } - } - return ErrorCode::OK; -} - -uint32_t match_to_uint32(const char* expression, const regmatch_t& match) { - if (match.rm_so == -1) return 0; - - size_t len = match.rm_eo - match.rm_so; - std::string s(expression + match.rm_so, len); - return std::stoul(s); -} - -std::string wait_and_get_property(const char* prop) { - std::string prop_value; - while (!android::base::WaitForPropertyCreation(prop)) { - LOG(ERROR) << "waited 15s for %s, still waiting..." << prop; - } - prop_value = android::base::GetProperty(prop, "" /* default */); - return prop_value; -} - - -uint32_t GetVendorPatchlevel(const char* patchlevel_str) { - regex_t regex; - if (regcomp(®ex, kVendorPatchlevelRegex, REG_EXTENDED) != 0) { - LOG(ERROR) << "Failed to compile Vendor patchlevel regex! " << kVendorPatchlevelRegex; - return 0; - } - - regmatch_t matches[kVendorPatchlevelMatchCount]; - int not_match = - regexec(®ex, patchlevel_str, kVendorPatchlevelMatchCount, matches, 0 /* flags */); - regfree(®ex); - if (not_match) { - LOG(ERROR) << "Vendor patchlevel string does not match expected format. Using patchlevel 0"; - return 0; - } - - uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]); - uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]); - uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]); - - if (month < 1 || month > 12) { - LOG(ERROR) << "Invalid patch month " << month; - return 0; - } - bool isLeapYear = (0 == year % 4) ? true : false; - int maxDaysForMonth = 31; - switch(month) { - case 4: case 6: case 9: case 11: - maxDaysForMonth = 30; - break; - case 2: - maxDaysForMonth = isLeapYear ? 29 : 28; - break; - } - if (day < 1 || day > maxDaysForMonth) { - LOG(ERROR) << "Invalid patch day " << day; - return 0; - } - return year * 10000 + month * 100 + day; -} - -uint32_t GetVendorPatchlevel() { - std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp); - return GetVendorPatchlevel(patchlevel.c_str()); -} - - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp b/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp deleted file mode 100644 index 17e5bb4c..00000000 --- a/HAL/keymaster/4.1/JavacardKeymaster4Device.cpp +++ /dev/null @@ -1,1522 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define JAVACARD_KEYMASTER_NAME "JavacardKeymaster4.1Device v1.0" -#define JAVACARD_KEYMASTER_AUTHOR "Android Open Source Project" -#define PROP_BUILD_QEMU "ro.kernel.qemu" -#define PROP_BUILD_FINGERPRINT "ro.build.fingerprint" -// Cuttlefish build fingerprint substring. -#define CUTTLEFISH_FINGERPRINT_SS "aosp_cf_" - -#define APDU_CLS 0x80 -#define APDU_P1 0x40 -#define APDU_P2 0x00 -#define APDU_RESP_STATUS_OK 0x9000 - -#define SW_KM_OPR 0UL -#define SB_KM_OPR 1UL -#define SE_POWER_RESET_STATUS_FLAG ( 1 << 30) - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -static std::unique_ptr pTransportFactory = nullptr; -constexpr size_t kOperationTableSize = 4; -/* - * Key is the operation handle generated by either SoftKM or StrongboxKM and - * value is either PUBLIC_OPERATION or PRIVATE_OPERATION - */ -std::map operationTable; - -struct KM_AUTH_LIST_Delete { - void operator()(KM_AUTH_LIST* p) { KM_AUTH_LIST_free(p); } -}; - -//Extended error codes -enum ExtendedErrors { - SW_CONDITIONS_NOT_SATISFIED = -10001, - UNSUPPORTED_CLA = -10002, - INVALID_P1P2 = -10003, - UNSUPPORTED_INSTRUCTION = -10004, - CMD_NOT_ALLOWED = -10005, - SW_WRONG_LENGTH = -10006, - INVALID_DATA = -10007, - CRYPTO_ILLEGAL_USE = -10008, - CRYPTO_ILLEGAL_VALUE = -10009, - CRYPTO_INVALID_INIT = -10010, - CRYPTO_NO_SUCH_ALGORITHM = -10011, - CRYPTO_UNINITIALIZED_KEY = -10012, - GENERIC_UNKNOWN_ERROR = -10013 -}; - -static inline std::unique_ptr& getTransportFactoryInstance() { - bool isEmulator = false; - if(pTransportFactory == nullptr) { - // Check if the current build is for emulator or device. - isEmulator = android::base::GetBoolProperty(PROP_BUILD_QEMU, false); - if (!isEmulator) { - std::string fingerprint = android::base::GetProperty(PROP_BUILD_FINGERPRINT, ""); - if (!fingerprint.empty()) { - if (fingerprint.find(CUTTLEFISH_FINGERPRINT_SS, 0) != std::string::npos) { - isEmulator = true; - } - } - } - pTransportFactory = std::unique_ptr(new se_transport::TransportFactory( - isEmulator)); - pTransportFactory->openConnection(); - } - return pTransportFactory; -} - -static inline bool findTag(const hidl_vec& params, Tag tag) { - size_t size = params.size(); - for(size_t i = 0; i < size; ++i) { - if(tag == params[i].tag) - return true; - } - return false; -} - -static inline bool getTag(const hidl_vec& params, Tag tag, KeyParameter& param) { - size_t size = params.size(); - for(size_t i = 0; i < size; ++i) { - if(tag == params[i].tag) { - param = params[i]; - return true; - } - } - return false; -} - -template -static T translateExtendedErrorsToHalErrors(T& errorCode) { - T err; - switch(static_cast(errorCode)) { - case SW_CONDITIONS_NOT_SATISFIED: - case UNSUPPORTED_CLA: - case INVALID_P1P2: - case INVALID_DATA: - case CRYPTO_ILLEGAL_USE: - case CRYPTO_ILLEGAL_VALUE: - case CRYPTO_INVALID_INIT: - case CRYPTO_UNINITIALIZED_KEY: - case GENERIC_UNKNOWN_ERROR: - err = T::UNKNOWN_ERROR; - break; - case CRYPTO_NO_SUCH_ALGORITHM: - err = T::UNSUPPORTED_ALGORITHM; - break; - case UNSUPPORTED_INSTRUCTION: - case CMD_NOT_ALLOWED: - case SW_WRONG_LENGTH: - err = T::UNIMPLEMENTED; - break; - default: - err = static_cast(errorCode); - break; - } - return err; -} - -/* Returns true if operation handle exists, otherwise false */ -static inline bool isOperationHandleExists(uint64_t opHandle) { - if (operationTable.end() == operationTable.find(opHandle)) { - return false; - } - return true; -} - -static inline OperationType getOperationType(uint64_t operationHandle) { - auto it = operationTable.find(operationHandle); - if (it == operationTable.end()) { - return OperationType::UNKNOWN; - } - return it->second; -} - -/* Clears all the strongbox operation handle entries from operation table */ -static void clearStrongboxOprHandleEntries(const std::unique_ptr& oprCtx) { - LOG(INFO) - << "Secure Element reset or applet upgrade detected. Removing existing operation handles"; - auto it = operationTable.begin(); - while (it != operationTable.end()) { - if (it->second == OperationType::PRIVATE_OPERATION) { // Strongbox operation - LOG(INFO) << "operation handle: " << it->first << " is removed"; - oprCtx->clearOperationData(it->first); - it = operationTable.erase(it); - } else { - ++it; - } - } -} - -/** - * Returns the negative value of the same number. - */ -static inline int32_t get2sCompliment(uint32_t value) { - return static_cast(~value+1); -} - -/** - * Clears all the strongbox operation handle entries if secure element power reset happens. - * And also extracts the error code value after unmasking the power reset status flag. - */ -static uint32_t handleErrorCode(const std::unique_ptr& oprCtx, uint32_t errorCode) { - //Check if secure element is reset - bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); - - if (isSeResetOccurred) { - //Clear the operation table for Strongbox operations entries. - clearStrongboxOprHandleEntries(oprCtx); - // Unmask the power reset status flag. - errorCode &= ~SE_POWER_RESET_STATUS_FLAG; - } - return errorCode; -} - -template -static std::tuple, T> decodeData(CborConverter& cb, const std::vector& response, bool - hasErrorCode, const std::unique_ptr& oprCtx) { - std::unique_ptr item(nullptr); - T errorCode = T::OK; - std::tie(item, errorCode) = cb.decodeData(response, hasErrorCode); - - uint32_t tempErrCode = handleErrorCode(oprCtx, static_cast(errorCode)); - - // SE sends errocode as unsigned value so convert the unsigned value - // into a signed value of same magnitude and copy back to errorCode. - errorCode = static_cast(get2sCompliment(tempErrCode)); - - if (T::OK != errorCode) { - LOG(ERROR) << "error in decodeData: " << (int32_t) errorCode; - errorCode = translateExtendedErrorsToHalErrors(errorCode); - } - LOG(DEBUG) << "decodeData status: " << (int32_t) errorCode; - return {std::move(item), errorCode}; -} - -ErrorCode encodeParametersVerified(const VerificationToken& verificationToken, std::vector& asn1ParamsVerified) { - if (verificationToken.parametersVerified.size() > 0) { - AuthorizationSet paramSet; - KeymasterBlob derBlob; - UniquePtr kmAuthList(KM_AUTH_LIST_new()); - - paramSet.Reinitialize(KmParamSet(verificationToken.parametersVerified)); - - auto err = build_auth_list(paramSet, kmAuthList.get()); - if (err != KM_ERROR_OK) { - return legacy_enum_conversion(err); - } - int len = i2d_KM_AUTH_LIST(kmAuthList.get(), nullptr); - if (len < 0) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - - if (!derBlob.Reset(len)) { - return legacy_enum_conversion(KM_ERROR_MEMORY_ALLOCATION_FAILED); - } - - uint8_t* p = derBlob.writable_data(); - len = i2d_KM_AUTH_LIST(kmAuthList.get(), &p); - if (len < 0) { - return legacy_enum_conversion(TranslateLastOpenSslError()); - } - asn1ParamsVerified.insert(asn1ParamsVerified.begin(), p, p+len); - derBlob.release(); - } - return ErrorCode::OK; -} - -ErrorCode prepareCborArrayFromKeyData(const hidl_vec& keyParams, KeyFormat keyFormat, const hidl_vec& blob, cppbor::Array& - array) { - ErrorCode errorCode = ErrorCode::OK; - AuthorizationSet paramSet; - keymaster_algorithm_t algorithm; - if(keyFormat == KeyFormat::PKCS8) { - - paramSet.Reinitialize(KmParamSet(keyParams)); - paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); - - if(KM_ALGORITHM_RSA == algorithm) { - std::vector privExp; - std::vector modulus; - if(ErrorCode::OK != (errorCode = rsaRawKeyFromPKCS8(std::vector(blob), privExp, modulus))) { - return errorCode; - } - array.add(privExp); - array.add(modulus); - } else if(KM_ALGORITHM_EC == algorithm) { - std::vector privKey; - std::vector pubKey; - EcCurve curve; - if(ErrorCode::OK != (errorCode = ecRawKeyFromPKCS8(std::vector(blob), privKey, pubKey, curve))) { - return errorCode; - } - array.add(privKey); - array.add(pubKey); - } else { - return ErrorCode::UNSUPPORTED_ALGORITHM; - } - } else if(keyFormat == KeyFormat::RAW) { - array.add(std::vector(blob)); - } - return errorCode; -} - -ErrorCode parseWrappedKey(const hidl_vec& wrappedKeyData, std::vector& iv, std::vector& transitKey, -std::vector& secureKey, std::vector& tag, hidl_vec& authList, KeyFormat& -keyFormat, std::vector& wrappedKeyDescription) { - KeymasterBlob kmIv; - KeymasterKeyBlob kmTransitKey; - KeymasterKeyBlob kmSecureKey; - KeymasterBlob kmTag; - AuthorizationSet authSet; - keymaster_key_format_t kmKeyFormat; - KeymasterBlob kmWrappedKeyDescription; - - size_t keyDataLen = wrappedKeyData.size(); - uint8_t *keyData = dup_buffer(wrappedKeyData.data(), keyDataLen); - keymaster_key_blob_t keyMaterial = {keyData, keyDataLen}; - - keymaster_error_t error = parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, - &kmSecureKey, &kmTag, &authSet, - &kmKeyFormat, &kmWrappedKeyDescription); - if (error != KM_ERROR_OK) return legacy_enum_conversion(error); - blob2Vec(kmIv.data, kmIv.data_length, iv); - blob2Vec(kmTransitKey.key_material, kmTransitKey.key_material_size, transitKey); - blob2Vec(kmSecureKey.key_material, kmSecureKey.key_material_size, secureKey); - blob2Vec(kmTag.data, kmTag.data_length, tag); - authList = kmParamSet2Hidl(authSet); - keyFormat = static_cast(kmKeyFormat); - blob2Vec(kmWrappedKeyDescription.data, kmWrappedKeyDescription.data_length, wrappedKeyDescription); - - return ErrorCode::OK; -} - -ErrorCode constructApduMessage(Instruction& ins, std::vector& inputData, std::vector& apduOut) { - apduOut.push_back(static_cast(APDU_CLS)); //CLS - apduOut.push_back(static_cast(ins)); //INS - apduOut.push_back(static_cast(APDU_P1)); //P1 - apduOut.push_back(static_cast(APDU_P2)); //P2 - - if(USHRT_MAX >= inputData.size()) { - // Send extended length APDU always as response size is not known to HAL. - // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le all set to 00. - // Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. - //Extended length 3 bytes, starts with 0x00 - apduOut.push_back(static_cast(0x00)); - if (inputData.size() > 0) { - apduOut.push_back(static_cast(inputData.size() >> 8)); - apduOut.push_back(static_cast(inputData.size() & 0xFF)); - //Data - apduOut.insert(apduOut.end(), inputData.begin(), inputData.end()); - } - //Expected length of output. - //Accepting complete length of output every time. - apduOut.push_back(static_cast(0x00)); - apduOut.push_back(static_cast(0x00)); - } else { - return (ErrorCode::INSUFFICIENT_BUFFER_SPACE); - } - - return (ErrorCode::OK);//success -} - -uint16_t getStatus(std::vector& inputData) { - //Last two bytes are the status SW0SW1 - return (inputData.at(inputData.size()-2) << 8) | (inputData.at(inputData.size()-1)); -} - -ErrorCode JavacardKeymaster4Device::sendData(Instruction ins, std::vector& inData, std::vector& response) { - ErrorCode ret = ErrorCode::UNKNOWN_ERROR; - std::vector apdu; - - ret = constructApduMessage(ins, inData, apdu); - if(ret != ErrorCode::OK) { - LOG(ERROR) << "error in constructApduMessage cmd: " << (int32_t)ins << " status: " << (int32_t)ret; - return ret; - } - - if(!getTransportFactoryInstance()->sendData(apdu.data(), apdu.size(), response)) { - LOG(ERROR) << "error in sendData cmd: " << (int32_t)ins << " status: " - << (int32_t)ErrorCode::SECURE_HW_COMMUNICATION_FAILED; - return (ErrorCode::SECURE_HW_COMMUNICATION_FAILED); - } - - // Response size should be greater than 2. Cbor output data followed by two bytes of APDU status. - if((response.size() <= 2) || (getStatus(response) != APDU_RESP_STATUS_OK)) { - LOG(ERROR) << "error in sendData cmd: " << (int32_t)ins << " status: " << getStatus(response); - return (ErrorCode::UNKNOWN_ERROR); - } - LOG(DEBUG) << "sendData cmd: " << (int32_t)ins << " status: " << (int32_t)ErrorCode::OK; - return (ErrorCode::OK);//success -} - -void JavacardKeymaster4Device::handleSendEarlyBootEndedEvent() { - if (isEarlyBootEventPending) { - LOG(INFO) << "JavacardKeymaster4Device::handleSendEarlyBootEndedEvent send earlyBootEnded Event."; - if (V41ErrorCode::OK == earlyBootEnded()) { - isEarlyBootEventPending = false; - } - } -} - -/** - * Sends android system properties like os_version, os_patchlevel and vendor_patchlevel to - * the Applet. - */ -ErrorCode JavacardKeymaster4Device::setAndroidSystemProperties() { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - - array.add(GetOsVersion()). - add(GetOsPatchlevel()). - add(GetVendorPatchlevel()); - - std::vector cborData = array.encode(); - errorCode = sendData(Instruction::INS_SET_VERSION_PATCHLEVEL_CMD, cborData, cborOutData); - if (ErrorCode::OK == errorCode) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - if (ErrorCode::OK != errorCode) - LOG(ERROR) << "Failed to set os_version, os_patchlevel and vendor_patchlevel err: " << (int32_t) errorCode; - - return errorCode; -} - -JavacardKeymaster4Device::JavacardKeymaster4Device(): softKm_(new ::keymaster::AndroidKeymaster( - []() -> auto { - auto context = new JavaCardSoftKeymasterContext(); - context->SetSystemVersion(GetOsVersion(), GetOsPatchlevel()); - return context; - }(), - kOperationTableSize, keymaster::MessageVersion(keymaster::KmVersion::KEYMASTER_4_1, - 0 /* km_date */) )), oprCtx_(new OperationContext()), - isEachSystemPropertySet(false), isEarlyBootEventPending(false) { - // Send Android system properties like os_version, os_patchlevel and vendor_patchlevel - // to the Applet. Incase if setting system properties fails here, again try setting - // it from computeSharedHmac. - - if (ErrorCode::OK == setAndroidSystemProperties()) { - LOG(ERROR) << "javacard strongbox : setAndroidSystemProperties from constructor - successful"; - isEachSystemPropertySet = true; - } - -} - -JavacardKeymaster4Device::~JavacardKeymaster4Device() {} - -// Methods from IKeymasterDevice follow. -Return JavacardKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) { - // When socket is not connected return hardware info parameters from HAL itself. - std::vector resp; - std::vector input; - std::unique_ptr item; - uint64_t securityLevel = static_cast(SecurityLevel::STRONGBOX); - hidl_string jcKeymasterName; - hidl_string jcKeymasterAuthor; - - ErrorCode ret = sendData(Instruction::INS_GET_HW_INFO_CMD, input, resp); - if (ret == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, ret) = decodeData(cborConverter_, std::vector(resp.begin(), resp.end()-2), - false, oprCtx_); - if (item != nullptr) { - std::vector temp; - if(!cborConverter_.getUint64(item, 0, securityLevel) || - !cborConverter_.getBinaryArray(item, 1, jcKeymasterName) || - !cborConverter_.getBinaryArray(item, 2, jcKeymasterAuthor)) { - LOG(ERROR) << "Failed to convert cbor data of INS_GET_HW_INFO_CMD"; - _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); - return Void(); - } - } - _hidl_cb(static_cast(securityLevel), jcKeymasterName, jcKeymasterAuthor); - return Void(); - } else { - // It should not come here, but incase if for any reason SB keymaster fails to getHardwareInfo - // return proper values from HAL. - LOG(ERROR) << "Failed to fetch getHardwareInfo from javacard returning fixed values from HAL itself"; - _hidl_cb(SecurityLevel::STRONGBOX, JAVACARD_KEYMASTER_NAME, JAVACARD_KEYMASTER_AUTHOR); - return Void(); - } -} - -Return JavacardKeymaster4Device::getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) { - std::vector cborData; - std::vector input; - std::unique_ptr item; - HmacSharingParameters hmacSharingParameters; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - errorCode = sendData(Instruction::INS_GET_HMAC_SHARING_PARAM_CMD, input, cborData); - if (ErrorCode::OK == errorCode) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborData.begin(), cborData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getHmacSharingParameters(item, 1, hmacSharingParameters)) { - LOG(ERROR) << "javacard strongbox : Failed to convert cbor data of INS_GET_HMAC_SHARING_PARAM_CMD"; - errorCode = ErrorCode::UNKNOWN_ERROR; - } - } - LOG(DEBUG) << "javacard strongbox : received getHmacSharingParameter from Javacard - successful"; - // Send earlyBootEnded if there is any pending earlybootEnded event. - handleSendEarlyBootEndedEvent(); - } - _hidl_cb(errorCode, hmacSharingParameters); - return Void(); -} - -Return JavacardKeymaster4Device::computeSharedHmac(const hidl_vec& params, computeSharedHmac_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - hidl_vec sharingCheck; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - std::vector tempVec; - cppbor::Array outerArray; - // The Android system properties like OS_VERSION, OS_PATCHLEVEL and VENDOR_PATCHLEVEL are to - // be delivered to the Applet when the HAL is first loaded. Incase if settting system properties - // failed at construction time then this is one of the ideal places to send this information - // to the Applet as computeSharedHmac is called everytime when Android device boots. - if (!isEachSystemPropertySet) { - errorCode = setAndroidSystemProperties(); - if (ErrorCode::OK != errorCode) { - LOG(ERROR) << " Failed to set os_version, os_patchlevel and vendor_patchlevel err: " << (int32_t)errorCode; - _hidl_cb(errorCode, sharingCheck); - return Void(); - } - - LOG(ERROR) << "javacard strongbox : setAndroidSystemProperties from ComputeSharedHmac - successful "; - - isEachSystemPropertySet = true; - } - - // Send earlyBootEnded if there is any pending earlybootEnded event. - handleSendEarlyBootEndedEvent(); - - for(size_t i = 0; i < params.size(); ++i) { - cppbor::Array innerArray; - innerArray.add(static_cast>(params[i].seed)); - for(size_t j = 0; j < params[i].nonce.size(); j++) { - tempVec.push_back(params[i].nonce[j]); - } - innerArray.add(tempVec); - tempVec.clear(); - outerArray.add(std::move(innerArray)); - } - array.add(std::move(outerArray)); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_COMPUTE_SHARED_HMAC_CMD, cborData, cborOutData); - if (ErrorCode::OK == errorCode) { - LOG(ERROR) << "javacard strongbox : received ComputeSharedHmac data from javacard"; - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - std::vector bstr; - if(!cborConverter_.getBinaryArray(item, 1, bstr)) { - LOG(ERROR) << "INS_COMPUTE_SHARED_HMAC_CMD: failed to convert cbor sharing check value"; - errorCode = ErrorCode::UNKNOWN_ERROR; - } else { - sharingCheck = bstr; - } - } - } - - LOG(ERROR) << "javacard strongbox : computeSharedHmac - sending sharingCheckToKeystore"; - - _hidl_cb(errorCode, sharingCheck); - return Void(); -} - -Return JavacardKeymaster4Device::verifyAuthorization(uint64_t , const hidl_vec& , const HardwareAuthToken& , verifyAuthorization_cb _hidl_cb) { - VerificationToken verificationToken; - LOG(DEBUG) << "Verify authorizations UNIMPLEMENTED"; - _hidl_cb(ErrorCode::UNIMPLEMENTED, verificationToken); - return Void(); -} - -Return JavacardKeymaster4Device::addRngEntropy(const hidl_vec& data) { - cppbor::Array array; - std::vector cborOutData; - std::unique_ptr item; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - /* Convert input data to cbor format */ - array.add(std::vector(data)); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_ADD_RNG_ENTROPY_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::generateKey(const hidl_vec& keyParams, generateKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - hidl_vec updatedParams(keyParams); - // Send earlyBootEnded if there is any pending earlybootEnded event. - handleSendEarlyBootEndedEvent(); - if(!findTag(keyParams, Tag::CREATION_DATETIME) && - !findTag(keyParams, Tag::ACTIVE_DATETIME)) { - //Add CREATION_DATETIME in HAL, as secure element is not having clock. - size_t size = keyParams.size(); - updatedParams.resize(size+1); - updatedParams[size].tag = Tag::CREATION_DATETIME; - updatedParams[size].f.dateTime = java_time(time(nullptr)); - } - - /* Convert to cbor format */ - cborConverter_.addKeyparameters(array, updatedParams); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_GENERATE_KEY_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || - !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { - //Clear the buffer. - keyBlob.setToExternal(nullptr, 0); - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_GENERATE_KEY_CMD: error while converting cbor data: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::importKey(const hidl_vec& keyParams, KeyFormat keyFormat, const hidl_vec& keyData, importKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - cppbor::Array subArray; - // Send earlyBootEnded if there is any pending earlybootEnded event. - handleSendEarlyBootEndedEvent(); - - if(keyFormat != KeyFormat::PKCS8 && keyFormat != KeyFormat::RAW) { - LOG(ERROR) << "INS_IMPORT_KEY_CMD unsupported key format " << (int32_t)keyFormat; - _hidl_cb(ErrorCode::UNSUPPORTED_KEY_FORMAT, keyBlob, keyCharacteristics); - return Void(); - } - cborConverter_.addKeyparameters(array, keyParams); - array.add(static_cast(keyFormat)); //javacard accepts only RAW. - - array.add(std::vector(keyData)); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_IMPORT_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || - !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { - //Clear the buffer. - keyBlob.setToExternal(nullptr, 0); - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_IMPORT_KEY_CMD: error while converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::importWrappedKey(const hidl_vec& wrappedKeyData, const hidl_vec& wrappingKeyBlob, const hidl_vec& maskingKey, const hidl_vec& unwrappingParams, uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - std::vector iv; - std::vector transitKey; - std::vector secureKey; - std::vector tag; - hidl_vec authList; - KeyFormat keyFormat; - std::vector wrappedKeyDescription; - // Send earlyBootEnded if there is any pending earlybootEnded event. - handleSendEarlyBootEndedEvent(); - if(ErrorCode::OK != (errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, - tag, authList, keyFormat, wrappedKeyDescription))) { - LOG(ERROR) << "INS_IMPORT_WRAPPED_KEY_CMD error while parsing wrapped key status: " << (int32_t) errorCode; - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); - } - cborConverter_.addKeyparameters(array, authList); - array.add(static_cast(keyFormat)); - array.add(secureKey); - array.add(tag); - array.add(iv); - array.add(transitKey); - array.add(std::vector(wrappingKeyBlob)); - array.add(std::vector(maskingKey)); - cborConverter_.addKeyparameters(array, unwrappingParams); - array.add(std::vector(wrappedKeyDescription)); - array.add(passwordSid); - array.add(biometricSid); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_IMPORT_WRAPPED_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, keyBlob) || - !cborConverter_.getKeyCharacteristics(item, 2, keyCharacteristics)) { - //Clear the buffer. - keyBlob.setToExternal(nullptr, 0); - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_IMPORT_WRAPPED_KEY_CMD: error while converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyBlob, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::getKeyCharacteristics(const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, getKeyCharacteristics_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - KeyCharacteristics keyCharacteristics; - - array.add(std::vector(keyBlob)); - array.add(std::vector(clientId)); - array.add(std::vector(appData)); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getKeyCharacteristics(item, 1, keyCharacteristics)) { - keyCharacteristics.softwareEnforced.setToExternal(nullptr, 0); - keyCharacteristics.hardwareEnforced.setToExternal(nullptr, 0); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_GET_KEY_CHARACTERISTICS_CMD: error while converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, keyCharacteristics); - return Void(); -} - -Return JavacardKeymaster4Device::exportKey(KeyFormat exportFormat, const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, exportKey_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - hidl_vec resultKeyBlob; - - //Check if keyblob is corrupted - getKeyCharacteristics(keyBlob, clientId, appData, - [&](ErrorCode error, KeyCharacteristics /*keyCharacteristics*/) { - errorCode = error; - }); - - if(errorCode != ErrorCode::OK) { - LOG(ERROR) << "Error in exportKey: " << (int32_t) errorCode; - _hidl_cb(errorCode, resultKeyBlob); - return Void(); - } - - ExportKeyRequest request(softKm_->message_version()); - request.key_format = legacy_enum_conversion(exportFormat); - request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); - - ExportKeyResponse response(softKm_->message_version()); - softKm_->ExportKey(request, &response); - - if(response.error == KM_ERROR_INCOMPATIBLE_ALGORITHM) { - //Symmetric Keys cannot be exported. - response.error = KM_ERROR_UNSUPPORTED_KEY_FORMAT; - LOG(ERROR) << "error in exportKey: unsupported algorithm or key format"; - } - if (response.error == KM_ERROR_OK) { - resultKeyBlob.setToExternal(response.key_data, response.key_data_length); - } - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "exportKey status: " << (int32_t) errorCode; - _hidl_cb(errorCode, resultKeyBlob); - return Void(); -} - - - -Return JavacardKeymaster4Device::attestKey(const hidl_vec& keyToAttest, const hidl_vec& attestParams, attestKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec keyBlob; - std::vector cborOutData; - hidl_vec> certChain; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - array.add(std::vector(keyToAttest)); - cborConverter_.addKeyparameters(array, attestParams); - std::vector cborData = array.encode(); - errorCode = sendData(Instruction::INS_ATTEST_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - std::vector> temp; - std::vector rootCert; - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getMultiBinaryArray(item, 1, temp)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_ATTEST_KEY_CMD: error in converting cbor data, status: " << (int32_t) errorCode; - } else { - cborData.clear(); - cborOutData.clear(); - errorCode = sendData(Instruction::INS_GET_CERT_CHAIN_CMD, cborData, cborOutData); - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), - cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - std::vector chain; - if(!cborConverter_.getBinaryArray(item, 1, chain)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "attestkey INS_GET_CERT_CHAIN_CMD: errorn in converting cbor data, status: " << (int32_t) errorCode; - } else { - if(ErrorCode::OK == (errorCode = getCertificateChain(chain, temp))) { - certChain.resize(temp.size()); - for(int i = 0; i < temp.size(); i++) { - certChain[i] = temp[i]; - } - } else { - LOG(ERROR) << "Error in attestkey getCertificateChain: " << (int32_t) errorCode; - } - } - } - } - } - } - } - _hidl_cb(errorCode, certChain); - return Void(); -} - -Return JavacardKeymaster4Device::upgradeKey(const hidl_vec& keyBlobToUpgrade, const hidl_vec& upgradeParams, upgradeKey_cb _hidl_cb) { - cppbor::Array array; - std::unique_ptr item; - hidl_vec upgradedKeyBlob; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - array.add(std::vector(keyBlobToUpgrade)); - cborConverter_.addKeyparameters(array, upgradeParams); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_UPGRADE_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - if (item != nullptr) { - if(!cborConverter_.getBinaryArray(item, 1, upgradedKeyBlob)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_UPGRADE_KEY_CMD: error in converting cbor data, status: " << (int32_t) errorCode; - } - } - } - _hidl_cb(errorCode, upgradedKeyBlob); - return Void(); -} - -Return JavacardKeymaster4Device::deleteKey(const hidl_vec& keyBlob) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - array.add(std::vector(keyBlob)); - std::vector cborData = array.encode(); - errorCode = sendData(Instruction::INS_DELETE_KEY_CMD, cborData, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::deleteAllKeys() { - std::unique_ptr item; - std::vector cborOutData; - std::vector input; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - errorCode = sendData(Instruction::INS_DELETE_ALL_KEYS_CMD, input, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::destroyAttestationIds() { - std::unique_ptr item; - std::vector cborOutData; - std::vector input; - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - - errorCode = sendData(Instruction::INS_DESTROY_ATT_IDS_CMD, input, cborOutData); - - if(errorCode == ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData(cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), - true, oprCtx_); - } - return errorCode; -} - -Return JavacardKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, const HardwareAuthToken& authToken, begin_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - uint64_t operationHandle = 0; - OperationType operType = OperationType::PRIVATE_OPERATION; - hidl_vec outParams; - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD purpose: " << (int32_t)purpose; - /* - * Asymmetric public key operations are processed inside softkeymaster and private - * key operations are processed inside strongbox keymaster. - * All symmetric key operations are processed inside strongbox keymaster. - * If the purpose is either ENCRYPT / VERIFY then the operation type is set - * to public operation and in case if the key turned out to be a symmetric key then - * handleBeginOperation() function fallbacks to private key operation. - */ - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD purpose: " << (int32_t)purpose; - if (KeyPurpose::ENCRYPT == purpose || KeyPurpose::VERIFY == purpose) { - operType = OperationType::PUBLIC_OPERATION; - } - errorCode = handleBeginOperation(purpose, keyBlob, inParams, authToken, outParams, - operationHandle, operType); - if (errorCode == ErrorCode::OK && isOperationHandleExists(operationHandle)) { - LOG(DEBUG) << "Operation handle " << operationHandle << "already exists" - "in the opertion table. so aborting this opertaion."; - // abort the operation. - errorCode = abortOperation(operationHandle, operType); - if (errorCode == ErrorCode::OK) { - // retry begin to get an another operation handle. - errorCode = handleBeginOperation(purpose, keyBlob, inParams, authToken, outParams, - operationHandle, operType); - if (errorCode == ErrorCode::OK && isOperationHandleExists(operationHandle)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD: Failed in begin operation as the" - "operation handle already exists in the operation table." - << (int32_t)errorCode; - // abort the operation. - auto abortErr = abortOperation(operationHandle, operType); - if (abortErr != ErrorCode::OK) { - LOG(ERROR) << "Fail to abort the operation."; - errorCode = abortErr; - } - } - } - } - // Create an entry inside the operation table for the new operation - // handle. - if (ErrorCode::OK == errorCode) operationTable[operationHandle] = operType; - - _hidl_cb(errorCode, outParams, operationHandle); - return Void(); -} - -ErrorCode JavacardKeymaster4Device::handleBeginPublicKeyOperation( - KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, - hidl_vec& outParams, uint64_t& operationHandle) { - BeginOperationRequest request(softKm_->message_version()); - request.purpose = legacy_enum_conversion(purpose); - request.SetKeyMaterial(keyBlob.data(), keyBlob.size()); - request.additional_params.Reinitialize(KmParamSet(inParams)); - - BeginOperationResponse response(softKm_->message_version()); - /* For Symmetric key operation, the BeginOperation returns - * KM_ERROR_INCOMPATIBLE_ALGORITHM error. */ - softKm_->BeginOperation(request, &response); - ErrorCode errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD softkm BeginOperation status: " << (int32_t)errorCode; - if (ErrorCode::OK == errorCode) { - outParams = kmParamSet2Hidl(response.output_params); - operationHandle = response.op_handle; - } else { - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in softkm BeginOperation status: " - << (int32_t)errorCode; - } - return errorCode; -} - -ErrorCode JavacardKeymaster4Device::handleBeginPrivateKeyOperation( - KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, - const HardwareAuthToken& authToken, hidl_vec& outParams, - uint64_t& operationHandle) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - cppbor::Array array; - std::vector cborOutData; - std::unique_ptr item; - std::unique_ptr blobItem = nullptr; - KeyCharacteristics keyCharacteristics; - KeyParameter param; - - // Send earlyBootEnded if there is any pending earlybootEnded event. - handleSendEarlyBootEndedEvent(); - /* Convert input data to cbor format */ - array.add(static_cast(purpose)); - array.add(std::vector(keyBlob)); - cborConverter_.addKeyparameters(array, inParams); - cborConverter_.addHardwareAuthToken(array, authToken); - std::vector cborData = array.encode(); - - // keyCharacteristics.hardwareEnforced is required to store algorithm, digest - // and padding values in operationInfo structure. To retrieve - // keyCharacteristics.hardwareEnforced, call getKeyCharacateristics. By - // calling getKeyCharacateristics also helps in finding a corrupted keyblob. - hidl_vec applicationId; - hidl_vec applicationData; - if (getTag(inParams, Tag::APPLICATION_ID, param)) { - applicationId = param.blob; - } - if (getTag(inParams, Tag::APPLICATION_DATA, param)) { - applicationData = param.blob; - } - // Call to getKeyCharacteristics. - getKeyCharacteristics(keyBlob, applicationId, applicationData, - [&](ErrorCode error, KeyCharacteristics keyChars) { - errorCode = error; - keyCharacteristics = keyChars; - }); - LOG(DEBUG) << "INS_BEGIN_OPERATION_CMD StrongboxKM getKeyCharacteristics status: " - << (int32_t)errorCode; - - if (errorCode == ErrorCode::OK) { - errorCode = ErrorCode::UNKNOWN_ERROR; - if (getTag(keyCharacteristics.hardwareEnforced, Tag::ALGORITHM, param)) { - errorCode = sendData(Instruction::INS_BEGIN_OPERATION_CMD, cborData, cborOutData); - if (errorCode == ErrorCode::OK) { - // Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = - decodeData(cborConverter_, - std::vector(cborOutData.begin(), cborOutData.end() - 2), - true, oprCtx_); - if (item != nullptr) { - if (!cborConverter_.getKeyParameters(item, 1, outParams) || - !cborConverter_.getUint64(item, 2, operationHandle)) { - errorCode = ErrorCode::UNKNOWN_ERROR; - outParams.setToExternal(nullptr, 0); - operationHandle = 0; - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD: error in converting cbor " - "data, status: " - << (int32_t)errorCode; - } else { - /* Store the operationInfo */ - oprCtx_->setOperationInfo(operationHandle, purpose, param.f.algorithm, - inParams); - } - } - } - } else { - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD couldn't find algorithm tag: " - << (int32_t)Tag::ALGORITHM; - } - } else { - LOG(ERROR) << "INS_BEGIN_OPERATION_CMD error in getKeyCharacteristics status: " - << (int32_t)errorCode; - } - return errorCode; -} - -ErrorCode JavacardKeymaster4Device::handleBeginOperation( - KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, - const HardwareAuthToken& authToken, hidl_vec& outParams, - uint64_t& operationHandle, OperationType& operType) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - if (operType == OperationType::PUBLIC_OPERATION) { - errorCode = - handleBeginPublicKeyOperation(purpose, keyBlob, inParams, outParams, operationHandle); - - // For Symmetric operations handleBeginPublicKeyOperation function - // returns INCOMPATIBLE_ALGORITHM error. Based on this error - // condition it fallbacks to private key operation. - if (errorCode == ErrorCode::INCOMPATIBLE_ALGORITHM) { - operType = OperationType::PRIVATE_OPERATION; - } - } - - if (operType == OperationType::PRIVATE_OPERATION) { - errorCode = handleBeginPrivateKeyOperation(purpose, keyBlob, inParams, authToken, outParams, - operationHandle); - } - return errorCode; -} - -Return -JavacardKeymaster4Device::update(uint64_t operationHandle, const hidl_vec& inParams, - const hidl_vec& input, const HardwareAuthToken& authToken, - const VerificationToken& verificationToken, update_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - uint32_t inputConsumed = 0; - hidl_vec outParams; - hidl_vec output; - UpdateOperationResponse response(softKm_->message_version()); - OperationType operType = getOperationType(operationHandle); - if (OperationType::UNKNOWN == operType) { // operation handle not found - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle " - "is passed or if" - << " secure element reset occurred."; - _hidl_cb(ErrorCode::INVALID_OPERATION_HANDLE, inputConsumed, outParams, output); - return Void(); - } - - if (OperationType::PUBLIC_OPERATION == operType) { - /* SW keymaster (Public key operation) */ - LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm operation "; - UpdateOperationRequest request(softKm_->message_version()); - request.op_handle = operationHandle; - request.input.Reinitialize(input.data(), input.size()); - request.additional_params.Reinitialize(KmParamSet(inParams)); - - softKm_->UpdateOperation(request, &response); - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "INS_UPDATE_OPERATION_CMD - swkm update operation status: " - << (int32_t)errorCode; - if (response.error == KM_ERROR_OK) { - inputConsumed = response.input_consumed; - outParams = kmParamSet2Hidl(response.output_params); - output = kmBuffer2hidlVec(response.output); - } else { - LOG(ERROR) << "INS_UPDATE_OPERATION_CMD - error swkm update operation status: " - << (int32_t)errorCode; - } - } else { - /* Strongbox Keymaster operation */ - std::vector tempOut; - /* OperationContext calls this below sendDataCallback callback function. This callback - * may be called multiple times if the input data is larger than MAX_ALLOWED_INPUT_SIZE. - */ - auto sendDataCallback = [&](std::vector& data, bool) -> ErrorCode { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - std::vector asn1ParamsVerified; - // For symmetic ciphers only block aligned data is send to javacard Applet to reduce the - // number of calls to - // javacard. If the input message is less than block size then it is buffered inside the - // HAL. so in case if - // after buffering there is no data to send to javacard don't call javacard applet. - // For AES GCM operations, even though the input length is 0(which is not block - // aligned), if there is ASSOCIATED_DATA present in KeyParameters. Then we need to make - // a call to javacard Applet. - if (data.size() == 0 && !findTag(inParams, Tag::ASSOCIATED_DATA)) { - // Return OK, since this is not error case. - LOG(DEBUG) << "sendDataCallback: data size is zero"; - return ErrorCode::OK; - } - - if (ErrorCode::OK != - (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { - LOG(ERROR) << "sendDataCallback: error in encodeParametersVerified status: " - << (int32_t)errorCode; - return errorCode; - } - - // Convert input data to cbor format - array.add(operationHandle); - cborConverter_.addKeyparameters(array, inParams); - array.add(data); - cborConverter_.addHardwareAuthToken(array, authToken); - cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_UPDATE_OPERATION_CMD, cborData, cborOutData); - - if (errorCode == ErrorCode::OK) { - // Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = - decodeData(cborConverter_, - std::vector(cborOutData.begin(), cborOutData.end() - 2), - true, oprCtx_); - if (item != nullptr) { - /*Ignore inputConsumed from javacard SE since HAL consumes all the input */ - // cborConverter_.getUint64(item, 1, inputConsumed); - // This callback function may gets called multiple times so parse and get the - // outParams only once. Otherwise there can be chance of duplicate entries in - // outParams. Use tempOut to collect all the cipher text and finally copy it to - // the output. getBinaryArray function appends the new cipher text at the end of - // the tempOut(std::vector). - if ((outParams.size() == 0 && - !cborConverter_.getKeyParameters(item, 2, outParams)) || - !cborConverter_.getBinaryArray(item, 3, tempOut)) { - outParams.setToExternal(nullptr, 0); - tempOut.clear(); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) << "sendDataCallback: INS_UPDATE_OPERATION_CMD: error while " - "converting cbor data, status: " - << (int32_t)errorCode; - } - } - } - return errorCode; - }; - if (ErrorCode::OK == - (errorCode = - oprCtx_->update(operationHandle, std::vector(input), sendDataCallback))) { - /* Consumed all the input */ - inputConsumed = input.size(); - output = tempOut; - } - LOG(DEBUG) << "Update operation status: " << (int32_t)errorCode; - if (ErrorCode::OK != errorCode) { - LOG(ERROR) << "Error in update operation, status: " << (int32_t)errorCode; - abort(operationHandle); - } - } - if (ErrorCode::OK != errorCode) { - /* Delete the entry from operation table. */ - LOG(ERROR) << "Delete entry from operation table, status: " << (int32_t)errorCode; - operationTable.erase(operationHandle); - } - - _hidl_cb(errorCode, inputConsumed, outParams, output); - return Void(); -} - -Return -JavacardKeymaster4Device::finish(uint64_t operationHandle, const hidl_vec& inParams, - const hidl_vec& input, const hidl_vec& signature, - const HardwareAuthToken& authToken, - const VerificationToken& verificationToken, finish_cb _hidl_cb) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - hidl_vec outParams; - hidl_vec output; - FinishOperationResponse response(softKm_->message_version()); - OperationType operType = getOperationType(operationHandle); - - if (OperationType::UNKNOWN == operType) { // operation handle not found - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid operation handle " - "is passed or if" - << " secure element reset occurred."; - _hidl_cb(ErrorCode::INVALID_OPERATION_HANDLE, outParams, output); - return Void(); - } - - if (OperationType::PUBLIC_OPERATION == operType) { - /* SW keymaster (Public key operation) */ - LOG(DEBUG) << "FINISH - swkm operation "; - FinishOperationRequest request(softKm_->message_version()); - request.op_handle = operationHandle; - request.input.Reinitialize(input.data(), input.size()); - request.signature.Reinitialize(signature.data(), signature.size()); - request.additional_params.Reinitialize(KmParamSet(inParams)); - - softKm_->FinishOperation(request, &response); - - errorCode = legacy_enum_conversion(response.error); - LOG(DEBUG) << "FINISH - swkm operation, status: " << (int32_t)errorCode; - - if (response.error == KM_ERROR_OK) { - outParams = kmParamSet2Hidl(response.output_params); - output = kmBuffer2hidlVec(response.output); - } else { - LOG(ERROR) << "Error in finish operation, status: " << (int32_t)errorCode; - } - } else { - /* Strongbox Keymaster operation */ - std::vector tempOut; - bool aadTag = false; - /* OperationContext calls this below sendDataCallback callback function. This callback - * may be called multiple times if the input data is larger than MAX_ALLOWED_INPUT_SIZE. - * This callback function decides whether to call update/finish instruction based on the - * input received from the OperationContext through finish variable. - * if finish variable is false update instruction is called, if it is true finish - * instruction is called. - */ - auto sendDataCallback = [&](std::vector& data, bool finish) -> ErrorCode { - cppbor::Array array; - Instruction ins; - std::unique_ptr item; - std::vector cborOutData; - int keyParamPos, outputPos; - std::vector asn1ParamsVerified; - - if (ErrorCode::OK != - (errorCode = encodeParametersVerified(verificationToken, asn1ParamsVerified))) { - LOG(ERROR) << "sendDataCallback: Error in encodeParametersVerified, status: " - << (int32_t)errorCode; - return errorCode; - } - - // In case if there is ASSOCIATED_DATA present in the keyparams, then make sure it is - // either passed with update call or finish call. Don't send ASSOCIATED_DATA in both - // update and finish calls. aadTag is used to check if ASSOCIATED_DATA is already sent - // in update call. If addTag is true then skip ASSOCIATED_DATA from keyparams in finish - // call. - // Convert input data to cbor format - array.add(operationHandle); - if (finish) { - std::vector finishParams; - LOG(DEBUG) << "sendDataCallback: finish operation"; - if (aadTag) { - for (int i = 0; i < inParams.size(); i++) { - if (inParams[i].tag != Tag::ASSOCIATED_DATA) - finishParams.push_back(inParams[i]); - } - } else { - finishParams = inParams; - } - cborConverter_.addKeyparameters(array, finishParams); - array.add(data); - array.add(std::vector(signature)); - ins = Instruction::INS_FINISH_OPERATION_CMD; - keyParamPos = 1; - outputPos = 2; - } else { - LOG(DEBUG) << "sendDataCallback: update operation"; - if (findTag(inParams, Tag::ASSOCIATED_DATA)) { - aadTag = true; - } - cborConverter_.addKeyparameters(array, inParams); - array.add(data); - ins = Instruction::INS_UPDATE_OPERATION_CMD; - keyParamPos = 2; - outputPos = 3; - } - cborConverter_.addHardwareAuthToken(array, authToken); - cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); - std::vector cborData = array.encode(); - errorCode = sendData(ins, cborData, cborOutData); - - if (errorCode == ErrorCode::OK) { - // Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = - decodeData(cborConverter_, - std::vector(cborOutData.begin(), cborOutData.end() - 2), - true, oprCtx_); - if (item != nullptr) { - // There is a change that this finish callback may gets called multiple times if - // the input data size is larger the MAX_ALLOWED_INPUT_SIZE (Refer - // OperationContext) so parse and get the outParams only once. Otherwise there - // can be chance of duplicate entries in outParams. Use tempOut to collect all - // the cipher text and finally copy it to the output. getBinaryArray function - // appends the new cipher text at the end of the tempOut(std::vector). - if ((outParams.size() == 0 && - !cborConverter_.getKeyParameters(item, keyParamPos, outParams)) || - !cborConverter_.getBinaryArray(item, outputPos, tempOut)) { - outParams.setToExternal(nullptr, 0); - tempOut.clear(); - errorCode = ErrorCode::UNKNOWN_ERROR; - LOG(ERROR) - << "sendDataCallback: error while converting cbor data in operation: " - << (int32_t)ins << " decodeData, status: " << (int32_t)errorCode; - } - } - } - return errorCode; - }; - if (ErrorCode::OK == - (errorCode = - oprCtx_->finish(operationHandle, std::vector(input), sendDataCallback))) { - output = tempOut; - } - if (ErrorCode::OK != errorCode) { - LOG(ERROR) << "Error in finish operation, status: " << (int32_t)errorCode; - abort(operationHandle); - } - } - /* Delete the entry from operation table. */ - operationTable.erase(operationHandle); - oprCtx_->clearOperationData(operationHandle); - LOG(DEBUG) << "finish operation, status: " << (int32_t)errorCode; - _hidl_cb(errorCode, outParams, output); - return Void(); -} - -ErrorCode JavacardKeymaster4Device::abortPrivateKeyOperation(uint64_t operationHandle) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - - /* Convert input data to cbor format */ - array.add(operationHandle); - std::vector cborData = array.encode(); - - errorCode = sendData(Instruction::INS_ABORT_OPERATION_CMD, cborData, cborOutData); - - if (errorCode == ErrorCode::OK) { - // Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end() - 2), true, - oprCtx_); - } - return errorCode; -} - -ErrorCode JavacardKeymaster4Device::abortPublicKeyOperation(uint64_t operationHandle) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - AbortOperationRequest request(softKm_->message_version()); - request.op_handle = operationHandle; - - AbortOperationResponse response(softKm_->message_version()); - softKm_->AbortOperation(request, &response); - - errorCode = legacy_enum_conversion(response.error); - return errorCode; -} - -ErrorCode JavacardKeymaster4Device::abortOperation(uint64_t operationHandle, - OperationType operType) { - if (operType == OperationType::UNKNOWN) return ErrorCode::UNKNOWN_ERROR; - - if (OperationType::PUBLIC_OPERATION == operType) { - return abortPublicKeyOperation(operationHandle); - } else { - return abortPrivateKeyOperation(operationHandle); - } -} - -Return JavacardKeymaster4Device::abort(uint64_t operationHandle) { - ErrorCode errorCode = ErrorCode::UNKNOWN_ERROR; - OperationType operType = getOperationType(operationHandle); - if (OperationType::UNKNOWN == operType) { // operation handle not found - LOG(ERROR) << " Operation handle is invalid. This could happen if invalid " - "operation handle is passed or if" - << " secure element reset occurred."; - return ErrorCode::INVALID_OPERATION_HANDLE; - } - - errorCode = abortOperation(operationHandle, operType); - if (errorCode == ErrorCode::OK) { - /* Delete the entry on this operationHandle */ - oprCtx_->clearOperationData(operationHandle); - operationTable.erase(operationHandle); - } - return errorCode; -} - -// Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. -Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) { - cppbor::Array array; - std::unique_ptr item; - std::vector cborOutData; - V41ErrorCode errorCode = V41ErrorCode::UNKNOWN_ERROR; - std::vector asn1ParamsVerified; - - if(V41ErrorCode::OK != (errorCode = static_cast(encodeParametersVerified(verificationToken, asn1ParamsVerified)))) { - LOG(DEBUG) << "INS_DEVICE_LOCKED_CMD: Error in encodeParametersVerified, status: " << (int32_t) errorCode; - return errorCode; - } - - /* Convert input data to cbor format */ - array.add(passwordOnly); - cborConverter_.addVerificationToken(array, verificationToken, asn1ParamsVerified); - std::vector cborData = array.encode(); - - errorCode = static_cast(sendData(Instruction::INS_DEVICE_LOCKED_CMD, cborData, cborOutData)); - - if(errorCode == V41ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); - } - return errorCode; -} - -Return<::android::hardware::keymaster::V4_1::ErrorCode> JavacardKeymaster4Device::earlyBootEnded() { - std::unique_ptr item; - std::string message; - std::vector cborOutData; - std::vector cborInput; - V41ErrorCode errorCode = V41ErrorCode::UNKNOWN_ERROR; - - errorCode = static_cast(sendData(Instruction::INS_EARLY_BOOT_ENDED_CMD, cborInput, cborOutData)); - - if(errorCode == V41ErrorCode::OK) { - //Skip last 2 bytes in cborData, it contains status. - std::tie(item, errorCode) = decodeData( - cborConverter_, std::vector(cborOutData.begin(), cborOutData.end()-2), true, oprCtx_); - } else { - // Incase of failure cache the event and send in the next immediate request to Applet. - isEarlyBootEventPending = true; - } - return errorCode; -} - -} // javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/HAL/keymaster/4.1/JavacardOperationContext.cpp b/HAL/keymaster/4.1/JavacardOperationContext.cpp deleted file mode 100644 index 64c13c71..00000000 --- a/HAL/keymaster/4.1/JavacardOperationContext.cpp +++ /dev/null @@ -1,383 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include -#include - -#define MAX_ALLOWED_INPUT_SIZE 256 -#define AES_BLOCK_SIZE 16 -#define DES_BLOCK_SIZE 8 -#define RSA_INPUT_MSG_LEN 256 -#define EC_INPUT_MSG_LEN 32 -#define MAX_EC_BUFFER_SIZE 32 - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -enum class Operation { - Update = 0, - Finish = 1 -}; - -inline ErrorCode hidlParamSet2OperatinInfo(const hidl_vec& params, OperationInfo& info) { - for(int i = 0; i < params.size(); i++) { - const KeyParameter ¶m = params[i]; - switch(param.tag) { - case Tag::ALGORITHM: - info.alg = static_cast(param.f.integer); - break; - case Tag::DIGEST: - info.digest = static_cast(param.f.integer); - break; - case Tag::PADDING: - info.pad = static_cast(param.f.integer); - break; - case Tag::BLOCK_MODE: - info.mode = static_cast(param.f.integer); - break; - case Tag::MAC_LENGTH: - // Convert to bytes. - info.macLength = (param.f.integer / 8); - break; - default: - continue; - } - } - return ErrorCode::OK; -} - -ErrorCode OperationContext::setOperationInfo(uint64_t operationHandle, KeyPurpose purpose, Algorithm alg, - const hidl_vec& params) { - ErrorCode errorCode = ErrorCode::OK; - OperationData data; - memset((void *)&data, 0, sizeof(OperationData)); - if(ErrorCode::OK != (errorCode = hidlParamSet2OperatinInfo(params, data.info))) { - return errorCode; - } - data.info.purpose = purpose; - data.info.alg = alg; - operationTable[operationHandle] = data; - return ErrorCode::OK; -} - -ErrorCode OperationContext::clearOperationData(uint64_t operHandle) { - size_t size = operationTable.erase(operHandle); - if(!size) - return ErrorCode::INVALID_OPERATION_HANDLE; - else - return ErrorCode::OK; -} - -ErrorCode OperationContext::validateInputData(uint64_t operHandle, Operation opr, - const std::vector& actualInput, std::vector& input) { - ErrorCode errorCode = ErrorCode::OK; - - OperationData& oprData = operationTable[operHandle]; - - if(KeyPurpose::SIGN == oprData.info.purpose) { - if(Algorithm::RSA == oprData.info.alg && Digest::NONE == oprData.info.digest) { - if((oprData.data.buf_len+actualInput.size()) > RSA_INPUT_MSG_LEN) - return ErrorCode::INVALID_INPUT_LENGTH; - } else if(Algorithm::EC == oprData.info.alg && Digest::NONE == oprData.info.digest) { - /* Silently truncate the input */ - if(oprData.data.buf_len >= EC_INPUT_MSG_LEN) { - return ErrorCode::OK; - } else if(actualInput.size()+oprData.data.buf_len > EC_INPUT_MSG_LEN) { - for(int i=oprData.data.buf_len,j=0; i < EC_INPUT_MSG_LEN; ++i,++j) { - input.push_back(actualInput[j]); - } - return ErrorCode::OK; - } - } - } - - if(KeyPurpose::DECRYPT == oprData.info.purpose && Algorithm::RSA == oprData.info.alg) { - if((oprData.data.buf_len+actualInput.size()) > RSA_INPUT_MSG_LEN) { - return ErrorCode::INVALID_INPUT_LENGTH; - } - } - if(opr == Operation::Finish) { - //If it is observed in finish operation that buffered data + input data exceeds the MAX_ALLOWED_INPUT_SIZE then - //combine both the data in a single buffer. This helps in making sure that no data is left out in the buffer after - //finish opertion. - if((oprData.data.buf_len+actualInput.size()) > MAX_ALLOWED_INPUT_SIZE) { - input.insert(input.end(), oprData.data.buf, oprData.data.buf + oprData.data.buf_len); - input.insert(input.end(), actualInput.begin(), actualInput.end()); - //As buffered data is already consumed earse the buffer. - if(oprData.data.buf_len != 0) { - memset(oprData.data.buf, 0x00, sizeof(oprData.data.buf)); - oprData.data.buf_len = 0; - } - return ErrorCode::OK; - } - } - input = actualInput; - return errorCode; -} - -ErrorCode OperationContext::update(uint64_t operHandle, const std::vector& actualInput, - sendDataToSE_cb cb) { - ErrorCode errorCode = ErrorCode::OK; - std::vector input; - - /* Validate the input data */ - if(ErrorCode::OK != (errorCode = validateInputData(operHandle, Operation::Update, actualInput, input))) { - return errorCode; - } - - if (input.size() > MAX_ALLOWED_INPUT_SIZE) { - int noOfChunks = input.size()/MAX_ALLOWED_INPUT_SIZE; - int extraData = input.size()%MAX_ALLOWED_INPUT_SIZE; - for(int i =0 ; i < noOfChunks; i++) { - auto first = input.cbegin() + (i*MAX_ALLOWED_INPUT_SIZE); - auto end = first + MAX_ALLOWED_INPUT_SIZE; - std::vector newInput(first, end); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput, - Operation::Update, cb))) { - return errorCode; - } - } - if(extraData > 0) { - std::vector finalInput(input.cend()-extraData, input.cend()); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput, - Operation::Update, cb))) { - return errorCode; - } - } - } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input, - Operation::Update, cb))) { - return errorCode; - } - } - return errorCode; -} - -ErrorCode OperationContext::finish(uint64_t operHandle, const std::vector& actualInput, sendDataToSE_cb cb) { - ErrorCode errorCode = ErrorCode::OK; - std::vector input; - /* Validate the input data */ - if(ErrorCode::OK != (errorCode = validateInputData(operHandle, Operation::Finish, actualInput, input))) { - return errorCode; - } - - if (input.size() > MAX_ALLOWED_INPUT_SIZE) { - int noOfChunks = input.size()/MAX_ALLOWED_INPUT_SIZE; - int extraData = input.size()%MAX_ALLOWED_INPUT_SIZE; - for(int i =0 ; i < noOfChunks; i++) { - auto first = input.cbegin() + (i*MAX_ALLOWED_INPUT_SIZE); - auto end = first + MAX_ALLOWED_INPUT_SIZE; - std::vector newInput(first, end); - if(extraData == 0 && (i == noOfChunks - 1)) { - //Last chunk - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput, - Operation::Finish, cb, true))) { - return errorCode; - } - - } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, newInput, - Operation::Update, cb))) { - return errorCode; - } - } - } - if(extraData > 0) { - std::vector finalInput(input.cend()-extraData, input.cend()); - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, finalInput, - Operation::Finish, cb, true))) { - return errorCode; - } - } - } else { - if(ErrorCode::OK != (errorCode = handleInternalUpdate(operHandle, input, - Operation::Finish, cb, true))) { - return errorCode; - } - } - return errorCode; -} - - /* - * This function is called for only Symmetric operations. It calculates the length of the data to be sent to the Applet - * by considering data from both of the sources i.e. buffered data and input data. It first Copies the data to the out - * buffer from buffered data and then the remaining from the input data. If the buffered data is empty then it copies - * data to out buffer from only input and similarly if the input is empty then it copies from only buffer. Incase if - * only a portion of the input data is consumed then the remaining portion of input data is buffered. - * For AES/TDES Encryption/Decryption operations with PKCS7 padding and for AES GCM Decryption operations a block size - * of data and tag length of data is always buffered respectively. This is done to make sure that there will be always - * a block size of data left for finish operation so that the Applet may remove the PKCS7 padding if any or get the tag - * data for AES GCM operation for authentication purpose. Once the data from the buffer is consumed then the buffer is - * cleared. No Buffering is done for other modes of operation. - */ -ErrorCode OperationContext::bufferData(uint64_t operHandle, std::vector& input, - Operation opr, std::vector& out) { - BufferedData& data = operationTable[operHandle].data; - int dataToSELen = 0;/*Length of the data to be send to the Applet.*/ - int inputConsumed = 0;/*Length of the data consumed from input */ - int bufferLengthConsumed = 0; /* Length of the data consumed from Buffer */ - int blockSize = 0; - int bufIndex = data.buf_len; - if(Algorithm::AES == operationTable[operHandle].info.alg) { - blockSize = AES_BLOCK_SIZE; - } else if(Algorithm::TRIPLE_DES == operationTable[operHandle].info.alg) { - blockSize = DES_BLOCK_SIZE; - } else { - return ErrorCode::INCOMPATIBLE_ALGORITHM; - } - - if(opr == Operation::Finish) { - //Copy the buffer to be send to SE. - out.insert(out.end(), data.buf, data.buf + data.buf_len); - dataToSELen = data.buf_len + input.size(); - bufferLengthConsumed = data.buf_len; - } else { - /*Update */ - //Calculate the block sized length on combined input of both buffered data and input data. - // AES/TDES, Encrypt/Decrypt PKCS7 Padding: - // Buffer till blockSize of data is received. - // AES GCM Decrypt: - // Buffer tag length bytes of data. - if (operationTable[operHandle].info.pad == PaddingMode::PKCS7) { - if (operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { - /* Buffer till we receive more than blockSize of data of atleast one byte*/ - dataToSELen = ((data.buf_len + input.size()) / blockSize) * blockSize; - int remaining = ((data.buf_len + input.size()) % blockSize); - if (dataToSELen >= blockSize && remaining == 0) { - dataToSELen -= blockSize; - } - } else { // Encrypt - dataToSELen = ((data.buf_len + input.size()) / blockSize) * blockSize; - } - } else if (operationTable[operHandle].info.mode == BlockMode::GCM && - operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT) { - /* Always Buffer mac length bytes */ - dataToSELen = 0; - if ((data.buf_len + input.size()) > operationTable[operHandle].info.macLength) { - dataToSELen = (data.buf_len + input.size()) - operationTable[operHandle].info.macLength; - } - } else { - /* No Buffering */ - dataToSELen = input.size(); - } - //Copy data to be send to SE from buffer, only if atleast a minimum block aligned size is available. - if(dataToSELen > 0) { - bufferLengthConsumed = (dataToSELen > data.buf_len) ? data.buf_len : dataToSELen; - out.insert(out.end(), data.buf, data.buf + bufferLengthConsumed); - } - } - - if(dataToSELen > 0) { - //If buffer length is greater than the data length to be send to SE, then input data consumed is 0. - //That means all the data to be send to SE is consumed from the buffer. - //The buffer length might become greater than dataToSELen in the cases where we are saving the last block of - //data i.e. AES/TDES Decryption with PKC7Padding or AES GCM Decryption operations. - inputConsumed = (data.buf_len > dataToSELen) ? 0 : (dataToSELen - data.buf_len); - - // Copy the buffer to be send to SE. - if (inputConsumed > 0) { - out.insert(out.end(), input.begin(), input.begin() + inputConsumed); - } - - if (bufferLengthConsumed < data.buf_len) { - // Only a portion of data is consumed from buffer so reorder the buffer data. - memmove(data.buf, (data.buf + bufferLengthConsumed), (data.buf_len - bufferLengthConsumed)); - memset((data.buf + data.buf_len - bufferLengthConsumed), 0x00, bufferLengthConsumed); - data.buf_len -= bufferLengthConsumed; - bufIndex = data.buf_len; - } else { - // All the data is consumed so clear buffer - if(data.buf_len != 0) { - memset(data.buf, 0x00, sizeof(data.buf)); - bufIndex = data.buf_len = 0; - } - } - } - - //Store the remaining buffer for later use. - data.buf_len += (input.size() - inputConsumed); - std::copy(input.begin() + inputConsumed, input.end(), data.buf + bufIndex); - return ErrorCode::OK; -} - -ErrorCode OperationContext::handleInternalUpdate(uint64_t operHandle, std::vector& data, Operation opr, - sendDataToSE_cb cb, bool finish) { - ErrorCode errorCode = ErrorCode::OK; - std::vector out; - if(Algorithm::AES == operationTable[operHandle].info.alg || - Algorithm::TRIPLE_DES == operationTable[operHandle].info.alg) { - /*Symmetric */ - if(ErrorCode::OK != (errorCode = bufferData(operHandle, data, - opr, out))) { - return errorCode; - } - //Call the callback under these condition - //1. if it is a finish operation. - //2. if there is some data to be send to Javacard.(either update or finish operation). - //3. if the operation is GCM Mode. Even though there is no data to be send there could be AAD data to be sent to - //javacard. - if(finish || out.size() > 0 || BlockMode::GCM == operationTable[operHandle].info.mode) { - if(ErrorCode::OK != (errorCode = cb(out, finish))) { - return errorCode; - } - } - } else { - /* Asymmetric */ - if(operationTable[operHandle].info.purpose == KeyPurpose::DECRYPT || - operationTable[operHandle].info.digest == Digest::NONE) { - //In case of Decrypt operation or Sign operation with no digest case, buffer the data in - //update call and send it to SE in finish call. - if(finish) { - //If finish flag is true all the data has to be sent to javacard. - out.insert(out.end(), operationTable[operHandle].data.buf, operationTable[operHandle].data.buf + - operationTable[operHandle].data.buf_len); - out.insert(out.end(), data.begin(), data.end()); - //As buffered data is already consumed earse the buffer. - if(operationTable[operHandle].data.buf_len != 0) { - memset(operationTable[operHandle].data.buf, 0x00, sizeof(operationTable[operHandle].data.buf)); - operationTable[operHandle].data.buf_len = 0; - } - if(ErrorCode::OK != (errorCode = cb(out, finish))) { - return errorCode; - } - } else { - //For strongbox keymaster, in NoDigest case the length of the input message for RSA should not be more than - //256 and for EC it should not be more than 32. This validation is already happening in - //validateInputData function. - size_t bufIndex = operationTable[operHandle].data.buf_len; - std::copy(data.begin(), data.end(), operationTable[operHandle].data.buf + bufIndex); - operationTable[operHandle].data.buf_len += data.size(); - } - } else { /* With Digest */ - out.insert(out.end(), data.begin(), data.end()); - //if len=0, then no need to call the callback, since there is no information to be send to javacard, - // but if finish flag is true irrespective of length the callback should be called. - if(!out.empty() || finish) { - if(ErrorCode::OK != (errorCode = cb(out, finish))) { - return errorCode; - } - } - } - } - return errorCode; -} - - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster diff --git a/HAL/keymaster/4.1/OmapiTransport.cpp b/HAL/keymaster/4.1/OmapiTransport.cpp deleted file mode 100644 index 5aaefc91..00000000 --- a/HAL/keymaster/4.1/OmapiTransport.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ -#include -#include -#include -#include -#include -#include -#include "Transport.h" - -#define PORT 8080 -#define IPADDR "10.9.40.24" -#define UNUSED_V(a) a=a - -namespace se_transport { - -bool OmapiTransport::openConnection() { - return true; -} - -bool OmapiTransport::sendData(const uint8_t* inData, const size_t inLen, std::vector& output) { - std::vector test(inData, inData+inLen); - output = std::move(test); - return true; -} - -bool OmapiTransport::closeConnection() { - return true; -} - -bool OmapiTransport::isConnected() { - return true; -} - -} diff --git a/HAL/keymaster/Android.bp b/HAL/keymaster/Android.bp deleted file mode 100644 index 9bfe7faa..00000000 --- a/HAL/keymaster/Android.bp +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (C) 2020 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - - -cc_binary { - name: "android.hardware.keymaster@4.1-strongbox.service", - relative_install_path: "hw", - vendor: true, - init_rc: ["4.1/android.hardware.keymaster@4.1-strongbox.service.rc"], - vintf_fragments: ["4.1/android.hardware.keymaster@4.1-strongbox.service.xml"], - srcs: [ - "4.1/service.cpp", - "4.1/JavacardKeymaster4Device.cpp", - "4.1/JavacardSoftKeymasterContext.cpp", - "4.1/JavacardOperationContext.cpp", - ], - local_include_dirs: [ - "include", - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libsoft_attestation_cert", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor_external", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libjc_transport", - "libjc_common", - "libcrypto", - ], - required: [ - "android.hardware.strongbox_keystore.xml", - ], -} - -cc_library { - name: "libJavacardKeymaster41", - srcs: [ - "4.1/JavacardKeymaster4Device.cpp", - "4.1/CborConverter.cpp", - "4.1/JavacardSoftKeymasterContext.cpp", - "4.1/JavacardOperationContext.cpp", - "4.1/CommonUtils.cpp", - ], - local_include_dirs: [ - "include", - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libsoft_attestation_cert", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor_external", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libjc_transport", - "libcrypto", - ], -} - -cc_library { - name: "libjc_transport", - vendor_available: true, - - srcs: [ - "4.1/SocketTransport.cpp", - "4.1/OmapiTransport.cpp" - ], - export_include_dirs: [ - "include" - ], - shared_libs: [ - "libbinder", - "libbase", - "liblog", - ], -} - -cc_library { - name: "libjc_common", - vendor_available: true, - srcs: [ - "4.1/CommonUtils.cpp", - "4.1/CborConverter.cpp", - ], - local_include_dirs: [ - "include", - ], - export_include_dirs: [ - "include" - ], - shared_libs: [ - "liblog", - "libcutils", - "libdl", - "libbase", - "libutils", - "libhardware", - "libhidlbase", - "libsoftkeymasterdevice", - "libsoft_attestation_cert", - "libkeymaster_messages", - "libkeymaster_portable", - "libcppbor_external", - "android.hardware.keymaster@4.1", - "android.hardware.keymaster@4.0", - "libcrypto", - ], -} - -prebuilt_etc { - name: "android.hardware.strongbox_keystore.xml", - sub_dir: "permissions", - vendor: true, - src: "4.1/android.hardware.strongbox_keystore.xml", -} diff --git a/HAL/keymaster/include/CborConverter.h b/HAL/keymaster/include/CborConverter.h deleted file mode 100644 index 45855244..00000000 --- a/HAL/keymaster/include/CborConverter.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#ifndef __CBOR_CONVERTER_H_ -#define __CBOR_CONVERTER_H_ - -#include -#include -#include -#include -#include -#include - -using namespace cppbor; - -using ::android::hardware::hidl_vec; -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::HardwareAuthToken; -using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType; -using ::android::hardware::keymaster::V4_0::HmacSharingParameters; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::VerificationToken; -using ::android::hardware::keymaster::V4_0::KeyCharacteristics; -using ::android::hardware::keymaster::V4_0::SecurityLevel; -using ::android::hardware::keymaster::V4_0::TagType; -using ::android::hardware::keymaster::V4_0::Tag; - -class CborConverter -{ - public: - CborConverter() = default; - ~CborConverter() = default; - - /** - * Parses the input data which is in CBOR format and returns a Tuple of Item pointer and the first element in the item pointer. - */ - template - std::tuple, T> decodeData(const std::vector& response, bool - hasErrorCode) { - const uint8_t* pos; - std::unique_ptr item(nullptr); - std::string message; - T errorCode = T::OK; - - std::tie(item, pos, message) = parse(response); - - if(item != nullptr && hasErrorCode) { - if(MajorType::ARRAY == getType(item)) { - if(!getErrorCode(item, 0, errorCode)) - item = nullptr; - } else if (MajorType::UINT == getType(item)) { - uint64_t err; - if(getUint64(item, err)) { - errorCode = static_cast(err); - } - item = nullptr; /*Already read the errorCode. So no need of sending item to client */ - } - } - return {std::move(item), errorCode}; - } - - /** - * Get the signed/unsigned integer value at a given position from the item pointer. - */ - template - bool getUint64(const std::unique_ptr& item, const uint32_t pos, T& value); - - /** - * Get the signed/unsigned integer value from the item pointer. - */ - template - bool getUint64(const std::unique_ptr& item, T& value); - - /** - * Get the HmacSharingParameters structure value at the given position from the item pointer. - */ - bool getHmacSharingParameters(const std::unique_ptr& item, const uint32_t pos, HmacSharingParameters& params); - - /** - * Get the Binary string at the given position from the item pointer. - */ - bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, ::android::hardware::hidl_string& value); - - /** - * Get the Binary string at the given position from the item pointer. - */ - bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, std::vector& value); - - /** - * Get the Binary string at the given position from the item pointer. - */ - bool getBinaryArray(const std::unique_ptr& item, const uint32_t pos, - ::android::hardware::hidl_vec& value); - /** - * Get the HardwareAuthToken value at the given position from the item pointer. - */ - bool getHardwareAuthToken(const std::unique_ptr& item, const uint32_t pos, HardwareAuthToken& authType); - - /** - * Get the list of KeyParameters value at the given position from the item pointer. - */ - bool getKeyParameters(const std::unique_ptr& item, const uint32_t pos, android::hardware::hidl_vec& keyParams); - - /** - * Adds the the list of KeyParameters values to the Array item. - */ - bool addKeyparameters(Array& array, const android::hardware::hidl_vec& - keyParams); - - /** - * Add HardwareAuthToken value to the Array item. - */ - bool addHardwareAuthToken(Array& array, const HardwareAuthToken& - authToken); - - /** - * Get the VerificationToken value at the given position from the item pointer. - */ - bool getVerificationToken(const std::unique_ptr& item, const uint32_t pos, VerificationToken& - token); - - /** - * Get the KeyCharacteristics value at the given position from the item pointer. - */ - bool getKeyCharacteristics(const std::unique_ptr &item, const uint32_t pos, - KeyCharacteristics& keyCharacteristics); - - /** - * Get the list of binary arrays at the given position from the item pointer. - */ - bool getMultiBinaryArray(const std::unique_ptr& item, const uint32_t pos, - std::vector>& data); - - /** - * Add VerificationToken value to the Array item. - */ - bool addVerificationToken(Array& array, const VerificationToken& - verificationToken, std::vector& encodedParamsVerified); - - /** - * Get the ErrorCode value at the give position from the item pointer. - */ - template) || - (std::is_same_v)>> - inline bool getErrorCode(const std::unique_ptr& item, const uint32_t pos, T& errorCode) { - bool ret = false; - uint64_t errorVal; - if (!getUint64(item, pos, errorVal)) { - return ret; - } - errorCode = static_cast(errorVal); - - ret = true; - return ret; - } - - private: - /** - * Get the type of the Item pointer. - */ - inline MajorType getType(const std::unique_ptr &item) { return item.get()->type(); } - - /** - * Construct Keyparameter structure from the pair of key and value. If TagType is ENUM_REP the value contains - * binary string. If TagType is UINT_REP or ULONG_REP the value contains Array of unsigned integers. - */ - bool getKeyParameter(const std::pair&, - const std::unique_ptr&> pair, std::vector& keyParam); - - /** - * Get the sub item pointer from the root item pointer at the given position. - */ - inline void getItemAtPos(const std::unique_ptr& item, const uint32_t pos, std::unique_ptr& subItem) { - Array* arr = nullptr; - - if (MajorType::ARRAY != getType(item)) { - return; - } - arr = const_cast(item.get()->asArray()); - if (arr->size() < (pos + 1)) { - return; - } - subItem = std::move((*arr)[pos]); - } -}; - -template -bool CborConverter::getUint64(const std::unique_ptr& item, T& value) { - bool ret = false; - if ((item == nullptr) || - (std::is_unsigned::value && (MajorType::UINT != getType(item))) || - ((std::is_signed::value && (MajorType::NINT != getType(item))))) { - return ret; - } - - if (std::is_unsigned::value) { - const Uint* uintVal = item.get()->asUint(); - value = uintVal->value(); - } - else { - const Nint* nintVal = item.get()->asNint(); - value = nintVal->value(); - } - ret = true; - return ret; //success -} - -template -bool CborConverter::getUint64(const std::unique_ptr& item, const uint32_t pos, T& value) { - std::unique_ptr intItem(nullptr); - getItemAtPos(item, pos, intItem); - return getUint64(intItem, value); -} - - - -#endif diff --git a/HAL/keymaster/include/CommonUtils.h b/HAL/keymaster/include/CommonUtils.h deleted file mode 100644 index 8fd247f7..00000000 --- a/HAL/keymaster/include/CommonUtils.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - - -#ifndef KEYMASTER_V4_1_JAVACARD_COMMONUTILS_H_ -#define KEYMASTER_V4_1_JAVACARD_COMMONUTILS_H_ - -#include -#include -#include - -namespace keymaster { -namespace V4_1 { -namespace javacard { -using ::android::hardware::hidl_vec; -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::Tag; -using ::android::hardware::keymaster::V4_0::KeyFormat; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::KeyPurpose; -using ::android::hardware::keymaster::V4_0::EcCurve; - -inline ErrorCode legacy_enum_conversion(const keymaster_error_t value) { - return static_cast(value); -} - -inline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) { - return static_cast(value); -} - -inline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) { - return static_cast(value); -} - -inline keymaster_tag_t legacy_enum_conversion(const Tag value) { - return keymaster_tag_t(value); -} - -inline Tag legacy_enum_conversion(const keymaster_tag_t value) { - return Tag(value); -} - -inline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) { - return keymaster_tag_get_type(tag); -} - -inline hidl_vec kmBuffer2hidlVec(const ::keymaster::Buffer& buf) { - return hidl_vec(buf.begin(), buf.end()); -} - -inline void blob2Vec(const uint8_t *from, size_t size, std::vector& to) { - for(int i = 0; i < size; ++i) { - to.push_back(from[i]); - } -} - -inline hidl_vec kmBlob2hidlVec(const keymaster_blob_t& blob) { - return hidl_vec(blob.data, blob.data+blob.data_length); -} - -keymaster_key_param_set_t hidlKeyParams2Km(const hidl_vec& keyParams); - -hidl_vec kmParamSet2Hidl(const keymaster_key_param_set_t& set); - -ErrorCode rsaRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& privateExp, std::vector& -pubModulus); - -ErrorCode ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& -publicKey, EcCurve& eccurve); - -ErrorCode getCertificateChain(std::vector& chainBuffer, std::vector>& certChain); - -uint32_t GetVendorPatchlevel(); - -class KmParamSet : public keymaster_key_param_set_t { - public: - explicit KmParamSet(const hidl_vec& keyParams) - : keymaster_key_param_set_t(hidlKeyParams2Km(keyParams)) {} - KmParamSet(KmParamSet&& other) : keymaster_key_param_set_t{other.params, other.length} { - other.length = 0; - other.params = nullptr; - } - KmParamSet(const KmParamSet&) = delete; - ~KmParamSet() { delete[] params; } -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster -#endif //KEYMASTER_V4_1_JAVACARD_COMMONUTILS_H_ diff --git a/HAL/keymaster/include/JavacardKeymaster4Device.h b/HAL/keymaster/include/JavacardKeymaster4Device.h deleted file mode 100644 index 617457b1..00000000 --- a/HAL/keymaster/include/JavacardKeymaster4Device.h +++ /dev/null @@ -1,168 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#ifndef KEYMASTER_V4_1_JAVACARD_JAVACARDKEYMASTER4DEVICE_H_ -#define KEYMASTER_V4_1_JAVACARD_JAVACARDKEYMASTER4DEVICE_H_ - -#include -#include -#include -#include -#include "CborConverter.h" -#include "TransportFactory.h" -#include -#include -#include -#include -#include -#include - -namespace keymaster { -namespace V4_1 { -namespace javacard { -#define INS_BEGIN_KM_CMD 0x00 -#define INS_END_KM_PROVISION_CMD 0x20 -#define INS_END_KM_CMD 0x7F - -using ::android::hardware::hidl_vec; -using ::android::hardware::hidl_string; -using ::android::hardware::Return; -using ::android::hardware::Void; - -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType; -using ::android::hardware::keymaster::V4_0::HardwareAuthToken; -using ::android::hardware::keymaster::V4_0::HmacSharingParameters; -using ::android::hardware::keymaster::V4_0::KeyCharacteristics; -using ::android::hardware::keymaster::V4_0::KeyFormat; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::KeyPurpose; -using ::android::hardware::keymaster::V4_0::OperationHandle; -using ::android::hardware::keymaster::V4_0::SecurityLevel; -using ::android::hardware::keymaster::V4_0::VerificationToken; -using ::android::hardware::keymaster::V4_1::IKeymasterDevice; -using ::android::hardware::keymaster::V4_0::Tag; - -using V41ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode; - -enum class OperationType { - /* Public operations are processed inside softkeymaster */ - PUBLIC_OPERATION = 0, - /* Private operations are processed inside strongbox */ - PRIVATE_OPERATION = 1, - UNKNOWN = 2, -}; - -enum class Instruction { - // Keymaster commands - INS_GENERATE_KEY_CMD = INS_END_KM_PROVISION_CMD+1, - INS_IMPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+2, - INS_IMPORT_WRAPPED_KEY_CMD = INS_END_KM_PROVISION_CMD+3, - INS_EXPORT_KEY_CMD = INS_END_KM_PROVISION_CMD+4, - INS_ATTEST_KEY_CMD = INS_END_KM_PROVISION_CMD+5, - INS_UPGRADE_KEY_CMD = INS_END_KM_PROVISION_CMD+6, - INS_DELETE_KEY_CMD = INS_END_KM_PROVISION_CMD+7, - INS_DELETE_ALL_KEYS_CMD = INS_END_KM_PROVISION_CMD+8, - INS_ADD_RNG_ENTROPY_CMD = INS_END_KM_PROVISION_CMD+9, - INS_COMPUTE_SHARED_HMAC_CMD = INS_END_KM_PROVISION_CMD+10, - INS_DESTROY_ATT_IDS_CMD = INS_END_KM_PROVISION_CMD+11, - INS_VERIFY_AUTHORIZATION_CMD = INS_END_KM_PROVISION_CMD+12, - INS_GET_HMAC_SHARING_PARAM_CMD = INS_END_KM_PROVISION_CMD+13, - INS_GET_KEY_CHARACTERISTICS_CMD = INS_END_KM_PROVISION_CMD+14, - INS_GET_HW_INFO_CMD = INS_END_KM_PROVISION_CMD+15, - INS_BEGIN_OPERATION_CMD = INS_END_KM_PROVISION_CMD+16, - INS_UPDATE_OPERATION_CMD = INS_END_KM_PROVISION_CMD+17, - INS_FINISH_OPERATION_CMD = INS_END_KM_PROVISION_CMD+18, - INS_ABORT_OPERATION_CMD = INS_END_KM_PROVISION_CMD+19, - INS_DEVICE_LOCKED_CMD = INS_END_KM_PROVISION_CMD+20, - INS_EARLY_BOOT_ENDED_CMD = INS_END_KM_PROVISION_CMD+21, - INS_GET_CERT_CHAIN_CMD = INS_END_KM_PROVISION_CMD+22, - INS_GET_PROVISION_STATUS_CMD = INS_BEGIN_KM_CMD+7, - INS_SET_VERSION_PATCHLEVEL_CMD = INS_BEGIN_KM_CMD+8, -}; - -class JavacardKeymaster4Device : public IKeymasterDevice { - public: - - JavacardKeymaster4Device(); - virtual ~JavacardKeymaster4Device(); - - // Methods from ::android::hardware::keymaster::V4_0::IKeymasterDevice follow. - Return getHardwareInfo(getHardwareInfo_cb _hidl_cb) override; - Return getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override; - Return computeSharedHmac(const hidl_vec& params, computeSharedHmac_cb _hidl_cb) override; - Return verifyAuthorization(uint64_t operationHandle, const hidl_vec& parametersToVerify, const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) override; - Return addRngEntropy(const hidl_vec& data) override; - Return generateKey(const hidl_vec& keyParams, generateKey_cb _hidl_cb) override; - Return importKey(const hidl_vec& keyParams, KeyFormat keyFormat, const hidl_vec& keyData, importKey_cb _hidl_cb) override; - Return importWrappedKey(const hidl_vec& wrappedKeyData, const hidl_vec& wrappingKeyBlob, const hidl_vec& maskingKey, const hidl_vec& unwrappingParams, uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) override; - Return getKeyCharacteristics(const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, getKeyCharacteristics_cb _hidl_cb) override; - Return exportKey(KeyFormat keyFormat, const hidl_vec& keyBlob, const hidl_vec& clientId, const hidl_vec& appData, exportKey_cb _hidl_cb) override; - Return attestKey(const hidl_vec& keyToAttest, const hidl_vec& attestParams, attestKey_cb _hidl_cb) override; - Return upgradeKey(const hidl_vec& keyBlobToUpgrade, const hidl_vec& upgradeParams, upgradeKey_cb _hidl_cb) override; - Return deleteKey(const hidl_vec& keyBlob) override; - Return deleteAllKeys() override; - Return destroyAttestationIds() override; - Return begin(KeyPurpose purpose, const hidl_vec& keyBlob, const hidl_vec& inParams, const HardwareAuthToken& authToken, begin_cb _hidl_cb) override; - Return update(uint64_t operationHandle, const hidl_vec& inParams, const hidl_vec& input, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, update_cb _hidl_cb) override; - Return finish(uint64_t operationHandle, const hidl_vec& inParams, const hidl_vec& input, const hidl_vec& signature, const HardwareAuthToken& authToken, const VerificationToken& verificationToken, finish_cb _hidl_cb) override; - Return abort(uint64_t operationHandle) override; - - // Methods from ::android::hardware::keymaster::V4_1::IKeymasterDevice follow. - Return deviceLocked(bool passwordOnly, const VerificationToken& verificationToken) override; - Return earlyBootEnded() override; - - private: - ErrorCode handleBeginPublicKeyOperation(KeyPurpose purpose, const hidl_vec& keyBlob, - const hidl_vec& inParams, - hidl_vec& outParams, - uint64_t& operationHandle); - - ErrorCode handleBeginPrivateKeyOperation(KeyPurpose purpose, const hidl_vec& keyBlob, - const hidl_vec& inParams, - const HardwareAuthToken& authToken, - hidl_vec& outParams, - uint64_t& operationHandle); - - ErrorCode handleBeginOperation(KeyPurpose purpose, const hidl_vec& keyBlob, - const hidl_vec& inParams, - const HardwareAuthToken& authToken, - hidl_vec& outParams, uint64_t& operationHandle, - OperationType& operType); - - ErrorCode abortOperation(uint64_t operationHandle, OperationType operType); - - ErrorCode abortPublicKeyOperation(uint64_t operationHandle); - - ErrorCode abortPrivateKeyOperation(uint64_t operationHandle); - - ErrorCode sendData(Instruction ins, std::vector& inData, std::vector& response); - ErrorCode setAndroidSystemProperties(); - void handleSendEarlyBootEndedEvent(); - - std::unique_ptr<::keymaster::AndroidKeymaster> softKm_; - std::unique_ptr oprCtx_; - bool isEachSystemPropertySet; - bool isEarlyBootEventPending; - CborConverter cborConverter_; -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster - -#endif // KEYMASTER_V4_1_JAVACARD_JAVACARDKEYMASTER4DEVICE_H_ diff --git a/HAL/keymaster/include/JavacardOperationContext.h b/HAL/keymaster/include/JavacardOperationContext.h deleted file mode 100644 index 0d452c67..00000000 --- a/HAL/keymaster/include/JavacardOperationContext.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#ifndef KEYMASTER_V4_1_JAVACARD_OPERATIONCONTEXT_H_ -#define KEYMASTER_V4_1_JAVACARD_OPERATIONCONTEXT_H_ - -#include -#include - -#define MAX_BUF_SIZE 256 - -namespace keymaster { -namespace V4_1 { -namespace javacard { - -using ::android::hardware::hidl_vec; -using ::android::hardware::keymaster::V4_0::ErrorCode; -using ::android::hardware::keymaster::V4_0::Algorithm; -using ::android::hardware::keymaster::V4_0::KeyPurpose; -using ::android::hardware::keymaster::V4_0::Digest; -using ::android::hardware::keymaster::V4_0::PaddingMode; -using ::android::hardware::keymaster::V4_0::KeyParameter; -using ::android::hardware::keymaster::V4_0::BlockMode; -using ::android::hardware::keymaster::V4_0::Tag; - -/** - * Callback function to send data back to the caller. - */ -using sendDataToSE_cb = std::function& data, bool finish)>; - -enum class Operation; - -/** - * This struct is used to store the buffered data. - */ -struct BufferedData { - uint8_t buf[MAX_BUF_SIZE]; - uint32_t buf_len; -}; - -/** - * This struct is used to store the operation info. - */ -struct OperationInfo { - Algorithm alg; - KeyPurpose purpose; - Digest digest; - PaddingMode pad; - BlockMode mode; - uint32_t macLength; -}; - -/** - * OperationContext uses this struct to store the buffered data and the correspoding operation info. - */ -struct OperationData { - OperationInfo info; - BufferedData data; -}; - -/** - * This class manages the data that is send for any crypto operation. - * - * For Symmetric operations, update function sends only block aligned data and stores the remaining data in the buffer - * so at any point the buffer may contain data ranging from 0 to a maximum of block size, where as finish function sends - * all the data (input data + buffered data) to the caller and clears the buffer. To support PKCS#7 padding removal, - * the last block size from the input is always buffered in update operation and this last block is sent in finish - * operation. - * - * For Asymmetric operations, if the operation is with Digest then the input data is not buffered, where as if the - * operation is with no Digest then update function buffers the input data and finish function extracts the data from - * buffer and sends to the caller. Update and finish functions does validation on the input data based on the algorithm. - * - * In General, the maximum allowed input data that is sent is limited to MAX_ALLOWED_INPUT_SIZE. If the input data - * exceeds this limit each update or finish function divides the input data into chunks of MAX_ALLOWED_INPUT_SIZE and - * sends each chunk back to the caller through update callback. - */ -class OperationContext { - -public: - OperationContext(){} - ~OperationContext() {} - /** - * In Begin operation caller has to call this function to store the operation data corresponding to the operation - * handle. - */ - ErrorCode setOperationInfo(uint64_t operationHandle, KeyPurpose purpose, Algorithm alg, const hidl_vec& params); - /** - * This function clears the operation data from the map. Caller has to call this function once the operation is done - * or if there is any error while processing the operation. - */ - ErrorCode clearOperationData(uint64_t operationHandle); - /** - * This function validaes the input data based on the algorithm and does process on the data to either store it or - * send back to the caller. The data is sent using sendDataTOSE_cb callback. - */ - ErrorCode update(uint64_t operHandle, const std::vector& input, sendDataToSE_cb cb); - /** - * This function validaes the input data based on the algorithm and send all the input data along with buffered data - * to the caller. The data is sent using sendDataTOSE_cb callback. - */ - ErrorCode finish(uint64_t operHandle, const std::vector& input, sendDataToSE_cb cb); - -private: - /** - * This is used to store the operation related info and the buffered data. Key is the operation handle and the value - * is OperationData. - */ - std::map operationTable; - - /* Helper functions */ - - /** - * This fucntion validates the input data based on the algorithm and the operation info parameters. This function - * also does a processing on the input data if either the algorithm is EC or if it is a Finish operation. For EC - * operations it truncates the input data if it exceeds 32 bytes for No Digest case. In case of finish operations - * this function combines both the buffered data and input data if both exceeds MAX_ALLOWED_INPUT_SIZE. - */ - ErrorCode validateInputData(uint64_t operHandle, Operation opr, const std::vector& actualInput, - std::vector& input); - /** - * This function is used for Symmetric operations. It extracts the block sized data from the input and buffers the - * reamining data for update calls only. For finish calls it extracts all the buffered data combines it with - * input data. - */ - ErrorCode bufferData(uint64_t operHandle, std::vector& input, - Operation opr, std::vector& out); - /** - * This function sends the data back to the caller using callback functions. It does some processing on input data - * for Asymmetic operations. - */ - ErrorCode handleInternalUpdate(uint64_t operHandle, std::vector& data, Operation opr, - sendDataToSE_cb cb, bool finish = false); - -}; - -} // namespace javacard -} // namespace V4_1 -} // namespace keymaster - -#endif // KEYMASTER_V4_1_JAVACARD_OPERATIONCONTEXT_H_ diff --git a/HAL/keymaster/include/Transport.h b/HAL/keymaster/include/Transport.h deleted file mode 100644 index c6674dca..00000000 --- a/HAL/keymaster/include/Transport.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ -#ifndef __SE_TRANSPORT__ -#define __SE_TRANSPORT__ - -namespace se_transport { - -/** - * ITransport is an abstract interface with a set of virtual methods that allow communication between the keymaster - * HAL and the secure element. - */ -class ITransport { - public: - virtual ~ITransport(){} - - /** - * Opens connection. - */ - virtual bool openConnection() = 0; - /** - * Send data over communication channel and receives data back from the remote end. - */ - virtual bool sendData(const uint8_t* inData, const size_t inLen, std::vector& output) = 0; - /** - * Closes the connection. - */ - virtual bool closeConnection() = 0; - /** - * Returns the state of the connection status. Returns true if the connection is active, false if connection is - * broken. - */ - virtual bool isConnected() = 0; - -}; - -/** - * OmapiTransport is derived from ITransport. This class gets the OMAPI service binder instance and uses IPC to - * communicate with OMAPI service. OMAPI inturn communicates with hardware via ISecureElement. - */ -class OmapiTransport : public ITransport { - -public: - - /** - * Gets the binder instance of ISEService, gets the reader corresponding to secure element, establishes a session - * and opens a basic channel. - */ - bool openConnection() override; - /** - * Transmists the data over the opened basic channel and receives the data back. - */ - bool sendData(const uint8_t* inData, const size_t inLen, std::vector& output) override; - - /** - * Closes the connection. - */ - bool closeConnection() override; - /** - * Returns the state of the connection status. Returns true if the connection is active, false if connection is - * broken. - */ - bool isConnected() override; - -}; - -class SocketTransport : public ITransport { - -public: - SocketTransport() : mSocket(-1), mSocketStatus(false){ - } - /** - * Creates a socket instance and connects to the provided server IP and port. - */ - bool openConnection() override; - /** - * Sends data over socket and receives data back. - */ - bool sendData(const uint8_t* inData, const size_t inLen, std::vector& output) override; - - /** - * Closes the connection. - */ - bool closeConnection() override; - /** - * Returns the state of the connection status. Returns true if the connection is active, false if connection is - * broken. - */ - bool isConnected() override; -private: - /** - * Socket instance. - */ - int mSocket; - bool mSocketStatus; -}; - -} -#endif /* __SE_TRANSPORT__ */ diff --git a/HAL/keymaster/include/TransportFactory.h b/HAL/keymaster/include/TransportFactory.h deleted file mode 100644 index b09e3ba9..00000000 --- a/HAL/keymaster/include/TransportFactory.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - ** - ** Copyright 2020, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ -#ifndef __SE_TRANSPORT_FACTORY__ -#define __SE_TRANSPORT_FACTORY__ - -#include "Transport.h" - -namespace se_transport { - -/** - * TransportFactory class decides which transport mechanism to be used to send data to secure element. In case of - * emulator the communication channel is socket and in case of device the communication channel is via OMAPI. - */ -class TransportFactory { - public: - TransportFactory(bool isEmulator) { - if (!isEmulator) - mTransport = std::unique_ptr(new OmapiTransport()); - else - mTransport = std::unique_ptr(new SocketTransport()); - } - - ~TransportFactory() {} - - /** - * Establishes a communication channel with the secure element. - */ - inline bool openConnection() { - return mTransport->openConnection(); - } - - /** - * Sends the data to the secure element and also receives back the data. - * This is a blocking call. - */ - inline bool sendData(const uint8_t* inData, const size_t inLen, std::vector& output) { - return mTransport->sendData(inData, inLen, output); - } - - /** - * Close the connection. - */ - inline bool closeConnection() { - return mTransport->closeConnection(); - } - - /** - * Returns the connection status of the communication channel. - */ - inline bool isConnected() { - return mTransport->isConnected(); - } - - private: - /** - * Holds the instance of either OmapiTransport class or SocketTransport class. - */ - std::unique_ptr mTransport; - -}; -} -#endif /* __SE_TRANSPORT_FACTORY__ */ diff --git a/HAL/keymaster/4.1/service.cpp b/HAL/keymasterService.cpp similarity index 61% rename from HAL/keymaster/4.1/service.cpp rename to HAL/keymasterService.cpp index cd7653d0..51d09a8d 100644 --- a/HAL/keymaster/4.1/service.cpp +++ b/HAL/keymasterService.cpp @@ -15,14 +15,27 @@ ** limitations under the License. */ +#include "KMUtils.h" +#include +#include +#include #include #include #include -#include +#include +using namespace javacard_keymaster; +using ::javacard_keymaster::JavacardKeymaster; +using ::javacard_keymaster::JavacardSecureElement; +using ::javacard_keymaster::SocketTransport; +using ::keymaster::V4_1::javacard::JavacardKeymaster4Device; int main() { ::android::hardware::configureRpcThreadpool(1, true); - auto keymaster = new ::keymaster::V4_1::javacard::JavacardKeymaster4Device(); + std::shared_ptr card = std::make_shared( + KmVersion::KEYMASTER_4_1, std::make_shared(), getOsVersion(), + getOsPatchlevel(), getVendorPatchlevel()); + std::shared_ptr jcImpl = std::make_shared(card); + auto keymaster = new JavacardKeymaster4Device(jcImpl); auto status = keymaster->registerAsService("strongbox"); if (status != android::OK) { LOG(FATAL) << "Could not register service for Javacard Keymaster 4.1 (" << status << ")"; diff --git a/HAL/keymintService.cpp b/HAL/keymintService.cpp new file mode 100644 index 00000000..54b990b7 --- /dev/null +++ b/HAL/keymintService.cpp @@ -0,0 +1,67 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "javacard.strongbox-service" + +#include "JavacardKeyMintDevice.h" +#include "JavacardRemotelyProvisionedComponentDevice.h" +#include "JavacardSecureElement.h" +#include "JavacardSharedSecret.h" +#include "KMUtils.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace javacard_keymaster; +using aidl::android::hardware::security::keymint::JavacardKeyMintDevice; +using aidl::android::hardware::security::keymint::JavacardRemotelyProvisionedComponentDevice; +using aidl::android::hardware::security::keymint::JavacardSharedSecret; +using aidl::android::hardware::security::keymint::SecurityLevel; +using ::javacard_keymaster::JavacardKeymaster; +using ::javacard_keymaster::JavacardSecureElement; +using ::javacard_keymaster::SocketTransport; + +template std::shared_ptr addService(Args&&... args) { + std::shared_ptr ser = ndk::SharedRefBase::make(std::forward(args)...); + auto instanceName = std::string(T::descriptor) + "/strongbox"; + LOG(INFO) << "adding javacard strongbox service instance: " << instanceName; + binder_status_t status = + AServiceManager_addService(ser->asBinder().get(), instanceName.c_str()); + CHECK(status == STATUS_OK); + return ser; +} + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + // Javacard Secure Element + std::shared_ptr card = std::make_shared( + KmVersion::KEYMINT_1, std::make_shared(), getOsVersion(), + getOsPatchlevel(), getVendorPatchlevel()); + std::shared_ptr jcImpl = std::make_shared(card); + // Add Keymint Service + addService(jcImpl); + // Add Shared Secret Service + addService(jcImpl); + // Add Remotely Provisioned Component Service + addService(card); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/ProvisioningTool/Makefile b/ProvisioningTool/keymaster/Makefile similarity index 100% rename from ProvisioningTool/Makefile rename to ProvisioningTool/keymaster/Makefile diff --git a/ProvisioningTool/README.md b/ProvisioningTool/keymaster/README.md similarity index 100% rename from ProvisioningTool/README.md rename to ProvisioningTool/keymaster/README.md diff --git a/ProvisioningTool/include/UniquePtr.h b/ProvisioningTool/keymaster/include/UniquePtr.h similarity index 100% rename from ProvisioningTool/include/UniquePtr.h rename to ProvisioningTool/keymaster/include/UniquePtr.h diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/keymaster/include/constants.h similarity index 97% rename from ProvisioningTool/include/constants.h rename to ProvisioningTool/keymaster/include/constants.h index ffc0011f..166fc88b 100644 --- a/ProvisioningTool/include/constants.h +++ b/ProvisioningTool/keymaster/include/constants.h @@ -70,7 +70,8 @@ constexpr uint64_t kTagAttestationIdModel = 2415919821u; constexpr uint64_t kCurveP256 = 1; constexpr uint64_t kAlgorithmEc = 3; constexpr uint64_t kDigestSha256 = 4; -constexpr uint64_t kPurposeAttest = 0x7F; +//constexpr uint64_t kPurposeAttest = 0x7F; +constexpr uint64_t kPurposeAttest = 0x07; constexpr uint64_t kKeyFormatRaw = 3; // json keys diff --git a/ProvisioningTool/include/cppbor/cppbor.h b/ProvisioningTool/keymaster/include/cppbor/cppbor.h similarity index 100% rename from ProvisioningTool/include/cppbor/cppbor.h rename to ProvisioningTool/keymaster/include/cppbor/cppbor.h diff --git a/ProvisioningTool/include/cppbor/cppbor_parse.h b/ProvisioningTool/keymaster/include/cppbor/cppbor_parse.h similarity index 100% rename from ProvisioningTool/include/cppbor/cppbor_parse.h rename to ProvisioningTool/keymaster/include/cppbor/cppbor_parse.h diff --git a/ProvisioningTool/include/json/assertions.h b/ProvisioningTool/keymaster/include/json/assertions.h similarity index 100% rename from ProvisioningTool/include/json/assertions.h rename to ProvisioningTool/keymaster/include/json/assertions.h diff --git a/ProvisioningTool/include/json/autolink.h b/ProvisioningTool/keymaster/include/json/autolink.h similarity index 100% rename from ProvisioningTool/include/json/autolink.h rename to ProvisioningTool/keymaster/include/json/autolink.h diff --git a/ProvisioningTool/include/json/config.h b/ProvisioningTool/keymaster/include/json/config.h similarity index 100% rename from ProvisioningTool/include/json/config.h rename to ProvisioningTool/keymaster/include/json/config.h diff --git a/ProvisioningTool/include/json/features.h b/ProvisioningTool/keymaster/include/json/features.h similarity index 100% rename from ProvisioningTool/include/json/features.h rename to ProvisioningTool/keymaster/include/json/features.h diff --git a/ProvisioningTool/include/json/forwards.h b/ProvisioningTool/keymaster/include/json/forwards.h similarity index 100% rename from ProvisioningTool/include/json/forwards.h rename to ProvisioningTool/keymaster/include/json/forwards.h diff --git a/ProvisioningTool/include/json/json.h b/ProvisioningTool/keymaster/include/json/json.h similarity index 100% rename from ProvisioningTool/include/json/json.h rename to ProvisioningTool/keymaster/include/json/json.h diff --git a/ProvisioningTool/include/json/reader.h b/ProvisioningTool/keymaster/include/json/reader.h similarity index 100% rename from ProvisioningTool/include/json/reader.h rename to ProvisioningTool/keymaster/include/json/reader.h diff --git a/ProvisioningTool/include/json/value.h b/ProvisioningTool/keymaster/include/json/value.h similarity index 100% rename from ProvisioningTool/include/json/value.h rename to ProvisioningTool/keymaster/include/json/value.h diff --git a/ProvisioningTool/include/json/version.h b/ProvisioningTool/keymaster/include/json/version.h similarity index 100% rename from ProvisioningTool/include/json/version.h rename to ProvisioningTool/keymaster/include/json/version.h diff --git a/ProvisioningTool/include/json/writer.h b/ProvisioningTool/keymaster/include/json/writer.h similarity index 100% rename from ProvisioningTool/include/json/writer.h rename to ProvisioningTool/keymaster/include/json/writer.h diff --git a/ProvisioningTool/include/socket.h b/ProvisioningTool/keymaster/include/socket.h similarity index 100% rename from ProvisioningTool/include/socket.h rename to ProvisioningTool/keymaster/include/socket.h diff --git a/ProvisioningTool/include/utils.h b/ProvisioningTool/keymaster/include/utils.h similarity index 100% rename from ProvisioningTool/include/utils.h rename to ProvisioningTool/keymaster/include/utils.h diff --git a/ProvisioningTool/lib/README.md b/ProvisioningTool/keymaster/lib/README.md similarity index 100% rename from ProvisioningTool/lib/README.md rename to ProvisioningTool/keymaster/lib/README.md diff --git a/ProvisioningTool/lib/libjsoncpp.a b/ProvisioningTool/keymaster/lib/libjsoncpp.a similarity index 100% rename from ProvisioningTool/lib/libjsoncpp.a rename to ProvisioningTool/keymaster/lib/libjsoncpp.a diff --git a/ProvisioningTool/lib/libjsoncpp.so b/ProvisioningTool/keymaster/lib/libjsoncpp.so similarity index 100% rename from ProvisioningTool/lib/libjsoncpp.so rename to ProvisioningTool/keymaster/lib/libjsoncpp.so diff --git a/ProvisioningTool/lib/libjsoncpp.so.0 b/ProvisioningTool/keymaster/lib/libjsoncpp.so.0 similarity index 100% rename from ProvisioningTool/lib/libjsoncpp.so.0 rename to ProvisioningTool/keymaster/lib/libjsoncpp.so.0 diff --git a/ProvisioningTool/lib/libjsoncpp.so.0.10.7 b/ProvisioningTool/keymaster/lib/libjsoncpp.so.0.10.7 similarity index 100% rename from ProvisioningTool/lib/libjsoncpp.so.0.10.7 rename to ProvisioningTool/keymaster/lib/libjsoncpp.so.0.10.7 diff --git a/ProvisioningTool/sample_json_cf.txt b/ProvisioningTool/keymaster/sample_json_cf.txt similarity index 100% rename from ProvisioningTool/sample_json_cf.txt rename to ProvisioningTool/keymaster/sample_json_cf.txt diff --git a/ProvisioningTool/sample_json_gf.txt b/ProvisioningTool/keymaster/sample_json_gf.txt similarity index 100% rename from ProvisioningTool/sample_json_gf.txt rename to ProvisioningTool/keymaster/sample_json_gf.txt diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/keymaster/src/construct_apdus.cpp similarity index 100% rename from ProvisioningTool/src/construct_apdus.cpp rename to ProvisioningTool/keymaster/src/construct_apdus.cpp diff --git a/ProvisioningTool/src/cppbor.cpp b/ProvisioningTool/keymaster/src/cppbor.cpp similarity index 100% rename from ProvisioningTool/src/cppbor.cpp rename to ProvisioningTool/keymaster/src/cppbor.cpp diff --git a/ProvisioningTool/src/cppbor_parse.cpp b/ProvisioningTool/keymaster/src/cppbor_parse.cpp similarity index 100% rename from ProvisioningTool/src/cppbor_parse.cpp rename to ProvisioningTool/keymaster/src/cppbor_parse.cpp diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/keymaster/src/provision.cpp similarity index 100% rename from ProvisioningTool/src/provision.cpp rename to ProvisioningTool/keymaster/src/provision.cpp diff --git a/ProvisioningTool/src/socket.cpp b/ProvisioningTool/keymaster/src/socket.cpp similarity index 100% rename from ProvisioningTool/src/socket.cpp rename to ProvisioningTool/keymaster/src/socket.cpp diff --git a/ProvisioningTool/src/utils.cpp b/ProvisioningTool/keymaster/src/utils.cpp similarity index 100% rename from ProvisioningTool/src/utils.cpp rename to ProvisioningTool/keymaster/src/utils.cpp diff --git a/ProvisioningTool/test_resources/batch_cert.der b/ProvisioningTool/keymaster/test_resources/batch_cert.der similarity index 100% rename from ProvisioningTool/test_resources/batch_cert.der rename to ProvisioningTool/keymaster/test_resources/batch_cert.der diff --git a/ProvisioningTool/test_resources/batch_key.der b/ProvisioningTool/keymaster/test_resources/batch_key.der similarity index 100% rename from ProvisioningTool/test_resources/batch_key.der rename to ProvisioningTool/keymaster/test_resources/batch_key.der diff --git a/ProvisioningTool/test_resources/ca_cert.der b/ProvisioningTool/keymaster/test_resources/ca_cert.der similarity index 100% rename from ProvisioningTool/test_resources/ca_cert.der rename to ProvisioningTool/keymaster/test_resources/ca_cert.der diff --git a/ProvisioningTool/test_resources/ca_key.der b/ProvisioningTool/keymaster/test_resources/ca_key.der similarity index 100% rename from ProvisioningTool/test_resources/ca_key.der rename to ProvisioningTool/keymaster/test_resources/ca_key.der diff --git a/ProvisioningTool/test_resources/intermediate_cert.der b/ProvisioningTool/keymaster/test_resources/intermediate_cert.der similarity index 100% rename from ProvisioningTool/test_resources/intermediate_cert.der rename to ProvisioningTool/keymaster/test_resources/intermediate_cert.der diff --git a/ProvisioningTool/test_resources/intermediate_key.der b/ProvisioningTool/keymaster/test_resources/intermediate_key.der similarity index 100% rename from ProvisioningTool/test_resources/intermediate_key.der rename to ProvisioningTool/keymaster/test_resources/intermediate_key.der diff --git a/ProvisioningTool/keymint/Makefile b/ProvisioningTool/keymint/Makefile new file mode 100644 index 00000000..7b992f47 --- /dev/null +++ b/ProvisioningTool/keymint/Makefile @@ -0,0 +1,58 @@ +CC = g++ +SRC_DIR = src + +CONSTRUCT_APDUS_SRC = $(SRC_DIR)/construct_apdus.cpp \ + $(SRC_DIR)/cppbor/cppbor.cpp \ + $(SRC_DIR)/cppbor/cppbor_parse.cpp \ + $(SRC_DIR)/utils.cpp \ + $(SRC_DIR)/cppcose/cppcose.cpp + +CONSTRUCT_APDUS_OBJFILES = $(CONSTRUCT_APDUS_SRC:.cpp=.o) +CONSTRUCT_APDUS_BIN = construct_keymint_apdus + +# source files for provision +PROVISION_SRC = $(SRC_DIR)/provision.cpp \ + $(SRC_DIR)/socket.cpp \ + $(SRC_DIR)/cppbor/cppbor.cpp \ + $(SRC_DIR)/cppbor/cppbor_parse.cpp \ + $(SRC_DIR)/utils.cpp \ + +#object files for keymint provision +PROVISION_OBJFILES = $(PROVISION_SRC:.cpp=.o) +PROVISION_BIN = provision_keymint + +ifeq ($(OS),Windows_NT) + uname_S := Windows +else + uname_S := $(shell uname -s) +endif + +ifeq ($(uname_S), Windows) + PLATFORM = -D__WIN32__ +endif +ifeq ($(uname_S), Linux) + PLATFORM = -D__LINUX__ +endif + +DEBUG = -g +CXXFLAGS = $(DEBUG) $(PLATFORM) -Wall -std=c++2a +CFLAGS = $(CXXFLAGS) -Iinclude +LDFLAGS = -Llib/ +LIB_JSON = -ljsoncpp +LIB_CRYPTO = -lcrypto +LDLIBS = $(LIB_JSON) $(LIB_CRYPTO) + +all: $(CONSTRUCT_APDUS_BIN) $(PROVISION_BIN) + +$(CONSTRUCT_APDUS_BIN): $(CONSTRUCT_APDUS_OBJFILES) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +$(PROVISION_BIN): $(PROVISION_OBJFILES) + $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) + +%.o: %.cpp + $(CC) $(CFLAGS) -c -o $@ $^ + +.PHONY: clean +clean: + rm -f $(CONSTRUCT_APDUS_OBJFILES) $(CONSTRUCT_APDUS_BIN) $(PROVISION_OBJFILES) $(PROVISION_BIN) diff --git a/ProvisioningTool/keymint/README.md b/ProvisioningTool/keymint/README.md new file mode 100644 index 00000000..21fd0034 --- /dev/null +++ b/ProvisioningTool/keymint/README.md @@ -0,0 +1,41 @@ +# Provisioning tool +This directory contains two tools. One which constructs the apdus and dumps them to a json file, Other which gets the apuds from the json file and provision them into a secure element simulator. Both the tools can be compiled and executed from a Linux machine. + +#### Build instruction +The default target generates both the executables. One construct_apdus and the other provision. +$ make +Individual targets can also be selected as shown below +$ make construct_apdus +$ make provision +Make clean will remove all the object files and binaries +$ make clean + +#### Environment setup +Before executing the binaries make sure LD_LIBRARY_PATH is set +export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH + +#### Sample resources for quick testing +one sample json files is located in this directory with name +[sample_json_keymint_cf.txt](sample_json_keymint_cf.txt) +for your reference. Use sample_json_keymint_cf.txt for keymint +cuttlefish target. Also the required certificates and keys can be found in +[test_resources](test_resources) directory for your reference. + +#### Usage for construct_apdus +

+Usage: construct_keymint_apdus options
+Valid options are:
+-h, --help                        show the help message and exit.
+-i, --input  jsonFile 	 Input json file 
+-o, --output jsonFile 	 Output json file
+
+ +#### Usage for provision +
+Usage: provision_keymint options
+Valid options are:
+-h, --help                      show the help message and exit.
+-i, --input  jsonFile 	  Input json file 
+-s, --provision_stautus   Prints the current provision status.
+-l, --lock_provision      Locks the provision state.
+
diff --git a/ProvisioningTool/keymint/include/UniquePtr.h b/ProvisioningTool/keymint/include/UniquePtr.h new file mode 100644 index 00000000..da74780b --- /dev/null +++ b/ProvisioningTool/keymint/include/UniquePtr.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include // for size_t + +#include + +// Default deleter for pointer types. +template struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + DefaultDelete() {} + void operator()(T* p) const { delete p; } +}; + +// Default deleter for array types. +template struct DefaultDelete { + enum { type_must_be_complete = sizeof(T) }; + void operator()(T* p) const { delete[] p; } +}; + +template > +using UniquePtr = std::unique_ptr; + + diff --git a/ProvisioningTool/keymint/include/constants.h b/ProvisioningTool/keymint/include/constants.h new file mode 100644 index 00000000..31684ff3 --- /dev/null +++ b/ProvisioningTool/keymint/include/constants.h @@ -0,0 +1,103 @@ +/* + ** + ** Copyright 2021, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#pragma once + +#include +#include +#include +#include +#include +#include "UniquePtr.h" + +#define SUCCESS 0 +#define FAILURE 1 +#define P1_40 0x40 +#define P1_50 0x50 +#define APDU_CLS 0x80 +#define APDU_P1 0x50 +#define APDU_P2 0x00 +#define INS_BEGIN_KM_CMD 0x00 +#define APDU_RESP_STATUS_OK 0x9000 + + + +template +struct OpenSslObjectDeleter { + void operator()(T* p) { FreeFunc(p); } +}; + +#define DEFINE_OPENSSL_OBJECT_POINTER(name) \ + typedef OpenSslObjectDeleter name##_Delete; \ + typedef UniquePtr name##_Ptr; + +DEFINE_OPENSSL_OBJECT_POINTER(EC_KEY) +DEFINE_OPENSSL_OBJECT_POINTER(EVP_PKEY) +DEFINE_OPENSSL_OBJECT_POINTER(X509) +DEFINE_OPENSSL_OBJECT_POINTER(EC_POINT) +DEFINE_OPENSSL_OBJECT_POINTER(EC_GROUP) +DEFINE_OPENSSL_OBJECT_POINTER(BN_CTX) +DEFINE_OPENSSL_OBJECT_POINTER(EVP_MD_CTX) + +typedef OpenSslObjectDeleter BIGNUM_Delete; +typedef UniquePtr BIGNUM_Ptr; + +// EC Affine point length for Nist P256. +constexpr uint32_t kAffinePointLength = 32; + +// Tags +constexpr uint64_t kTagAlgorithm = 268435458u; +constexpr uint64_t kTagDigest = 536870917u; +constexpr uint64_t kTagCurve = 268435466u; +constexpr uint64_t kTagPurpose = 536870913u; +constexpr uint64_t kTagAttestationIdBrand = 2415919814u; +constexpr uint64_t kTagAttestationIdDevice = 2415919815u; +constexpr uint64_t kTagAttestationIdProduct = 2415919816u; +constexpr uint64_t kTagAttestationIdSerial = 2415919817u; +constexpr uint64_t kTagAttestationIdImei = 2415919818u; +constexpr uint64_t kTagAttestationIdMeid = 2415919819u; +constexpr uint64_t kTagAttestationIdManufacturer = 2415919820u; +constexpr uint64_t kTagAttestationIdModel = 2415919821u; + +// Values +constexpr uint64_t kCurveP256 = 1; +constexpr uint64_t kAlgorithmEc = 3; +constexpr uint64_t kDigestSha256 = 4; +constexpr uint64_t kPurposeAttest = 0x7F; +constexpr uint64_t kKeyFormatRaw = 3; + +// json keys +constexpr char kAttestKey[] = "attest_key"; +constexpr char kAttestCertChain[] = "attest_cert_chain"; +constexpr char kAttestCertParams[] = "attest_cert_params"; +constexpr char kSharedSecret[] = "shared_secret"; +constexpr char kBootParams[] = "boot_params"; +constexpr char kAttestationIds[] = "attestation_ids"; +constexpr char kDeviceUniqueKey[] = "device_unique_key"; +constexpr char kAdditionalCertChain[] = "additional_cert_chain"; +constexpr char kSignerInfo[] = "signer_info"; +constexpr char kProvisionStatus[] = "provision_status"; +constexpr char kLockProvision[] = "lock_provision"; + +// Instruction constatnts +// TODO Modify according to keymint +constexpr int kAttestationIdsCmd = INS_BEGIN_KM_CMD + 3; +constexpr int kPresharedSecretCmd = INS_BEGIN_KM_CMD + 4; +constexpr int kLockProvisionCmd = INS_BEGIN_KM_CMD + 6; +constexpr int kGetProvisionStatusCmd = INS_BEGIN_KM_CMD + 7; +constexpr int kBootParamsCmd = INS_BEGIN_KM_CMD + 5; +constexpr int kDeviceUniqueKeyCmd = INS_BEGIN_KM_CMD + 10; +constexpr int kAdditionalCertChainCmd = INS_BEGIN_KM_CMD + 11; diff --git a/ProvisioningTool/keymint/include/cppbor/cppbor.h b/ProvisioningTool/keymint/include/cppbor/cppbor.h new file mode 100644 index 00000000..45ae67cf --- /dev/null +++ b/ProvisioningTool/keymint/include/cppbor/cppbor.h @@ -0,0 +1,1113 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace cppbor { + +enum MajorType : uint8_t { + UINT = 0 << 5, + NINT = 1 << 5, + BSTR = 2 << 5, + TSTR = 3 << 5, + ARRAY = 4 << 5, + MAP = 5 << 5, + SEMANTIC = 6 << 5, + SIMPLE = 7 << 5, +}; + +enum SimpleType { + BOOLEAN, + NULL_T, // Only two supported, as yet. +}; + +enum SpecialAddlInfoValues : uint8_t { + FALSE = 20, + TRUE = 21, + NULL_V = 22, + ONE_BYTE_LENGTH = 24, + TWO_BYTE_LENGTH = 25, + FOUR_BYTE_LENGTH = 26, + EIGHT_BYTE_LENGTH = 27, +}; + +class Item; +class Uint; +class Nint; +class Int; +class Tstr; +class Bstr; +class Simple; +class Bool; +class Array; +class Map; +class Null; +class SemanticTag; +class EncodedItem; +class ViewTstr; +class ViewBstr; + +/** + * Returns the size of a CBOR header that contains the additional info value addlInfo. + */ +size_t headerSize(uint64_t addlInfo); + +/** + * Encodes a CBOR header with the specified type and additional info into the range [pos, end). + * Returns a pointer to one past the last byte written, or nullptr if there isn't sufficient space + * to write the header. + */ +uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end); + +using EncodeCallback = std::function; + +/** + * Encodes a CBOR header with the specified type and additional info, passing each byte in turn to + * encodeCallback. + */ +void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback); + +/** + * Encodes a CBOR header witht he specified type and additional info, writing each byte to the + * provided OutputIterator. + */ +template ::iterator_category>>> +void encodeHeader(MajorType type, uint64_t addlInfo, OutputIterator iter) { + return encodeHeader(type, addlInfo, [&](uint8_t v) { *iter++ = v; }); +} + +/** + * Item represents a CBOR-encodeable data item. Item is an abstract interface with a set of virtual + * methods that allow encoding of the item or conversion to the appropriate derived type. + */ +class Item { + public: + virtual ~Item() {} + + /** + * Returns the CBOR type of the item. + */ + virtual MajorType type() const = 0; + + // These methods safely downcast an Item to the appropriate subclass. + virtual Int* asInt() { return nullptr; } + const Int* asInt() const { return const_cast(this)->asInt(); } + virtual Uint* asUint() { return nullptr; } + const Uint* asUint() const { return const_cast(this)->asUint(); } + virtual Nint* asNint() { return nullptr; } + const Nint* asNint() const { return const_cast(this)->asNint(); } + virtual Tstr* asTstr() { return nullptr; } + const Tstr* asTstr() const { return const_cast(this)->asTstr(); } + virtual Bstr* asBstr() { return nullptr; } + const Bstr* asBstr() const { return const_cast(this)->asBstr(); } + virtual Simple* asSimple() { return nullptr; } + const Simple* asSimple() const { return const_cast(this)->asSimple(); } + virtual Map* asMap() { return nullptr; } + const Map* asMap() const { return const_cast(this)->asMap(); } + virtual Array* asArray() { return nullptr; } + const Array* asArray() const { return const_cast(this)->asArray(); } + + virtual ViewTstr* asViewTstr() { return nullptr; } + const ViewTstr* asViewTstr() const { return const_cast(this)->asViewTstr(); } + virtual ViewBstr* asViewBstr() { return nullptr; } + const ViewBstr* asViewBstr() const { return const_cast(this)->asViewBstr(); } + + // Like those above, these methods safely downcast an Item when it's actually a SemanticTag. + // However, if you think you want to use these methods, you probably don't. Typically, the way + // you should handle tagged Items is by calling the appropriate method above (e.g. asInt()) + // which will return a pointer to the tagged Item, rather than the tag itself. If you want to + // find out if the Item* you're holding is to something with one or more tags applied, see + // semanticTagCount() and semanticTag() below. + virtual SemanticTag* asSemanticTag() { return nullptr; } + const SemanticTag* asSemanticTag() const { return const_cast(this)->asSemanticTag(); } + + /** + * Returns the number of semantic tags prefixed to this Item. + */ + virtual size_t semanticTagCount() const { return 0; } + + /** + * Returns the semantic tag at the specified nesting level `nesting`, iff `nesting` is less than + * the value returned by semanticTagCount(). + * + * CBOR tags are "nested" by applying them in sequence. The "rightmost" tag is the "inner" tag. + * That is, given: + * + * 4(5(6("AES"))) which encodes as C1 C2 C3 63 414553 + * + * The tstr "AES" is tagged with 6. The combined entity ("AES" tagged with 6) is tagged with 5, + * etc. So in this example, semanticTagCount() would return 3, and semanticTag(0) would return + * 5 semanticTag(1) would return 5 and semanticTag(2) would return 4. For values of n > 2, + * semanticTag(n) will return 0, but this is a meaningless value. + * + * If this layering is confusing, you probably don't have to worry about it. Nested tagging does + * not appear to be common, so semanticTag(0) is the only one you'll use. + */ + virtual uint64_t semanticTag(size_t /* nesting */ = 0) const { return 0; } + + /** + * Returns true if this is a "compound" item, i.e. one that contains one or more other items. + */ + virtual bool isCompound() const { return false; } + + bool operator==(const Item& other) const&; + bool operator!=(const Item& other) const& { return !(*this == other); } + + /** + * Returns the number of bytes required to encode this Item into CBOR. Note that if this is a + * complex Item, calling this method will require walking the whole tree. + */ + virtual size_t encodedSize() const = 0; + + /** + * Encodes the Item into buffer referenced by range [*pos, end). Returns a pointer to one past + * the last position written. Returns nullptr if there isn't enough space to encode. + */ + virtual uint8_t* encode(uint8_t* pos, const uint8_t* end) const = 0; + + /** + * Encodes the Item by passing each encoded byte to encodeCallback. + */ + virtual void encode(EncodeCallback encodeCallback) const = 0; + + /** + * Clones the Item + */ + virtual std::unique_ptr clone() const = 0; + + /** + * Encodes the Item into the provided OutputIterator. + */ + template ::iterator_category> + void encode(OutputIterator i) const { + return encode([&](uint8_t v) { *i++ = v; }); + } + + /** + * Encodes the Item into a new std::vector. + */ + std::vector encode() const { + std::vector retval; + retval.reserve(encodedSize()); + encode(std::back_inserter(retval)); + return retval; + } + + /** + * Encodes the Item into a new std::string. + */ + std::string toString() const { + std::string retval; + retval.reserve(encodedSize()); + encode([&](uint8_t v) { retval.push_back(v); }); + return retval; + } + + /** + * Encodes only the header of the Item. + */ + inline uint8_t* encodeHeader(uint64_t addlInfo, uint8_t* pos, const uint8_t* end) const { + return ::cppbor::encodeHeader(type(), addlInfo, pos, end); + } + + /** + * Encodes only the header of the Item. + */ + inline void encodeHeader(uint64_t addlInfo, EncodeCallback encodeCallback) const { + ::cppbor::encodeHeader(type(), addlInfo, encodeCallback); + } +}; + +/** + * EncodedItem represents a bit of already-encoded CBOR. Caveat emptor: It does no checking to + * ensure that the provided data is a valid encoding, cannot be meaninfully-compared with other + * kinds of items and you cannot use the as*() methods to find out what's inside it. + */ +class EncodedItem : public Item { + public: + explicit EncodedItem(std::vector value) : mValue(std::move(value)) {} + + bool operator==(const EncodedItem& other) const& { return mValue == other.mValue; } + + // Type can't be meaningfully-obtained. We could extract the type from the first byte and return + // it, but you can't do any of the normal things with an EncodedItem so there's no point. + MajorType type() const override { + assert(false); + return static_cast(-1); + } + size_t encodedSize() const override { return mValue.size(); } + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + if (end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); + } + void encode(EncodeCallback encodeCallback) const override { + std::for_each(mValue.begin(), mValue.end(), encodeCallback); + } + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + std::vector mValue; +}; + +/** + * Int is an abstraction that allows Uint and Nint objects to be manipulated without caring about + * the sign. + */ +class Int : public Item { + public: + bool operator==(const Int& other) const& { return value() == other.value(); } + + virtual int64_t value() const = 0; + using Item::asInt; + Int* asInt() override { return this; } +}; + +/** + * Uint is a concrete Item that implements CBOR major type 0. + */ +class Uint : public Int { + public: + static constexpr MajorType kMajorType = UINT; + + explicit Uint(uint64_t v) : mValue(v) {} + + bool operator==(const Uint& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asUint; + Uint* asUint() override { return this; } + + size_t encodedSize() const override { return headerSize(mValue); } + + int64_t value() const override { return mValue; } + uint64_t unsignedValue() const { return mValue; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(mValue, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue, encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + uint64_t mValue; +}; + +/** + * Nint is a concrete Item that implements CBOR major type 1. + + * Note that it is incapable of expressing the full range of major type 1 values, becaue it can only + * express values that fall into the range [std::numeric_limits::min(), -1]. It cannot + * express values in the range [std::numeric_limits::min() - 1, + * -std::numeric_limits::max()]. + */ +class Nint : public Int { + public: + static constexpr MajorType kMajorType = NINT; + + explicit Nint(int64_t v); + + bool operator==(const Nint& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asNint; + Nint* asNint() override { return this; } + size_t encodedSize() const override { return headerSize(addlInfo()); } + + int64_t value() const override { return mValue; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(addlInfo(), pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(addlInfo(), encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + uint64_t addlInfo() const { return -1ll - mValue; } + + int64_t mValue; +}; + +/** + * Bstr is a concrete Item that implements major type 2. + */ +class Bstr : public Item { + public: + static constexpr MajorType kMajorType = BSTR; + + // Construct an empty Bstr + explicit Bstr() {} + + // Construct from a vector + explicit Bstr(std::vector v) : mValue(std::move(v)) {} + + // Construct from a string + explicit Bstr(const std::string& v) + : mValue(reinterpret_cast(v.data()), + reinterpret_cast(v.data()) + v.size()) {} + + // Construct from a pointer/size pair + explicit Bstr(const std::pair& buf) + : mValue(buf.first, buf.first + buf.second) {} + + // Construct from a pair of iterators + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + explicit Bstr(const std::pair& pair) : mValue(pair.first, pair.second) {} + + // Construct from an iterator range. + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + Bstr(I1 begin, I2 end) : mValue(begin, end) {} + + bool operator==(const Bstr& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asBstr; + Bstr* asBstr() override { return this; } + size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::vector& value() const { return mValue; } + std::vector&& moveValue() { return std::move(mValue); } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::vector mValue; +}; + +/** + * ViewBstr is a read-only version of Bstr backed by std::string_view + */ +class ViewBstr : public Item { + public: + static constexpr MajorType kMajorType = BSTR; + + // Construct an empty ViewBstr + explicit ViewBstr() {} + + // Construct from a string_view of uint8_t values + explicit ViewBstr(std::basic_string_view v) : mView(std::move(v)) {} + + // Construct from a string_view + explicit ViewBstr(std::string_view v) + : mView(reinterpret_cast(v.data()), v.size()) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + ViewBstr(I1 begin, I2 end) : mView(begin, end) {} + + // Construct from a uint8_t pointer pair + ViewBstr(const uint8_t* begin, const uint8_t* end) + : mView(begin, std::distance(begin, end)) {} + + bool operator==(const ViewBstr& other) const& { return mView == other.mView; } + + MajorType type() const override { return kMajorType; } + using Item::asViewBstr; + ViewBstr* asViewBstr() override { return this; } + size_t encodedSize() const override { return headerSize(mView.size()) + mView.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mView.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::basic_string_view& view() const { return mView; } + + std::unique_ptr clone() const override { return std::make_unique(mView); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::basic_string_view mView; +}; + +/** + * Tstr is a concrete Item that implements major type 3. + */ +class Tstr : public Item { + public: + static constexpr MajorType kMajorType = TSTR; + + // Construct from a string + explicit Tstr(std::string v) : mValue(std::move(v)) {} + + // Construct from a string_view + explicit Tstr(const std::string_view& v) : mValue(v) {} + + // Construct from a C string + explicit Tstr(const char* v) : mValue(std::string(v)) {} + + // Construct from a pair of iterators + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + explicit Tstr(const std::pair& pair) : mValue(pair.first, pair.second) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + Tstr(I1 begin, I2 end) : mValue(begin, end) {} + + bool operator==(const Tstr& other) const& { return mValue == other.mValue; } + + MajorType type() const override { return kMajorType; } + using Item::asTstr; + Tstr* asTstr() override { return this; } + size_t encodedSize() const override { return headerSize(mValue.size()) + mValue.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::string& value() const { return mValue; } + std::string&& moveValue() { return std::move(mValue); } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::string mValue; +}; + +/** + * ViewTstr is a read-only version of Tstr backed by std::string_view + */ +class ViewTstr : public Item { + public: + static constexpr MajorType kMajorType = TSTR; + + // Construct an empty ViewTstr + explicit ViewTstr() {} + + // Construct from a string_view + explicit ViewTstr(std::string_view v) : mView(std::move(v)) {} + + // Construct from an iterator range + template ::iterator_category, + typename = typename std::iterator_traits::iterator_category> + ViewTstr(I1 begin, I2 end) : mView(begin, end) {} + + // Construct from a uint8_t pointer pair + ViewTstr(const uint8_t* begin, const uint8_t* end) + : mView(reinterpret_cast(begin), + std::distance(begin, end)) {} + + bool operator==(const ViewTstr& other) const& { return mView == other.mView; } + + MajorType type() const override { return kMajorType; } + using Item::asViewTstr; + ViewTstr* asViewTstr() override { return this; } + size_t encodedSize() const override { return headerSize(mView.size()) + mView.size(); } + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mView.size(), encodeCallback); + encodeValue(encodeCallback); + } + + const std::string_view& view() const { return mView; } + + std::unique_ptr clone() const override { return std::make_unique(mView); } + + private: + void encodeValue(EncodeCallback encodeCallback) const; + + std::string_view mView; +}; + +/* + * Array is a concrete Item that implements CBOR major type 4. + * + * Note that Arrays are not copyable. This is because copying them is expensive and making them + * move-only ensures that they're never copied accidentally. If you actually want to copy an Array, + * use the clone() method. + */ +class Array : public Item { + public: + static constexpr MajorType kMajorType = ARRAY; + + Array() = default; + Array(const Array& other) = delete; + Array(Array&&) = default; + Array& operator=(const Array&) = delete; + Array& operator=(Array&&) = default; + + bool operator==(const Array& other) const&; + + /** + * Construct an Array from a variable number of arguments of different types. See + * details::makeItem below for details on what types may be provided. In general, this accepts + * all of the types you'd expect and doest the things you'd expect (integral values are addes as + * Uint or Nint, std::string and char* are added as Tstr, bools are added as Bool, etc.). + */ + template + Array(Args&&... args); + + /** + * Append a single element to the Array, of any compatible type. + */ + template + Array& add(T&& v) &; + template + Array&& add(T&& v) &&; + + bool isCompound() const override { return true; } + + virtual size_t size() const { return mEntries.size(); } + + size_t encodedSize() const override { + return std::accumulate(mEntries.begin(), mEntries.end(), headerSize(size()), + [](size_t sum, auto& entry) { return sum + entry->encodedSize(); }); + } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + const std::unique_ptr& operator[](size_t index) const { return get(index); } + std::unique_ptr& operator[](size_t index) { return get(index); } + + const std::unique_ptr& get(size_t index) const { return mEntries[index]; } + std::unique_ptr& get(size_t index) { return mEntries[index]; } + + MajorType type() const override { return kMajorType; } + using Item::asArray; + Array* asArray() override { return this; } + + std::unique_ptr clone() const override; + + auto begin() { return mEntries.begin(); } + auto begin() const { return mEntries.begin(); } + auto end() { return mEntries.end(); } + auto end() const { return mEntries.end(); } + + protected: + std::vector> mEntries; +}; + +/* + * Map is a concrete Item that implements CBOR major type 5. + * + * Note that Maps are not copyable. This is because copying them is expensive and making them + * move-only ensures that they're never copied accidentally. If you actually want to copy a + * Map, use the clone() method. + */ +class Map : public Item { + public: + static constexpr MajorType kMajorType = MAP; + + using entry_type = std::pair, std::unique_ptr>; + + Map() = default; + Map(const Map& other) = delete; + Map(Map&&) = default; + Map& operator=(const Map& other) = delete; + Map& operator=(Map&&) = default; + + bool operator==(const Map& other) const&; + + /** + * Construct a Map from a variable number of arguments of different types. An even number of + * arguments must be provided (this is verified statically). See details::makeItem below for + * details on what types may be provided. In general, this accepts all of the types you'd + * expect and doest the things you'd expect (integral values are addes as Uint or Nint, + * std::string and char* are added as Tstr, bools are added as Bool, etc.). + */ + template + Map(Args&&... args); + + /** + * Append a key/value pair to the Map, of any compatible types. + */ + template + Map& add(Key&& key, Value&& value) &; + template + Map&& add(Key&& key, Value&& value) &&; + + bool isCompound() const override { return true; } + + virtual size_t size() const { return mEntries.size(); } + + size_t encodedSize() const override { + return std::accumulate( + mEntries.begin(), mEntries.end(), headerSize(size()), [](size_t sum, auto& entry) { + return sum + entry.first->encodedSize() + entry.second->encodedSize(); + }); + } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + /** + * Find and return the value associated with `key`, if any. + * + * If the searched-for `key` is not present, returns `nullptr`. + * + * Note that if the map is canonicalized (sorted), Map::get() peforms a binary search. If your + * map is large and you're searching in it many times, it may be worthwhile to canonicalize it + * to make Map::get() faster. Any use of a method that might modify the map disables the + * speedup. + */ + template + const std::unique_ptr& get(Key key) const; + + // Note that use of non-const operator[] marks the map as not canonicalized. + auto& operator[](size_t index) { + mCanonicalized = false; + return mEntries[index]; + } + const auto& operator[](size_t index) const { return mEntries[index]; } + + MajorType type() const override { return kMajorType; } + using Item::asMap; + Map* asMap() override { return this; } + + /** + * Sorts the map in canonical order, as defined in RFC 7049. Use this before encoding if you + * want canonicalization; cppbor does not canonicalize by default, though the integer encodings + * are always canonical and cppbor does not support indefinite-length encodings, so map order + * canonicalization is the only thing that needs to be done. + * + * @param recurse If set to true, canonicalize() will also walk the contents of the map and + * canonicalize any contained maps as well. + */ + Map& canonicalize(bool recurse = false) &; + Map&& canonicalize(bool recurse = false) && { + canonicalize(recurse); + return std::move(*this); + } + + bool isCanonical() { return mCanonicalized; } + + std::unique_ptr clone() const override; + + auto begin() { + mCanonicalized = false; + return mEntries.begin(); + } + auto begin() const { return mEntries.begin(); } + auto end() { + mCanonicalized = false; + return mEntries.end(); + } + auto end() const { return mEntries.end(); } + + // Returns true if a < b, per CBOR map key canonicalization rules. + static bool keyLess(const Item* a, const Item* b); + + protected: + std::vector mEntries; + + private: + bool mCanonicalized = false; +}; + +class SemanticTag : public Item { + public: + static constexpr MajorType kMajorType = SEMANTIC; + + template + SemanticTag(uint64_t tagValue, T&& taggedItem); + SemanticTag(const SemanticTag& other) = delete; + SemanticTag(SemanticTag&&) = default; + SemanticTag& operator=(const SemanticTag& other) = delete; + SemanticTag& operator=(SemanticTag&&) = default; + + bool operator==(const SemanticTag& other) const& { + return mValue == other.mValue && *mTaggedItem == *other.mTaggedItem; + } + + bool isCompound() const override { return true; } + + virtual size_t size() const { return 1; } + + // Encoding returns the tag + enclosed Item. + size_t encodedSize() const override { return headerSize(mValue) + mTaggedItem->encodedSize(); } + + using Item::encode; // Make base versions visible. + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override; + void encode(EncodeCallback encodeCallback) const override; + + // type() is a bit special. In normal usage it should return the wrapped type, but during + // parsing when we haven't yet parsed the tagged item, it needs to return SEMANTIC. + MajorType type() const override { return mTaggedItem ? mTaggedItem->type() : SEMANTIC; } + using Item::asSemanticTag; + SemanticTag* asSemanticTag() override { return this; } + + // Type information reflects the enclosed Item. Note that if the immediately-enclosed Item is + // another tag, these methods will recurse down to the non-tag Item. + using Item::asInt; + Int* asInt() override { return mTaggedItem->asInt(); } + using Item::asUint; + Uint* asUint() override { return mTaggedItem->asUint(); } + using Item::asNint; + Nint* asNint() override { return mTaggedItem->asNint(); } + using Item::asTstr; + Tstr* asTstr() override { return mTaggedItem->asTstr(); } + using Item::asBstr; + Bstr* asBstr() override { return mTaggedItem->asBstr(); } + using Item::asSimple; + Simple* asSimple() override { return mTaggedItem->asSimple(); } + using Item::asMap; + Map* asMap() override { return mTaggedItem->asMap(); } + using Item::asArray; + Array* asArray() override { return mTaggedItem->asArray(); } + using Item::asViewTstr; + ViewTstr* asViewTstr() override { return mTaggedItem->asViewTstr(); } + using Item::asViewBstr; + ViewBstr* asViewBstr() override { return mTaggedItem->asViewBstr(); } + + std::unique_ptr clone() const override; + + size_t semanticTagCount() const override; + uint64_t semanticTag(size_t nesting = 0) const override; + + protected: + SemanticTag() = default; + SemanticTag(uint64_t value) : mValue(value) {} + uint64_t mValue; + std::unique_ptr mTaggedItem; +}; + +/** + * Simple is abstract Item that implements CBOR major type 7. It is intended to be subclassed to + * create concrete Simple types. At present only Bool is provided. + */ +class Simple : public Item { + public: + static constexpr MajorType kMajorType = SIMPLE; + + bool operator==(const Simple& other) const&; + + virtual SimpleType simpleType() const = 0; + MajorType type() const override { return kMajorType; } + + Simple* asSimple() override { return this; } + + virtual const Bool* asBool() const { return nullptr; }; + virtual const Null* asNull() const { return nullptr; }; +}; + +/** + * Bool is a concrete type that implements CBOR major type 7, with additional item values for TRUE + * and FALSE. + */ +class Bool : public Simple { + public: + static constexpr SimpleType kSimpleType = BOOLEAN; + + explicit Bool(bool v) : mValue(v) {} + + bool operator==(const Bool& other) const& { return mValue == other.mValue; } + + SimpleType simpleType() const override { return kSimpleType; } + const Bool* asBool() const override { return this; } + + size_t encodedSize() const override { return 1; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(mValue ? TRUE : FALSE, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(mValue ? TRUE : FALSE, encodeCallback); + } + + bool value() const { return mValue; } + + std::unique_ptr clone() const override { return std::make_unique(mValue); } + + private: + bool mValue; +}; + +/** + * Null is a concrete type that implements CBOR major type 7, with additional item value for NULL + */ +class Null : public Simple { + public: + static constexpr SimpleType kSimpleType = NULL_T; + + explicit Null() {} + + SimpleType simpleType() const override { return kSimpleType; } + const Null* asNull() const override { return this; } + + size_t encodedSize() const override { return 1; } + + using Item::encode; + uint8_t* encode(uint8_t* pos, const uint8_t* end) const override { + return encodeHeader(NULL_V, pos, end); + } + void encode(EncodeCallback encodeCallback) const override { + encodeHeader(NULL_V, encodeCallback); + } + + std::unique_ptr clone() const override { return std::make_unique(); } +}; + +/** + * Returns pretty-printed CBOR for |item| + * + * If a byte-string is larger than |maxBStrSize| its contents will not be printed, instead the value + * of the form "" will be + * printed. Pass zero for |maxBStrSize| to disable this. + * + * The |mapKeysToNotPrint| parameter specifies the name of map values to not print. This is useful + * for unit tests. + */ +std::string prettyPrint(const Item* item, size_t maxBStrSize = 32, + const std::vector& mapKeysNotToPrint = {}); + +/** + * Returns pretty-printed CBOR for |value|. + * + * Only valid CBOR should be passed to this function. + * + * If a byte-string is larger than |maxBStrSize| its contents will not be printed, instead the value + * of the form "" will be + * printed. Pass zero for |maxBStrSize| to disable this. + * + * The |mapKeysToNotPrint| parameter specifies the name of map values to not print. This is useful + * for unit tests. + */ +std::string prettyPrint(const std::vector& encodedCbor, size_t maxBStrSize = 32, + const std::vector& mapKeysNotToPrint = {}); + +/** + * Details. Mostly you shouldn't have to look below, except perhaps at the docstring for makeItem. + */ +namespace details { + +template +struct is_iterator_pair_over : public std::false_type {}; + +template +struct is_iterator_pair_over< + std::pair, V, + typename std::enable_if_t::value_type>>> + : public std::true_type {}; + +template +struct is_unique_ptr_of_subclass_of_v : public std::false_type {}; + +template +struct is_unique_ptr_of_subclass_of_v, + typename std::enable_if_t>> + : public std::true_type {}; + +/* check if type is one of std::string (1), std::string_view (2), null-terminated char* (3) or pair + * of iterators (4)*/ +template +struct is_text_type_v : public std::false_type {}; + +template +struct is_text_type_v< + T, typename std::enable_if_t< + /* case 1 */ // + std::is_same_v>, std::string> + /* case 2 */ // + || std::is_same_v>, std::string_view> + /* case 3 */ // + || std::is_same_v>, char*> // + || std::is_same_v>, const char*> + /* case 4 */ + || details::is_iterator_pair_over::value>> : public std::true_type {}; + +/** + * Construct a unique_ptr from many argument types. Accepts: + * + * (a) booleans; + * (b) integers, all sizes and signs; + * (c) text strings, as defined by is_text_type_v above; + * (d) byte strings, as std::vector(d1), pair of iterators (d2) or pair + * (d3); and + * (e) Item subclass instances, including Array and Map. Items may be provided by naked pointer + * (e1), unique_ptr (e2), reference (e3) or value (e3). If provided by reference or value, will + * be moved if possible. If provided by pointer, ownership is taken. + * (f) null pointer; + * (g) enums, using the underlying integer value. + */ +template +std::unique_ptr makeItem(T v) { + Item* p = nullptr; + if constexpr (/* case a */ std::is_same_v) { + p = new Bool(v); + } else if constexpr (/* case b */ std::is_integral_v) { // b + if (v < 0) { + p = new Nint(v); + } else { + p = new Uint(static_cast(v)); + } + } else if constexpr (/* case c */ // + details::is_text_type_v::value) { + p = new Tstr(v); + } else if constexpr (/* case d1 */ // + std::is_same_v>, + std::vector> + /* case d2 */ // + || details::is_iterator_pair_over::value + /* case d3 */ // + || std::is_same_v>, + std::pair>) { + p = new Bstr(v); + } else if constexpr (/* case e1 */ // + std::is_pointer_v && + std::is_base_of_v>) { + p = v; + } else if constexpr (/* case e2 */ // + details::is_unique_ptr_of_subclass_of_v::value) { + p = v.release(); + } else if constexpr (/* case e3 */ // + std::is_base_of_v) { + p = new T(std::move(v)); + } else if constexpr (/* case f */ std::is_null_pointer_v) { + p = new Null(); + } else if constexpr (/* case g */ std::is_enum_v) { + return makeItem(static_cast>(v)); + } else { + // It's odd that this can't be static_assert(false), since it shouldn't be evaluated if one + // of the above ifs matches. But static_assert(false) always triggers. + static_assert(std::is_same_v, "makeItem called with unsupported type"); + } + return std::unique_ptr(p); +} + +inline void map_helper(Map& /* map */) {} + +template +inline void map_helper(Map& map, Key&& key, Value&& value, Rest&&... rest) { + map.add(std::forward(key), std::forward(value)); + map_helper(map, std::forward(rest)...); +} + +} // namespace details + +template >> || ...)>> +Array::Array(Args&&... args) { + mEntries.reserve(sizeof...(args)); + (mEntries.push_back(details::makeItem(std::forward(args))), ...); +} + +template +Array& Array::add(T&& v) & { + mEntries.push_back(details::makeItem(std::forward(v))); + return *this; +} + +template +Array&& Array::add(T&& v) && { + mEntries.push_back(details::makeItem(std::forward(v))); + return std::move(*this); +} + +template > +Map::Map(Args&&... args) { + static_assert((sizeof...(Args)) % 2 == 0, "Map must have an even number of entries"); + mEntries.reserve(sizeof...(args) / 2); + details::map_helper(*this, std::forward(args)...); +} + +template +Map& Map::add(Key&& key, Value&& value) & { + mEntries.push_back({details::makeItem(std::forward(key)), + details::makeItem(std::forward(value))}); + mCanonicalized = false; + return *this; +} + +template +Map&& Map::add(Key&& key, Value&& value) && { + this->add(std::forward(key), std::forward(value)); + return std::move(*this); +} + +static const std::unique_ptr kEmptyItemPtr; + +template || std::is_enum_v || + details::is_text_type_v::value>> +const std::unique_ptr& Map::get(Key key) const { + auto keyItem = details::makeItem(key); + + if (mCanonicalized) { + // It's sorted, so binary-search it. + auto found = std::lower_bound(begin(), end(), keyItem.get(), + [](const entry_type& entry, const Item* key) { + return keyLess(entry.first.get(), key); + }); + return (found == end() || *found->first != *keyItem) ? kEmptyItemPtr : found->second; + } else { + // Unsorted, do a linear search. + auto found = std::find_if( + begin(), end(), [&](const entry_type& entry) { return *entry.first == *keyItem; }); + return found == end() ? kEmptyItemPtr : found->second; + } +} + +template +SemanticTag::SemanticTag(uint64_t value, T&& taggedItem) + : mValue(value), mTaggedItem(details::makeItem(std::forward(taggedItem))) {} + +} // namespace cppbor diff --git a/ProvisioningTool/keymint/include/cppbor/cppbor_parse.h b/ProvisioningTool/keymint/include/cppbor/cppbor_parse.h new file mode 100644 index 00000000..22cd18d0 --- /dev/null +++ b/ProvisioningTool/keymint/include/cppbor/cppbor_parse.h @@ -0,0 +1,195 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "cppbor.h" + +namespace cppbor { + +using ParseResult = std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */>; + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, end). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +ParseResult parse(const uint8_t* begin, const uint8_t* end); + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, end). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + * + * The returned CBOR data item will contain View* items backed by + * std::string_view types over the input range. + * WARNING! If the input range changes underneath, the corresponding views will + * carry the same change. + */ +ParseResult parseWithViews(const uint8_t* begin, const uint8_t* end); + +/** + * Parse the first CBOR data item (possibly compound) from the byte vector. + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const std::vector& encoding) { + return parse(encoding.data(), encoding.data() + encoding.size()); +} + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, begin + size). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const uint8_t* begin, size_t size) { + return parse(begin, begin + size); +} + +/** + * Parse the first CBOR data item (possibly compound) from the range [begin, begin + size). + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + * + * The returned CBOR data item will contain View* items backed by + * std::string_view types over the input range. + * WARNING! If the input range changes underneath, the corresponding views will + * carry the same change. + */ +inline ParseResult parseWithViews(const uint8_t* begin, size_t size) { + return parseWithViews(begin, begin + size); +} + +/** + * Parse the first CBOR data item (possibly compound) from the value contained in a Bstr. + * + * Returns a tuple of Item pointer, buffer pointer and error message. If parsing is successful, the + * Item pointer is non-null, the buffer pointer points to the first byte after the + * successfully-parsed item and the error message string is empty. If parsing fails, the Item + * pointer is null, the buffer pointer points to the first byte that was unparseable (the first byte + * of a data item header that is malformed in some way, e.g. an invalid value, or a length that is + * too large for the remaining buffer, etc.) and the string contains an error message describing the + * problem encountered. + */ +inline ParseResult parse(const Bstr* bstr) { + if (!bstr) + return ParseResult(nullptr, nullptr, "Null Bstr pointer"); + return parse(bstr->value()); +} + +class ParseClient; + +/** + * Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the + * provided ParseClient when elements are found. + */ +void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient); + +/** + * Parse the CBOR data in the range [begin, end) in streaming fashion, calling methods on the + * provided ParseClient when elements are found. Uses the View* item types + * instead of the copying ones. + */ +void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient); + +/** + * Parse the CBOR data in the vector in streaming fashion, calling methods on the + * provided ParseClient when elements are found. + */ +inline void parse(const std::vector& encoding, ParseClient* parseClient) { + return parse(encoding.data(), encoding.data() + encoding.size(), parseClient); +} + +/** + * A pure interface that callers of the streaming parse functions must implement. + */ +class ParseClient { + public: + virtual ~ParseClient() {} + + /** + * Called when an item is found. The Item pointer points to the found item; use type() and + * the appropriate as*() method to examine the value. hdrBegin points to the first byte of the + * header, valueBegin points to the first byte of the value and end points one past the end of + * the item. In the case of header-only items, such as integers, and compound items (ARRAY, + * MAP or SEMANTIC) whose end has not yet been found, valueBegin and end are equal and point to + * the byte past the header. + * + * Note that for compound types (ARRAY, MAP, and SEMANTIC), the Item will have no content. For + * Map and Array items, the size() method will return a correct value, but the index operators + * are unsafe, and the object cannot be safely compared with another Array/Map. + * + * The method returns a ParseClient*. In most cases "return this;" will be the right answer, + * but a different ParseClient may be returned, which the parser will begin using. If the method + * returns nullptr, parsing will be aborted immediately. + */ + virtual ParseClient* item(std::unique_ptr& item, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end) = 0; + + /** + * Called when the end of a compound item (MAP or ARRAY) is found. The item argument will be + * the same one passed to the item() call -- and may be empty if item() moved its value out. + * hdrBegin, valueBegin and end point to the beginning of the item header, the beginning of the + * first contained value, and one past the end of the last contained value, respectively. + * + * Note that the Item will have no content. + * + * As with item(), itemEnd() can change the ParseClient by returning a different one, or end the + * parsing by returning nullptr; + */ + virtual ParseClient* itemEnd(std::unique_ptr& item, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end) = 0; + + /** + * Called when parsing encounters an error. position is set to the first unparsed byte (one + * past the last successfully-parsed byte) and errorMessage contains an message explaining what + * sort of error occurred. + */ + virtual void error(const uint8_t* position, const std::string& errorMessage) = 0; +}; + +} // namespace cppbor diff --git a/ProvisioningTool/keymint/include/cppcose/cppcose.h b/ProvisioningTool/keymint/include/cppcose/cppcose.h new file mode 100644 index 00000000..09a2d76f --- /dev/null +++ b/ProvisioningTool/keymint/include/cppcose/cppcose.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace cppcose { + +template class ErrMsgOr; +using bytevec = std::vector; + +constexpr int kCoseSign1EntryCount = 4; +constexpr int kCoseSign1ProtectedParams = 0; +constexpr int kCoseSign1UnprotectedParams = 1; +constexpr int kCoseSign1Payload = 2; +constexpr int kCoseSign1Signature = 3; + +constexpr int kCoseMac0EntryCount = 4; +constexpr int kCoseMac0ProtectedParams = 0; +constexpr int kCoseMac0UnprotectedParams = 1; +constexpr int kCoseMac0Payload = 2; +constexpr int kCoseMac0Tag = 3; + +constexpr int kCoseEncryptEntryCount = 4; +constexpr int kCoseEncryptProtectedParams = 0; +constexpr int kCoseEncryptUnprotectedParams = 1; +constexpr int kCoseEncryptPayload = 2; +constexpr int kCoseEncryptRecipients = 3; + +enum Label : int { + ALGORITHM = 1, + KEY_ID = 4, + IV = 5, + COSE_KEY = -1, +}; + +enum CoseKeyAlgorithm : int { + AES_GCM_256 = 3, + HMAC_256 = 5, + ES256 = -7, // ECDSA with SHA-256 + EDDSA = -8, + ECDH_ES_HKDF_256 = -25, +}; + +enum CoseKeyCurve : int { P256 = 1, X25519 = 4, ED25519 = 6 }; +enum CoseKeyType : int { OCTET_KEY_PAIR = 1, EC2 = 2, SYMMETRIC_KEY = 4 }; +enum CoseKeyOps : int { SIGN = 1, VERIFY = 2, ENCRYPT = 3, DECRYPT = 4 }; + +constexpr int kAesGcmNonceLength = 12; +constexpr int kAesGcmTagSize = 16; +constexpr int kAesGcmKeySize = 32; +constexpr int kAesGcmKeySizeBits = 256; + +template class ErrMsgOr { + public: + ErrMsgOr(std::string errMsg) // NOLINT(google-explicit-constructor) + : errMsg_(std::move(errMsg)) {} + ErrMsgOr(const char* errMsg) // NOLINT(google-explicit-constructor) + : errMsg_(errMsg) {} + ErrMsgOr(T val) // NOLINT(google-explicit-constructor) + : value_(std::move(val)) {} + + explicit operator bool() const { return value_.has_value(); } + + T* operator->() & { + assert(value_); + return &value_.value(); + } + T& operator*() & { + assert(value_); + return value_.value(); + }; + T&& operator*() && { + assert(value_); + return std::move(value_).value(); + }; + + const std::string& message() { return errMsg_; } + std::string moveMessage() { return std::move(errMsg_); } + + T moveValue() { + assert(value_); + return std::move(value_).value(); + } + + private: + std::string errMsg_; + std::optional value_; +}; + +class CoseKey { + public: + CoseKey() {} + CoseKey(const CoseKey&) = delete; + CoseKey(CoseKey&&) = default; + + enum Label : int { + KEY_TYPE = 1, + KEY_ID = 2, + ALGORITHM = 3, + KEY_OPS = 4, + CURVE = -1, + PUBKEY_X = -2, + PUBKEY_Y = -3, + PRIVATE_KEY = -4, + TEST_KEY = -70000 // Application-defined + }; + + static ErrMsgOr parse(const bytevec& coseKey) { + auto [parsedKey, _, errMsg] = cppbor::parse(coseKey); + if (!parsedKey) return errMsg + " when parsing key"; + if (!parsedKey->asMap()) return "CoseKey must be a map"; + return CoseKey(static_cast(parsedKey.release())); + } + + static ErrMsgOr parse(const bytevec& coseKey, CoseKeyType expectedKeyType, + CoseKeyAlgorithm expectedAlgorithm, CoseKeyCurve expectedCurve) { + auto key = parse(coseKey); + if (!key) return key; + + if (!key->checkIntValue(CoseKey::KEY_TYPE, expectedKeyType) || + !key->checkIntValue(CoseKey::ALGORITHM, expectedAlgorithm) || + !key->checkIntValue(CoseKey::CURVE, expectedCurve)) { + return "Unexpected key type:"; + } + + return key; + } + static ErrMsgOr parseP256(const bytevec& coseKey) { + auto key = parse(coseKey, EC2, ES256, P256); + if (!key) return key; + + auto& pubkey_x = key->getMap().get(PUBKEY_X); + auto& pubkey_y = key->getMap().get(PUBKEY_Y); + if (!pubkey_x || !pubkey_y || !pubkey_x->asBstr() || !pubkey_y->asBstr() || + pubkey_x->asBstr()->value().size() != 32 || pubkey_y->asBstr()->value().size() != 32) { + return "Invalid P256 public key"; + } + + return key; + } + + std::optional getIntValue(Label label) { + const auto& value = key_->get(label); + if (!value || !value->asInt()) return {}; + return value->asInt()->value(); + } + + std::optional getBstrValue(Label label) { + const auto& value = key_->get(label); + if (!value || !value->asBstr()) return {}; + return value->asBstr()->value(); + } + + const cppbor::Map& getMap() const { return *key_; } + cppbor::Map&& moveMap() { return std::move(*key_); } + + bool checkIntValue(Label label, int expectedValue) { + const auto& value = key_->get(label); + return value && value->asInt() && value->asInt()->value() == expectedValue; + } + + void add(Label label, int value) { key_->add(label, value); } + void add(Label label, bytevec value) { key_->add(label, std::move(value)); } + + bytevec encode() { return key_->canonicalize().encode(); } + + private: + explicit CoseKey(cppbor::Map* parsedKey) : key_(parsedKey) {} + + // This is the full parsed key structure. + std::unique_ptr key_; +}; + +ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, + const bytevec& payload, const bytevec& aad); +ErrMsgOr constructCoseSign1(const bytevec& key, const bytevec& payload, + const bytevec& aad); +ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, + const bytevec& payload, const bytevec& aad); +/** + * Verify and parse a COSE_Sign1 message, returning the payload. + * + * @param ignoreSignature indicates whether signature verification should be skipped. If true, no + * verification of the signature will be done. + * + * @param coseSign1 is the COSE_Sign1 to verify and parse. + * + * @param signingCoseKey is a CBOR-encoded COSE_Key to use to verify the signature. The bytevec may + * be empty, in which case the function assumes that coseSign1's payload is the COSE_Key to + * use, i.e. that coseSign1 is a self-signed "certificate". + */ +ErrMsgOr verifyAndParseCoseSign1(bool ignoreSignature, + const cppbor::Array* coseSign1, + const bytevec& signingCoseKey, + const bytevec& aad); +} // namespace cppcose diff --git a/ProvisioningTool/keymint/include/json/assertions.h b/ProvisioningTool/keymint/include/json/assertions.h new file mode 100644 index 00000000..fbec7ae0 --- /dev/null +++ b/ProvisioningTool/keymint/include/json/assertions.h @@ -0,0 +1,54 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_ASSERTIONS_H_INCLUDED +#define CPPTL_JSON_ASSERTIONS_H_INCLUDED + +#include +#include + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +/** It should not be possible for a maliciously designed file to + * cause an abort() or seg-fault, so these macros are used only + * for pre-condition violations and internal logic errors. + */ +#if JSON_USE_EXCEPTION + +// @todo <= add detail about condition in exception +# define JSON_ASSERT(condition) \ + {if (!(condition)) {Json::throwLogicError( "assert json failed" );}} + +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + Json::throwLogicError(oss.str()); \ + abort(); \ + } + +#else // JSON_USE_EXCEPTION + +# define JSON_ASSERT(condition) assert(condition) + +// The call to assert() will show the failure message in debug builds. In +// release builds we abort, for a core-dump or debugger. +# define JSON_FAIL_MESSAGE(message) \ + { \ + std::ostringstream oss; oss << message; \ + assert(false && oss.str().c_str()); \ + abort(); \ + } + + +#endif + +#define JSON_ASSERT_MESSAGE(condition, message) \ + if (!(condition)) { \ + JSON_FAIL_MESSAGE(message); \ + } + +#endif // CPPTL_JSON_ASSERTIONS_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/autolink.h b/ProvisioningTool/keymint/include/json/autolink.h new file mode 100644 index 00000000..6fcc8afa --- /dev/null +++ b/ProvisioningTool/keymint/include/json/autolink.h @@ -0,0 +1,25 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_AUTOLINK_H_INCLUDED +#define JSON_AUTOLINK_H_INCLUDED + +#include "config.h" + +#ifdef JSON_IN_CPPTL +#include +#endif + +#if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && \ + !defined(JSON_IN_CPPTL) +#define CPPTL_AUTOLINK_NAME "json" +#undef CPPTL_AUTOLINK_DLL +#ifdef JSON_DLL +#define CPPTL_AUTOLINK_DLL +#endif +#include "autolink.h" +#endif + +#endif // JSON_AUTOLINK_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/config.h b/ProvisioningTool/keymint/include/json/config.h new file mode 100644 index 00000000..5ca32281 --- /dev/null +++ b/ProvisioningTool/keymint/include/json/config.h @@ -0,0 +1,119 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_CONFIG_H_INCLUDED +#define JSON_CONFIG_H_INCLUDED + +/// If defined, indicates that json library is embedded in CppTL library. +//# define JSON_IN_CPPTL 1 + +/// If defined, indicates that json may leverage CppTL library +//# define JSON_USE_CPPTL 1 +/// If defined, indicates that cpptl vector based map should be used instead of +/// std::map +/// as Value container. +//# define JSON_USE_CPPTL_SMALLMAP 1 + +// If non-zero, the library uses exceptions to report bad input instead of C +// assertion macros. The default is to use exceptions. +#ifndef JSON_USE_EXCEPTION +#define JSON_USE_EXCEPTION 1 +#endif + +/// If defined, indicates that the source file is amalgated +/// to prevent private header inclusion. +/// Remarks: it is automatically defined in the generated amalgated header. +// #define JSON_IS_AMALGAMATION + +#ifdef JSON_IN_CPPTL +#include +#ifndef JSON_USE_CPPTL +#define JSON_USE_CPPTL 1 +#endif +#endif + +#ifdef JSON_IN_CPPTL +#define JSON_API CPPTL_API +#elif defined(JSON_DLL_BUILD) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllexport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#elif defined(JSON_DLL) +#if defined(_MSC_VER) +#define JSON_API __declspec(dllimport) +#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING +#endif // if defined(_MSC_VER) +#endif // ifdef JSON_IN_CPPTL +#if !defined(JSON_API) +#define JSON_API +#endif + +#if !defined(JSON_HAS_UNIQUE_PTR) +#if __cplusplus >= 201103L +#define JSON_HAS_UNIQUE_PTR (1) +#elif _MSC_VER >= 1600 +#define JSON_HAS_UNIQUE_PTR (1) +#else +#define JSON_HAS_UNIQUE_PTR (0) +#endif +#endif + +// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for +// integer +// Storages, and 64 bits integer support is disabled. +// #define JSON_NO_INT64 1 + +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) +#endif + +#if !defined(JSONCPP_DEPRECATED) +#define JSONCPP_DEPRECATED(message) +#endif // if !defined(JSONCPP_DEPRECATED) + +namespace Json { +typedef int Int; +typedef unsigned int UInt; +#if defined(JSON_NO_INT64) +typedef int LargestInt; +typedef unsigned int LargestUInt; +#undef JSON_HAS_INT64 +#else // if defined(JSON_NO_INT64) +// For Microsoft Visual use specific types as long long is not supported +#if defined(_MSC_VER) // Microsoft Visual Studio +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#else // if defined(_MSC_VER) // Other platforms, use long long +typedef long long int Int64; +typedef unsigned long long int UInt64; +#endif // if defined(_MSC_VER) +typedef Int64 LargestInt; +typedef UInt64 LargestUInt; +#define JSON_HAS_INT64 +#endif // if defined(JSON_NO_INT64) +} // end namespace Json + +#endif // JSON_CONFIG_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/features.h b/ProvisioningTool/keymint/include/json/features.h new file mode 100644 index 00000000..78135478 --- /dev/null +++ b/ProvisioningTool/keymint/include/json/features.h @@ -0,0 +1,51 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_FEATURES_H_INCLUDED +#define CPPTL_JSON_FEATURES_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +/** \brief Configuration passed to reader and writer. + * This configuration object can be used to force the Reader or Writer + * to behave in a standard conforming way. + */ +class JSON_API Features { +public: + /** \brief A configuration that allows all features and assumes all strings + * are UTF-8. + * - C & C++ comments are allowed + * - Root object can be any JSON value + * - Assumes Value strings are encoded in UTF-8 + */ + static Features all(); + + /** \brief A configuration that is strictly compatible with the JSON + * specification. + * - Comments are forbidden. + * - Root object must be either an array or an object value. + * - Assumes Value strings are encoded in UTF-8 + */ + static Features strictMode(); + + /** \brief Initialize the configuration like JsonConfig::allFeatures; + */ + Features(); + + /// \c true if comments are allowed. Default: \c true. + bool allowComments_; + + /// \c true if root must be either an array or an object value. Default: \c + /// false. + bool strictRoot_; +}; + +} // namespace Json + +#endif // CPPTL_JSON_FEATURES_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/forwards.h b/ProvisioningTool/keymint/include/json/forwards.h new file mode 100644 index 00000000..ccfe09ab --- /dev/null +++ b/ProvisioningTool/keymint/include/json/forwards.h @@ -0,0 +1,37 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_FORWARDS_H_INCLUDED +#define JSON_FORWARDS_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "config.h" +#endif // if !defined(JSON_IS_AMALGAMATION) + +namespace Json { + +// writer.h +class FastWriter; +class StyledWriter; + +// reader.h +class Reader; + +// features.h +class Features; + +// value.h +typedef unsigned int ArrayIndex; +class StaticString; +class Path; +class PathArgument; +class Value; +class ValueIteratorBase; +class ValueIterator; +class ValueConstIterator; + +} // namespace Json + +#endif // JSON_FORWARDS_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/json.h b/ProvisioningTool/keymint/include/json/json.h new file mode 100644 index 00000000..8f10ac2b --- /dev/null +++ b/ProvisioningTool/keymint/include/json/json.h @@ -0,0 +1,15 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_JSON_H_INCLUDED +#define JSON_JSON_H_INCLUDED + +#include "autolink.h" +#include "value.h" +#include "reader.h" +#include "writer.h" +#include "features.h" + +#endif // JSON_JSON_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/reader.h b/ProvisioningTool/keymint/include/json/reader.h new file mode 100644 index 00000000..9c9923a5 --- /dev/null +++ b/ProvisioningTool/keymint/include/json/reader.h @@ -0,0 +1,360 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_READER_H_INCLUDED +#define CPPTL_JSON_READER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "features.h" +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +/** \brief Unserialize a JSON document into a + *Value. + * + * \deprecated Use CharReader and CharReaderBuilder. + */ +class JSON_API Reader { +public: + typedef char Char; + typedef const Char* Location; + + /** \brief Constructs a Reader allowing all features + * for parsing. + */ + Reader(); + + /** \brief Constructs a Reader allowing the specified feature set + * for parsing. + */ + Reader(const Features& features); + + /** \brief Read a Value from a JSON + * document. + * \param document UTF-8 encoded string containing the document to read. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + * back during + * serialization, \c false to discard comments. + * This parameter is ignored if + * Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + * error occurred. + */ + bool + parse(const std::string& document, Value& root, bool collectComments = true); + + /** \brief Read a Value from a JSON + document. + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param collectComments \c true to collect comment and allow writing them + back during + * serialization, \c false to discard comments. + * This parameter is ignored if + Features::allowComments_ + * is \c false. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + + /// \brief Parse from input stream. + /// \see Json::operator>>(std::istream&, Json::Value&). + bool parse(std::istream& is, Value& root, bool collectComments = true); + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + * \deprecated Use getFormattedErrorMessages() instead (typo fix). + */ + JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.") + std::string getFormatedErrorMessages() const; + + /** \brief Returns a user friendly string that list errors in the parsed + * document. + * \return Formatted error message with the list of errors with their location + * in + * the parsed document. An empty string is returned if no error + * occurred + * during parsing. + */ + std::string getFormattedErrorMessages() const; + +private: + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + std::string message_; + Location extra_; + }; + + typedef std::deque Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + void readNumber(); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, std::string& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + bool decodeUnicodeCodePoint(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool decodeUnicodeEscapeSequence(Token& token, + Location& current, + Location end, + unsigned int& unicode); + bool addError(const std::string& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const std::string& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + std::string getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + typedef std::stack Nodes; + Nodes nodes_; + Errors errors_; + std::string document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + std::string commentsBefore_; + Features features_; + bool collectComments_; +}; // Reader + +/** Interface for reading JSON from a char array. + */ +class JSON_API CharReader { +public: + virtual ~CharReader() {} + /** \brief Read a Value from a JSON + document. + * The document must be a UTF-8 encoded string containing the document to read. + * + * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the + document to read. + * \param endDoc Pointer on the end of the UTF-8 encoded string of the + document to read. + * Must be >= beginDoc. + * \param root [out] Contains the root value of the document if it was + * successfully parsed. + * \param errs [out] Formatted error messages (if not NULL) + * a user friendly string that lists errors in the parsed + * document. + * \return \c true if the document was successfully parsed, \c false if an + error occurred. + */ + virtual bool parse( + char const* beginDoc, char const* endDoc, + Value* root, std::string* errs) = 0; + + class Factory { + public: + virtual ~Factory() {} + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual CharReader* newCharReader() const = 0; + }; // Factory +}; // CharReader + +/** \brief Build a CharReader implementation. + +Usage: +\code + using namespace Json; + CharReaderBuilder builder; + builder["collectComments"] = false; + Value value; + std::string errs; + bool ok = parseFromStream(builder, std::cin, &value, &errs); +\endcode +*/ +class JSON_API CharReaderBuilder : public CharReader::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + These are case-sensitive. + Available settings (case-sensitive): + - `"collectComments": false or true` + - true to collect comment and allow writing them + back during serialization, false to discard comments. + This parameter is ignored if allowComments is false. + - `"allowComments": false or true` + - true if comments are allowed. + - `"strictRoot": false or true` + - true if root must be either an array or an object value + - `"allowDroppedNullPlaceholders": false or true` + - true if dropped null placeholders are allowed. (See StreamWriterBuilder.) + - `"allowNumericKeys": false or true` + - true if numeric object keys are allowed. + - `"allowSingleQuotes": false or true` + - true if '' are allowed for strings (both keys and values) + - `"stackLimit": integer` + - Exceeding stackLimit (recursive depth of `readValue()`) will + cause an exception. + - This is a security issue (seg-faults caused by deeply nested JSON), + so the default is low. + - `"failIfExtra": false or true` + - If true, `parse()` returns false when extra non-whitespace trails + the JSON value in the input string. + - `"rejectDupKeys": false or true` + - If true, `parse()` returns false when a key is duplicated within an object. + - `"allowSpecialFloats": false or true` + - If true, special float values (NaNs and infinities) are allowed + and their values are lossfree restorable. + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + CharReaderBuilder(); + virtual ~CharReaderBuilder(); + + virtual CharReader* newCharReader() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults + */ + static void setDefaults(Json::Value* settings); + /** Same as old Features::strictMode(). + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode + */ + static void strictMode(Json::Value* settings); +}; + +/** Consume entire stream and use its begin/end. + * Someday we might have a real StreamReader, but for now this + * is convenient. + */ +bool JSON_API parseFromStream( + CharReader::Factory const&, + std::istream&, + Value* root, std::string* errs); + +/** \brief Read from 'sin' into 'root'. + + Always keep comments from the input JSON. + + This can be used to read a file into a particular sub-object. + For example: + \code + Json::Value root; + cin >> root["dir"]["file"]; + cout << root; + \endcode + Result: + \verbatim + { + "dir": { + "file": { + // The input stream JSON would be nested here. + } + } + } + \endverbatim + \throw std::exception on parse error. + \see Json::operator<<() +*/ +JSON_API std::istream& operator>>(std::istream&, Value&); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_READER_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/value.h b/ProvisioningTool/keymint/include/json/value.h new file mode 100644 index 00000000..66433f88 --- /dev/null +++ b/ProvisioningTool/keymint/include/json/value.h @@ -0,0 +1,850 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef CPPTL_JSON_H_INCLUDED +#define CPPTL_JSON_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "forwards.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +#ifndef JSON_USE_CPPTL_SMALLMAP +#include +#else +#include +#endif +#ifdef JSON_USE_CPPTL +#include +#endif + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +//Conditional NORETURN attribute on the throw functions would: +// a) suppress false positives from static code analysis +// b) possibly improve optimization opportunities. +#if !defined(JSONCPP_NORETURN) +# if defined(_MSC_VER) +# define JSONCPP_NORETURN __declspec(noreturn) +# elif defined(__GNUC__) +# define JSONCPP_NORETURN __attribute__ ((__noreturn__)) +# else +# define JSONCPP_NORETURN +# endif +#endif + +/** \brief JSON (JavaScript Object Notation). + */ +namespace Json { + +/** Base class for all exceptions we throw. + * + * We use nothing but these internally. Of course, STL can throw others. + */ +class JSON_API Exception : public std::exception { +public: + Exception(std::string const& msg); + virtual ~Exception() throw(); + virtual char const* what() const throw(); +protected: + std::string const msg_; +}; + +/** Exceptions which the user cannot easily avoid. + * + * E.g. out-of-memory (when we use malloc), stack-overflow, malicious input + * + * \remark derived from Json::Exception + */ +class JSON_API RuntimeError : public Exception { +public: + RuntimeError(std::string const& msg); +}; + +/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros. + * + * These are precondition-violations (user bugs) and internal errors (our bugs). + * + * \remark derived from Json::Exception + */ +class JSON_API LogicError : public Exception { +public: + LogicError(std::string const& msg); +}; + +/// used internally +JSONCPP_NORETURN void throwRuntimeError(std::string const& msg); +/// used internally +JSONCPP_NORETURN void throwLogicError(std::string const& msg); + +/** \brief Type of the value held by a Value object. + */ +enum ValueType { + nullValue = 0, ///< 'null' value + intValue, ///< signed integer value + uintValue, ///< unsigned integer value + realValue, ///< double value + stringValue, ///< UTF-8 string value + booleanValue, ///< bool value + arrayValue, ///< array value (ordered list) + objectValue ///< object value (collection of name/value pairs). +}; + +enum CommentPlacement { + commentBefore = 0, ///< a comment placed on the line before a value + commentAfterOnSameLine, ///< a comment just after a value on the same line + commentAfter, ///< a comment on the line after a value (only make sense for + /// root value) + numberOfCommentPlacement +}; + +//# ifdef JSON_USE_CPPTL +// typedef CppTL::AnyEnumerator EnumMemberNames; +// typedef CppTL::AnyEnumerator EnumValues; +//# endif + +/** \brief Lightweight wrapper to tag static string. + * + * Value constructor and objectValue member assignement takes advantage of the + * StaticString and avoid the cost of string duplication when storing the + * string or the member name. + * + * Example of usage: + * \code + * Json::Value aValue( StaticString("some text") ); + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ +class JSON_API StaticString { +public: + explicit StaticString(const char* czstring) : c_str_(czstring) {} + + operator const char*() const { return c_str_; } + + const char* c_str() const { return c_str_; } + +private: + const char* c_str_; +}; + +/** \brief Represents a JSON value. + * + * This class is a discriminated union wrapper that can represents a: + * - signed integer [range: Value::minInt - Value::maxInt] + * - unsigned integer (range: 0 - Value::maxUInt) + * - double + * - UTF-8 string + * - boolean + * - 'null' + * - an ordered list of Value + * - collection of name/value pairs (javascript object) + * + * The type of the held value is represented by a #ValueType and + * can be obtained using type(). + * + * Values of an #objectValue or #arrayValue can be accessed using operator[]() + * methods. + * Non-const methods will automatically create the a #nullValue element + * if it does not exist. + * The sequence of an #arrayValue will be automatically resized and initialized + * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue. + * + * The get() methods can be used to obtain default value in the case the + * required element does not exist. + * + * It is possible to iterate over the list of a #objectValue values using + * the getMemberNames() method. + * + * \note #Value string-length fit in size_t, but keys must be < 2^30. + * (The reason is an implementation detail.) A #CharReader will raise an + * exception if a bound is exceeded to avoid security holes in your app, + * but the Value API does *not* check bounds. That is the responsibility + * of the caller. + */ +class JSON_API Value { + friend class ValueIteratorBase; +public: + typedef std::vector Members; + typedef ValueIterator iterator; + typedef ValueConstIterator const_iterator; + typedef Json::UInt UInt; + typedef Json::Int Int; +#if defined(JSON_HAS_INT64) + typedef Json::UInt64 UInt64; + typedef Json::Int64 Int64; +#endif // defined(JSON_HAS_INT64) + typedef Json::LargestInt LargestInt; + typedef Json::LargestUInt LargestUInt; + typedef Json::ArrayIndex ArrayIndex; + + static const Value& nullRef; +#if !defined(__ARMEL__) + /// \deprecated This exists for binary compatibility only. Use nullRef. + static const Value null; +#endif + /// Minimum signed integer value that can be stored in a Json::Value. + static const LargestInt minLargestInt; + /// Maximum signed integer value that can be stored in a Json::Value. + static const LargestInt maxLargestInt; + /// Maximum unsigned integer value that can be stored in a Json::Value. + static const LargestUInt maxLargestUInt; + + /// Minimum signed int value that can be stored in a Json::Value. + static const Int minInt; + /// Maximum signed int value that can be stored in a Json::Value. + static const Int maxInt; + /// Maximum unsigned int value that can be stored in a Json::Value. + static const UInt maxUInt; + +#if defined(JSON_HAS_INT64) + /// Minimum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 minInt64; + /// Maximum signed 64 bits int value that can be stored in a Json::Value. + static const Int64 maxInt64; + /// Maximum unsigned 64 bits int value that can be stored in a Json::Value. + static const UInt64 maxUInt64; +#endif // defined(JSON_HAS_INT64) + +//MW: workaround for bug in NVIDIAs CUDA 7.5 nvcc compiler +#ifdef __NVCC__ +public: +#else +private: +#endif //__NVCC__ +#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + class CZString { + public: + enum DuplicationPolicy { + noDuplication = 0, + duplicate, + duplicateOnCopy + }; + CZString(ArrayIndex index); + CZString(char const* str, unsigned length, DuplicationPolicy allocate); + CZString(CZString const& other); + ~CZString(); + CZString& operator=(CZString other); + bool operator<(CZString const& other) const; + bool operator==(CZString const& other) const; + ArrayIndex index() const; + //const char* c_str() const; ///< \deprecated + char const* data() const; + unsigned length() const; + bool isStaticString() const; + + private: + void swap(CZString& other); + + struct StringStorage { + unsigned policy_: 2; + unsigned length_: 30; // 1GB max + }; + + char const* cstr_; // actually, a prefixed string, unless policy is noDup + union { + ArrayIndex index_; + StringStorage storage_; + }; + }; + +public: +#ifndef JSON_USE_CPPTL_SMALLMAP + typedef std::map ObjectValues; +#else + typedef CppTL::SmallMap ObjectValues; +#endif // ifndef JSON_USE_CPPTL_SMALLMAP +#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION + +public: + /** \brief Create a default Value of the given type. + + This is a very useful constructor. + To create an empty array, pass arrayValue. + To create an empty object, pass objectValue. + Another Value can then be set to this one by assignment. +This is useful since clear() and resize() will not alter types. + + Examples: +\code +Json::Value null_value; // null +Json::Value arr_value(Json::arrayValue); // [] +Json::Value obj_value(Json::objectValue); // {} +\endcode + */ + Value(ValueType type = nullValue); + Value(Int value); + Value(UInt value); +#if defined(JSON_HAS_INT64) + Value(Int64 value); + Value(UInt64 value); +#endif // if defined(JSON_HAS_INT64) + Value(double value); + Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.) + Value(const char* begin, const char* end); ///< Copy all, incl zeroes. + /** \brief Constructs a value from a static string. + + * Like other value string constructor but do not duplicate the string for + * internal storage. The given string must remain alive after the call to this + * constructor. + * \note This works only for null-terminated strings. (We cannot change the + * size of this class, so we have nowhere to store the length, + * which might be computed later for various operations.) + * + * Example of usage: + * \code + * static StaticString foo("some text"); + * Json::Value aValue(foo); + * \endcode + */ + Value(const StaticString& value); + Value(const std::string& value); ///< Copy data() til size(). Embedded zeroes too. +#ifdef JSON_USE_CPPTL + Value(const CppTL::ConstString& value); +#endif + Value(bool value); + /// Deep copy. + Value(const Value& other); + ~Value(); + + /// Deep copy, then swap(other). + /// \note Over-write existing comments. To preserve comments, use #swapPayload(). + Value &operator=(const Value &other); + /// Swap everything. + void swap(Value& other); + /// Swap values but leave comments and source offsets in place. + void swapPayload(Value& other); + + ValueType type() const; + + /// Compare payload only, not comments etc. + bool operator<(const Value& other) const; + bool operator<=(const Value& other) const; + bool operator>=(const Value& other) const; + bool operator>(const Value& other) const; + bool operator==(const Value& other) const; + bool operator!=(const Value& other) const; + int compare(const Value& other) const; + + const char* asCString() const; ///< Embedded zeroes could cause you trouble! + std::string asString() const; ///< Embedded zeroes are possible. + /** Get raw char* of string-value. + * \return false if !string. (Seg-fault if str or end are NULL.) + */ + bool getString( + char const** begin, char const** end) const; +#ifdef JSON_USE_CPPTL + CppTL::ConstString asConstString() const; +#endif + Int asInt() const; + UInt asUInt() const; +#if defined(JSON_HAS_INT64) + Int64 asInt64() const; + UInt64 asUInt64() const; +#endif // if defined(JSON_HAS_INT64) + LargestInt asLargestInt() const; + LargestUInt asLargestUInt() const; + float asFloat() const; + double asDouble() const; + bool asBool() const; + + bool isNull() const; + bool isBool() const; + bool isInt() const; + bool isInt64() const; + bool isUInt() const; + bool isUInt64() const; + bool isIntegral() const; + bool isDouble() const; + bool isNumeric() const; + bool isString() const; + bool isArray() const; + bool isObject() const; + + bool isConvertibleTo(ValueType other) const; + + /// Number of values in array or object + ArrayIndex size() const; + + /// \brief Return true if empty array, empty object, or null; + /// otherwise, false. + bool empty() const; + + /// Return isNull() + bool operator!() const; + + /// Remove all object members and array elements. + /// \pre type() is arrayValue, objectValue, or nullValue + /// \post type() is unchanged + void clear(); + + /// Resize the array to size elements. + /// New elements are initialized to null. + /// May only be called on nullValue or arrayValue. + /// \pre type() is arrayValue or nullValue + /// \post type() is arrayValue + void resize(ArrayIndex size); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](ArrayIndex index); + + /// Access an array element (zero based index ). + /// If the array contains less than index element, then null value are + /// inserted + /// in the array so that its size is index+1. + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + Value& operator[](int index); + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](ArrayIndex index) const; + + /// Access an array element (zero based index ) + /// (You may need to say 'value[0u]' to get your compiler to distinguish + /// this from the operator[] which takes a string.) + const Value& operator[](int index) const; + + /// If the array contains at least index+1 elements, returns the element + /// value, + /// otherwise returns defaultValue. + Value get(ArrayIndex index, const Value& defaultValue) const; + /// Return true if index < size(). + bool isValidIndex(ArrayIndex index) const; + /// \brief Append value to array at the end. + /// + /// Equivalent to jsonvalue[jsonvalue.size()] = value; + Value& append(const Value& value); + + /// Access an object value by name, create a null member if it does not exist. + /// \note Because of our implementation, keys are limited to 2^30 -1 chars. + /// Exceeding that will cause an exception. + Value& operator[](const char* key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const char* key) const; + /// Access an object value by name, create a null member if it does not exist. + /// \param key may contain embedded nulls. + Value& operator[](const std::string& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + /// \param key may contain embedded nulls. + const Value& operator[](const std::string& key) const; + /** \brief Access an object value by name, create a null member if it does not + exist. + + * If the object has no entry for that name, then the member name used to store + * the new entry is not duplicated. + * Example of use: + * \code + * Json::Value object; + * static const StaticString code("code"); + * object[code] = 1234; + * \endcode + */ + Value& operator[](const StaticString& key); +#ifdef JSON_USE_CPPTL + /// Access an object value by name, create a null member if it does not exist. + Value& operator[](const CppTL::ConstString& key); + /// Access an object value by name, returns null if there is no member with + /// that name. + const Value& operator[](const CppTL::ConstString& key) const; +#endif + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const char* key, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \note key may contain embedded nulls. + Value get(const char* begin, const char* end, const Value& defaultValue) const; + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + /// \param key may contain embedded nulls. + Value get(const std::string& key, const Value& defaultValue) const; +#ifdef JSON_USE_CPPTL + /// Return the member named key if it exist, defaultValue otherwise. + /// \note deep copy + Value get(const CppTL::ConstString& key, const Value& defaultValue) const; +#endif + /// Most general and efficient version of isMember()const, get()const, + /// and operator[]const + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + Value const* find(char const* begin, char const* end) const; + /// Most general and efficient version of object-mutators. + /// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30 + /// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue. + Value const* demand(char const* begin, char const* end); + /// \brief Remove and return the named member. + /// + /// Do nothing if it did not exist. + /// \return the removed Value, or null. + /// \pre type() is objectValue or nullValue + /// \post type() is unchanged + /// \deprecated + Value removeMember(const char* key); + /// Same as removeMember(const char*) + /// \param key may contain embedded nulls. + /// \deprecated + Value removeMember(const std::string& key); + /// Same as removeMember(const char* begin, const char* end, Value* removed), + /// but 'key' is null-terminated. + bool removeMember(const char* key, Value* removed); + /** \brief Remove the named map member. + + Update 'removed' iff removed. + \param key may contain embedded nulls. + \return true iff removed (no exceptions) + */ + bool removeMember(std::string const& key, Value* removed); + /// Same as removeMember(std::string const& key, Value* removed) + bool removeMember(const char* begin, const char* end, Value* removed); + /** \brief Remove the indexed array element. + + O(n) expensive operations. + Update 'removed' iff removed. + \return true iff removed (no exceptions) + */ + bool removeIndex(ArrayIndex i, Value* removed); + + /// Return true if the object has a member named key. + /// \note 'key' must be null-terminated. + bool isMember(const char* key) const; + /// Return true if the object has a member named key. + /// \param key may contain embedded nulls. + bool isMember(const std::string& key) const; + /// Same as isMember(std::string const& key)const + bool isMember(const char* begin, const char* end) const; +#ifdef JSON_USE_CPPTL + /// Return true if the object has a member named key. + bool isMember(const CppTL::ConstString& key) const; +#endif + + /// \brief Return a list of the member names. + /// + /// If null, return an empty list. + /// \pre type() is objectValue or nullValue + /// \post if type() was nullValue, it remains nullValue + Members getMemberNames() const; + + //# ifdef JSON_USE_CPPTL + // EnumMemberNames enumMemberNames() const; + // EnumValues enumValues() const; + //# endif + + /// \deprecated Always pass len. + JSONCPP_DEPRECATED("Use setComment(std::string const&) instead.") + void setComment(const char* comment, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const char* comment, size_t len, CommentPlacement placement); + /// Comments must be //... or /* ... */ + void setComment(const std::string& comment, CommentPlacement placement); + bool hasComment(CommentPlacement placement) const; + /// Include delimiters and embedded newlines. + std::string getComment(CommentPlacement placement) const; + + std::string toStyledString() const; + + const_iterator begin() const; + const_iterator end() const; + + iterator begin(); + iterator end(); + +private: + void initBasic(ValueType type, bool allocated = false); + + Value& resolveReference(const char* key); + Value& resolveReference(const char* key, const char* end); + + struct CommentInfo { + CommentInfo(); + ~CommentInfo(); + + void setComment(const char* text, size_t len); + + char* comment_; + }; + + // struct MemberNamesTransform + //{ + // typedef const char *result_type; + // const char *operator()( const CZString &name ) const + // { + // return name.c_str(); + // } + //}; + + union ValueHolder { + LargestInt int_; + LargestUInt uint_; + double real_; + bool bool_; + char* string_; // actually ptr to unsigned, followed by str, unless !allocated_ + ObjectValues* map_; + } value_; + ValueType type_ : 8; + unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. + // If not allocated_, string_ must be null-terminated. + CommentInfo* comments_; +}; + +/** \brief Experimental and untested: represents an element of the "path" to + * access a node. + */ +class JSON_API PathArgument { +public: + friend class Path; + + PathArgument(); + PathArgument(ArrayIndex index); + PathArgument(const char* key); + PathArgument(const std::string& key); + +private: + enum Kind { + kindNone = 0, + kindIndex, + kindKey + }; + std::string key_; + ArrayIndex index_; + Kind kind_; +}; + +/** \brief Experimental and untested: represents a "path" to access a node. + * + * Syntax: + * - "." => root node + * - ".[n]" => elements at index 'n' of root node (an array value) + * - ".name" => member named 'name' of root node (an object value) + * - ".name1.name2.name3" + * - ".[0][1][2].name1[3]" + * - ".%" => member name is provided as parameter + * - ".[%]" => index is provied as parameter + */ +class JSON_API Path { +public: + Path(const std::string& path, + const PathArgument& a1 = PathArgument(), + const PathArgument& a2 = PathArgument(), + const PathArgument& a3 = PathArgument(), + const PathArgument& a4 = PathArgument(), + const PathArgument& a5 = PathArgument()); + + const Value& resolve(const Value& root) const; + Value resolve(const Value& root, const Value& defaultValue) const; + /// Creates the "path" to access the specified node and returns a reference on + /// the node. + Value& make(Value& root) const; + +private: + typedef std::vector InArgs; + typedef std::vector Args; + + void makePath(const std::string& path, const InArgs& in); + void addPathInArg(const std::string& path, + const InArgs& in, + InArgs::const_iterator& itInArg, + PathArgument::Kind kind); + void invalidPath(const std::string& path, int location); + + Args args_; +}; + +/** \brief base class for Value iterators. + * + */ +class JSON_API ValueIteratorBase { +public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef unsigned int size_t; + typedef int difference_type; + typedef ValueIteratorBase SelfType; + + bool operator==(const SelfType& other) const { return isEqual(other); } + + bool operator!=(const SelfType& other) const { return !isEqual(other); } + + difference_type operator-(const SelfType& other) const { + return other.computeDistance(*this); + } + + /// Return either the index or the member name of the referenced value as a + /// Value. + Value key() const; + + /// Return the index of the referenced Value, or -1 if it is not an arrayValue. + UInt index() const; + + /// Return the member name of the referenced Value, or "" if it is not an + /// objectValue. + /// \note Avoid `c_str()` on result, as embedded zeroes are possible. + std::string name() const; + + /// Return the member name of the referenced Value. "" if it is not an + /// objectValue. + /// \deprecated This cannot be used for UTF-8 strings, since there can be embedded nulls. + JSONCPP_DEPRECATED("Use `key = name();` instead.") + char const* memberName() const; + /// Return the member name of the referenced Value, or NULL if it is not an + /// objectValue. + /// \note Better version than memberName(). Allows embedded nulls. + char const* memberName(char const** end) const; + +protected: + Value& deref() const; + + void increment(); + + void decrement(); + + difference_type computeDistance(const SelfType& other) const; + + bool isEqual(const SelfType& other) const; + + void copy(const SelfType& other); + +private: + Value::ObjectValues::iterator current_; + // Indicates that iterator is for a null value. + bool isNull_; + +public: + // For some reason, BORLAND needs these at the end, rather + // than earlier. No idea why. + ValueIteratorBase(); + explicit ValueIteratorBase(const Value::ObjectValues::iterator& current); +}; + +/** \brief const iterator for object and array value. + * + */ +class JSON_API ValueConstIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef const Value value_type; + //typedef unsigned int size_t; + //typedef int difference_type; + typedef const Value& reference; + typedef const Value* pointer; + typedef ValueConstIterator SelfType; + + ValueConstIterator(); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueConstIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const ValueIteratorBase& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +/** \brief Iterator for object and array value. + */ +class JSON_API ValueIterator : public ValueIteratorBase { + friend class Value; + +public: + typedef Value value_type; + typedef unsigned int size_t; + typedef int difference_type; + typedef Value& reference; + typedef Value* pointer; + typedef ValueIterator SelfType; + + ValueIterator(); + ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueIterator& other); + +private: +/*! \internal Use by Value to create an iterator. + */ + explicit ValueIterator(const Value::ObjectValues::iterator& current); +public: + SelfType& operator=(const SelfType& other); + + SelfType operator++(int) { + SelfType temp(*this); + ++*this; + return temp; + } + + SelfType operator--(int) { + SelfType temp(*this); + --*this; + return temp; + } + + SelfType& operator--() { + decrement(); + return *this; + } + + SelfType& operator++() { + increment(); + return *this; + } + + reference operator*() const { return deref(); } + + pointer operator->() const { return &deref(); } +}; + +} // namespace Json + + +namespace std { +/// Specialize std::swap() for Json::Value. +template<> +inline void swap(Json::Value& a, Json::Value& b) { a.swap(b); } +} + + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // CPPTL_JSON_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/version.h b/ProvisioningTool/keymint/include/json/version.h new file mode 100644 index 00000000..d0f3dcb0 --- /dev/null +++ b/ProvisioningTool/keymint/include/json/version.h @@ -0,0 +1,13 @@ +// DO NOT EDIT. This file (and "version") is generated by CMake. +// Run CMake configure step to update it. +#ifndef JSON_VERSION_H_INCLUDED +# define JSON_VERSION_H_INCLUDED + +# define JSONCPP_VERSION_STRING "0.10.7" +# define JSONCPP_VERSION_MAJOR 0 +# define JSONCPP_VERSION_MINOR 10 +# define JSONCPP_VERSION_PATCH 7 +# define JSONCPP_VERSION_QUALIFIER +# define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) + +#endif // JSON_VERSION_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/json/writer.h b/ProvisioningTool/keymint/include/json/writer.h new file mode 100644 index 00000000..a7fd11d2 --- /dev/null +++ b/ProvisioningTool/keymint/include/json/writer.h @@ -0,0 +1,320 @@ +// Copyright 2007-2010 Baptiste Lepilleur +// Distributed under MIT license, or public domain if desired and +// recognized in your jurisdiction. +// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE + +#ifndef JSON_WRITER_H_INCLUDED +#define JSON_WRITER_H_INCLUDED + +#if !defined(JSON_IS_AMALGAMATION) +#include "value.h" +#endif // if !defined(JSON_IS_AMALGAMATION) +#include +#include +#include + +// Disable warning C4251: : needs to have dll-interface to +// be used by... +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(push) +#pragma warning(disable : 4251) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +namespace Json { + +class Value; + +/** + +Usage: +\code + using namespace Json; + void writeToStdout(StreamWriter::Factory const& factory, Value const& value) { + std::unique_ptr const writer( + factory.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush + } +\endcode +*/ +class JSON_API StreamWriter { +protected: + std::ostream* sout_; // not owned; will not delete +public: + StreamWriter(); + virtual ~StreamWriter(); + /** Write Value into document as configured in sub-class. + Do not take ownership of sout, but maintain a reference during function. + \pre sout != NULL + \return zero on success (For now, we always return zero, so check the stream instead.) + \throw std::exception possibly, depending on configuration + */ + virtual int write(Value const& root, std::ostream* sout) = 0; + + /** \brief A simple abstract factory. + */ + class JSON_API Factory { + public: + virtual ~Factory(); + /** \brief Allocate a CharReader via operator new(). + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const = 0; + }; // Factory +}; // StreamWriter + +/** \brief Write into stringstream, then return string, for convenience. + * A StreamWriter will be created from the factory, used, and then deleted. + */ +std::string JSON_API writeString(StreamWriter::Factory const& factory, Value const& root); + + +/** \brief Build a StreamWriter implementation. + +Usage: +\code + using namespace Json; + Value value = ...; + StreamWriterBuilder builder; + builder["commentStyle"] = "None"; + builder["indentation"] = " "; // or whatever you like + std::unique_ptr writer( + builder.newStreamWriter()); + writer->write(value, &std::cout); + std::cout << std::endl; // add lf and flush +\endcode +*/ +class JSON_API StreamWriterBuilder : public StreamWriter::Factory { +public: + // Note: We use a Json::Value so that we can add data-members to this class + // without a major version bump. + /** Configuration of this builder. + Available settings (case-sensitive): + - "commentStyle": "None" or "All" + - "indentation": "" + - "enableYAMLCompatibility": false or true + - slightly change the whitespace around colons + - "dropNullPlaceholders": false or true + - Drop the "null" string from the writer's output for nullValues. + Strictly speaking, this is not valid JSON. But when the output is being + fed to a browser's Javascript, it makes for smaller output and the + browser can handle the output just fine. + - "useSpecialFloats": false or true + - If true, outputs non-finite floating point values in the following way: + NaN values as "NaN", positive infinity as "Infinity", and negative infinity + as "-Infinity". + + You can examine 'settings_` yourself + to see the defaults. You can also write and read them just like any + JSON Value. + \sa setDefaults() + */ + Json::Value settings_; + + StreamWriterBuilder(); + virtual ~StreamWriterBuilder(); + + /** + * \throw std::exception if something goes wrong (e.g. invalid settings) + */ + virtual StreamWriter* newStreamWriter() const; + + /** \return true if 'settings' are legal and consistent; + * otherwise, indicate bad settings via 'invalid'. + */ + bool validate(Json::Value* invalid) const; + /** A simple way to update a specific setting. + */ + Value& operator[](std::string key); + + /** Called by ctor, but you can use this to reset settings_. + * \pre 'settings' != NULL (but Json::null is fine) + * \remark Defaults: + * \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults + */ + static void setDefaults(Json::Value* settings); +}; + +/** \brief Abstract class for writers. + * \deprecated Use StreamWriter. (And really, this is an implementation detail.) + */ +class JSON_API Writer { +public: + virtual ~Writer(); + + virtual std::string write(const Value& root) = 0; +}; + +/** \brief Outputs a Value in JSON format + *without formatting (not human friendly). + * + * The JSON document is written in a single line. It is not intended for 'human' + *consumption, + * but may be usefull to support feature such as RPC where bandwith is limited. + * \sa Reader, Value + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API FastWriter : public Writer { + +public: + FastWriter(); + virtual ~FastWriter() {} + + void enableYAMLCompatibility(); + +public: // overridden from Writer + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + + std::string document_; + bool yamlCompatiblityEnabled_; +}; + +/** \brief Writes a Value in JSON format in a + *human friendly way. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + *line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + *types, + * and all the values fit on one lines, then print the array on a single + *line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + *#CommentPlacement. + * + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledWriter : public Writer { +public: + StyledWriter(); + virtual ~StyledWriter() {} + +public: // overridden from Writer + /** \brief Serialize a Value in JSON format. + * \param root Value to serialize. + * \return String containing the JSON document that represents the root value. + */ + virtual std::string write(const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::string document_; + std::string indentString_; + int rightMargin_; + int indentSize_; + bool addChildValues_; +}; + +/** \brief Writes a Value in JSON format in a + human friendly way, + to a stream rather than to a string. + * + * The rules for line break and indent are as follow: + * - Object value: + * - if empty then print {} without indent and line break + * - if not empty the print '{', line break & indent, print one value per + line + * and then unindent and line break and print '}'. + * - Array value: + * - if empty then print [] without indent and line break + * - if the array contains no object value, empty array or some other value + types, + * and all the values fit on one lines, then print the array on a single + line. + * - otherwise, it the values do not fit on one line, or the array contains + * object or non empty array, then print one value per line. + * + * If the Value have comments then they are outputed according to their + #CommentPlacement. + * + * \param indentation Each level will be indented by this amount extra. + * \sa Reader, Value, Value::setComment() + * \deprecated Use StreamWriterBuilder. + */ +class JSON_API StyledStreamWriter { +public: + StyledStreamWriter(std::string indentation = "\t"); + ~StyledStreamWriter() {} + +public: + /** \brief Serialize a Value in JSON format. + * \param out Stream to write to. (Can be ostringstream, e.g.) + * \param root Value to serialize. + * \note There is no point in deriving from Writer, since write() should not + * return a value. + */ + void write(std::ostream& out, const Value& root); + +private: + void writeValue(const Value& value); + void writeArrayValue(const Value& value); + bool isMultineArray(const Value& value); + void pushValue(const std::string& value); + void writeIndent(); + void writeWithIndent(const std::string& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(const Value& root); + void writeCommentAfterValueOnSameLine(const Value& root); + bool hasCommentForValue(const Value& value); + static std::string normalizeEOL(const std::string& text); + + typedef std::vector ChildValues; + + ChildValues childValues_; + std::ostream* document_; + std::string indentString_; + int rightMargin_; + std::string indentation_; + bool addChildValues_ : 1; + bool indented_ : 1; +}; + +#if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(Int value); +std::string JSON_API valueToString(UInt value); +#endif // if defined(JSON_HAS_INT64) +std::string JSON_API valueToString(LargestInt value); +std::string JSON_API valueToString(LargestUInt value); +std::string JSON_API valueToString(double value); +std::string JSON_API valueToString(bool value); +std::string JSON_API valueToQuotedString(const char* value); + +/// \brief Output using the StyledStreamWriter. +/// \see Json::operator>>() +JSON_API std::ostream& operator<<(std::ostream&, const Value& root); + +} // namespace Json + +#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) +#pragma warning(pop) +#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) + +#endif // JSON_WRITER_H_INCLUDED diff --git a/ProvisioningTool/keymint/include/socket.h b/ProvisioningTool/keymint/include/socket.h new file mode 100644 index 00000000..8f47325a --- /dev/null +++ b/ProvisioningTool/keymint/include/socket.h @@ -0,0 +1,53 @@ +/* + ** + ** Copyright 2021, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#pragma once + +class SocketTransport +{ +public: + static inline std::shared_ptr getInstance() { + static std::shared_ptr socket = std::shared_ptr(new SocketTransport()); + return socket; + } + + ~SocketTransport(); + /** + * Creates a socket instance and connects to the provided server IP and port. + */ + bool openConnection(); + /** + * Sends data over socket and receives data back. + */ + bool sendData(const std::vector &inData, std::vector &output); + /** + * Closes the connection. + */ + bool closeConnection(); + /** + * Returns the state of the connection status. Returns true if the connection is active, + * false if connection is broken. + */ + bool isConnected(); + +private: + SocketTransport() : mSocket(-1), socketStatus(false) {} + /** + * Socket instance. + */ + int mSocket; + bool socketStatus; +}; \ No newline at end of file diff --git a/ProvisioningTool/keymint/include/utils.h b/ProvisioningTool/keymint/include/utils.h new file mode 100644 index 00000000..9eb991bd --- /dev/null +++ b/ProvisioningTool/keymint/include/utils.h @@ -0,0 +1,30 @@ +/* + ** + ** Copyright 2021, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#pragma once +#include +#include +#include +#include +#include + +std::string getHexString(std::vector& input); + +std::string hex2str(std::string a); + +int readJsonFile(Json::Value& root, std::string& inputFileName); + +int writeJsonFile(Json::Value& writerRoot, std::string& outputFileName); \ No newline at end of file diff --git a/ProvisioningTool/keymint/lib/README.md b/ProvisioningTool/keymint/lib/README.md new file mode 100644 index 00000000..d9ac780e --- /dev/null +++ b/ProvisioningTool/keymint/lib/README.md @@ -0,0 +1,25 @@ +# Instructions to build jsoncpp +Download the code from below opensource link: +https://github.com/open-source-parsers/jsoncpp/tree/0.y.z + +#### Unzip it +
+unzip jsoncpp-0.y.z.zip
+cd jsoncpp-0.y.z
+
+ +#### Build +
+$ mkdir -p build/debug
+$ cd build/debug
+$ cmake -DCMAKE_BUILD_TYPE=debug -DBUILD_STATIC_LIBS=ON -DBUILD_SHARED_LIBS=ON -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" ../..
+$ make
+
+ +#### Check the generated static and dynamic link library +
+$ find . -name *.a
+./src/lib_json/libjsoncpp.a
+$ find . -name *.so
+./src/lib_json/libjsoncpp.so
+
diff --git a/ProvisioningTool/keymint/lib/libjsoncpp.a b/ProvisioningTool/keymint/lib/libjsoncpp.a new file mode 100644 index 00000000..601854f4 Binary files /dev/null and b/ProvisioningTool/keymint/lib/libjsoncpp.a differ diff --git a/ProvisioningTool/keymint/lib/libjsoncpp.so b/ProvisioningTool/keymint/lib/libjsoncpp.so new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/keymint/lib/libjsoncpp.so differ diff --git a/ProvisioningTool/keymint/lib/libjsoncpp.so.0 b/ProvisioningTool/keymint/lib/libjsoncpp.so.0 new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/keymint/lib/libjsoncpp.so.0 differ diff --git a/ProvisioningTool/keymint/lib/libjsoncpp.so.0.10.7 b/ProvisioningTool/keymint/lib/libjsoncpp.so.0.10.7 new file mode 100755 index 00000000..a23ae279 Binary files /dev/null and b/ProvisioningTool/keymint/lib/libjsoncpp.so.0.10.7 differ diff --git a/ProvisioningTool/keymint/sample_json_keymint_cf.txt b/ProvisioningTool/keymint/sample_json_keymint_cf.txt new file mode 100644 index 00000000..70f9c71e --- /dev/null +++ b/ProvisioningTool/keymint/sample_json_keymint_cf.txt @@ -0,0 +1,28 @@ +{ + "attest_ids": { + "brand": "generic", + "device": "vsoc_x86_64", + "product": "aosp_cf_x86_64_phone", + "serial": "", + "imei": "000000000000000", + "meid": "000000000000000", + "manufacturer": "Google", + "model": "Cuttlefish x86_64 phone" + }, + "shared_secret": "0000000000000000000000000000000000000000000000000000000000000000", + "set_boot_params": { + "boot_patch_level": 0, + "verified_boot_key": "0000000000000000000000000000000000000000000000000000000000000000", + "verified_boot_key_hash": "0000000000000000000000000000000000000000000000000000000000000000", + "boot_state": 2, + "device_locked": 1 + }, + "device_unique_key": "test_resources/batch_key.der", + "signer_info": { + "signer_name": "Google", + "signing_keys": [ + "test_resources/ca_key.der", + "test_resources/intermediate_key.der" + ] + } +} diff --git a/ProvisioningTool/keymint/src/construct_apdus.cpp b/ProvisioningTool/keymint/src/construct_apdus.cpp new file mode 100644 index 00000000..190e3a78 --- /dev/null +++ b/ProvisioningTool/keymint/src/construct_apdus.cpp @@ -0,0 +1,612 @@ +/* + ** + ** Copyright 2021, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cppbor/cppbor.h" +#include "cppcose/cppcose.h" + +// static globals. +static std::string inputFileName; +static std::string outputFileName; +Json::Value root; +Json::Value writerRoot; + +using namespace std; +using cppbor::Array; +using cppbor::Map; +using cppbor::Bstr; +using cppcose::CoseKey; +using cppcose::EC2; +using cppcose::ES256; +using cppcose::P256; +using cppcose::SIGN; +using cppcose::bytevec; + + +// static function declarations +static int processInputFile(); +static int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, + std::vector& pub_x, std::vector& pub_y); +static int processAttestationIds(); +static int processSharedSecret(); +static int processSetBootParameters(); +static int readDataFromFile(const char *fileName, std::vector& data); +static int addApduHeader(const int ins, std::vector& inputData); +static int getIntValue(Json::Value& Obj, const char* key, uint32_t *value); +static int getBlobValue(Json::Value& Obj, const char* key, std::vector& blob); +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); + + +// Print usage. +void usage() { + printf("Usage: Please give json files with values as input to generate the apdus command. Please refer to sample_json files available in the folder for reference. Sample json files are written using hardcode parameters to be used for testing setup on cuttlefilsh emulator and goldfish emulators\n"); + printf("construct_keymint_apdus [options]\n"); + printf("Valid options are:\n"); + printf("-h, --help show this help message and exit.\n"); + printf("-i, --input jsonFile \t Input json file \n"); + printf("-o, --output jsonFile \t Output json file \n"); +} + + +int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, + std::vector& pub_x, std::vector& pub_y) { + 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. + BIGNUM_Ptr x(BN_new()); + BIGNUM_Ptr y(BN_new()); + std::vector dataX(kAffinePointLength); + std::vector dataY(kAffinePointLength); + BN_CTX_Ptr ctx(BN_CTX_new()); + if (ctx == nullptr) { + printf("\nFailed to get BN_CTX \n"); + return FAILURE; + } + const EC_POINT *point = EC_KEY_get0_public_key(ec_key.get()); + + if (!EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key.get()), point, x.get(), + y.get(), ctx.get())) { + printf("\nFailed to get affine coordinates\n"); + return FAILURE; + } + if (BN_bn2binpad(x.get(), dataX.data(), kAffinePointLength) != kAffinePointLength) { + printf("\nFailed to get x coordinate\n"); + return FAILURE; + } + if (BN_bn2binpad(y.get(), dataY.data(), kAffinePointLength) != kAffinePointLength) { + printf("\nFailed to get y coordinate\n"); + return FAILURE; + } + pub_x = dataX; + pub_y = dataY; + return SUCCESS; +} + +int getIntValue(Json::Value& bootParamsObj, const char* key, uint32_t *value) { + Json::Value val = bootParamsObj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isInt()) + return FAILURE; + + *value = (uint32_t)val.asInt(); + + return SUCCESS; +} + +int getStringValue(Json::Value& Obj, const char* key, std::string& str) { + Json::Value val = Obj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isString()) + return FAILURE; + + str = val.asString(); + + return SUCCESS; + +} + +int getBlobValue(Json::Value& bootParamsObj, const char* key, std::vector& blob) { + Json::Value val = bootParamsObj[key]; + if(val.empty()) + return FAILURE; + + if(!val.isString()) + return FAILURE; + + std::string blobStr = hex2str(val.asString()); + + for(char ch : blobStr) { + blob.push_back((uint8_t)ch); + } + + return SUCCESS; +} + + +// 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. +int processInputFile() { + + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + if (0 != processDeviceUniqueKey() || + 0 != processAdditionalCertificateChain() || + 0 != processAttestationIds() || + 0 != processSharedSecret() || + 0 != processSetBootParameters()) { + return FAILURE; + } + if (SUCCESS != writeJsonFile(writerRoot, outputFileName)) { + return FAILURE; + } + printf("\n Successfully written json to outfile: %s\n ", outputFileName.c_str()); + return SUCCESS; +} + +int processAdditionalCertificateChain() { + Json::Value signerInfo = root.get(kSignerInfo, Json::Value::nullRef); + if (!signerInfo.isNull()) { + std::string signerName; + std::string signingKeyFile; + std::vector previousKey; + Array array; + + if (SUCCESS != getStringValue(signerInfo, "signer_name", signerName)) { + printf("\n Improper value for signer_name in json file \n"); + return FAILURE; + } + + Json::Value keys = signerInfo.get("signing_keys", Json::Value::nullRef); + if (!keys.isNull()) { + if (!keys.isArray()) { + printf("\n Improper value for signing_keys in json file \n"); + return FAILURE; + } + for(uint32_t i = 0; i < keys.size(); i++) { + std::vector data; + std::vector privateKey; + std::vector x_coord; + std::vector y_coord; + + if (!keys[i].isString()) { + printf("\n Improper value for signing_keys in json file \n"); + return FAILURE; + } + + if(SUCCESS != readDataFromFile(keys[i].asString().data(), data)) { + printf("\n Failed to read the attestation key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, x_coord, y_coord)) { + return FAILURE; + } + + if (i == 0) { + // self-signed. + previousKey = privateKey; + } + + auto rootCoseSign = + cppcose::constructCoseSign1(previousKey, /* Signing key */ + cppbor::Map() /* Payload CoseKey */ + .add(CoseKey::KEY_TYPE, EC2) + .add(CoseKey::ALGORITHM, ES256) + .add(CoseKey::CURVE, P256) + .add(CoseKey::KEY_OPS, SIGN) + .add(CoseKey::PUBKEY_X, x_coord) + .add(CoseKey::PUBKEY_Y, y_coord) + .canonicalize() + .encode(), + {} /* AAD */); + if (!rootCoseSign) { + printf("\n Failed to construct CoseSign1 %s\n", rootCoseSign.moveMessage().c_str()); + return FAILURE; + } + + // Add to cbor array + array.add(rootCoseSign.moveValue()); + previousKey = privateKey; + } + } + + std::vector dk_priv; + std::vector dk_pub_x; + std::vector dk_pub_y; + if (SUCCESS == getDeviceUniqueKey(dk_priv, dk_pub_x, dk_pub_y)) { + auto dkCoseSign = + cppcose::constructCoseSign1(previousKey, /* Signing key */ + cppbor::Map() /* Payload CoseKey */ + .add(CoseKey::KEY_TYPE, EC2) + .add(CoseKey::ALGORITHM, ES256) + .add(CoseKey::CURVE, P256) + .add(CoseKey::KEY_OPS, SIGN) + .add(CoseKey::PUBKEY_X, dk_pub_x) + .add(CoseKey::PUBKEY_Y, dk_pub_y) + .canonicalize() + .encode(), + {} /* AAD */); + if (!dkCoseSign) { + printf("\n Failed to construct CoseSign1 %s\n", dkCoseSign.moveMessage().c_str()); + return FAILURE; + } + array.add(dkCoseSign.moveValue()); + std::vector cborData = Map().add(signerName, std::move(array)).encode(); + if(SUCCESS != addApduHeader(kAdditionalCertChainCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAdditionalCertChain] = getHexString(cborData); + } else { + return FAILURE; + } + + } else { + printf("\n Improper value for signer_info in json file \n"); + return FAILURE; + } + printf("\n Constructed additional cert chain APDU successfully. \n"); + return SUCCESS; +} + +int getDeviceUniqueKey(bytevec& privKey, bytevec& x, bytevec& y) { + Json::Value keyFile = root.get(kDeviceUniqueKey, Json::Value::nullRef); + if (!keyFile.isNull()) { + std::vector data; + + std::string keyFileName = keyFile.asString(); + if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { + printf("\n Failed to read the attestation key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privKey, x, y)) { + return FAILURE; + } + } else { + printf("\n Improper value for device_unique_key in json file \n"); + return FAILURE; + } + return SUCCESS; +} + +int processDeviceUniqueKey() { + std::vector privateKey; + std::vector x_coord; + std::vector y_coord; + if (SUCCESS == getDeviceUniqueKey(privateKey, x_coord, y_coord)) { + // Construct COSE_Key + cppbor::Map cose_public_key_map = cppbor::Map() + .add(CoseKey::KEY_TYPE, EC2) + .add(CoseKey::ALGORITHM, ES256) + .add(CoseKey::CURVE, P256) + .add(CoseKey::KEY_OPS, SIGN) + .add(CoseKey::PUBKEY_X, x_coord) + .add(CoseKey::PUBKEY_Y, y_coord) + .add(CoseKey::PRIVATE_KEY, privateKey); + + Array array; + array.add(std::move(cose_public_key_map.canonicalize())); + std::vector cborData = array.encode(); + + if(SUCCESS != addApduHeader(kDeviceUniqueKeyCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kDeviceUniqueKey] = getHexString(cborData); + + } else { + return FAILURE; + } + printf("\n Constructed device unique key APDU successfully. \n"); + return SUCCESS; +} + + +int processAttestationIds() { + //AttestIDParams params; + Json::Value attestIds = root.get("attest_ids", Json::Value::nullRef); + if (!attestIds.isNull()) { + Json::Value value; + Map map; + Json::Value::Members keys = attestIds.getMemberNames(); + for(std::string key : keys) { + value = attestIds[key]; + if(value.empty()) { + continue; + } + if (!value.isString()) { + printf("\n Fail: Value for each attest ids key should be a string in the json file \n"); + return FAILURE; + } + std::string idVal = value.asString(); + if (0 == key.compare("brand")) { + map.add(kTagAttestationIdBrand, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("device")) { + map.add(kTagAttestationIdDevice, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("product")) { + map.add(kTagAttestationIdProduct, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("serial")) { + map.add(kTagAttestationIdSerial, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("imei")) { + map.add(kTagAttestationIdImei, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("meid")) { + map.add(kTagAttestationIdMeid, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("manufacturer")) { + map.add(kTagAttestationIdManufacturer, std::vector(idVal.begin(), idVal.end())); + } else if(0 == key.compare("model")) { + map.add(kTagAttestationIdModel, std::vector(idVal.begin(), idVal.end())); + } else { + printf("\n unknown attestation id key:%s \n", key.c_str()); + return FAILURE; + } + } + + //------------------------- + // construct cbor input. + Array array; + array.add(std::move(map)); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kAttestationIdsCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestationIds] = getHexString(cborData); + //------------------------- + } else { + printf("\n Fail: Improper value found for attest_ids key inside the json file \n"); + return FAILURE; + } + printf("\n Constructed attestation ids APDU successfully \n"); + return SUCCESS; +} + +int processSharedSecret() { + Json::Value sharedSecret = root.get("shared_secret", Json::Value::nullRef); + if (!sharedSecret.isNull()) { + + if (!sharedSecret.isString()) { + printf("\n Fail: Value for shared secret key should be string inside the json file\n"); + return FAILURE; + } + std::string secret = hex2str(sharedSecret.asString()); + std::vector data(secret.begin(), secret.end()); + // -------------------------- + // Construct apdu. + Array array; + array.add(data); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kPresharedSecretCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kSharedSecret] = getHexString(cborData); + // -------------------------- + } else { + printf("\n Fail: Improper value for shared_secret key inside the json file\n"); + return FAILURE; + } + printf("\n Constructed shared secret APDU successfully \n"); + return SUCCESS; +} + +int processSetBootParameters() { + uint32_t bootPatchLevel; + std::vector verifiedBootKey; + std::vector verifiedBootKeyHash; + uint32_t verifiedBootState; + uint32_t deviceLocked; + Json::Value bootParamsObj = root.get("set_boot_params", Json::Value::nullRef); + if (!bootParamsObj.isNull()) { + + if(SUCCESS != getIntValue(bootParamsObj, "boot_patch_level", &bootPatchLevel)) { + printf("\n Invalid value for boot_patch_level or boot_patch_level tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBlobValue(bootParamsObj, "verified_boot_key", verifiedBootKey)) { + printf("\n Invalid value for verified_boot_key or verified_boot_key tag missing\n"); + return FAILURE; + } + if(SUCCESS != getBlobValue(bootParamsObj, "verified_boot_key_hash", verifiedBootKeyHash)) { + printf("\n Invalid value for verified_boot_key_hash or verified_boot_key_hash tag missing\n"); + return FAILURE; + } + if(SUCCESS != getIntValue(bootParamsObj, "boot_state", &verifiedBootState)) { + printf("\n Invalid value for boot_state or boot_state tag missing\n"); + return FAILURE; + } + if(SUCCESS != getIntValue(bootParamsObj, "device_locked", &deviceLocked)) { + printf("\n Invalid value for device_locked or device_locked tag missing\n"); + return FAILURE; + } + + } else { + printf("\n Fail: Improper value found for set_boot_params key inside the json file\n"); + return FAILURE; + } + //--------------------------------- + // prepare cbor data. + Array array; + array.add(bootPatchLevel). + add(verifiedBootKey). /* Verified Boot Key */ + add(verifiedBootKeyHash). /* Verified Boot Hash */ + add(verifiedBootState). /* boot state */ + add(deviceLocked); /* device locked */ + + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kBootParamsCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kBootParams] = getHexString(cborData); + + //--------------------------------- + printf("\n Constructed boot paramters APDU successfully \n"); + return SUCCESS; +} + + + +int addApduHeader(const int ins, std::vector& inputData) { + if(USHRT_MAX >= inputData.size()) { + // Send extended length APDU always as response size is not known to HAL. + // Case 1: Lc > 0 CLS | INS | P1 | P2 | 00 | 2 bytes of Lc | CommandData | 2 bytes of Le all set to 00. + // Case 2: Lc = 0 CLS | INS | P1 | P2 | 3 bytes of Le all set to 00. + //Extended length 3 bytes, starts with 0x00 + if (inputData.size() > 0) { + inputData.insert(inputData.begin(), static_cast(inputData.size() & 0xFF)); // LSB + inputData.insert(inputData.begin(), static_cast(inputData.size() >> 8)); // MSB + } + inputData.insert(inputData.begin(), static_cast(0x00)); + //Expected length of output. + //Accepting complete length of output every time. + inputData.push_back(static_cast(0x00)); + inputData.push_back(static_cast(0x00)); + } else { + printf("\n Failed to construct apdu. input data larger than USHORT_MAX.\n"); + return FAILURE; + } + + inputData.insert(inputData.begin(), static_cast(APDU_P2));//P2 + inputData.insert(inputData.begin(), static_cast(APDU_P1));//P1 + inputData.insert(inputData.begin(), static_cast(ins));//INS + inputData.insert(inputData.begin(), static_cast(APDU_CLS));//CLS + return SUCCESS; +} + +int readDataFromFile(const char *filename, std::vector& data) { + FILE *fp; + int ret = SUCCESS; + fp = fopen(filename, "rb"); + if(fp == NULL) { + printf("\nFailed to open file: \n"); + return FAILURE; + } + fseek(fp, 0L, SEEK_END); + long int filesize = ftell(fp); + rewind(fp); + std::unique_ptr buf(new uint8_t[filesize]); + if( 0 == fread(buf.get(), filesize, 1, fp)) { + printf("\n No content in the file \n"); + ret = FAILURE; + goto exit; + } + data.insert(data.end(), buf.get(), buf.get() + filesize); +exit: + fclose(fp); + return ret; +} + +int main(int argc, char* argv[]) { + int c; + struct option longOpts[] = { + {"input", required_argument, NULL, 'i'}, + {"output", required_argument, NULL, 'o'}, + {"help", no_argument, NULL, 'h'}, + {0,0,0,0} + }; + + if (argc <= 1) { + printf("\n Invalid command \n"); + usage(); + return FAILURE; + } + + /* getopt_long stores the option index here. */ + while ((c = getopt_long(argc, argv, ":hi:o:", longOpts, NULL)) != -1) { + switch(c) { + case 'i': + // input file + inputFileName = std::string(optarg); + std::cout << "input file: " << inputFileName << std::endl; + break; + case 'o': + // output file + outputFileName = std::string(optarg); + std::cout << "output file: " << outputFileName << std::endl; + break; + case 'h': + // help + usage(); + return SUCCESS; + case ':': + printf("\n missing argument\n"); + usage(); + return FAILURE; + case '?': + default: + printf("\n Invalid option\n"); + usage(); + return FAILURE; + } + } + if (inputFileName.empty() || outputFileName.empty() || optind < argc) { + printf("\n Missing mandatory arguments \n"); + usage(); + return FAILURE; + } + // Process input file; construct apuds and store in output json file. + processInputFile(); + return SUCCESS; +} diff --git a/ProvisioningTool/keymint/src/cppbor.o b/ProvisioningTool/keymint/src/cppbor.o new file mode 100644 index 00000000..05ed2f99 Binary files /dev/null and b/ProvisioningTool/keymint/src/cppbor.o differ diff --git a/ProvisioningTool/keymint/src/cppbor/cppbor.cpp b/ProvisioningTool/keymint/src/cppbor/cppbor.cpp new file mode 100644 index 00000000..3414b8e5 --- /dev/null +++ b/ProvisioningTool/keymint/src/cppbor/cppbor.cpp @@ -0,0 +1,626 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include + +using std::string; +using std::vector; + + +#if !defined(__TRUSTY__) && !defined(__LINUX__) +#include +#define LOG_TAG "CppBor" +#else +#define CHECK(x) (void)(x) +#endif + +#ifdef __LINUX__ +#define ERROR "ERROR: " +#define LOG(x) std::cout << x +#endif + +namespace cppbor { + +namespace { + +template ::value>> +Iterator writeBigEndian(T value, Iterator pos) { + for (unsigned i = 0; i < sizeof(value); ++i) { + *pos++ = static_cast(value >> (8 * (sizeof(value) - 1))); + value = static_cast(value << 8); + } + return pos; +} + +template ::value>> +void writeBigEndian(T value, std::function& cb) { + for (unsigned i = 0; i < sizeof(value); ++i) { + cb(static_cast(value >> (8 * (sizeof(value) - 1)))); + value = static_cast(value << 8); + } +} + +bool cborAreAllElementsNonCompound(const Item* compoundItem) { + if (compoundItem->type() == ARRAY) { + const Array* array = compoundItem->asArray(); + for (size_t n = 0; n < array->size(); n++) { + const Item* entry = (*array)[n].get(); + switch (entry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + } + } else { + const Map* map = compoundItem->asMap(); + for (auto& [keyEntry, valueEntry] : *map) { + switch (keyEntry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + switch (valueEntry->type()) { + case ARRAY: + case MAP: + return false; + default: + break; + } + } + } + return true; +} + +bool prettyPrintInternal(const Item* item, string& out, size_t indent, size_t maxBStrSize, + const vector& mapKeysToNotPrint) { + if (!item) { + out.append(""); + return false; + } + + char buf[80]; + + string indentString(indent, ' '); + + size_t tagCount = item->semanticTagCount(); + while (tagCount > 0) { + --tagCount; + snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", item->semanticTag(tagCount)); + out.append(buf); + } + + switch (item->type()) { + case SEMANTIC: + // Handled above. + break; + + case UINT: + snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue()); + out.append(buf); + break; + + case NINT: + snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value()); + out.append(buf); + break; + + case BSTR: { + const uint8_t* valueData; + size_t valueSize; + const Bstr* bstr = item->asBstr(); + if (bstr != nullptr) { + const vector& value = bstr->value(); + valueData = value.data(); + valueSize = value.size(); + } else { + const ViewBstr* viewBstr = item->asViewBstr(); + assert(viewBstr != nullptr); + + std::basic_string_view view = viewBstr->view(); + valueData = view.data(); + valueSize = view.size(); + } + + if (valueSize > maxBStrSize) { + unsigned char digest[SHA_DIGEST_LENGTH]; + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, valueData, valueSize); + SHA1_Final(digest, &ctx); + char buf2[SHA_DIGEST_LENGTH * 2 + 1]; + for (size_t n = 0; n < SHA_DIGEST_LENGTH; n++) { + snprintf(buf2 + n * 2, 3, "%02x", digest[n]); + } + snprintf(buf, sizeof(buf), "", valueSize, buf2); + out.append(buf); + } else { + out.append("{"); + for (size_t n = 0; n < valueSize; n++) { + if (n > 0) { + out.append(", "); + } + snprintf(buf, sizeof(buf), "0x%02x", valueData[n]); + out.append(buf); + } + out.append("}"); + } + } break; + + case TSTR: + out.append("'"); + { + // TODO: escape "'" characters + if (item->asTstr() != nullptr) { + out.append(item->asTstr()->value().c_str()); + } else { + const ViewTstr* viewTstr = item->asViewTstr(); + assert(viewTstr != nullptr); + out.append(viewTstr->view()); + } + } + out.append("'"); + break; + + case ARRAY: { + const Array* array = item->asArray(); + if (array->size() == 0) { + out.append("[]"); + } else if (cborAreAllElementsNonCompound(array)) { + out.append("["); + for (size_t n = 0; n < array->size(); n++) { + if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(", "); + } + out.append("]"); + } else { + out.append("[\n" + indentString); + for (size_t n = 0; n < array->size(); n++) { + out.append(" "); + if (!prettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(",\n" + indentString); + } + out.append("]"); + } + } break; + + case MAP: { + const Map* map = item->asMap(); + + if (map->size() == 0) { + out.append("{}"); + } else { + out.append("{\n" + indentString); + for (auto& [map_key, map_value] : *map) { + out.append(" "); + + if (!prettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + out.append(" : "); + if (map_key->type() == TSTR && + std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(), + map_key->asTstr()->value()) != mapKeysToNotPrint.end()) { + out.append(""); + } else { + if (!prettyPrintInternal(map_value.get(), out, indent + 2, maxBStrSize, + mapKeysToNotPrint)) { + return false; + } + } + out.append(",\n" + indentString); + } + out.append("}"); + } + } break; + + case SIMPLE: + const Bool* asBool = item->asSimple()->asBool(); + const Null* asNull = item->asSimple()->asNull(); + if (asBool != nullptr) { + out.append(asBool->value() ? "true" : "false"); + } else if (asNull != nullptr) { + out.append("null"); + } else { +#ifndef __TRUSTY__ + LOG(ERROR) << "Only boolean/null is implemented for SIMPLE"; +#endif // __TRUSTY__ + return false; + } + break; + } + + return true; +} + +} // namespace + +size_t headerSize(uint64_t addlInfo) { + if (addlInfo < ONE_BYTE_LENGTH) return 1; + if (addlInfo <= std::numeric_limits::max()) return 2; + if (addlInfo <= std::numeric_limits::max()) return 3; + if (addlInfo <= std::numeric_limits::max()) return 5; + return 9; +} + +uint8_t* encodeHeader(MajorType type, uint64_t addlInfo, uint8_t* pos, const uint8_t* end) { + size_t sz = headerSize(addlInfo); + if (end - pos < static_cast(sz)) return nullptr; + switch (sz) { + case 1: + *pos++ = type | static_cast(addlInfo); + return pos; + case 2: + *pos++ = type | ONE_BYTE_LENGTH; + *pos++ = static_cast(addlInfo); + return pos; + case 3: + *pos++ = type | TWO_BYTE_LENGTH; + return writeBigEndian(static_cast(addlInfo), pos); + case 5: + *pos++ = type | FOUR_BYTE_LENGTH; + return writeBigEndian(static_cast(addlInfo), pos); + case 9: + *pos++ = type | EIGHT_BYTE_LENGTH; + return writeBigEndian(addlInfo, pos); + default: + CHECK(false); // Impossible to get here. + return nullptr; + } +} + +void encodeHeader(MajorType type, uint64_t addlInfo, EncodeCallback encodeCallback) { + size_t sz = headerSize(addlInfo); + switch (sz) { + case 1: + encodeCallback(type | static_cast(addlInfo)); + break; + case 2: + encodeCallback(type | ONE_BYTE_LENGTH); + encodeCallback(static_cast(addlInfo)); + break; + case 3: + encodeCallback(type | TWO_BYTE_LENGTH); + writeBigEndian(static_cast(addlInfo), encodeCallback); + break; + case 5: + encodeCallback(type | FOUR_BYTE_LENGTH); + writeBigEndian(static_cast(addlInfo), encodeCallback); + break; + case 9: + encodeCallback(type | EIGHT_BYTE_LENGTH); + writeBigEndian(addlInfo, encodeCallback); + break; + default: + CHECK(false); // Impossible to get here. + } +} + +bool Item::operator==(const Item& other) const& { + if (type() != other.type()) return false; + switch (type()) { + case UINT: + return *asUint() == *(other.asUint()); + case NINT: + return *asNint() == *(other.asNint()); + case BSTR: + if (asBstr() != nullptr && other.asBstr() != nullptr) { + return *asBstr() == *(other.asBstr()); + } + if (asViewBstr() != nullptr && other.asViewBstr() != nullptr) { + return *asViewBstr() == *(other.asViewBstr()); + } + // Interesting corner case: comparing a Bstr and ViewBstr with + // identical contents. The function currently returns false for + // this case. + // TODO: if it should return true, this needs a deep comparison + return false; + case TSTR: + if (asTstr() != nullptr && other.asTstr() != nullptr) { + return *asTstr() == *(other.asTstr()); + } + if (asViewTstr() != nullptr && other.asViewTstr() != nullptr) { + return *asViewTstr() == *(other.asViewTstr()); + } + // Same corner case as Bstr + return false; + case ARRAY: + return *asArray() == *(other.asArray()); + case MAP: + return *asMap() == *(other.asMap()); + case SIMPLE: + return *asSimple() == *(other.asSimple()); + case SEMANTIC: + return *asSemanticTag() == *(other.asSemanticTag()); + default: + CHECK(false); // Impossible to get here. + return false; + } +} + +Nint::Nint(int64_t v) : mValue(v) { + CHECK(v < 0); +} + +bool Simple::operator==(const Simple& other) const& { + if (simpleType() != other.simpleType()) return false; + + switch (simpleType()) { + case BOOLEAN: + return *asBool() == *(other.asBool()); + case NULL_T: + return true; + default: + CHECK(false); // Impossible to get here. + return false; + } +} + +uint8_t* Bstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mValue.size(), pos, end); + if (!pos || end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); +} + +void Bstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mValue) { + encodeCallback(c); + } +} + +uint8_t* ViewBstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mView.size(), pos, end); + if (!pos || end - pos < static_cast(mView.size())) return nullptr; + return std::copy(mView.begin(), mView.end(), pos); +} + +void ViewBstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mView) { + encodeCallback(static_cast(c)); + } +} + +uint8_t* Tstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mValue.size(), pos, end); + if (!pos || end - pos < static_cast(mValue.size())) return nullptr; + return std::copy(mValue.begin(), mValue.end(), pos); +} + +void Tstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mValue) { + encodeCallback(static_cast(c)); + } +} + +uint8_t* ViewTstr::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(mView.size(), pos, end); + if (!pos || end - pos < static_cast(mView.size())) return nullptr; + return std::copy(mView.begin(), mView.end(), pos); +} + +void ViewTstr::encodeValue(EncodeCallback encodeCallback) const { + for (auto c : mView) { + encodeCallback(static_cast(c)); + } +} + +bool Array::operator==(const Array& other) const& { + return size() == other.size() + // Can't use vector::operator== because the contents are pointers. std::equal lets us + // provide a predicate that does the dereferencing. + && std::equal(mEntries.begin(), mEntries.end(), other.mEntries.begin(), + [](auto& a, auto& b) -> bool { return *a == *b; }); +} + +uint8_t* Array::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(size(), pos, end); + if (!pos) return nullptr; + for (auto& entry : mEntries) { + pos = entry->encode(pos, end); + if (!pos) return nullptr; + } + return pos; +} + +void Array::encode(EncodeCallback encodeCallback) const { + encodeHeader(size(), encodeCallback); + for (auto& entry : mEntries) { + entry->encode(encodeCallback); + } +} + +std::unique_ptr Array::clone() const { + auto res = std::make_unique(); + for (size_t i = 0; i < mEntries.size(); i++) { + res->add(mEntries[i]->clone()); + } + return res; +} + +bool Map::operator==(const Map& other) const& { + return size() == other.size() + // Can't use vector::operator== because the contents are pairs of pointers. std::equal + // lets us provide a predicate that does the dereferencing. + && std::equal(begin(), end(), other.begin(), [](auto& a, auto& b) { + return *a.first == *b.first && *a.second == *b.second; + }); +} + +uint8_t* Map::encode(uint8_t* pos, const uint8_t* end) const { + pos = encodeHeader(size(), pos, end); + if (!pos) return nullptr; + for (auto& entry : mEntries) { + pos = entry.first->encode(pos, end); + if (!pos) return nullptr; + pos = entry.second->encode(pos, end); + if (!pos) return nullptr; + } + return pos; +} + +void Map::encode(EncodeCallback encodeCallback) const { + encodeHeader(size(), encodeCallback); + for (auto& entry : mEntries) { + entry.first->encode(encodeCallback); + entry.second->encode(encodeCallback); + } +} + +bool Map::keyLess(const Item* a, const Item* b) { + // CBOR map canonicalization rules are: + + // 1. If two keys have different lengths, the shorter one sorts earlier. + if (a->encodedSize() < b->encodedSize()) return true; + if (a->encodedSize() > b->encodedSize()) return false; + + // 2. If two keys have the same length, the one with the lower value in (byte-wise) lexical + // order sorts earlier. This requires encoding both items. + auto encodedA = a->encode(); + auto encodedB = b->encode(); + + return std::lexicographical_compare(encodedA.begin(), encodedA.end(), // + encodedB.begin(), encodedB.end()); +} + +void recursivelyCanonicalize(std::unique_ptr& item) { + switch (item->type()) { + case UINT: + case NINT: + case BSTR: + case TSTR: + case SIMPLE: + return; + + case ARRAY: + std::for_each(item->asArray()->begin(), item->asArray()->end(), + recursivelyCanonicalize); + return; + + case MAP: + item->asMap()->canonicalize(true /* recurse */); + return; + + case SEMANTIC: + // This can't happen. SemanticTags delegate their type() method to the contained Item's + // type. + assert(false); + return; + } +} + +Map& Map::canonicalize(bool recurse) & { + if (recurse) { + for (auto& entry : mEntries) { + recursivelyCanonicalize(entry.first); + recursivelyCanonicalize(entry.second); + } + } + + if (size() < 2 || mCanonicalized) { + // Trivially or already canonical; do nothing. + return *this; + } + + std::sort(begin(), end(), + [](auto& a, auto& b) { return keyLess(a.first.get(), b.first.get()); }); + mCanonicalized = true; + return *this; +} + +std::unique_ptr Map::clone() const { + auto res = std::make_unique(); + for (auto& [key, value] : *this) { + res->add(key->clone(), value->clone()); + } + res->mCanonicalized = mCanonicalized; + return res; +} + +std::unique_ptr SemanticTag::clone() const { + return std::make_unique(mValue, mTaggedItem->clone()); +} + +uint8_t* SemanticTag::encode(uint8_t* pos, const uint8_t* end) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + pos = ::cppbor::encodeHeader(kMajorType, mValue, pos, end); + if (!pos) return nullptr; + return mTaggedItem->encode(pos, end); +} + +void SemanticTag::encode(EncodeCallback encodeCallback) const { + // Can't use the encodeHeader() method that calls type() to get the major type, since that will + // return the tagged Item's type. + ::cppbor::encodeHeader(kMajorType, mValue, encodeCallback); + mTaggedItem->encode(encodeCallback); +} + +size_t SemanticTag::semanticTagCount() const { + size_t levelCount = 1; // Count this level. + const SemanticTag* cur = this; + while (cur->mTaggedItem && (cur = cur->mTaggedItem->asSemanticTag()) != nullptr) ++levelCount; + return levelCount; +} + +uint64_t SemanticTag::semanticTag(size_t nesting) const { + // Getting the value of a specific nested tag is a bit tricky, because we start with the outer + // tag and don't know how many are inside. We count the number of nesting levels to find out + // how many there are in total, then to get the one we want we have to walk down levelCount - + // nesting steps. + size_t levelCount = semanticTagCount(); + if (nesting >= levelCount) return 0; + + levelCount -= nesting; + const SemanticTag* cur = this; + while (--levelCount > 0) cur = cur->mTaggedItem->asSemanticTag(); + + return cur->mValue; +} + +string prettyPrint(const Item* item, size_t maxBStrSize, const vector& mapKeysToNotPrint) { + string out; + prettyPrintInternal(item, out, 0, maxBStrSize, mapKeysToNotPrint); + return out; +} +string prettyPrint(const vector& encodedCbor, size_t maxBStrSize, + const vector& mapKeysToNotPrint) { + auto [item, _, message] = parse(encodedCbor); + if (item == nullptr) { +#ifndef __TRUSTY__ + LOG(ERROR) << "Data to pretty print is not valid CBOR: " << message; +#endif // __TRUSTY__ + return ""; + } + + return prettyPrint(item.get(), maxBStrSize, mapKeysToNotPrint); +} + +} // namespace cppbor diff --git a/ProvisioningTool/keymint/src/cppbor/cppbor_parse.cpp b/ProvisioningTool/keymint/src/cppbor/cppbor_parse.cpp new file mode 100644 index 00000000..b1803310 --- /dev/null +++ b/ProvisioningTool/keymint/src/cppbor/cppbor_parse.cpp @@ -0,0 +1,389 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "cppbor/cppbor_parse.h" + +#include + +#if !defined( __TRUSTY__) && !defined(__LINUX__) +#include +#define LOG_TAG "CppBor" +#else +#define CHECK(x) (void)(x) +#endif + +namespace cppbor { + +namespace { + +std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail, + const std::string& type) { + char buf[1024]; + snprintf(buf, sizeof(buf), "Need %zu byte(s) for %s, have %zu.", bytesNeeded, type.c_str(), + bytesAvail); + return std::string(buf); +} + +template >> +std::tuple parseLength(const uint8_t* pos, const uint8_t* end, + ParseClient* parseClient) { + if (pos + sizeof(T) > end) { + parseClient->error(pos - 1, insufficientLengthString(sizeof(T), end - pos, "length field")); + return {false, 0, pos}; + } + + const uint8_t* intEnd = pos + sizeof(T); + T result = 0; + do { + result = static_cast((result << 8) | *pos++); + } while (pos < intEnd); + return {true, result, pos}; +} + +std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, + bool emitViews, ParseClient* parseClient); + +std::tuple handleUint(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(value); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleNint(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + if (value > std::numeric_limits::max()) { + parseClient->error(hdrBegin, "NINT values that don't fit in int64_t are not supported."); + return {hdrBegin, nullptr /* end parsing */}; + } + std::unique_ptr item = std::make_unique(-1 - static_cast(value)); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleBool(uint64_t value, const uint8_t* hdrBegin, + const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(value == TRUE); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +std::tuple handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd, + ParseClient* parseClient) { + std::unique_ptr item = std::make_unique(); + return {hdrEnd, + parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; +} + +template +std::tuple handleString(uint64_t length, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end, + const std::string& errLabel, + ParseClient* parseClient) { + if (end - valueBegin < static_cast(length)) { + parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel)); + return {hdrBegin, nullptr /* end parsing */}; + } + + std::unique_ptr item = std::make_unique(valueBegin, valueBegin + length); + return {valueBegin + length, + parseClient->item(item, hdrBegin, valueBegin, valueBegin + length)}; +} + +class IncompleteItem { + public: + virtual ~IncompleteItem() {} + virtual void add(std::unique_ptr item) = 0; +}; + +class IncompleteArray : public Array, public IncompleteItem { + public: + explicit IncompleteArray(size_t size) : mSize(size) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return mSize; } + + void add(std::unique_ptr item) override { + mEntries.reserve(mSize); + mEntries.push_back(std::move(item)); + } + + private: + size_t mSize; +}; + +class IncompleteMap : public Map, public IncompleteItem { + public: + explicit IncompleteMap(size_t size) : mSize(size) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return mSize; } + + void add(std::unique_ptr item) override { + if (mKeyHeldForAdding) { + mEntries.reserve(mSize); + mEntries.push_back({std::move(mKeyHeldForAdding), std::move(item)}); + } else { + mKeyHeldForAdding = std::move(item); + } + } + + private: + std::unique_ptr mKeyHeldForAdding; + size_t mSize; +}; + +class IncompleteSemanticTag : public SemanticTag, public IncompleteItem { + public: + explicit IncompleteSemanticTag(uint64_t value) : SemanticTag(value) {} + + // We return the "complete" size, rather than the actual size. + size_t size() const override { return 1; } + + void add(std::unique_ptr item) override { mTaggedItem = std::move(item); } +}; + +std::tuple handleEntries(size_t entryCount, const uint8_t* hdrBegin, + const uint8_t* pos, const uint8_t* end, + const std::string& typeName, + bool emitViews, + ParseClient* parseClient) { + while (entryCount > 0) { + --entryCount; + if (pos == end) { + parseClient->error(hdrBegin, "Not enough entries for " + typeName + "."); + return {hdrBegin, nullptr /* end parsing */}; + } + std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient); + if (!parseClient) return {hdrBegin, nullptr}; + } + return {pos, parseClient}; +} + +std::tuple handleCompound( + std::unique_ptr item, uint64_t entryCount, const uint8_t* hdrBegin, + const uint8_t* valueBegin, const uint8_t* end, const std::string& typeName, + bool emitViews, ParseClient* parseClient) { + parseClient = + parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */); + if (!parseClient) return {hdrBegin, nullptr}; + + const uint8_t* pos; + std::tie(pos, parseClient) = + handleEntries(entryCount, hdrBegin, valueBegin, end, typeName, emitViews, parseClient); + if (!parseClient) return {hdrBegin, nullptr}; + + return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)}; +} + +std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, + bool emitViews, ParseClient* parseClient) { + const uint8_t* pos = begin; + + MajorType type = static_cast(*pos & 0xE0); + uint8_t tagInt = *pos & 0x1F; + ++pos; + + bool success = true; + uint64_t addlData; + if (tagInt < ONE_BYTE_LENGTH) { + addlData = tagInt; + } else if (tagInt > EIGHT_BYTE_LENGTH) { + parseClient->error( + begin, + "Reserved additional information value or unsupported indefinite length item."); + return {begin, nullptr}; + } else { + switch (tagInt) { + case ONE_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case TWO_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case FOUR_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + case EIGHT_BYTE_LENGTH: + std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); + break; + + default: + CHECK(false); // It's impossible to get here + break; + } + } + + if (!success) return {begin, nullptr}; + + switch (type) { + case UINT: + return handleUint(addlData, begin, pos, parseClient); + + case NINT: + return handleNint(addlData, begin, pos, parseClient); + + case BSTR: + if (emitViews) { + return handleString(addlData, begin, pos, end, "byte string", parseClient); + } else { + return handleString(addlData, begin, pos, end, "byte string", parseClient); + } + + case TSTR: + if (emitViews) { + return handleString(addlData, begin, pos, end, "text string", parseClient); + } else { + return handleString(addlData, begin, pos, end, "text string", parseClient); + } + + case ARRAY: + return handleCompound(std::make_unique(addlData), addlData, begin, pos, + end, "array", emitViews, parseClient); + + case MAP: + return handleCompound(std::make_unique(addlData), addlData * 2, begin, + pos, end, "map", emitViews, parseClient); + + case SEMANTIC: + return handleCompound(std::make_unique(addlData), 1, begin, pos, + end, "semantic", emitViews, parseClient); + + case SIMPLE: + switch (addlData) { + case TRUE: + case FALSE: + return handleBool(addlData, begin, pos, parseClient); + case NULL_V: + return handleNull(begin, pos, parseClient); + default: + parseClient->error(begin, "Unsupported floating-point or simple value."); + return {begin, nullptr}; + } + } + CHECK(false); // Impossible to get here. + return {}; +} + +class FullParseClient : public ParseClient { + public: + virtual ParseClient* item(std::unique_ptr& item, const uint8_t*, const uint8_t*, + const uint8_t* end) override { + if (mParentStack.empty() && !item->isCompound()) { + // This is the first and only item. + mTheItem = std::move(item); + mPosition = end; + return nullptr; // We're done. + } + + if (item->isCompound()) { + // Starting a new compound data item, i.e. a new parent. Save it on the parent stack. + // It's safe to save a raw pointer because the unique_ptr is guaranteed to stay in + // existence until the corresponding itemEnd() call. + mParentStack.push(item.get()); + return this; + } else { + appendToLastParent(std::move(item)); + return this; + } + } + + virtual ParseClient* itemEnd(std::unique_ptr& item, const uint8_t*, const uint8_t*, + const uint8_t* end) override { + CHECK(item->isCompound() && item.get() == mParentStack.top()); + mParentStack.pop(); + + if (mParentStack.empty()) { + mTheItem = std::move(item); + mPosition = end; + return nullptr; // We're done + } else { + appendToLastParent(std::move(item)); + return this; + } + } + + virtual void error(const uint8_t* position, const std::string& errorMessage) override { + mPosition = position; + mErrorMessage = errorMessage; + } + + std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> + parseResult() { + std::unique_ptr p = std::move(mTheItem); + return {std::move(p), mPosition, std::move(mErrorMessage)}; + } + + private: + void appendToLastParent(std::unique_ptr item) { + auto parent = mParentStack.top(); +//#if __has_feature(cxx_rtti) + assert(dynamic_cast(parent)); +//#endif + + IncompleteItem* parentItem{}; + if (parent->type() == ARRAY) { + parentItem = static_cast(parent); + } else if (parent->type() == MAP) { + parentItem = static_cast(parent); + } else if (parent->asSemanticTag()) { + parentItem = static_cast(parent); + } else { + CHECK(false); // Impossible to get here. + } + parentItem->add(std::move(item)); + } + + std::unique_ptr mTheItem; + std::stack mParentStack; + const uint8_t* mPosition = nullptr; + std::string mErrorMessage; +}; + +} // anonymous namespace + +void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { + parseRecursively(begin, end, false, parseClient); +} + +std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> +parse(const uint8_t* begin, const uint8_t* end) { + FullParseClient parseClient; + parse(begin, end, &parseClient); + return parseClient.parseResult(); +} + +void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { + parseRecursively(begin, end, true, parseClient); +} + +std::tuple /* result */, const uint8_t* /* newPos */, + std::string /* errMsg */> +parseWithViews(const uint8_t* begin, const uint8_t* end) { + FullParseClient parseClient; + parseWithViews(begin, end, &parseClient); + return parseClient.parseResult(); +} + +} // namespace cppbor diff --git a/ProvisioningTool/keymint/src/cppbor_parse.o b/ProvisioningTool/keymint/src/cppbor_parse.o new file mode 100644 index 00000000..584b3faa Binary files /dev/null and b/ProvisioningTool/keymint/src/cppbor_parse.o differ diff --git a/ProvisioningTool/keymint/src/cppcose/cppcose.cpp b/ProvisioningTool/keymint/src/cppcose/cppcose.cpp new file mode 100644 index 00000000..e0c5aaab --- /dev/null +++ b/ProvisioningTool/keymint/src/cppcose/cppcose.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cppcose { + +ErrMsgOr ECDSA_sign(const bytevec& key, bytevec& input) { + EVP_PKEY_CTX* pkeyCtx = NULL; + EVP_MD_CTX_Ptr digestCtx(EVP_MD_CTX_new()); + auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); + if (bn.get() == nullptr) { + return "Error creating BIGNUM for private key"; + } + auto privEcKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (EC_KEY_set_private_key(privEcKey.get(), bn.get()) != 1) { + return "Error setting private key from BIGNUM"; + } + auto privPkey = EVP_PKEY_Ptr(EVP_PKEY_new()); + if (EVP_PKEY_set1_EC_KEY(privPkey.get(), privEcKey.get()) != 1) { + return "Error setting private key"; + } + + if (EVP_DigestSignInit(digestCtx.get(), &pkeyCtx, EVP_sha256(), nullptr /* engine */, privPkey.get()) != + 1) { + return "Failed to do digest sign init."; + } + size_t outlen = EVP_PKEY_size(privPkey.get()); + bytevec signature(outlen); + if (!EVP_DigestSign(digestCtx.get(), signature.data(), &outlen, input.data(), input.size())) { + return "Ecdsa sign failed."; + } + return signature; +} + +bool ECDSA_verify(const bytevec& input, const bytevec& signature, const bytevec& key) { + EVP_PKEY_CTX* pkeyCtx = NULL; + EVP_MD_CTX_Ptr digestCtx(EVP_MD_CTX_new()); + auto ecGroup = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + auto ecKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (ecGroup.get() == nullptr) { + return "Failed to get EC_GROUP from curve name"; + } + auto ecPoint = EC_POINT_Ptr(EC_POINT_new(ecGroup.get())); + if (ecPoint.get() == nullptr) { + return "Failed to get EC_POINT from EC_GROUP"; + } + if (EC_POINT_oct2point(ecGroup.get(), ecPoint.get(), key.data(), key.size(), nullptr) != + 1) { + return 0; + } + // set public key + if (EC_KEY_set_public_key(ecKey.get(), ecPoint.get()) != 1) { + return 0; + } + auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); + if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { + return 0; + } + if (EVP_DigestVerifyInit(digestCtx.get(), &pkeyCtx, EVP_sha256(), nullptr /* engine */, pkey.get()) != + 1) { + return 0; + } + return EVP_DigestVerify(digestCtx.get(), signature.data(), signature.size(), input.data(), input.size()); +} + +ErrMsgOr getEcPointFromAffineCoordinates(const bytevec& pubx, const bytevec& puby) { + auto ecGroup = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + if (ecGroup.get() == nullptr) { + return "Failed to get EC_GROUP from curve name"; + } + auto ecPoint = EC_POINT_Ptr(EC_POINT_new(ecGroup.get())); + if (ecPoint.get() == nullptr) { + return "Failed to get EC_POINT from EC_GROUP"; + } + auto bn_x = BIGNUM_Ptr(BN_bin2bn(pubx.data(), pubx.size(), nullptr)); + if (bn_x.get() == nullptr) { + return "Error creating BIGNUM for peer public key X coordinate"; + } + auto bn_y = BIGNUM_Ptr(BN_bin2bn(puby.data(), puby.size(), nullptr)); + if (bn_y.get() == nullptr) { + return "Error creating BIGNUM for peer public key Y coordinate"; + } + if (!EC_POINT_set_affine_coordinates(ecGroup.get(), ecPoint.get(), bn_x.get(), bn_y.get(), + nullptr)) { + return "Failed to set affine coordinates"; + } + size_t pubKeyLen; + pubKeyLen = EC_POINT_point2oct(ecGroup.get(), ecPoint.get(), POINT_CONVERSION_UNCOMPRESSED, + nullptr, 0, nullptr); + if (pubKeyLen == 0) { + return "Failed to convert EC_POINT to buffer."; + } + bytevec pubkey(pubKeyLen); + EC_POINT_point2oct(ecGroup.get(), ecPoint.get(), POINT_CONVERSION_UNCOMPRESSED, pubkey.data(), + pubKeyLen, nullptr); + return pubkey; +} + +ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec signatureInput = cppbor::Array() + .add("Signature1") // + .add(protectedParams) + .add(aad) + .add(payload) + .encode(); + auto signature = ECDSA_sign(key, signatureInput); + if (!signature) return "Signing failed"; + return signature; +} + +ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec protParms = protectedParams.add(ALGORITHM, ES256).canonicalize().encode(); + auto signature = createCoseSign1Signature(key, protParms, payload, aad); + if (!signature) return signature.moveMessage(); + + return cppbor::Array() + .add(std::move(protParms)) + .add(cppbor::Map() /* unprotected parameters */) + .add(std::move(payload)) + .add(std::move(*signature)); +} + +ErrMsgOr constructCoseSign1(const bytevec& key, const bytevec& payload, + const bytevec& aad) { + return constructCoseSign1(key, {} /* protectedParams */, payload, aad); +} + +ErrMsgOr verifyAndParseCoseSign1(bool ignoreSignature, const cppbor::Array* coseSign1, + const bytevec& signingCoseKey, const bytevec& aad) { + if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) { + return "Invalid COSE_Sign1"; + } + + const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr(); + const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap(); + const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr(); + + if (!protectedParams || !unprotectedParams || !payload) { + return "Missing input parameters"; + } + + auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams); + if (!parsedProtParams) { + return errMsg + " when parsing protected params."; + } + if (!parsedProtParams->asMap()) { + return "Protected params must be a map"; + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); + if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { + return "Unsupported signature algorithm"; + } + + if (!ignoreSignature) { + const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr(); + if (!signature || signature->value().empty()) { + return "Missing signature input"; + } + + bool selfSigned = signingCoseKey.empty(); + + bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); + + auto key = + CoseKey::parseP256(selfSigned ? payload->value() : signingCoseKey); + if (!key) return "Bad signing key: " + key.moveMessage(); + + + auto pubkey = getEcPointFromAffineCoordinates( + *key->getBstrValue(CoseKey::PUBKEY_X), *key->getBstrValue(CoseKey::PUBKEY_Y)); + if (!pubkey) return pubkey.moveMessage(); + + if (!ECDSA_verify(signatureInput, signature->value(), *pubkey)) { + return "Signature verification failed"; + } + } + + return payload->value(); +} +} // namespace cppcose diff --git a/ProvisioningTool/keymint/src/provision.cpp b/ProvisioningTool/keymint/src/provision.cpp new file mode 100644 index 00000000..e70e5ce8 --- /dev/null +++ b/ProvisioningTool/keymint/src/provision.cpp @@ -0,0 +1,329 @@ +/* + ** + ** Copyright 2021, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#include +#include +#include +#include +#include "socket.h" +#include +#include +#include +#include +#include +#include + +#define SE_POWER_RESET_STATUS_FLAG (1 << 30) +// 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_BOOT_PARAM = 0x20, + PROVISION_STATUS_PROVISIONING_LOCKED = 0x40, +}; + +// TODO keymint provision status and lock +std::string provisionStatusApdu = hex2str("80084000000000"); +std::string lockProvisionApdu = hex2str("80074000000000"); + +Json::Value root; +static std::string inputFileName; +using cppbor::Item; +using cppbor::Array; +using cppbor::Uint; +using cppbor::MajorType; + +// static function declarations +static uint16_t getApduStatus(std::vector& inputData); +static int sendData(std::shared_ptr& pSocket, std::string input, std::vector& response); +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); + + +// Print usage. +void usage() { + printf("Usage: Please consturcture the apdu(s) with help of construct apdu tool and pass the output file to this utility.\n"); + printf("provision_keymint [options]\n"); + printf("Valid options are:\n"); + 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"); + +} + +static uint16_t getApduStatus(std::vector& inputData) { + // Last two bytes are the status SW0SW1 + uint8_t SW0 = inputData.at(inputData.size() - 2); + uint8_t SW1 = inputData.at(inputData.size() - 1); + return (SW0 << 8 | SW1); +} + +static int sendData(std::shared_ptr& pSocket, std::string input, std::vector& response) { + + std::vector apdu(input.begin(), input.end()); + + if(!pSocket->sendData(apdu, response)) { + std::cout << "Failed to provision attestation key" << std::endl; + return FAILURE; + } + + // Response size should be greater than 2. Cbor output data followed by two bytes of APDU + // status. + if ((response.size() <= 2) || (getApduStatus(response) != APDU_RESP_STATUS_OK)) { + printf("\n Received error response with error: %d\n", getApduStatus(response)); + return FAILURE; + } + // remove the status bytes + response.pop_back(); + response.pop_back(); + return SUCCESS; +} + +int getUint64(const std::unique_ptr &item, const uint32_t pos, uint64_t* value) { + Array *arr = nullptr; + + if (MajorType::ARRAY != item.get()->type()) { + return FAILURE; + } + arr = const_cast(item.get()->asArray()); + if (arr->size() < (pos + 1)) { + return FAILURE; + } + *value = arr->get(pos)->asUint()->value(); + return SUCCESS; +} + + +uint64_t unmaskPowerResetFlag(uint64_t errorCode) { + bool isSeResetOccurred = (0 != (errorCode & SE_POWER_RESET_STATUS_FLAG)); + + if (isSeResetOccurred) { + printf("\n Secure element reset happened\n"); + errorCode &= ~SE_POWER_RESET_STATUS_FLAG; + } + return errorCode; +} + +int provisionData(std::shared_ptr& pSocket, std::string apdu, std::vector& response) { + if (SUCCESS != sendData(pSocket, apdu, response)) { + return FAILURE; + } + auto [item, pos, message] = cppbor::parse(response); + if(item != nullptr) { + uint64_t err; + if(MajorType::ARRAY == item.get()->type()) { + if(SUCCESS != getUint64(item, 0, &err)) { + printf("\n Failed to parse the error code \n"); + return FAILURE; + } + } else if (MajorType::UINT == item.get()->type()) { + const Uint* uintVal = item.get()->asUint(); + err = uintVal->value(); + } + err = unmaskPowerResetFlag(err); + if (err != 0) { + printf("\n Failed with error:%ld", err); + return FAILURE; + } + } else { + printf("\n Failed to parse the response\n"); + return FAILURE; + } + return SUCCESS; +} + +int provisionData(std::shared_ptr& pSocket, const char* jsonKey) { + Json::Value val = root.get(jsonKey, Json::Value::nullRef); + if (!val.isNull()) { + if (val.isString()) { + std::vector response; + if (SUCCESS != provisionData(pSocket, hex2str(val.asString()), response)) { + printf("\n Error while provisioning %s \n", jsonKey); + return FAILURE; + } + } else { + printf("\n Fail: Expected (%s) tag value is string. \n", jsonKey); + return FAILURE; + } + } + printf("\n Successfully provisioned %s \n", jsonKey); + return SUCCESS; +} + +int openConnection(std::shared_ptr& pSocket) { + if (!pSocket->isConnected()) { + if (!pSocket->openConnection()) + return FAILURE; + } else { + printf("\n Socket already opened.\n"); + } + return SUCCESS; +} + +// Parses the input json file. Sends the apdus to JCServer. +int processInputFile() { + // Parse Json file + if (0 != readJsonFile(root, inputFileName)) { + return FAILURE; + } + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + 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)) { + return FAILURE; + } + return SUCCESS; +} + +int lockProvision() { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + if (SUCCESS != provisionData(pSocket, lockProvisionApdu, response)) { + printf("\n Failed to lock provision.\n"); + return FAILURE; + } + printf("\n Provision lock is successfull.\n"); + return SUCCESS; +} + +int getProvisionStatus() { + std::vector response; + std::shared_ptr pSocket = SocketTransport::getInstance(); + if (SUCCESS != openConnection(pSocket)) { + printf("\n Failed to open connection \n"); + return FAILURE; + } + + if (SUCCESS != provisionData(pSocket, provisionStatusApdu, response)) { + printf("\n Failed to get provision status \n"); + return FAILURE; + } + auto [item, pos, message] = cppbor::parse(response); + 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_ATTESTATION_KEY)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_PRESHARED_SECRET)) && + (0 != (status & ProvisionStatus::PROVISION_STATUS_BOOT_PARAM))) { + printf("\n SE is provisioned \n"); + } else { + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_KEY)) { + printf("\n Attestation key is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_CHAIN)) { + printf("\n Attestation certificate chain is not provisioned \n"); + } + if (0 == (status & ProvisionStatus::PROVISION_STATUS_ATTESTATION_CERT_PARAMS)) { + 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_BOOT_PARAM)) { + printf("\n Boot params are not provisioned \n"); + } + } + } else { + printf("\n Fail to parse the response \n"); + return FAILURE; + } + return SUCCESS; +} + +int main(int argc, char* argv[]) { + int c; + bool provisionStatusSet = false; + bool lockProvisionSet = false; + + struct option longOpts[] = { + {"input", required_argument, NULL, 'i'}, + {"provision_status", no_argument, NULL, 's'}, + {"lock_provision", no_argument, NULL, 'l'}, + {"help", no_argument, NULL, 'h'}, + {0,0,0,0} + }; + + if (argc <= 1) { + printf("\n Invalid command \n"); + usage(); + return FAILURE; + } + + /* getopt_long stores the option index here. */ + while ((c = getopt_long(argc, argv, ":hls:i:", longOpts, NULL)) != -1) { + switch(c) { + case 'i': + // input file + inputFileName = std::string(optarg); + std::cout << "input file: " << inputFileName << std::endl; + break; + case 's': + provisionStatusSet = true; + break; + case 'l': + lockProvisionSet = true; + break; + case 'h': + // help + usage(); + return SUCCESS; + case ':': + printf("\n Required arguments missing.\n"); + usage(); + return FAILURE; + case '?': + default: + printf("\n Invalid option\n"); + usage(); + return FAILURE; + } + } + // Process input file; send apuds to JCServer over socket. + if (argc >= 3) { + if (SUCCESS != processInputFile()) { + return FAILURE; + } + } + if (provisionStatusSet) + getProvisionStatus(); + if (lockProvisionSet) + lockProvision(); + return SUCCESS; +} + + diff --git a/ProvisioningTool/keymint/src/socket.cpp b/ProvisioningTool/keymint/src/socket.cpp new file mode 100644 index 00000000..137cab3b --- /dev/null +++ b/ProvisioningTool/keymint/src/socket.cpp @@ -0,0 +1,109 @@ +/* + ** + ** Copyright 2021, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "socket.h" + +#define PORT 8080 +#define IPADDR "127.0.0.1" +#define MAX_RECV_BUFFER_SIZE 2500 + +using namespace std; + +SocketTransport::~SocketTransport() { + if (closeConnection()) + std::cout << "Socket is closed"; +} + +bool SocketTransport::openConnection() { + struct sockaddr_in serv_addr; + if ((mSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("Socket "); + return false; + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(PORT); + + // Convert IPv4 and IPv6 addresses from text to binary form + if (inet_pton(AF_INET, IPADDR, &serv_addr.sin_addr) <= 0) { + std::cout << "Invalid address/ Address not supported."; + return false; + } + + if (connect(mSocket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { + close(mSocket); + perror("Socket "); + return false; + } + socketStatus = true; + return true; +} + +bool SocketTransport::sendData(const std::vector& inData, std::vector& output) { + uint8_t buffer[MAX_RECV_BUFFER_SIZE]; + int count = 1; + while (!socketStatus && count++ < 5) { + sleep(1); + std::cout << "Trying to open socket connection... count: " << count; + openConnection(); + } + + if (count >= 5) { + std::cout << "Failed to open socket connection"; + return false; + } + + if (0 > send(mSocket, inData.data(), inData.size(), 0)) { + static int connectionResetCnt = 0; /* To avoid loop */ + if (ECONNRESET == errno && connectionResetCnt == 0) { + // Connection reset. Try open socket and then sendData. + socketStatus = false; + connectionResetCnt++; + return sendData(inData, output); + } + std::cout << "Failed to send data over socket err: " << errno; + connectionResetCnt = 0; + return false; + } + + ssize_t valRead = read(mSocket, buffer, MAX_RECV_BUFFER_SIZE); + if (0 > valRead) { + std::cout << "Failed to read data from socket."; + } + for (ssize_t i = 0; i < valRead; i++) { + output.push_back(buffer[i]); + } + return true; +} + +bool SocketTransport::closeConnection() { + close(mSocket); + socketStatus = false; + return true; +} + +bool SocketTransport::isConnected() { + return socketStatus; +} + diff --git a/ProvisioningTool/keymint/src/utils.cpp b/ProvisioningTool/keymint/src/utils.cpp new file mode 100644 index 00000000..41ad8a6c --- /dev/null +++ b/ProvisioningTool/keymint/src/utils.cpp @@ -0,0 +1,96 @@ +/* + ** + ** Copyright 2021, The Android Open Source Project + ** + ** Licensed under the Apache License, Version 2.0 (the "License"); + ** you may not use this file except in compliance with the License. + ** You may obtain a copy of the License at + ** + ** http://www.apache.org/licenses/LICENSE-2.0 + ** + ** Unless required by applicable law or agreed to in writing, software + ** distributed under the License is distributed on an "AS IS" BASIS, + ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ** See the License for the specific language governing permissions and + ** limitations under the License. + */ +#include +#include +#include +#include + + +constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9' + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f' + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +std::string getHexString(std::vector& input) { + std::stringstream ss; + for (auto b : input) { + ss << std::setw(2) << std::setfill('0') << std::hex << (int) (b & 0xFF); + } + return ss.str(); +} + + +std::string hex2str(std::string a) { + std::string b; + size_t num = a.size() / 2; + b.resize(num); + for (size_t i = 0; i < num; i++) { + b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]); + } + return b; +} + + +// Parses the json file and returns 0 if success; otherwise 1. +int readJsonFile(Json::Value& root, std::string& inputFileName) { + Json::CharReaderBuilder builder; + std::string errorMessage; + + if(!root.empty()) { + printf("\n Already parsed \n"); + return 1; + } + std::ifstream stream(inputFileName); + if (Json::parseFromStream(builder, stream, &root, &errorMessage)) { + printf("\n Parsed json file successfully.\n"); + return 0; + } else { + printf("\n Failed to parse json file error:%s\n", errorMessage.c_str()); + return 1; + } +} + +// Write the json data to the output file. +int writeJsonFile(Json::Value& writerRoot, std::string& outputFileName) { + + std::ofstream ofs; + // Delete file if already exists. + std::remove(outputFileName.data()); + ofs.open(outputFileName, std::ofstream::out | std::ios_base::app); + if (ofs.fail()) { + printf("\n Fail to open the output file:%s", outputFileName.c_str()); + return FAILURE; + } + + Json::StyledWriter styledWriter; + ofs << styledWriter.write(writerRoot); + + ofs.close(); + return SUCCESS; +} \ No newline at end of file diff --git a/ProvisioningTool/keymint/test_resources/batch_cert.der b/ProvisioningTool/keymint/test_resources/batch_cert.der new file mode 100644 index 00000000..355bc984 Binary files /dev/null and b/ProvisioningTool/keymint/test_resources/batch_cert.der differ diff --git a/ProvisioningTool/keymint/test_resources/batch_key.der b/ProvisioningTool/keymint/test_resources/batch_key.der new file mode 100644 index 00000000..f4902073 Binary files /dev/null and b/ProvisioningTool/keymint/test_resources/batch_key.der differ diff --git a/ProvisioningTool/keymint/test_resources/batch_key.pem b/ProvisioningTool/keymint/test_resources/batch_key.pem new file mode 100644 index 00000000..95ea1988 --- /dev/null +++ b/ProvisioningTool/keymint/test_resources/batch_key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg/AbtV+kD2f4/MjQP +02kKTegLCM0XHF/l06o00gkLshqhRANCAAT3hPSu8oDK4OY4Y4M5ZddMPXUTejvN +GsqhSx2haqIT9fXukJLrj2ex0KJuAhqDEltojlBlNWah7oZiIuYAYVSG +-----END PRIVATE KEY----- diff --git a/ProvisioningTool/keymint/test_resources/ca_cert.der b/ProvisioningTool/keymint/test_resources/ca_cert.der new file mode 100644 index 00000000..f574a4c5 Binary files /dev/null and b/ProvisioningTool/keymint/test_resources/ca_cert.der differ diff --git a/ProvisioningTool/keymint/test_resources/ca_key.der b/ProvisioningTool/keymint/test_resources/ca_key.der new file mode 100644 index 00000000..e687b07f Binary files /dev/null and b/ProvisioningTool/keymint/test_resources/ca_key.der differ diff --git a/ProvisioningTool/keymint/test_resources/intermediate_cert.der b/ProvisioningTool/keymint/test_resources/intermediate_cert.der new file mode 100644 index 00000000..615f423e Binary files /dev/null and b/ProvisioningTool/keymint/test_resources/intermediate_cert.der differ diff --git a/ProvisioningTool/keymint/test_resources/intermediate_key.der b/ProvisioningTool/keymint/test_resources/intermediate_key.der new file mode 100644 index 00000000..888e4f38 Binary files /dev/null and b/ProvisioningTool/keymint/test_resources/intermediate_key.der differ diff --git a/README.md b/README.md index 670e94a1..55d34d74 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# JavaCardKeymaster -JavaCard implementation of the [Android Keymaster 4.1 HAL](https://android.googlesource.com/platform/hardware/interfaces/+/master/keymaster/4.1/IKeymasterDevice.hal) (most of the specification is in the [Android Keymaster 4.0 HAL](https://android.googlesource.com/platform/hardware/interfaces/+/master/keymaster/4.0/IKeymasterDevice.hal)), intended for creation of StrongBox Keymaster instances to support the [Android Hardware-backed Keystore](https://source.android.com/security/keystore). +**JavaCardKeymaster** +JavaCard implementation of the following: +1) [Android Keymint HAL](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/) +2) [Android SharedSecret HAL](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/sharedsecret/aidl/android/hardware/security/sharedsecret/) +3) [Remote Key Provisiong HAL](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl) -Here is the [JavaCard Applet design doc](https://docs.google.com/document/d/1bTAmhDqCNq1HYzChNDv8kLJEi64cwTIZ2PfdMMz3o8U/edit#heading=h.gjdgxs) and the [HAL design doc](https://docs.google.com/document/d/1-1MLJ781wAPJ2YxCdCtHMepld8F8KVAxpPtCw9J3b3o/edit#heading=h.gjdgxs) (the content will move here when it stablizes, for now these are a limited-access links). diff --git a/TestingTools/JCProxy/JCProxy.iml b/TestingTools/JCProxy/JCProxy.iml new file mode 100644 index 00000000..b71f1e5b --- /dev/null +++ b/TestingTools/JCProxy/JCProxy.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java index 2de1feba..becbdb30 100644 --- a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java +++ b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCProxyMain.java @@ -15,18 +15,36 @@ */ public class JCProxyMain { + public static final String KEYMASTER = "keymaster"; + public static final String KEYMINT = "keymint"; + public static final byte KEYMASTER_INSTALL_PARAM = 0x01; + public static final byte KEYMINT_INSTALL_PARAM = 0x02; + + public static byte getKMInstallationParamter(String kmSpecificVersion) { + if (kmSpecificVersion.equals(KEYMASTER)) { + return KEYMASTER_INSTALL_PARAM; + } else { + return KEYMINT_INSTALL_PARAM; + } + } + public static void main(String[] args) { - if (args.length < 1) { + if (args.length < 2) { System.out.println("Port no is expected as argument."); return; } int port = Integer.parseInt(args[0]); + String specificationImpl = args[1]; + if (!(specificationImpl.equals("keymaster") || specificationImpl.equals("keymint"))) { + System.out.println("Specification string should be either keymaster or keymint."); + return; + } Simulator simulator = new JCardSimulator(); try (ServerSocket serverSocket = new ServerSocket(port)) { simulator.initaliseSimulator(); - if (!simulator.setupKeymasterOnSimulator()) { + if (!simulator.setupKeymasterOnSimulator(getKMInstallationParamter(specificationImpl))) { System.out.println("Failed to setup Java card keymaster simulator."); System.exit(-1); } @@ -35,7 +53,7 @@ public static void main(String[] args) { while (true) { try { Socket socket = serverSocket.accept(); - System.out.println("\n\n\n\n\n"); + System.out.println("\n"); System.out.println("------------------------New client connected on " + socket.getPort() + "--------------------"); OutputStream output = null; diff --git a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCardSimulator.java b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCardSimulator.java index 7af495f3..1333c1e1 100644 --- a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCardSimulator.java +++ b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/JCardSimulator.java @@ -30,9 +30,12 @@ public void disconnectSimulator() throws Exception { } @Override - public boolean setupKeymasterOnSimulator() throws Exception { + public boolean setupKeymasterOnSimulator(byte applicationSpecificParam) throws Exception { AID appletAID1 = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID1, KMJCardSimApplet.class); + byte[] data = new byte[2]; + data[0] = 0x01; // length + data[1] = applicationSpecificParam; + simulator.installApplet(appletAID1, KMJCardSimApplet.class, data, (short) 0, (byte) data.length); // Select applet simulator.selectApplet(appletAID1); return true; diff --git a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/Simulator.java b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/Simulator.java index 6c4f9bbc..43fecd30 100644 --- a/TestingTools/JCProxy/src/com/android/javacard/jcproxy/Simulator.java +++ b/TestingTools/JCProxy/src/com/android/javacard/jcproxy/Simulator.java @@ -7,7 +7,7 @@ public interface Simulator { void disconnectSimulator() throws Exception; - public boolean setupKeymasterOnSimulator() throws Exception; + public boolean setupKeymasterOnSimulator(byte applicationSpecificParam) throws Exception; byte[] executeApdu(byte[] apdu) throws Exception; diff --git a/aosp_integration_patches/cts_tests_tests_keystore.patch b/patches/keymaster/aosp_integration_patches/cts_tests_tests_keystore.patch similarity index 100% rename from aosp_integration_patches/cts_tests_tests_keystore.patch rename to patches/keymaster/aosp_integration_patches/cts_tests_tests_keystore.patch diff --git a/aosp_integration_patches/device_google_cuttlefish.patch b/patches/keymaster/aosp_integration_patches/device_google_cuttlefish.patch similarity index 100% rename from aosp_integration_patches/device_google_cuttlefish.patch rename to patches/keymaster/aosp_integration_patches/device_google_cuttlefish.patch diff --git a/aosp_integration_patches/hardware_interfaces_keymaster.patch b/patches/keymaster/aosp_integration_patches/hardware_interfaces_keymaster.patch similarity index 100% rename from aosp_integration_patches/hardware_interfaces_keymaster.patch rename to patches/keymaster/aosp_integration_patches/hardware_interfaces_keymaster.patch diff --git a/aosp_integration_patches/omapi_patches/JavacardKeymaster.patch b/patches/keymaster/aosp_integration_patches/omapi_patches/JavacardKeymaster.patch similarity index 100% rename from aosp_integration_patches/omapi_patches/JavacardKeymaster.patch rename to patches/keymaster/aosp_integration_patches/omapi_patches/JavacardKeymaster.patch diff --git a/aosp_integration_patches/omapi_patches/packages_apps_secureElement.patch b/patches/keymaster/aosp_integration_patches/omapi_patches/packages_apps_secureElement.patch similarity index 100% rename from aosp_integration_patches/omapi_patches/packages_apps_secureElement.patch rename to patches/keymaster/aosp_integration_patches/omapi_patches/packages_apps_secureElement.patch diff --git a/aosp_integration_patches/system_sepolicy.patch b/patches/keymaster/aosp_integration_patches/system_sepolicy.patch similarity index 100% rename from aosp_integration_patches/system_sepolicy.patch rename to patches/keymaster/aosp_integration_patches/system_sepolicy.patch diff --git a/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch b/patches/keymaster/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch similarity index 100% rename from aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch rename to patches/keymaster/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch diff --git a/aosp_integration_patches_aosp_12_r15/hardware_interfaces_keymaster.patch b/patches/keymaster/aosp_integration_patches_aosp_12_r15/hardware_interfaces_keymaster.patch similarity index 100% rename from aosp_integration_patches_aosp_12_r15/hardware_interfaces_keymaster.patch rename to patches/keymaster/aosp_integration_patches_aosp_12_r15/hardware_interfaces_keymaster.patch diff --git a/aosp_integration_patches_aosp_12_r15/system_security_keystore2.patch b/patches/keymaster/aosp_integration_patches_aosp_12_r15/system_security_keystore2.patch similarity index 100% rename from aosp_integration_patches_aosp_12_r15/system_security_keystore2.patch rename to patches/keymaster/aosp_integration_patches_aosp_12_r15/system_security_keystore2.patch diff --git a/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch b/patches/keymaster/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch similarity index 100% rename from aosp_integration_patches_aosp_12_r15/system_sepolicy.patch rename to patches/keymaster/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch diff --git a/patches/keymint/aosp_integration_patches/device_google_cuttlefish.patch b/patches/keymint/aosp_integration_patches/device_google_cuttlefish.patch new file mode 100644 index 00000000..1f5e0765 --- /dev/null +++ b/patches/keymint/aosp_integration_patches/device_google_cuttlefish.patch @@ -0,0 +1,60 @@ +diff --git a/shared/device.mk b/shared/device.mk +index 54042c107..622c6a549 100644 +--- a/shared/device.mk ++++ b/shared/device.mk +@@ -607,6 +607,9 @@ endif + PRODUCT_PACKAGES += \ + $(LOCAL_KEYMINT_PRODUCT_PACKAGE) + ++PRODUCT_PACKAGES += \ ++ android.hardware.security.keymint-service.strongbox \ ++ + # Keymint configuration + ifneq ($(LOCAL_PREFER_VENDOR_APEX),true) + PRODUCT_COPY_FILES += \ +diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts +index 55b8d964e..a0e88fb2e 100644 +--- a/shared/sepolicy/vendor/file_contexts ++++ b/shared/sepolicy/vendor/file_contexts +@@ -85,6 +85,7 @@ + /vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default u:object_r:hal_input_classifier_default_exec:s0 + /vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.mock u:object_r:hal_thermal_default_exec:s0 + /vendor/bin/hw/android\.hardware\.security\.keymint-service\.remote u:object_r:hal_keymint_remote_exec:s0 ++/vendor/bin/hw/android\.hardware\.security\.keymint-service\.strongbox u:object_r:hal_keymint_strongbox_exec:s0 + /vendor/bin/hw/android\.hardware\.keymaster@4\.1-service.remote u:object_r:hal_keymaster_remote_exec:s0 + /vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service.remote u:object_r:hal_gatekeeper_remote_exec:s0 + /vendor/bin/hw/android\.hardware\.confirmationui@1\.0-service.cuttlefish u:object_r:hal_confirmationui_cuttlefish_exec:s0 +diff --git a/shared/sepolicy/vendor/hal_keymint_strongbox.te b/shared/sepolicy/vendor/hal_keymint_strongbox.te +new file mode 100644 +index 000000000..839fd1a6b +--- /dev/null ++++ b/shared/sepolicy/vendor/hal_keymint_strongbox.te +@@ -0,0 +1,14 @@ ++type hal_keymint_strongbox, domain; ++hal_server_domain(hal_keymint_strongbox, hal_keymint) ++ ++type hal_keymint_strongbox_exec, exec_type, vendor_file_type, file_type; ++init_daemon_domain(hal_keymint_strongbox) ++ ++vndbinder_use(hal_keymint_strongbox) ++get_prop(hal_keymint_strongbox, vendor_security_patch_level_prop); ++ ++# Allow access to sockets ++allow hal_keymint_strongbox self:tcp_socket { connect create write read getattr getopt setopt }; ++allow hal_keymint_strongbox port_type:tcp_socket name_connect; ++allow hal_keymint_strongbox port:tcp_socket { name_connect }; ++allow hal_keymint_strongbox vendor_data_file:file { open read getattr }; +diff --git a/shared/sepolicy/vendor/service_contexts b/shared/sepolicy/vendor/service_contexts +index d20d026cf..b8f0155ab 100644 +--- a/shared/sepolicy/vendor/service_contexts ++++ b/shared/sepolicy/vendor/service_contexts +@@ -4,6 +4,9 @@ android.hardware.neuralnetworks.IDevice/nnapi-sample_float_slow u:object_r:hal_n + android.hardware.neuralnetworks.IDevice/nnapi-sample_minimal u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_quant u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_sl_shim u:object_r:hal_neuralnetworks_service:s0 ++android.hardware.security.keymint.IKeyMintDevice/strongbox u:object_r:hal_keymint_service:s0 ++android.hardware.security.sharedsecret.ISharedSecret/strongbox u:object_r:hal_sharedsecret_service:s0 ++android.hardware.security.keymint.IRemotelyProvisionedComponent/strongbox u:object_r:hal_keymint_service:s0 + + # Binder service mappings + gce u:object_r:gce_service:s0 diff --git a/patches/keymint/aosp_integration_patches/hardware_interfaces.patch b/patches/keymint/aosp_integration_patches/hardware_interfaces.patch new file mode 100644 index 00000000..15df3c87 --- /dev/null +++ b/patches/keymint/aosp_integration_patches/hardware_interfaces.patch @@ -0,0 +1,1129 @@ +diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml +index c39db36ae..39cce859b 100644 +--- a/compatibility_matrices/compatibility_matrix.current.xml ++++ b/compatibility_matrices/compatibility_matrix.current.xml +@@ -355,6 +355,7 @@ + + IRemotelyProvisionedComponent + default ++ strongbox + + + +diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +index 64550eff2..061efa87c 100644 +--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp ++++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +@@ -198,7 +198,7 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { + AttestationKey attest_key; + vector attest_key_characteristics; + vector attest_key_cert_chain; +- ASSERT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .RsaKey(2048, 65537) + .AttestKey() +@@ -209,7 +209,14 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + {} /* attestation signing key */, &attest_key.keyBlob, +- &attest_key_characteristics, &attest_key_cert_chain)); ++ &attest_key_characteristics, &attest_key_cert_chain); ++ //Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + + EXPECT_GT(attest_key_cert_chain.size(), 1); + verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false); +@@ -297,7 +304,7 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { + attest_key_opt = attest_key; + } + +- EXPECT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .RsaKey(2048, 65537) + .AttestKey() +@@ -308,7 +315,14 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ &cert_chain_list[i]); ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); +@@ -369,7 +383,7 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { + attest_key_opt = attest_key; + } + +- EXPECT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .EcdsaKey(EcCurve::P_256) + .AttestKey() +@@ -380,7 +394,13 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ &cert_chain_list[i]); ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); +@@ -420,6 +440,10 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { + * cross sign each other and be chained together. + */ + TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ // Strongbox does not support factory attest keys. ++ GTEST_SKIP() << "Test not applicable to StrongBox device"; ++ } + const int chain_size = 6; + vector> key_blob_list(chain_size); + vector> cert_chain_list(chain_size); +@@ -443,6 +467,7 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { + attest_key_opt = attest_key; + } + ++ + if ((i & 0x1) == 1) { + EXPECT_EQ(ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder() +diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +index 6140df135..b73d325c6 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp ++++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +@@ -1156,6 +1156,15 @@ vector KeyMintAidlTestBase::InvalidCurves() { + } + } + ++vector KeyMintAidlTestBase::ValidExponents() { ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ return {65537}; ++ } else { ++ return {3, 65537}; ++ } ++} ++ ++ + vector KeyMintAidlTestBase::ValidDigests(bool withNone, bool withMD5) { + switch (SecLevel()) { + case SecurityLevel::SOFTWARE: +diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +index 7b3b9d4b4..c564d509a 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h ++++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +@@ -250,7 +250,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + .SetDefaultValidity(); + tagModifier(&rsaBuilder); + errorCode = GenerateKey(rsaBuilder, &rsaKeyData.blob, &rsaKeyData.characteristics); +- EXPECT_EQ(expectedReturn, errorCode); ++ if (!(SecLevel() == SecurityLevel::STRONGBOX && ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED == errorCode)) { ++ EXPECT_EQ(expectedReturn, errorCode); ++ } + + /* ECDSA */ + KeyData ecdsaKeyData; +@@ -262,7 +264,10 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + .SetDefaultValidity(); + tagModifier(&ecdsaBuilder); + errorCode = GenerateKey(ecdsaBuilder, &ecdsaKeyData.blob, &ecdsaKeyData.characteristics); +- EXPECT_EQ(expectedReturn, errorCode); ++ if (!(SecLevel() == SecurityLevel::STRONGBOX && ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED == errorCode)) { ++ EXPECT_EQ(expectedReturn, errorCode); ++ } ++ + return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}; + } + bool IsSecure() const { return securityLevel_ != SecurityLevel::SOFTWARE; } +@@ -279,6 +284,7 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + vector InvalidCurves(); + + vector ValidDigests(bool withNone, bool withMD5); ++ vector ValidExponents(); + + static vector build_params() { + auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor); +diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp +index 2a7911cc3..1af544617 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp ++++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp +@@ -912,8 +912,8 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { + for (auto key_size : ValidKeySizes(Algorithm::RSA)) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) +@@ -923,8 +923,14 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); + ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -1045,8 +1051,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { + + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 65537) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) +@@ -1055,8 +1060,14 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); + ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + AuthorizationSet auths; + for (auto& entry : key_characteristics) { +@@ -1157,15 +1168,21 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { + vector key_blob; + vector key_characteristics; + +- ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); + } + + /* +@@ -1275,8 +1292,8 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { + for (auto key_size : ValidKeySizes(Algorithm::RSA)) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) +@@ -1287,7 +1304,14 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); +@@ -1418,8 +1442,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { + for (auto curve : ValidCurves()) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) +@@ -1428,7 +1452,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -1506,6 +1538,12 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { + // Tag not required to be supported by all KeyMint implementations. + continue; + } ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ continue; ++ } + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + +@@ -1555,8 +1593,14 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + builder.push_back(tag); +- ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS, +- GenerateKey(builder, &key_blob, &key_characteristics)); ++ ++ auto result = GenerateKey(builder, &key_blob, &key_characteristics); ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ continue; ++ } ++ ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS, result); + } + } + +@@ -1567,6 +1611,10 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { + * attestation extension. + */ + TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) { ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ // Strongbox does not support factory attest keys. ++ GTEST_SKIP() << "Test not applicable to StrongBox device"; ++ } + auto challenge = "hello"; + auto app_id = "foo"; + auto subject = "cert subj 2"; +@@ -1634,6 +1682,10 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationIdTags) { + * Verifies that creation of an attested ECDSA key with a UNIQUE_ID included. + */ + TEST_P(NewKeyGenerationTest, EcdsaAttestationUniqueId) { ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ // Strongbox does not support factory attest keys. ++ GTEST_SKIP() << "Test not applicable to StrongBox device"; ++ } + auto get_unique_id = [this](const std::string& app_id, uint64_t datetime, + vector* unique_id, bool reset = false) { + auto challenge = "hello"; +@@ -1756,6 +1808,13 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + +@@ -1834,13 +1893,19 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { + vector key_blob; + vector key_characteristics; + +- ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); + } + + /* +@@ -1897,14 +1962,21 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { + const string app_id(length, 'a'); + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -3945,25 +4017,27 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; + * Verifies that raw RSA decryption works. + */ + TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) { +- for (uint64_t exponent : {3, 65537}) { +- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() +- .Authorization(TAG_NO_AUTH_REQUIRED) +- .RsaEncryptionKey(2048, exponent) +- .Padding(PaddingMode::NONE) +- .SetDefaultValidity())); + +- string message = string(2048 / 8, 'a'); +- auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); +- string ciphertext1 = LocalRsaEncryptMessage(message, params); +- EXPECT_EQ(2048U / 8, ciphertext1.size()); ++ for (uint64_t exponent : ValidExponents()) ++ { ++ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .RsaEncryptionKey(2048, exponent) ++ .Padding(PaddingMode::NONE) ++ .SetDefaultValidity())); + +- string ciphertext2 = LocalRsaEncryptMessage(message, params); +- EXPECT_EQ(2048U / 8, ciphertext2.size()); ++ string message = string(2048 / 8, 'a'); ++ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); ++ string ciphertext1 = LocalRsaEncryptMessage(message, params); ++ EXPECT_EQ(2048U / 8, ciphertext1.size()); + +- // Unpadded RSA is deterministic +- EXPECT_EQ(ciphertext1, ciphertext2); ++ string ciphertext2 = LocalRsaEncryptMessage(message, params); ++ EXPECT_EQ(2048U / 8, ciphertext2.size()); + +- CheckedDeleteKey(); ++ // Unpadded RSA is deterministic ++ EXPECT_EQ(ciphertext1, ciphertext2); ++ ++ CheckedDeleteKey(); + } + } + +@@ -6503,7 +6577,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { + size_t i; + + for (i = 0; i < max_operations; i++) { +- result = Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, op_handles[i]); ++ result = Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params, op_handles[i]); + if (ErrorCode::OK != result) { + break; + } +@@ -6511,12 +6585,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { + EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result); + // Try again just in case there's a weird overflow bug + EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, +- Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); ++ Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); + for (size_t j = 0; j < i; j++) { + EXPECT_EQ(ErrorCode::OK, Abort(op_handles[j])) + << "Aboort failed for i = " << j << std::endl; + } +- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); ++ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); + AbortIfNeeded(); + } + +@@ -6615,7 +6689,6 @@ TEST_P(KeyAgreementTest, Ecdh) { + OPENSSL_free(p); + + // Generate EC key in KeyMint (only access to public key material) +- vector challenge = {0x41, 0x42}; + EXPECT_EQ( + ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder() +@@ -6624,7 +6697,6 @@ TEST_P(KeyAgreementTest, Ecdh) { + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .Authorization(TAG_ALGORITHM, Algorithm::EC) + .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62}) +- .Authorization(TAG_ATTESTATION_CHALLENGE, challenge) + .SetDefaultValidity())) + << "Failed to generate key"; + ASSERT_GT(cert_chain_.size(), 0); +@@ -6704,14 +6776,24 @@ TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { + CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK); + + for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { ++ ++ if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) { ++ continue; ++ } + ASSERT_GT(keyData.blob.size(), 0U); + AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics); + EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params; + } + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); +- CheckedDeleteKey(&rsaKeyData.blob); +- CheckedDeleteKey(&ecdsaKeyData.blob); ++ ++ if (rsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&rsaKeyData.blob); ++ } ++ if (ecdsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&ecdsaKeyData.blob); ++ } ++ + } + + /* +@@ -6727,14 +6809,21 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { + }); + + for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { ++ if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) { ++ continue; ++ } + ASSERT_GT(keyData.blob.size(), 0U); + AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics); + EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params; + } + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); +- CheckedDeleteKey(&rsaKeyData.blob); +- CheckedDeleteKey(&ecdsaKeyData.blob); ++ if (rsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&rsaKeyData.blob); ++ } ++ if (ecdsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&ecdsaKeyData.blob); ++ } + } + + /* +diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +index 76fb79b61..6e57d913b 100644 +--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp ++++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +@@ -164,6 +164,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParamgetHardwareInfo(&rpcHardwareInfo).isOk()); + } + + static vector build_params() { +@@ -173,6 +174,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam provisionable_; ++ RpcHardwareInfo rpcHardwareInfo; + }; + + using GenerateKeyTests = VtsRemotelyProvisionedComponentTests; +@@ -273,11 +275,10 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) { + class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + protected: + CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) { +- generateTestEekChain(3); + } + + void generateTestEekChain(size_t eekLength) { +- auto chain = generateEekChain(eekLength, eekId_); ++ auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_); + EXPECT_TRUE(chain) << chain.message(); + if (chain) testEekChain_ = chain.moveValue(); + testEekLength_ = eekLength; +@@ -298,6 +299,17 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + } + } + ++ ErrMsgOr getSessionKey(ErrMsgOr>& senderPubkey) { ++ if (rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_25519 || ++ rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_NONE) { ++ return x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, ++ senderPubkey->first, false /* senderIsA */); ++ } else { ++ return ECDH_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, ++ senderPubkey->first, false /* senderIsA */); ++ } ++ } ++ + void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const bytevec& keysToSignMac, const ProtectedData& protectedData, + std::vector* bccOutput = nullptr) { +@@ -310,9 +322,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + ASSERT_TRUE(senderPubkey) << senderPubkey.message(); + EXPECT_EQ(senderPubkey->second, eekId_); + +- auto sessionKey = +- x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, +- senderPubkey->first, false /* senderIsA */); ++ auto sessionKey = getSessionKey(senderPubkey); + ASSERT_TRUE(sessionKey) << sessionKey.message(); + + auto protectedDataPayload = +@@ -322,7 +332,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload); + ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg; + ASSERT_TRUE(parsedPayload->asArray()); +- EXPECT_EQ(parsedPayload->asArray()->size(), 2U); ++ EXPECT_LE(parsedPayload->asArray()->size(), 3U); + + auto& signedMac = parsedPayload->asArray()->get(0); + auto& bcc = parsedPayload->asArray()->get(1); +@@ -406,6 +416,7 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -445,7 +456,7 @@ TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, {} /* keysToSign */, getProdEekChain(), challenge_, &deviceInfo, ++ testMode, {} /* keysToSign */, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, + &protectedData, &keysToSignMac); + EXPECT_TRUE(status.isOk()); + } +@@ -486,7 +497,7 @@ TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, keysToSign_, getProdEekChain(), challenge_, &deviceInfo, &protectedData, ++ testMode, keysToSign_, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, &protectedData, + &keysToSignMac); + EXPECT_TRUE(status.isOk()); + } +@@ -502,6 +513,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + testMode, {keyWithCorruptMac}, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -521,7 +533,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, {keyWithCorruptMac}, getProdEekChain(), challenge_, &deviceInfo, ++ testMode, {keyWithCorruptMac}, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, + &protectedData, &keysToSignMac); + ASSERT_FALSE(status.isOk()) << status.getMessage(); + EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); +@@ -535,7 +547,7 @@ TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) { + bool testMode = false; + generateKeys(testMode, 4 /* numKeys */); + +- auto prodEekChain = getProdEekChain(); ++ auto prodEekChain = getProdEekChain(rpcHardwareInfo.supportedEekCurve); + auto [parsedChain, _, parseErr] = cppbor::parse(prodEekChain); + ASSERT_NE(parsedChain, nullptr) << parseErr; + ASSERT_NE(parsedChain->asArray(), nullptr); +@@ -566,7 +578,7 @@ TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) { + + // Build an EEK chain that omits the first self-signed cert. + auto truncatedChain = cppbor::Array(); +- auto [chain, _, parseErr] = cppbor::parse(getProdEekChain()); ++ auto [chain, _, parseErr] = cppbor::parse(getProdEekChain(rpcHardwareInfo.supportedEekCurve)); + ASSERT_TRUE(chain); + auto eekChain = chain->asArray(); + ASSERT_NE(eekChain, nullptr); +@@ -594,6 +606,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + true /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -612,6 +625,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + false /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp +index 36969bbdb..b3552d38b 100644 +--- a/security/keymint/support/Android.bp ++++ b/security/keymint/support/Android.bp +@@ -66,6 +66,7 @@ cc_library { + "libcppcose_rkp", + "libcrypto", + "libjsoncpp", ++ "android.hardware.security.keymint-V1-ndk", + ], + } + +diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h +index 406b7a9b7..4d9ed2b0c 100644 +--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h ++++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h +@@ -52,6 +52,20 @@ inline constexpr uint8_t kCoseEncodedGeekCert[] = { + 0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2, + 0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04}; + ++// The Google ECDSA root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1 ++inline constexpr uint8_t kCoseEncodedEcdsaRootCert[] = { ++ 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x58, 0x4d, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, ++ 0x21, 0x58, 0x20, 0xf7, 0x14, 0x8a, 0xdb, 0x97, 0xf4, 0xcc, 0x53, 0xef, 0xd2, 0x64, 0x11, ++ 0xc4, 0xe3, 0x75, 0x1f, 0x66, 0x1f, 0xa4, 0x71, 0x0c, 0x6c, 0xcf, 0xfa, 0x09, 0x46, 0x80, ++ 0x74, 0x87, 0x54, 0xf2, 0xad, 0x22, 0x58, 0x20, 0x5e, 0x7f, 0x5b, 0xf6, 0xec, 0xe4, 0xf6, ++ 0x19, 0xcc, 0xff, 0x13, 0x37, 0xfd, 0x0f, 0xa1, 0xc8, 0x93, 0xdb, 0x18, 0x06, 0x76, 0xc4, ++ 0x5d, 0xe6, 0xd7, 0x6a, 0x77, 0x86, 0xc3, 0x2d, 0xaf, 0x8f, 0x58, 0x47, 0x30, 0x45, 0x02, ++ 0x20, 0x2f, 0x97, 0x8e, 0x42, 0xfb, 0xbe, 0x07, 0x2d, 0x95, 0x47, 0x85, 0x47, 0x93, 0x40, ++ 0xb0, 0x1f, 0xd4, 0x9b, 0x47, 0xa4, 0xc4, 0x44, 0xa9, 0xf2, 0xa1, 0x07, 0x87, 0x10, 0xc7, ++ 0x9f, 0xcb, 0x11, 0x02, 0x21, 0x00, 0xf4, 0xbf, 0x9f, 0xe8, 0x3b, 0xe0, 0xe7, 0x34, 0x4c, ++ 0x15, 0xfc, 0x7b, 0xc3, 0x7e, 0x33, 0x05, 0xf4, 0xd1, 0x34, 0x3c, 0xed, 0x02, 0x04, 0x60, ++ 0x7a, 0x15, 0xe0, 0x79, 0xd3, 0x8a, 0xff, 0x24}; ++ + /** + * Generates random bytes. + */ +@@ -67,12 +81,12 @@ struct EekChain { + * Generates an X25518 EEK with the specified eekId and an Ed25519 chain of the + * specified length. All keys are generated randomly. + */ +-ErrMsgOr generateEekChain(size_t length, const bytevec& eekId); ++ErrMsgOr generateEekChain(int32_t supportedEekCurve, size_t length, const bytevec& eekId); + + /** + * Returns the CBOR-encoded, production Google Endpoint Encryption Key chain. + */ +-bytevec getProdEekChain(); ++bytevec getProdEekChain(int32_t supportedEekCurve); + + struct BccEntryData { + bytevec pubKey; +diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp +index 0cbee5104..ae5120f8b 100644 +--- a/security/keymint/support/remote_prov_utils.cpp ++++ b/security/keymint/support/remote_prov_utils.cpp +@@ -17,15 +17,195 @@ + #include + #include + ++#include + #include + #include + #include ++#include ++#include ++#include ++#include + #include ++#include + #include + #include + + namespace aidl::android::hardware::security::keymint::remote_prov { + ++constexpr int kP256AffinePointSize = 32; ++ ++using EC_KEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; ++ ++ErrMsgOr ecKeyGetPrivateKey(const EC_KEY* ecKey) { ++ // Extract private key. ++ const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey); ++ if (bignum == nullptr) { ++ return "Error getting bignum from private key"; ++ } ++ int size = BN_num_bytes(bignum); ++ // Pad with zeros incase the length is lesser than 32. ++ bytevec privKey(32, 0); ++ BN_bn2bin(bignum, privKey.data() + 32 - size); ++ return privKey; ++} ++ ++ErrMsgOr ecKeyGetPublicKey(const EC_KEY* ecKey) { ++ // Extract public key. ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ const EC_POINT* point = EC_KEY_get0_public_key(ecKey); ++ if (point == nullptr) return "Error getting ecpoint from public key"; ++ ++ int size = EC_POINT_point2oct(group.get(), point, ++ POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, ++ nullptr); ++ if (size == 0) { ++ return "Error generating public key encoding"; ++ } ++ ++ bytevec publicKey; ++ publicKey.resize(size); ++ EC_POINT_point2oct(group.get(), point, ++ POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), ++ publicKey.size(), nullptr); ++ return publicKey; ++} ++ ++ErrMsgOr> getAffineCoordinates( ++ const bytevec& pubKey) { ++ auto group = EC_GROUP_Ptr( ++ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), pubKey.data(), ++ pubKey.size(), nullptr) != 1) { ++ return "Error decoding publicKey"; ++ } ++ BIGNUM_Ptr x(BN_new()); ++ BIGNUM_Ptr y(BN_new()); ++ BN_CTX_Ptr ctx(BN_CTX_new()); ++ if (!ctx.get()) return "Failed to create BN_CTX instance"; ++ ++ if (!EC_POINT_get_affine_coordinates_GFp(group.get(), point.get(), ++ x.get(), y.get(), ++ ctx.get())) { ++ return "Failed to get affine coordinates from ECPoint"; ++ } ++ bytevec pubX(kP256AffinePointSize); ++ bytevec pubY(kP256AffinePointSize); ++ if (BN_bn2binpad(x.get(), pubX.data(), kP256AffinePointSize) != ++ kP256AffinePointSize) { ++ return "Error in converting absolute value of x cordinate to big-endian"; ++ } ++ if (BN_bn2binpad(y.get(), pubY.data(), kP256AffinePointSize) != ++ kP256AffinePointSize) { ++ return "Error in converting absolute value of y cordinate to big-endian"; ++ } ++ return std::make_tuple(std::move(pubX), std::move(pubY)); ++} ++ ++ErrMsgOr> generateEc256KeyPair() { ++ auto ec_key = EC_KEY_Ptr(EC_KEY_new()); ++ if (ec_key.get() == nullptr) { ++ return "Failed to allocate ec key"; ++ } ++ ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ ++ if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 || ++ EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) { ++ return "Error generating key"; ++ } ++ ++ auto privKey = ecKeyGetPrivateKey(ec_key.get()); ++ if (!privKey) return privKey.moveMessage(); ++ ++ auto pubKey = ecKeyGetPublicKey(ec_key.get()); ++ if (!pubKey) return pubKey.moveMessage(); ++ ++ return std::make_tuple(pubKey.moveValue(), privKey.moveValue()); ++} ++ ++ErrMsgOr> generateX25519KeyPair() { ++ /* Generate X25519 key pair */ ++ bytevec pubKey(X25519_PUBLIC_VALUE_LEN); ++ bytevec privKey(X25519_PRIVATE_KEY_LEN); ++ X25519_keypair(pubKey.data(), privKey.data()); ++ return std::make_tuple(std::move(pubKey), std::move(privKey)); ++} ++ ++ErrMsgOr> generateED25519KeyPair() { ++ /* Generate ED25519 key pair */ ++ bytevec pubKey(ED25519_PUBLIC_KEY_LEN); ++ bytevec privKey(ED25519_PRIVATE_KEY_LEN); ++ ED25519_keypair(pubKey.data(), privKey.data()); ++ return std::make_tuple(std::move(pubKey), std::move(privKey)); ++} ++ ++ErrMsgOr> generateKeyPair( ++ int32_t supportedEekCurve, bool isEek) { ++ ++ switch (supportedEekCurve) { ++ case RpcHardwareInfo::CURVE_NONE: ++ case RpcHardwareInfo::CURVE_25519: ++ if (isEek) { ++ return generateX25519KeyPair(); ++ } ++ return generateED25519KeyPair(); ++ case RpcHardwareInfo::CURVE_P256: ++ return generateEc256KeyPair(); ++ default: ++ return "Unknown EEK Curve."; ++ } ++} ++ ++ErrMsgOr constructCoseKey(int32_t supportedEekCurve, const bytevec& eekId, ++ const bytevec& pubKey) { ++ CoseKeyType keyType; ++ CoseKeyAlgorithm algorithm; ++ CoseKeyCurve curve; ++ bytevec pubX; ++ bytevec pubY; ++ switch (supportedEekCurve) { ++ case RpcHardwareInfo::CURVE_NONE: ++ case RpcHardwareInfo::CURVE_25519: ++ keyType = OCTET_KEY_PAIR; ++ algorithm = (eekId.empty()) ? EDDSA : ECDH_ES_HKDF_256; ++ curve = (eekId.empty()) ? ED25519 : cppcose::X25519; ++ pubX = pubKey; ++ break; ++ case RpcHardwareInfo::CURVE_P256: { ++ keyType = EC2; ++ algorithm = (eekId.empty()) ? ES256 : ECDH_ES_HKDF_256; ++ curve = P256; ++ auto affineCoordinates = getAffineCoordinates(pubKey); ++ if (!affineCoordinates) return affineCoordinates.moveMessage(); ++ std::tie(pubX, pubY) = affineCoordinates.moveValue(); ++ } break; ++ default: ++ return "Unknown EEK Curve."; ++ } ++ cppbor::Map coseKey = cppbor::Map() ++ .add(CoseKey::KEY_TYPE, keyType) ++ .add(CoseKey::ALGORITHM, algorithm) ++ .add(CoseKey::CURVE, curve) ++ .add(CoseKey::PUBKEY_X, pubX); ++ ++ if (!pubY.empty()) coseKey.add(CoseKey::PUBKEY_Y, pubY); ++ if (!eekId.empty()) coseKey.add(CoseKey::KEY_ID, eekId); ++ ++ return coseKey.canonicalize().encode(); ++} ++ + bytevec kTestMacKey(32 /* count */, 0 /* byte value */); + + bytevec randomBytes(size_t numBytes) { +@@ -34,7 +214,17 @@ bytevec randomBytes(size_t numBytes) { + return retval; + } + +-ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { ++ErrMsgOr constructCoseSign1(int32_t supportedEekCurve, const bytevec& key, ++ const bytevec& payload, const bytevec& aad) { ++ if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { ++ return constructECDSACoseSign1(key, {} /* protectedParams */, payload, aad); ++ } else { ++ return cppcose::constructCoseSign1(key, payload, aad); ++ } ++} ++ ++ErrMsgOr generateEekChain(int32_t supportedEekCurve, size_t length, ++ const bytevec& eekId) { + if (length < 2) { + return "EEK chain must contain at least 2 certs."; + } +@@ -43,42 +233,31 @@ ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { + + bytevec prev_priv_key; + for (size_t i = 0; i < length - 1; ++i) { +- bytevec pub_key(ED25519_PUBLIC_KEY_LEN); +- bytevec priv_key(ED25519_PRIVATE_KEY_LEN); +- +- ED25519_keypair(pub_key.data(), priv_key.data()); ++ auto keyPair = generateKeyPair(supportedEekCurve, false); ++ if (!keyPair) keyPair.moveMessage(); ++ auto [pub_key, priv_key] = keyPair.moveValue(); + + // The first signing key is self-signed. + if (prev_priv_key.empty()) prev_priv_key = priv_key; + +- auto coseSign1 = constructCoseSign1(prev_priv_key, +- cppbor::Map() /* payload CoseKey */ +- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR) +- .add(CoseKey::ALGORITHM, EDDSA) +- .add(CoseKey::CURVE, ED25519) +- .add(CoseKey::PUBKEY_X, pub_key) +- .canonicalize() +- .encode(), ++ auto coseKey = constructCoseKey(supportedEekCurve, {}, pub_key); ++ if (!coseKey) return coseKey.moveMessage(); ++ ++ auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), + {} /* AAD */); + if (!coseSign1) return coseSign1.moveMessage(); + eekChain.add(coseSign1.moveValue()); + + prev_priv_key = priv_key; + } ++ auto keyPair = generateKeyPair(supportedEekCurve, true); ++ if (!keyPair) keyPair.moveMessage(); ++ auto [pub_key, priv_key] = keyPair.moveValue(); + +- bytevec pub_key(X25519_PUBLIC_VALUE_LEN); +- bytevec priv_key(X25519_PRIVATE_KEY_LEN); +- X25519_keypair(pub_key.data(), priv_key.data()); ++ auto coseKey = constructCoseKey(supportedEekCurve, eekId, pub_key); ++ if (!coseKey) return coseKey.moveMessage(); + +- auto coseSign1 = constructCoseSign1(prev_priv_key, +- cppbor::Map() /* payload CoseKey */ +- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR) +- .add(CoseKey::KEY_ID, eekId) +- .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256) +- .add(CoseKey::CURVE, cppcose::X25519) +- .add(CoseKey::PUBKEY_X, pub_key) +- .canonicalize() +- .encode(), ++ auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), + {} /* AAD */); + if (!coseSign1) return coseSign1.moveMessage(); + eekChain.add(coseSign1.moveValue()); +@@ -86,16 +265,15 @@ ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { + return EekChain{eekChain.encode(), pub_key, priv_key}; + } + +-bytevec getProdEekChain() { +- bytevec prodEek; +- prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert)); +- +- // In CBOR encoding, 0x82 indicates an array of two items +- prodEek.push_back(0x82); +- prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)); +- prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)); +- +- return prodEek; ++bytevec getProdEekChain(int32_t supportedEekCurve) { ++ cppbor::Array chain; ++ if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedEcdsaRootCert), std::end(kCoseEncodedEcdsaRootCert)))); ++ } else { ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)))); ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)))); ++ } ++ return chain.encode(); + } + + ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, +@@ -122,7 +300,8 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); +- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { ++ if (!algorithm || !algorithm->asInt() || (algorithm->asInt()->value() != EDDSA && ++ algorithm->asInt()->value() != ES256)) { + return "Unsupported signature algorithm"; + } + +@@ -136,16 +315,35 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, + if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry"; + + bool selfSigned = signingCoseKey.empty(); +- auto key = ++ bytevec key; ++ if (algorithm->asInt()->value() == EDDSA) { ++ auto key = + CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey); +- if (!key) return "Bad signing key: " + key.moveMessage(); ++ if (!key) return "Bad signing key: " + key.moveMessage(); + +- bytevec signatureInput = ++ bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); + +- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), +- key->getBstrValue(CoseKey::PUBKEY_X)->data())) { +- return "Signature verification failed"; ++ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), ++ key->getBstrValue(CoseKey::PUBKEY_X)->data())) { ++ return "Signature verification failed"; ++ } ++ } else { // P256 ++ auto key = ++ CoseKey::parseP256(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || ++ key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ auto publicKey = key->getEcPublicKey(); ++ if (!publicKey) return publicKey.moveMessage(); ++ ++ bytevec signatureInput = ++ cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); ++ ++ if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), signature->value())) { ++ return "Signature verification failed"; ++ } + } + + return serializedKey->asBstr()->value(); +diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp +index 8697c5190..0009bf713 100644 +--- a/security/keymint/support/remote_prov_utils_test.cpp ++++ b/security/keymint/support/remote_prov_utils_test.cpp +@@ -14,6 +14,7 @@ + * limitations under the License. + */ + ++#include + #include + #include + #include +@@ -35,13 +36,13 @@ using ::keymaster::validateAndExtractEekPubAndId; + using ::testing::ElementsAreArray; + + TEST(RemoteProvUtilsTest, GenerateEekChainInvalidLength) { +- ASSERT_FALSE(generateEekChain(1, /*eekId=*/{})); ++ ASSERT_FALSE(generateEekChain(CURVE_25519, 1, /*eekId=*/{})); + } + + TEST(RemoteProvUtilsTest, GenerateEekChain) { + bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0}; + for (size_t length : {2, 3, 31}) { +- auto get_eek_result = generateEekChain(length, kTestEekId); ++ auto get_eek_result = generateEekChain(CURVE_25519, length, kTestEekId); + ASSERT_TRUE(get_eek_result) << get_eek_result.message(); + + auto& [chain, pubkey, privkey] = *get_eek_result; diff --git a/patches/keymint/aosp_integration_patches/system_keymaster.patch b/patches/keymint/aosp_integration_patches/system_keymaster.patch new file mode 100644 index 00000000..b994b768 --- /dev/null +++ b/patches/keymint/aosp_integration_patches/system_keymaster.patch @@ -0,0 +1,441 @@ +diff --git a/cppcose/cppcose.cpp b/cppcose/cppcose.cpp +index bfe9928..5009bfe 100644 +--- a/cppcose/cppcose.cpp ++++ b/cppcose/cppcose.cpp +@@ -21,10 +21,17 @@ + + #include + #include ++#include + + #include + + namespace cppcose { ++constexpr int kP256AffinePointSize = 32; ++ ++using EVP_PKEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; ++using ECDSA_SIG_Ptr = bssl::UniquePtr; ++using EC_KEY_Ptr = bssl::UniquePtr; + + namespace { + +@@ -51,6 +58,92 @@ ErrMsgOr> aesGcmInitAndProcessAad(const bytevec& + return std::move(ctx); + } + ++ ++ErrMsgOr signEcdsaDigest(const bytevec& key, const bytevec& data) { ++ auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); ++ if (bn.get() == nullptr) { ++ return "Error creating BIGNUM"; ++ } ++ ++ auto ec_key = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (EC_KEY_set_private_key(ec_key.get(), bn.get()) != 1) { ++ return "Error setting private key from BIGNUM"; ++ } ++ ++ ECDSA_SIG* sig = ECDSA_do_sign(data.data(), data.size(), ec_key.get()); ++ if (sig == nullptr) { ++ return "Error signing digest"; ++ } ++ size_t len = i2d_ECDSA_SIG(sig, nullptr); ++ bytevec signature(len); ++ unsigned char* p = (unsigned char*)signature.data(); ++ i2d_ECDSA_SIG(sig, &p); ++ ECDSA_SIG_free(sig); ++ return signature; ++} ++ ++ErrMsgOr ecdh(const bytevec& publicKey, const bytevec& privateKey) { ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != ++ 1) { ++ return "Error decoding publicKey"; ++ } ++ auto ecKey = EC_KEY_Ptr(EC_KEY_new()); ++ auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); ++ if (ecKey.get() == nullptr || pkey.get() == nullptr) { ++ return "Memory allocation failed"; ++ } ++ if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { ++ return "Error setting group"; ++ } ++ if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { ++ return "Error setting point"; ++ } ++ if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { ++ return "Error setting key"; ++ } ++ ++ auto bn = BIGNUM_Ptr(BN_bin2bn(privateKey.data(), privateKey.size(), nullptr)); ++ if (bn.get() == nullptr) { ++ return "Error creating BIGNUM for private key"; ++ } ++ auto privEcKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (EC_KEY_set_private_key(privEcKey.get(), bn.get()) != 1) { ++ return "Error setting private key from BIGNUM"; ++ } ++ auto privPkey = EVP_PKEY_Ptr(EVP_PKEY_new()); ++ if (EVP_PKEY_set1_EC_KEY(privPkey.get(), privEcKey.get()) != 1) { ++ return "Error setting private key"; ++ } ++ ++ auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(privPkey.get(), NULL)); ++ if (ctx.get() == nullptr) { ++ return "Error creating context"; ++ } ++ ++ if (EVP_PKEY_derive_init(ctx.get()) != 1) { ++ return "Error initializing context"; ++ } ++ ++ if (EVP_PKEY_derive_set_peer(ctx.get(), pkey.get()) != 1) { ++ return "Error setting peer"; ++ } ++ ++ /* Determine buffer length for shared secret */ ++ size_t secretLen = 0; ++ if (EVP_PKEY_derive(ctx.get(), NULL, &secretLen) != 1) { ++ return "Error determing length of shared secret"; ++ } ++ bytevec sharedSecret; ++ sharedSecret.resize(secretLen); ++ ++ if (EVP_PKEY_derive(ctx.get(), sharedSecret.data(), &secretLen) != 1) { ++ return "Error deriving shared secret"; ++ } ++ return sharedSecret; ++} ++ + } // namespace + + ErrMsgOr generateHmacSha256(const bytevec& key, const bytevec& data) { +@@ -134,6 +227,17 @@ ErrMsgOr verifyAndParseCoseMac0(const cppbor::Item* macIt + return payload->value(); + } + ++ErrMsgOr createECDSACoseSign1Signature(const bytevec& key, const bytevec& protectedParams, ++ const bytevec& payload, const bytevec& aad) { ++ bytevec signatureInput = cppbor::Array() ++ .add("Signature1") // ++ .add(protectedParams) ++ .add(aad) ++ .add(payload) ++ .encode(); ++ return signEcdsaDigest(key, sha256(signatureInput)); ++} ++ + ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec signatureInput = cppbor::Array() +@@ -152,6 +256,19 @@ ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& pr + return signature; + } + ++ErrMsgOr constructECDSACoseSign1(const bytevec& key, cppbor::Map protectedParams, ++ const bytevec& payload, const bytevec& aad) { ++ bytevec protParms = protectedParams.add(ALGORITHM, ES256).canonicalize().encode(); ++ auto signature = createECDSACoseSign1Signature(key, protParms, payload, aad); ++ if (!signature) return signature.moveMessage(); ++ ++ return cppbor::Array() ++ .add(std::move(protParms)) ++ .add(cppbor::Map() /* unprotected parameters */) ++ .add(std::move(payload)) ++ .add(std::move(*signature)); ++} ++ + ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec protParms = protectedParams.add(ALGORITHM, EDDSA).canonicalize().encode(); +@@ -193,7 +310,8 @@ ErrMsgOr verifyAndParseCoseSign1(const cppbor::Array* coseSign1, + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); +- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { ++ if (!algorithm || !algorithm->asInt() || ++ !(algorithm->asInt()->value() == EDDSA || algorithm->asInt()->value() == ES256)) { + return "Unsupported signature algorithm"; + } + +@@ -203,17 +321,30 @@ ErrMsgOr verifyAndParseCoseSign1(const cppbor::Array* coseSign1, + } + + bool selfSigned = signingCoseKey.empty(); +- auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey); +- if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty()) { +- return "Bad signing key: " + key.moveMessage(); +- } +- + bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); +- +- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), +- key->getBstrValue(CoseKey::PUBKEY_X)->data())) { +- return "Signature verification failed"; ++ if (algorithm->asInt()->value() == EDDSA) { ++ auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ ++ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), ++ key->getBstrValue(CoseKey::PUBKEY_X)->data())) { ++ return "Signature verification failed"; ++ } ++ } else { // P256 ++ auto key = CoseKey::parseP256(selfSigned ? payload->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || ++ key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ auto publicKey = key->getEcPublicKey(); ++ if (!publicKey) return publicKey.moveMessage(); ++ ++ if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), signature->value())) { ++ return "Signature verification failed"; ++ } + } + + return payload->value(); +@@ -294,28 +425,47 @@ getSenderPubKeyFromCoseEncrypt(const cppbor::Item* coseEncrypt) { + if (!senderCoseKey || !senderCoseKey->asMap()) return "Invalid sender COSE_Key"; + + auto& keyType = senderCoseKey->asMap()->get(CoseKey::KEY_TYPE); +- if (!keyType || !keyType->asInt() || keyType->asInt()->value() != OCTET_KEY_PAIR) { ++ if (!keyType || !keyType->asInt() || (keyType->asInt()->value() != OCTET_KEY_PAIR && ++ keyType->asInt()->value() != EC2)) { + return "Invalid key type"; + } + + auto& curve = senderCoseKey->asMap()->get(CoseKey::CURVE); +- if (!curve || !curve->asInt() || curve->asInt()->value() != X25519) { ++ if (!curve || !curve->asInt() || ++ (keyType->asInt()->value() == OCTET_KEY_PAIR && curve->asInt()->value() != X25519) || ++ (keyType->asInt()->value() == EC2 && curve->asInt()->value() != P256)) { + return "Unsupported curve"; + } + +- auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); +- if (!pubkey || !pubkey->asBstr() || +- pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) { +- return "Invalid X25519 public key"; ++ bytevec publicKey; ++ if (keyType->asInt()->value() == EC2) { ++ auto& pubX = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); ++ if (!pubX || !pubX->asBstr() || pubX->asBstr()->value().size() != kP256AffinePointSize) { ++ return "Invalid EC public key"; ++ } ++ auto& pubY = senderCoseKey->asMap()->get(CoseKey::PUBKEY_Y); ++ if (!pubY || !pubY->asBstr() || pubY->asBstr()->value().size() != kP256AffinePointSize) { ++ return "Invalid EC public key"; ++ } ++ auto key = CoseKey::getEcPublicKey(pubX->asBstr()->value(), pubY->asBstr()->value()); ++ if (!key) return key.moveMessage(); ++ publicKey = key.moveValue(); ++ } else { ++ auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); ++ if (!pubkey || !pubkey->asBstr() || ++ pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) { ++ return "Invalid X25519 public key"; ++ } ++ publicKey = pubkey->asBstr()->value(); + } + + auto& key_id = unprotParms->asMap()->get(KEY_ID); + if (key_id && key_id->asBstr()) { +- return std::make_pair(pubkey->asBstr()->value(), key_id->asBstr()->value()); ++ return std::make_pair(publicKey, key_id->asBstr()->value()); + } + + // If no key ID, just return an empty vector. +- return std::make_pair(pubkey->asBstr()->value(), bytevec{}); ++ return std::make_pair(publicKey, bytevec{}); + } + + ErrMsgOr decryptCoseEncrypt(const bytevec& key, const cppbor::Item* coseEncrypt, +@@ -367,6 +517,43 @@ ErrMsgOr decryptCoseEncrypt(const bytevec& key, const cppbor::Item* cos + return aesGcmDecrypt(key, nonce->asBstr()->value(), aad, ciphertext->asBstr()->value()); + } + ++ErrMsgOr ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, ++ const bytevec& pubKeyB, bool senderIsA) { ++ if (privKeyA.empty() || pubKeyA.empty() || pubKeyB.empty()) { ++ return "Missing input key parameters"; ++ } ++ ++ auto rawSharedKey = ecdh(pubKeyB, privKeyA); ++ if (!rawSharedKey) return rawSharedKey.moveMessage(); ++ ++ bytevec kdfContext = cppbor::Array() ++ .add(AES_GCM_256) ++ .add(cppbor::Array() // Sender Info ++ .add(cppbor::Bstr("client")) ++ .add(bytevec{} /* nonce */) ++ .add(senderIsA ? pubKeyA : pubKeyB)) ++ .add(cppbor::Array() // Recipient Info ++ .add(cppbor::Bstr("server")) ++ .add(bytevec{} /* nonce */) ++ .add(senderIsA ? pubKeyB : pubKeyA)) ++ .add(cppbor::Array() // SuppPubInfo ++ .add(kAesGcmKeySizeBits) // output key length ++ .add(bytevec{})) // protected ++ .encode(); ++ ++ bytevec retval(SHA256_DIGEST_LENGTH); ++ bytevec salt{}; ++ if (!HKDF(retval.data(), retval.size(), // ++ EVP_sha256(), // ++ rawSharedKey->data(), rawSharedKey->size(), // ++ salt.data(), salt.size(), // ++ kdfContext.data(), kdfContext.size())) { ++ return "ECDH HKDF failed"; ++ } ++ ++ return retval; ++} ++ + ErrMsgOr x25519_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, + const bytevec& pubKeyB, bool senderIsA) { + if (privKeyA.empty() || pubKeyA.empty() || pubKeyB.empty()) { +@@ -460,4 +647,43 @@ ErrMsgOr aesGcmDecrypt(const bytevec& key, const bytevec& nonce, const + return plaintext; + } + ++bytevec sha256(const bytevec& data) { ++ bytevec ret(SHA256_DIGEST_LENGTH); ++ SHA256_CTX ctx; ++ SHA256_Init(&ctx); ++ SHA256_Update(&ctx, data.data(), data.size()); ++ SHA256_Final((unsigned char*)ret.data(), &ctx); ++ return ret; ++} ++ ++bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature) { ++ const unsigned char* p = (unsigned char*)signature.data(); ++ auto sig = ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, signature.size())); ++ if (sig.get() == nullptr) { ++ return false; ++ } ++ ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), key.data(), key.size(), nullptr) != 1) { ++ return false; ++ } ++ auto ecKey = EC_KEY_Ptr(EC_KEY_new()); ++ if (ecKey.get() == nullptr) { ++ return false; ++ } ++ if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { ++ return false; ++ } ++ if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { ++ return false; ++ } ++ ++ int rc = ECDSA_do_verify(digest.data(), digest.size(), sig.get(), ecKey.get()); ++ if (rc != 1) { ++ return false; ++ } ++ return true; ++} ++ + } // namespace cppcose +diff --git a/include/keymaster/cppcose/cppcose.h b/include/keymaster/cppcose/cppcose.h +index 0f97388..03251f1 100644 +--- a/include/keymaster/cppcose/cppcose.h ++++ b/include/keymaster/cppcose/cppcose.h +@@ -24,17 +24,25 @@ + + #include + #include +- ++#include ++#include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include + + namespace cppcose { + ++using BIGNUM_Ptr = bssl::UniquePtr; ++using EC_GROUP_Ptr = bssl::UniquePtr; ++using EC_POINT_Ptr = bssl::UniquePtr; ++using BN_CTX_Ptr = bssl::UniquePtr; ++ + template class ErrMsgOr; + using bytevec = std::vector; + using HmacSha256 = std::array; +@@ -203,6 +211,41 @@ class CoseKey { + return key; + } + ++ static ErrMsgOr getEcPublicKey(const bytevec& pubX, const bytevec& pubY) { ++ auto bnX = BIGNUM_Ptr(BN_bin2bn(pubX.data(), pubX.size(), nullptr)); ++ if (bnX.get() == nullptr) { ++ return "Error creating BIGNUM X Coordinate"; ++ } ++ auto bnY = BIGNUM_Ptr(BN_bin2bn(pubY.data(), pubY.size(), nullptr)); ++ if (bnY.get() == nullptr) { ++ return "Error creating BIGNUM Y Coordinate"; ++ } ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (!point) return "Failed to create EC_POINT instance"; ++ BN_CTX_Ptr ctx(BN_CTX_new()); ++ if (!ctx.get()) return "Failed to create BN_CTX instance"; ++ if (!EC_POINT_set_affine_coordinates_GFp(group.get(), point.get(), bnX.get(), bnY.get(), ++ ctx.get())) { ++ return "Failed to set affine coordinates."; ++ } ++ int size = EC_POINT_point2oct(group.get(), point.get(), POINT_CONVERSION_UNCOMPRESSED, ++ nullptr, 0, nullptr); ++ if (size == 0) { ++ return "Error generating public key encoding"; ++ } ++ bytevec publicKey(size); ++ EC_POINT_point2oct(group.get(), point.get(), POINT_CONVERSION_UNCOMPRESSED, ++ publicKey.data(), publicKey.size(), nullptr); ++ return publicKey; ++ } ++ ++ ErrMsgOr getEcPublicKey() { ++ auto pubX = getBstrValue(PUBKEY_X).value(); ++ auto pubY = getBstrValue(PUBKEY_Y).value(); ++ return getEcPublicKey(pubX, pubY); ++ } ++ + std::optional getIntValue(Label label) { + const auto& value = key_->get(label); + if (!value || !value->asInt()) return {}; +@@ -252,6 +295,8 @@ ErrMsgOr constructCoseSign1(const bytevec& key, const bytevec& pa + const bytevec& aad); + ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, + const bytevec& payload, const bytevec& aad); ++ErrMsgOr constructECDSACoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, ++ const bytevec& payload, const bytevec& aad); + /** + * Verify and parse a COSE_Sign1 message, returning the payload. + * +@@ -282,7 +327,10 @@ decryptCoseEncrypt(const bytevec& key, const cppbor::Item* encryptItem, const by + + ErrMsgOr x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytevec& senderPrivKey, + const bytevec& recipientPubKey, bool senderIsA); +- ++ErrMsgOr ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, ++ const bytevec& pubKeyB, bool senderIsA); ++bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature); ++bytevec sha256(const bytevec& data); + ErrMsgOr aesGcmEncrypt(const bytevec& key, const bytevec& nonce, + const bytevec& aad, + const bytevec& plaintext); diff --git a/patches/keymint/aosp_integration_patches/system_sepolicy.patch b/patches/keymint/aosp_integration_patches/system_sepolicy.patch new file mode 100644 index 00000000..3e0ce8d5 --- /dev/null +++ b/patches/keymint/aosp_integration_patches/system_sepolicy.patch @@ -0,0 +1,20 @@ +diff --git a/public/hal_neverallows.te b/public/hal_neverallows.te +index e77ea9df0..74f799681 100644 +--- a/public/hal_neverallows.te ++++ b/public/hal_neverallows.te +@@ -2,6 +2,7 @@ + # network capabilities + neverallow { + halserverdomain ++ -hal_keymint_server + -hal_bluetooth_server + -hal_can_controller_server + -hal_wifi_server +@@ -37,6 +38,7 @@ neverallow { + + neverallow { + halserverdomain ++ -hal_keymint_server + -hal_automotive_socket_exemption + -hal_can_controller_server + -hal_tetheroffload_server diff --git a/patches/keymint/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch b/patches/keymint/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch new file mode 100644 index 00000000..b0fca48f --- /dev/null +++ b/patches/keymint/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch @@ -0,0 +1,62 @@ +diff --git a/shared/device.mk b/shared/device.mk +index 8647d0175..d1955772f 100644 +--- a/shared/device.mk ++++ b/shared/device.mk +@@ -538,6 +538,10 @@ endif + PRODUCT_PACKAGES += \ + $(LOCAL_KEYMINT_PRODUCT_PACKAGE) + ++PRODUCT_PACKAGES += \ ++ android.hardware.security.keymint-service.strongbox ++ ++ + # Keymint configuration + PRODUCT_COPY_FILES += \ + frameworks/native/data/etc/android.software.device_id_attestation.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.device_id_attestation.xml +diff --git a/shared/sepolicy/vendor/file_contexts b/shared/sepolicy/vendor/file_contexts +index 20538a50f..2b74242f7 100644 +--- a/shared/sepolicy/vendor/file_contexts ++++ b/shared/sepolicy/vendor/file_contexts +@@ -87,6 +87,7 @@ + /vendor/bin/hw/android\.hardware\.input\.classifier@1\.0-service.default u:object_r:hal_input_classifier_default_exec:s0 + /vendor/bin/hw/android\.hardware\.thermal@2\.0-service\.mock u:object_r:hal_thermal_default_exec:s0 + /vendor/bin/hw/android\.hardware\.security\.keymint-service\.remote u:object_r:hal_keymint_remote_exec:s0 ++/vendor/bin/hw/android\.hardware\.security\.keymint-service\.strongbox u:object_r:hal_keymint_strongbox_exec:s0 + /vendor/bin/hw/android\.hardware\.keymaster@4\.1-service.remote u:object_r:hal_keymaster_remote_exec:s0 + /vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service.remote u:object_r:hal_gatekeeper_remote_exec:s0 + /vendor/bin/hw/android\.hardware\.oemlock-service.example u:object_r:hal_oemlock_default_exec:s0 +diff --git a/shared/sepolicy/vendor/hal_keymint_strongbox.te b/shared/sepolicy/vendor/hal_keymint_strongbox.te +new file mode 100644 +index 000000000..09d0da267 +--- /dev/null ++++ b/shared/sepolicy/vendor/hal_keymint_strongbox.te +@@ -0,0 +1,15 @@ ++type hal_keymint_strongbox, domain; ++hal_server_domain(hal_keymint_strongbox, hal_keymint) ++ ++type hal_keymint_strongbox_exec, exec_type, vendor_file_type, file_type; ++init_daemon_domain(hal_keymint_strongbox) ++ ++vndbinder_use(hal_keymint_strongbox) ++get_prop(hal_keymint_strongbox, vendor_security_patch_level_prop); ++ ++# Allow access to sockets ++allow hal_keymint_strongbox self:tcp_socket { connect create write read getattr getopt setopt }; ++allow hal_keymint_strongbox port_type:tcp_socket name_connect; ++allow hal_keymint_strongbox port:tcp_socket { name_connect }; ++allow hal_keymint_strongbox vendor_data_file:file { open read getattr }; ++ +diff --git a/shared/sepolicy/vendor/service_contexts b/shared/sepolicy/vendor/service_contexts +index d20d026cf..b8f0155ab 100644 +--- a/shared/sepolicy/vendor/service_contexts ++++ b/shared/sepolicy/vendor/service_contexts +@@ -4,6 +4,9 @@ android.hardware.neuralnetworks.IDevice/nnapi-sample_float_slow u:object_r:hal_n + android.hardware.neuralnetworks.IDevice/nnapi-sample_minimal u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_quant u:object_r:hal_neuralnetworks_service:s0 + android.hardware.neuralnetworks.IDevice/nnapi-sample_sl_shim u:object_r:hal_neuralnetworks_service:s0 ++android.hardware.security.keymint.IKeyMintDevice/strongbox u:object_r:hal_keymint_service:s0 ++android.hardware.security.sharedsecret.ISharedSecret/strongbox u:object_r:hal_sharedsecret_service:s0 ++android.hardware.security.keymint.IRemotelyProvisionedComponent/strongbox u:object_r:hal_keymint_service:s0 + + # Binder service mappings + gce u:object_r:gce_service:s0 diff --git a/patches/keymint/aosp_integration_patches_aosp_12_r15/hardware_interfaces.patch b/patches/keymint/aosp_integration_patches_aosp_12_r15/hardware_interfaces.patch new file mode 100644 index 00000000..bf456260 --- /dev/null +++ b/patches/keymint/aosp_integration_patches_aosp_12_r15/hardware_interfaces.patch @@ -0,0 +1,1213 @@ +diff --git a/compatibility_matrices/compatibility_matrix.6.xml b/compatibility_matrices/compatibility_matrix.6.xml +index aee2c5164..1391bbf54 100644 +--- a/compatibility_matrices/compatibility_matrix.6.xml ++++ b/compatibility_matrices/compatibility_matrix.6.xml +@@ -349,6 +349,13 @@ + default + + ++ ++ android.hardware.security.keymint ++ ++ IRemotelyProvisionedComponent ++ strongbox ++ ++ + + android.hardware.light + 1 +diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml +index 8b6e8414d..4955db7d7 100644 +--- a/compatibility_matrices/compatibility_matrix.current.xml ++++ b/compatibility_matrices/compatibility_matrix.current.xml +@@ -66,7 +66,7 @@ + + IEvsEnumerator + default +- [a-z]+/[0-9]+ ++ [a-z]/[0-9] + + + +@@ -168,7 +168,7 @@ + 2.4-7 + + ICameraProvider +- [^/]+/[0-9]+ ++ [^/]/[0-9] + + + +@@ -349,6 +349,13 @@ + default + + ++ ++ android.hardware.security.keymint ++ ++ IRemotelyProvisionedComponent ++ strongbox ++ ++ + + android.hardware.light + 1 +@@ -511,6 +518,15 @@ + strongbox + + ++ ++ android.hardware.security.sharedsecret ++ 1 ++ ++ ISharedSecret ++ strongbox ++ ++ ++ + + android.hardware.sensors + 1.0 +diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +index 26ed34427..2d5bc9575 100644 +--- a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp ++++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp +@@ -198,7 +198,7 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { + AttestationKey attest_key; + vector attest_key_characteristics; + vector attest_key_cert_chain; +- ASSERT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .AttestKey() +@@ -209,7 +209,13 @@ TEST_P(AttestKeyTest, RsaAttestedAttestKeys) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + {} /* attestation signing key */, &attest_key.keyBlob, +- &attest_key_characteristics, &attest_key_cert_chain)); ++ &attest_key_characteristics, &attest_key_cert_chain); ++ //Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + + EXPECT_GT(attest_key_cert_chain.size(), 1); + verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false); +@@ -297,7 +303,7 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { + attest_key_opt = attest_key; + } + +- EXPECT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .AttestKey() +@@ -308,8 +314,13 @@ TEST_P(AttestKeyTest, RsaAttestKeyChaining) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); +- ++ &cert_chain_list[i]); ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); + ASSERT_GT(cert_chain_list[i].size(), 0); +@@ -369,7 +380,7 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { + attest_key_opt = attest_key; + } + +- EXPECT_EQ(ErrorCode::OK, ++ auto result = + GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .AttestKey() +@@ -380,8 +391,13 @@ TEST_P(AttestKeyTest, EcAttestKeyChaining) { + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), + attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); +- ++ &cert_chain_list[i]); ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); + ASSERT_GT(cert_chain_list[i].size(), 0); +@@ -442,35 +458,40 @@ TEST_P(AttestKeyTest, AlternateAttestKeyChaining) { + attest_key.keyBlob = key_blob_list[i - 1]; + attest_key_opt = attest_key; + } +- ++ ErrorCode result; + if ((i & 0x1) == 1) { +- EXPECT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() +- .EcdsaSigningKey(EcCurve::P_256) +- .AttestKey() +- .AttestationChallenge("foo") +- .AttestationApplicationId("bar") +- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) +- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) +- .Authorization(TAG_NO_AUTH_REQUIRED) +- .SetDefaultValidity(), +- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ result = ++ GenerateKey(AuthorizationSetBuilder() ++ .EcdsaSigningKey(EcCurve::P_256) ++ .AttestKey() ++ .AttestationChallenge("foo") ++ .AttestationApplicationId("bar") ++ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) ++ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .SetDefaultValidity(), ++ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, ++ &cert_chain_list[i]); + } else { +- EXPECT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() +- .RsaSigningKey(2048, 65537) +- .AttestKey() +- .AttestationChallenge("foo") +- .AttestationApplicationId("bar") +- .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) +- .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) +- .Authorization(TAG_NO_AUTH_REQUIRED) +- .SetDefaultValidity(), +- attest_key_opt, &key_blob_list[i], &attested_key_characteristics, +- &cert_chain_list[i])); ++ result = ++ GenerateKey(AuthorizationSetBuilder() ++ .RsaSigningKey(2048, 65537) ++ .AttestKey() ++ .AttestationChallenge("foo") ++ .AttestationApplicationId("bar") ++ .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) ++ .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .SetDefaultValidity(), ++ attest_key_opt, &key_blob_list[i], &attested_key_characteristics, ++ &cert_chain_list[i]); + } +- ++ // Strongbox does not support Factory provisioned attestation key. ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics); + AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics); + ASSERT_GT(cert_chain_list[i].size(), 0); +diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +index 20324117b..741bcf8f6 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp ++++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp +@@ -1145,6 +1145,15 @@ vector KeyMintAidlTestBase::InvalidCurves() { + } + } + ++vector KeyMintAidlTestBase::ValidExponents() { ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ return {65537}; ++ } else { ++ return {3, 65537}; ++ } ++} ++ ++ + vector KeyMintAidlTestBase::ValidDigests(bool withNone, bool withMD5) { + switch (SecLevel()) { + case SecurityLevel::SOFTWARE: +diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +index ec3fcf6a3..0561a9b94 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h ++++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h +@@ -250,7 +250,9 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + .SetDefaultValidity(); + tagModifier(&rsaBuilder); + errorCode = GenerateKey(rsaBuilder, &rsaKeyData.blob, &rsaKeyData.characteristics); +- EXPECT_EQ(expectedReturn, errorCode); ++ if (!(SecLevel() == SecurityLevel::STRONGBOX && ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED == errorCode)) { ++ EXPECT_EQ(expectedReturn, errorCode); ++ } + + /* ECDSA */ + KeyData ecdsaKeyData; +@@ -262,7 +264,10 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + .SetDefaultValidity(); + tagModifier(&ecdsaBuilder); + errorCode = GenerateKey(ecdsaBuilder, &ecdsaKeyData.blob, &ecdsaKeyData.characteristics); +- EXPECT_EQ(expectedReturn, errorCode); ++ if (!(SecLevel() == SecurityLevel::STRONGBOX && ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED == errorCode)) { ++ EXPECT_EQ(expectedReturn, errorCode); ++ } ++ + return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}; + } + bool IsSecure() const { return securityLevel_ != SecurityLevel::SOFTWARE; } +@@ -279,6 +284,7 @@ class KeyMintAidlTestBase : public ::testing::TestWithParam { + vector InvalidCurves(); + + vector ValidDigests(bool withNone, bool withMD5); ++ vector ValidExponents(); + + static vector build_params() { + auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor); +diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp +index 5a87b8385..d30f9dae9 100644 +--- a/security/keymint/aidl/vts/functional/KeyMintTest.cpp ++++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp +@@ -902,8 +902,8 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { + for (auto key_size : ValidKeySizes(Algorithm::RSA)) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) +@@ -913,8 +913,14 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); + ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -1031,8 +1037,7 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { + + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaEncryptionKey(key_size, 65537) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) +@@ -1041,8 +1046,14 @@ TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); + ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + ASSERT_GT(key_blob.size(), 0U); + AuthorizationSet auths; + for (auto& entry : key_characteristics) { +@@ -1143,15 +1154,21 @@ TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) { + vector key_blob; + vector key_characteristics; + +- ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(2048, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) + .AttestationChallenge(challenge) + .Authorization(TAG_NO_AUTH_REQUIRED) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); + } + + /* +@@ -1261,8 +1278,8 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { + for (auto key_size : ValidKeySizes(Algorithm::RSA)) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .RsaSigningKey(key_size, 65537) + .Digest(Digest::NONE) + .Padding(PaddingMode::NONE) +@@ -1273,7 +1290,14 @@ TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); + + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); +@@ -1404,8 +1428,8 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { + for (auto curve : ValidCurves()) { + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, +- GenerateKey(AuthorizationSetBuilder() ++ ++ auto result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(curve) + .Digest(Digest::NONE) +@@ -1414,7 +1438,15 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestation) { + .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob) + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -1491,6 +1523,12 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { + // Tag not required to be supported by all KeyMint implementations. + continue; + } ++ ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ continue; ++ } + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + +@@ -1540,8 +1578,14 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(); + builder.push_back(tag); +- ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS, +- GenerateKey(builder, &key_blob, &key_characteristics)); ++ ++ auto result = GenerateKey(builder, &key_blob, &key_characteristics); ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ continue; ++ } ++ ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS, result); + } + } + +@@ -1577,6 +1621,13 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) { + .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der) + .SetDefaultValidity(), + &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ + ASSERT_EQ(result, ErrorCode::OK); + ASSERT_GT(key_blob.size(), 0U); + +@@ -1655,13 +1706,19 @@ TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) { + vector key_blob; + vector key_characteristics; + +- ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, +- GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ ++ // Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING, result); + } + + /* +@@ -1718,14 +1775,21 @@ TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) { + const string app_id(length, 'a'); + vector key_blob; + vector key_characteristics; +- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ auto result = GenerateKey(AuthorizationSetBuilder() + .Authorization(TAG_NO_AUTH_REQUIRED) + .EcdsaSigningKey(EcCurve::P_256) + .Digest(Digest::NONE) + .AttestationChallenge(challenge) + .AttestationApplicationId(app_id) + .SetDefaultValidity(), +- &key_blob, &key_characteristics)); ++ &key_blob, &key_characteristics); ++ //Strongbox does not support Factory provisioned attestation key ++ if (SecLevel() == SecurityLevel::STRONGBOX) { ++ ASSERT_EQ(ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED, result); ++ return; ++ } ++ ASSERT_EQ(ErrorCode::OK, result); ++ + ASSERT_GT(key_blob.size(), 0U); + CheckBaseParams(key_characteristics); + CheckCharacteristics(key_blob, key_characteristics); +@@ -3755,25 +3819,27 @@ typedef KeyMintAidlTestBase EncryptionOperationsTest; + * Verifies that raw RSA decryption works. + */ + TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) { +- for (uint64_t exponent : {3, 65537}) { +- ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() +- .Authorization(TAG_NO_AUTH_REQUIRED) +- .RsaEncryptionKey(2048, exponent) +- .Padding(PaddingMode::NONE) +- .SetDefaultValidity())); + +- string message = string(2048 / 8, 'a'); +- auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); +- string ciphertext1 = LocalRsaEncryptMessage(message, params); +- EXPECT_EQ(2048U / 8, ciphertext1.size()); ++ for (uint64_t exponent : ValidExponents()) ++ { ++ ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder() ++ .Authorization(TAG_NO_AUTH_REQUIRED) ++ .RsaEncryptionKey(2048, exponent) ++ .Padding(PaddingMode::NONE) ++ .SetDefaultValidity())); + +- string ciphertext2 = LocalRsaEncryptMessage(message, params); +- EXPECT_EQ(2048U / 8, ciphertext2.size()); ++ string message = string(2048 / 8, 'a'); ++ auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE); ++ string ciphertext1 = LocalRsaEncryptMessage(message, params); ++ EXPECT_EQ(2048U / 8, ciphertext1.size()); + +- // Unpadded RSA is deterministic +- EXPECT_EQ(ciphertext1, ciphertext2); ++ string ciphertext2 = LocalRsaEncryptMessage(message, params); ++ EXPECT_EQ(2048U / 8, ciphertext2.size()); + +- CheckedDeleteKey(); ++ // Unpadded RSA is deterministic ++ EXPECT_EQ(ciphertext1, ciphertext2); ++ ++ CheckedDeleteKey(); + } + } + +@@ -6255,7 +6321,7 @@ TEST_P(ClearOperationsTest, TooManyOperations) { + size_t i; + + for (i = 0; i < max_operations; i++) { +- result = Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, op_handles[i]); ++ result = Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params, op_handles[i]); + if (ErrorCode::OK != result) { + break; + } +@@ -6263,12 +6329,12 @@ TEST_P(ClearOperationsTest, TooManyOperations) { + EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result); + // Try again just in case there's a weird overflow bug + EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, +- Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); ++ Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); + for (size_t j = 0; j < i; j++) { + EXPECT_EQ(ErrorCode::OK, Abort(op_handles[j])) + << "Aboort failed for i = " << j << std::endl; + } +- EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params)); ++ EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, key_blob_, params, &out_params)); + AbortIfNeeded(); + } + +@@ -6367,7 +6433,6 @@ TEST_P(KeyAgreementTest, Ecdh) { + OPENSSL_free(p); + + // Generate EC key in KeyMint (only access to public key material) +- vector challenge = {0x41, 0x42}; + EXPECT_EQ( + ErrorCode::OK, + GenerateKey(AuthorizationSetBuilder() +@@ -6376,7 +6441,6 @@ TEST_P(KeyAgreementTest, Ecdh) { + .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY) + .Authorization(TAG_ALGORITHM, Algorithm::EC) + .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62}) +- .Authorization(TAG_ATTESTATION_CHALLENGE, challenge) + .SetDefaultValidity())) + << "Failed to generate key"; + ASSERT_GT(cert_chain_.size(), 0); +@@ -6456,14 +6520,24 @@ TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) { + CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK); + + for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { ++ ++ if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) { ++ continue; ++ } + ASSERT_GT(keyData.blob.size(), 0U); + AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics); + EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params; + } + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); +- CheckedDeleteKey(&rsaKeyData.blob); +- CheckedDeleteKey(&ecdsaKeyData.blob); ++ ++ if (rsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&rsaKeyData.blob); ++ } ++ if (ecdsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&ecdsaKeyData.blob); ++ } ++ + } + + /* +@@ -6479,14 +6553,21 @@ TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) { + }); + + for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) { ++ if (SecLevel() == SecurityLevel::STRONGBOX && keyData.blob.size() == 0U) { ++ continue; ++ } + ASSERT_GT(keyData.blob.size(), 0U); + AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics); + EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params; + } + CheckedDeleteKey(&aesKeyData.blob); + CheckedDeleteKey(&hmacKeyData.blob); +- CheckedDeleteKey(&rsaKeyData.blob); +- CheckedDeleteKey(&ecdsaKeyData.blob); ++ if (rsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&rsaKeyData.blob); ++ } ++ if (ecdsaKeyData.blob.size() != 0U) { ++ CheckedDeleteKey(&ecdsaKeyData.blob); ++ } + } + + /* +diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +index 38f358686..74e44c7b4 100644 +--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp ++++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +@@ -164,6 +164,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParamgetHardwareInfo(&rpcHardwareInfo).isOk()); + } + + static vector build_params() { +@@ -173,6 +174,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam provisionable_; ++ RpcHardwareInfo rpcHardwareInfo; + }; + + using GenerateKeyTests = VtsRemotelyProvisionedComponentTests; +@@ -273,11 +275,10 @@ TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) { + class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + protected: + CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) { +- generateTestEekChain(3); + } + + void generateTestEekChain(size_t eekLength) { +- auto chain = generateEekChain(eekLength, eekId_); ++ auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_); + EXPECT_TRUE(chain) << chain.message(); + if (chain) testEekChain_ = chain.moveValue(); + testEekLength_ = eekLength; +@@ -298,6 +299,17 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + } + } + ++ ErrMsgOr getSessionKey(ErrMsgOr>& senderPubkey) { ++ if (rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_25519 || ++ rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_NONE) { ++ return x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, ++ senderPubkey->first, false /* senderIsA */); ++ } else { ++ return ECDH_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, ++ senderPubkey->first, false /* senderIsA */); ++ } ++ } ++ + void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const bytevec& keysToSignMac, const ProtectedData& protectedData, + std::vector* bccOutput = nullptr) { +@@ -310,9 +322,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + ASSERT_TRUE(senderPubkey) << senderPubkey.message(); + EXPECT_EQ(senderPubkey->second, eekId_); + +- auto sessionKey = +- x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey, +- senderPubkey->first, false /* senderIsA */); ++ auto sessionKey = getSessionKey(senderPubkey); + ASSERT_TRUE(sessionKey) << sessionKey.message(); + + auto protectedDataPayload = +@@ -322,7 +332,7 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { + auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload); + ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg; + ASSERT_TRUE(parsedPayload->asArray()); +- EXPECT_EQ(parsedPayload->asArray()->size(), 2U); ++ EXPECT_LE(parsedPayload->asArray()->size(), 3U); + + auto& signedMac = parsedPayload->asArray()->get(0); + auto& bcc = parsedPayload->asArray()->get(1); +@@ -406,6 +416,7 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -445,7 +456,7 @@ TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, {} /* keysToSign */, getProdEekChain(), challenge_, &deviceInfo, ++ testMode, {} /* keysToSign */, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, + &protectedData, &keysToSignMac); + EXPECT_TRUE(status.isOk()); + } +@@ -486,7 +497,7 @@ TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, keysToSign_, getProdEekChain(), challenge_, &deviceInfo, &protectedData, ++ testMode, keysToSign_, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, &protectedData, + &keysToSignMac); + EXPECT_TRUE(status.isOk()); + } +@@ -502,6 +513,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + testMode, {keyWithCorruptMac}, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -521,7 +533,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) { + DeviceInfo deviceInfo; + ProtectedData protectedData; + auto status = provisionable_->generateCertificateRequest( +- testMode, {keyWithCorruptMac}, getProdEekChain(), challenge_, &deviceInfo, ++ testMode, {keyWithCorruptMac}, getProdEekChain(rpcHardwareInfo.supportedEekCurve), challenge_, &deviceInfo, + &protectedData, &keysToSignMac); + ASSERT_FALSE(status.isOk()) << status.getMessage(); + EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); +@@ -535,7 +547,7 @@ TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) { + bool testMode = false; + generateKeys(testMode, 4 /* numKeys */); + +- auto prodEekChain = getProdEekChain(); ++ auto prodEekChain = getProdEekChain(rpcHardwareInfo.supportedEekCurve); + auto [parsedChain, _, parseErr] = cppbor::parse(prodEekChain); + ASSERT_NE(parsedChain, nullptr) << parseErr; + ASSERT_NE(parsedChain->asArray(), nullptr); +@@ -566,7 +578,7 @@ TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) { + + // Build an EEK chain that omits the first self-signed cert. + auto truncatedChain = cppbor::Array(); +- auto [chain, _, parseErr] = cppbor::parse(getProdEekChain()); ++ auto [chain, _, parseErr] = cppbor::parse(getProdEekChain(rpcHardwareInfo.supportedEekCurve)); + ASSERT_TRUE(chain); + auto eekChain = chain->asArray(); + ASSERT_NE(eekChain, nullptr); +@@ -594,6 +606,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + true /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +@@ -612,6 +625,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) { + bytevec keysToSignMac; + DeviceInfo deviceInfo; + ProtectedData protectedData; ++ generateTestEekChain(3); + auto status = provisionable_->generateCertificateRequest( + false /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, + &protectedData, &keysToSignMac); +diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp +index 9e218b6a3..73fb8c277 100644 +--- a/security/keymint/support/Android.bp ++++ b/security/keymint/support/Android.bp +@@ -62,6 +62,7 @@ cc_library { + "libcppcose_rkp", + "libcrypto", + "libjsoncpp", ++ "android.hardware.security.keymint-V1-ndk_platform", + ], + } + +diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h +index 406b7a9b7..4d9ed2b0c 100644 +--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h ++++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h +@@ -52,6 +52,20 @@ inline constexpr uint8_t kCoseEncodedGeekCert[] = { + 0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2, + 0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04}; + ++// The Google ECDSA root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1 ++inline constexpr uint8_t kCoseEncodedEcdsaRootCert[] = { ++ 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa0, 0x58, 0x4d, 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, ++ 0x21, 0x58, 0x20, 0xf7, 0x14, 0x8a, 0xdb, 0x97, 0xf4, 0xcc, 0x53, 0xef, 0xd2, 0x64, 0x11, ++ 0xc4, 0xe3, 0x75, 0x1f, 0x66, 0x1f, 0xa4, 0x71, 0x0c, 0x6c, 0xcf, 0xfa, 0x09, 0x46, 0x80, ++ 0x74, 0x87, 0x54, 0xf2, 0xad, 0x22, 0x58, 0x20, 0x5e, 0x7f, 0x5b, 0xf6, 0xec, 0xe4, 0xf6, ++ 0x19, 0xcc, 0xff, 0x13, 0x37, 0xfd, 0x0f, 0xa1, 0xc8, 0x93, 0xdb, 0x18, 0x06, 0x76, 0xc4, ++ 0x5d, 0xe6, 0xd7, 0x6a, 0x77, 0x86, 0xc3, 0x2d, 0xaf, 0x8f, 0x58, 0x47, 0x30, 0x45, 0x02, ++ 0x20, 0x2f, 0x97, 0x8e, 0x42, 0xfb, 0xbe, 0x07, 0x2d, 0x95, 0x47, 0x85, 0x47, 0x93, 0x40, ++ 0xb0, 0x1f, 0xd4, 0x9b, 0x47, 0xa4, 0xc4, 0x44, 0xa9, 0xf2, 0xa1, 0x07, 0x87, 0x10, 0xc7, ++ 0x9f, 0xcb, 0x11, 0x02, 0x21, 0x00, 0xf4, 0xbf, 0x9f, 0xe8, 0x3b, 0xe0, 0xe7, 0x34, 0x4c, ++ 0x15, 0xfc, 0x7b, 0xc3, 0x7e, 0x33, 0x05, 0xf4, 0xd1, 0x34, 0x3c, 0xed, 0x02, 0x04, 0x60, ++ 0x7a, 0x15, 0xe0, 0x79, 0xd3, 0x8a, 0xff, 0x24}; ++ + /** + * Generates random bytes. + */ +@@ -67,12 +81,12 @@ struct EekChain { + * Generates an X25518 EEK with the specified eekId and an Ed25519 chain of the + * specified length. All keys are generated randomly. + */ +-ErrMsgOr generateEekChain(size_t length, const bytevec& eekId); ++ErrMsgOr generateEekChain(int32_t supportedEekCurve, size_t length, const bytevec& eekId); + + /** + * Returns the CBOR-encoded, production Google Endpoint Encryption Key chain. + */ +-bytevec getProdEekChain(); ++bytevec getProdEekChain(int32_t supportedEekCurve); + + struct BccEntryData { + bytevec pubKey; +diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp +index 0cbee5104..ae5120f8b 100644 +--- a/security/keymint/support/remote_prov_utils.cpp ++++ b/security/keymint/support/remote_prov_utils.cpp +@@ -17,15 +17,195 @@ + #include + #include + ++#include + #include + #include + #include ++#include ++#include ++#include ++#include + #include ++#include + #include + #include + + namespace aidl::android::hardware::security::keymint::remote_prov { + ++constexpr int kP256AffinePointSize = 32; ++ ++using EC_KEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; ++ ++ErrMsgOr ecKeyGetPrivateKey(const EC_KEY* ecKey) { ++ // Extract private key. ++ const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey); ++ if (bignum == nullptr) { ++ return "Error getting bignum from private key"; ++ } ++ int size = BN_num_bytes(bignum); ++ // Pad with zeros incase the length is lesser than 32. ++ bytevec privKey(32, 0); ++ BN_bn2bin(bignum, privKey.data() + 32 - size); ++ return privKey; ++} ++ ++ErrMsgOr ecKeyGetPublicKey(const EC_KEY* ecKey) { ++ // Extract public key. ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ const EC_POINT* point = EC_KEY_get0_public_key(ecKey); ++ if (point == nullptr) return "Error getting ecpoint from public key"; ++ ++ int size = EC_POINT_point2oct(group.get(), point, ++ POINT_CONVERSION_UNCOMPRESSED, nullptr, 0, ++ nullptr); ++ if (size == 0) { ++ return "Error generating public key encoding"; ++ } ++ ++ bytevec publicKey; ++ publicKey.resize(size); ++ EC_POINT_point2oct(group.get(), point, ++ POINT_CONVERSION_UNCOMPRESSED, publicKey.data(), ++ publicKey.size(), nullptr); ++ return publicKey; ++} ++ ++ErrMsgOr> getAffineCoordinates( ++ const bytevec& pubKey) { ++ auto group = EC_GROUP_Ptr( ++ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), pubKey.data(), ++ pubKey.size(), nullptr) != 1) { ++ return "Error decoding publicKey"; ++ } ++ BIGNUM_Ptr x(BN_new()); ++ BIGNUM_Ptr y(BN_new()); ++ BN_CTX_Ptr ctx(BN_CTX_new()); ++ if (!ctx.get()) return "Failed to create BN_CTX instance"; ++ ++ if (!EC_POINT_get_affine_coordinates_GFp(group.get(), point.get(), ++ x.get(), y.get(), ++ ctx.get())) { ++ return "Failed to get affine coordinates from ECPoint"; ++ } ++ bytevec pubX(kP256AffinePointSize); ++ bytevec pubY(kP256AffinePointSize); ++ if (BN_bn2binpad(x.get(), pubX.data(), kP256AffinePointSize) != ++ kP256AffinePointSize) { ++ return "Error in converting absolute value of x cordinate to big-endian"; ++ } ++ if (BN_bn2binpad(y.get(), pubY.data(), kP256AffinePointSize) != ++ kP256AffinePointSize) { ++ return "Error in converting absolute value of y cordinate to big-endian"; ++ } ++ return std::make_tuple(std::move(pubX), std::move(pubY)); ++} ++ ++ErrMsgOr> generateEc256KeyPair() { ++ auto ec_key = EC_KEY_Ptr(EC_KEY_new()); ++ if (ec_key.get() == nullptr) { ++ return "Failed to allocate ec key"; ++ } ++ ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (group.get() == nullptr) { ++ return "Error creating EC group by curve name"; ++ } ++ ++ if (EC_KEY_set_group(ec_key.get(), group.get()) != 1 || ++ EC_KEY_generate_key(ec_key.get()) != 1 || EC_KEY_check_key(ec_key.get()) < 0) { ++ return "Error generating key"; ++ } ++ ++ auto privKey = ecKeyGetPrivateKey(ec_key.get()); ++ if (!privKey) return privKey.moveMessage(); ++ ++ auto pubKey = ecKeyGetPublicKey(ec_key.get()); ++ if (!pubKey) return pubKey.moveMessage(); ++ ++ return std::make_tuple(pubKey.moveValue(), privKey.moveValue()); ++} ++ ++ErrMsgOr> generateX25519KeyPair() { ++ /* Generate X25519 key pair */ ++ bytevec pubKey(X25519_PUBLIC_VALUE_LEN); ++ bytevec privKey(X25519_PRIVATE_KEY_LEN); ++ X25519_keypair(pubKey.data(), privKey.data()); ++ return std::make_tuple(std::move(pubKey), std::move(privKey)); ++} ++ ++ErrMsgOr> generateED25519KeyPair() { ++ /* Generate ED25519 key pair */ ++ bytevec pubKey(ED25519_PUBLIC_KEY_LEN); ++ bytevec privKey(ED25519_PRIVATE_KEY_LEN); ++ ED25519_keypair(pubKey.data(), privKey.data()); ++ return std::make_tuple(std::move(pubKey), std::move(privKey)); ++} ++ ++ErrMsgOr> generateKeyPair( ++ int32_t supportedEekCurve, bool isEek) { ++ ++ switch (supportedEekCurve) { ++ case RpcHardwareInfo::CURVE_NONE: ++ case RpcHardwareInfo::CURVE_25519: ++ if (isEek) { ++ return generateX25519KeyPair(); ++ } ++ return generateED25519KeyPair(); ++ case RpcHardwareInfo::CURVE_P256: ++ return generateEc256KeyPair(); ++ default: ++ return "Unknown EEK Curve."; ++ } ++} ++ ++ErrMsgOr constructCoseKey(int32_t supportedEekCurve, const bytevec& eekId, ++ const bytevec& pubKey) { ++ CoseKeyType keyType; ++ CoseKeyAlgorithm algorithm; ++ CoseKeyCurve curve; ++ bytevec pubX; ++ bytevec pubY; ++ switch (supportedEekCurve) { ++ case RpcHardwareInfo::CURVE_NONE: ++ case RpcHardwareInfo::CURVE_25519: ++ keyType = OCTET_KEY_PAIR; ++ algorithm = (eekId.empty()) ? EDDSA : ECDH_ES_HKDF_256; ++ curve = (eekId.empty()) ? ED25519 : cppcose::X25519; ++ pubX = pubKey; ++ break; ++ case RpcHardwareInfo::CURVE_P256: { ++ keyType = EC2; ++ algorithm = (eekId.empty()) ? ES256 : ECDH_ES_HKDF_256; ++ curve = P256; ++ auto affineCoordinates = getAffineCoordinates(pubKey); ++ if (!affineCoordinates) return affineCoordinates.moveMessage(); ++ std::tie(pubX, pubY) = affineCoordinates.moveValue(); ++ } break; ++ default: ++ return "Unknown EEK Curve."; ++ } ++ cppbor::Map coseKey = cppbor::Map() ++ .add(CoseKey::KEY_TYPE, keyType) ++ .add(CoseKey::ALGORITHM, algorithm) ++ .add(CoseKey::CURVE, curve) ++ .add(CoseKey::PUBKEY_X, pubX); ++ ++ if (!pubY.empty()) coseKey.add(CoseKey::PUBKEY_Y, pubY); ++ if (!eekId.empty()) coseKey.add(CoseKey::KEY_ID, eekId); ++ ++ return coseKey.canonicalize().encode(); ++} ++ + bytevec kTestMacKey(32 /* count */, 0 /* byte value */); + + bytevec randomBytes(size_t numBytes) { +@@ -34,7 +214,17 @@ bytevec randomBytes(size_t numBytes) { + return retval; + } + +-ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { ++ErrMsgOr constructCoseSign1(int32_t supportedEekCurve, const bytevec& key, ++ const bytevec& payload, const bytevec& aad) { ++ if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { ++ return constructECDSACoseSign1(key, {} /* protectedParams */, payload, aad); ++ } else { ++ return cppcose::constructCoseSign1(key, payload, aad); ++ } ++} ++ ++ErrMsgOr generateEekChain(int32_t supportedEekCurve, size_t length, ++ const bytevec& eekId) { + if (length < 2) { + return "EEK chain must contain at least 2 certs."; + } +@@ -43,42 +233,31 @@ ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { + + bytevec prev_priv_key; + for (size_t i = 0; i < length - 1; ++i) { +- bytevec pub_key(ED25519_PUBLIC_KEY_LEN); +- bytevec priv_key(ED25519_PRIVATE_KEY_LEN); +- +- ED25519_keypair(pub_key.data(), priv_key.data()); ++ auto keyPair = generateKeyPair(supportedEekCurve, false); ++ if (!keyPair) keyPair.moveMessage(); ++ auto [pub_key, priv_key] = keyPair.moveValue(); + + // The first signing key is self-signed. + if (prev_priv_key.empty()) prev_priv_key = priv_key; + +- auto coseSign1 = constructCoseSign1(prev_priv_key, +- cppbor::Map() /* payload CoseKey */ +- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR) +- .add(CoseKey::ALGORITHM, EDDSA) +- .add(CoseKey::CURVE, ED25519) +- .add(CoseKey::PUBKEY_X, pub_key) +- .canonicalize() +- .encode(), ++ auto coseKey = constructCoseKey(supportedEekCurve, {}, pub_key); ++ if (!coseKey) return coseKey.moveMessage(); ++ ++ auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), + {} /* AAD */); + if (!coseSign1) return coseSign1.moveMessage(); + eekChain.add(coseSign1.moveValue()); + + prev_priv_key = priv_key; + } ++ auto keyPair = generateKeyPair(supportedEekCurve, true); ++ if (!keyPair) keyPair.moveMessage(); ++ auto [pub_key, priv_key] = keyPair.moveValue(); + +- bytevec pub_key(X25519_PUBLIC_VALUE_LEN); +- bytevec priv_key(X25519_PRIVATE_KEY_LEN); +- X25519_keypair(pub_key.data(), priv_key.data()); ++ auto coseKey = constructCoseKey(supportedEekCurve, eekId, pub_key); ++ if (!coseKey) return coseKey.moveMessage(); + +- auto coseSign1 = constructCoseSign1(prev_priv_key, +- cppbor::Map() /* payload CoseKey */ +- .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR) +- .add(CoseKey::KEY_ID, eekId) +- .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256) +- .add(CoseKey::CURVE, cppcose::X25519) +- .add(CoseKey::PUBKEY_X, pub_key) +- .canonicalize() +- .encode(), ++ auto coseSign1 = constructCoseSign1(supportedEekCurve, prev_priv_key, coseKey.moveValue(), + {} /* AAD */); + if (!coseSign1) return coseSign1.moveMessage(); + eekChain.add(coseSign1.moveValue()); +@@ -86,16 +265,15 @@ ErrMsgOr generateEekChain(size_t length, const bytevec& eekId) { + return EekChain{eekChain.encode(), pub_key, priv_key}; + } + +-bytevec getProdEekChain() { +- bytevec prodEek; +- prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert)); +- +- // In CBOR encoding, 0x82 indicates an array of two items +- prodEek.push_back(0x82); +- prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)); +- prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)); +- +- return prodEek; ++bytevec getProdEekChain(int32_t supportedEekCurve) { ++ cppbor::Array chain; ++ if (supportedEekCurve == RpcHardwareInfo::CURVE_P256) { ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedEcdsaRootCert), std::end(kCoseEncodedEcdsaRootCert)))); ++ } else { ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert)))); ++ chain.add(cppbor::EncodedItem(bytevec(std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert)))); ++ } ++ return chain.encode(); + } + + ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, +@@ -122,7 +300,8 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); +- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { ++ if (!algorithm || !algorithm->asInt() || (algorithm->asInt()->value() != EDDSA && ++ algorithm->asInt()->value() != ES256)) { + return "Unsupported signature algorithm"; + } + +@@ -136,16 +315,35 @@ ErrMsgOr verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1, + if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry"; + + bool selfSigned = signingCoseKey.empty(); +- auto key = ++ bytevec key; ++ if (algorithm->asInt()->value() == EDDSA) { ++ auto key = + CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey); +- if (!key) return "Bad signing key: " + key.moveMessage(); ++ if (!key) return "Bad signing key: " + key.moveMessage(); + +- bytevec signatureInput = ++ bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); + +- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), +- key->getBstrValue(CoseKey::PUBKEY_X)->data())) { +- return "Signature verification failed"; ++ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), ++ key->getBstrValue(CoseKey::PUBKEY_X)->data())) { ++ return "Signature verification failed"; ++ } ++ } else { // P256 ++ auto key = ++ CoseKey::parseP256(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || ++ key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ auto publicKey = key->getEcPublicKey(); ++ if (!publicKey) return publicKey.moveMessage(); ++ ++ bytevec signatureInput = ++ cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); ++ ++ if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), signature->value())) { ++ return "Signature verification failed"; ++ } + } + + return serializedKey->asBstr()->value(); +diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp +index 8697c5190..0009bf713 100644 +--- a/security/keymint/support/remote_prov_utils_test.cpp ++++ b/security/keymint/support/remote_prov_utils_test.cpp +@@ -14,6 +14,7 @@ + * limitations under the License. + */ + ++#include + #include + #include + #include +@@ -35,13 +36,13 @@ using ::keymaster::validateAndExtractEekPubAndId; + using ::testing::ElementsAreArray; + + TEST(RemoteProvUtilsTest, GenerateEekChainInvalidLength) { +- ASSERT_FALSE(generateEekChain(1, /*eekId=*/{})); ++ ASSERT_FALSE(generateEekChain(CURVE_25519, 1, /*eekId=*/{})); + } + + TEST(RemoteProvUtilsTest, GenerateEekChain) { + bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0}; + for (size_t length : {2, 3, 31}) { +- auto get_eek_result = generateEekChain(length, kTestEekId); ++ auto get_eek_result = generateEekChain(CURVE_25519, length, kTestEekId); + ASSERT_TRUE(get_eek_result) << get_eek_result.message(); + + auto& [chain, pubkey, privkey] = *get_eek_result; diff --git a/patches/keymint/aosp_integration_patches_aosp_12_r15/system_keymaster.patch b/patches/keymint/aosp_integration_patches_aosp_12_r15/system_keymaster.patch new file mode 100644 index 00000000..b994b768 --- /dev/null +++ b/patches/keymint/aosp_integration_patches_aosp_12_r15/system_keymaster.patch @@ -0,0 +1,441 @@ +diff --git a/cppcose/cppcose.cpp b/cppcose/cppcose.cpp +index bfe9928..5009bfe 100644 +--- a/cppcose/cppcose.cpp ++++ b/cppcose/cppcose.cpp +@@ -21,10 +21,17 @@ + + #include + #include ++#include + + #include + + namespace cppcose { ++constexpr int kP256AffinePointSize = 32; ++ ++using EVP_PKEY_Ptr = bssl::UniquePtr; ++using EVP_PKEY_CTX_Ptr = bssl::UniquePtr; ++using ECDSA_SIG_Ptr = bssl::UniquePtr; ++using EC_KEY_Ptr = bssl::UniquePtr; + + namespace { + +@@ -51,6 +58,92 @@ ErrMsgOr> aesGcmInitAndProcessAad(const bytevec& + return std::move(ctx); + } + ++ ++ErrMsgOr signEcdsaDigest(const bytevec& key, const bytevec& data) { ++ auto bn = BIGNUM_Ptr(BN_bin2bn(key.data(), key.size(), nullptr)); ++ if (bn.get() == nullptr) { ++ return "Error creating BIGNUM"; ++ } ++ ++ auto ec_key = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (EC_KEY_set_private_key(ec_key.get(), bn.get()) != 1) { ++ return "Error setting private key from BIGNUM"; ++ } ++ ++ ECDSA_SIG* sig = ECDSA_do_sign(data.data(), data.size(), ec_key.get()); ++ if (sig == nullptr) { ++ return "Error signing digest"; ++ } ++ size_t len = i2d_ECDSA_SIG(sig, nullptr); ++ bytevec signature(len); ++ unsigned char* p = (unsigned char*)signature.data(); ++ i2d_ECDSA_SIG(sig, &p); ++ ECDSA_SIG_free(sig); ++ return signature; ++} ++ ++ErrMsgOr ecdh(const bytevec& publicKey, const bytevec& privateKey) { ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) != ++ 1) { ++ return "Error decoding publicKey"; ++ } ++ auto ecKey = EC_KEY_Ptr(EC_KEY_new()); ++ auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new()); ++ if (ecKey.get() == nullptr || pkey.get() == nullptr) { ++ return "Memory allocation failed"; ++ } ++ if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { ++ return "Error setting group"; ++ } ++ if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { ++ return "Error setting point"; ++ } ++ if (EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()) != 1) { ++ return "Error setting key"; ++ } ++ ++ auto bn = BIGNUM_Ptr(BN_bin2bn(privateKey.data(), privateKey.size(), nullptr)); ++ if (bn.get() == nullptr) { ++ return "Error creating BIGNUM for private key"; ++ } ++ auto privEcKey = EC_KEY_Ptr(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); ++ if (EC_KEY_set_private_key(privEcKey.get(), bn.get()) != 1) { ++ return "Error setting private key from BIGNUM"; ++ } ++ auto privPkey = EVP_PKEY_Ptr(EVP_PKEY_new()); ++ if (EVP_PKEY_set1_EC_KEY(privPkey.get(), privEcKey.get()) != 1) { ++ return "Error setting private key"; ++ } ++ ++ auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(privPkey.get(), NULL)); ++ if (ctx.get() == nullptr) { ++ return "Error creating context"; ++ } ++ ++ if (EVP_PKEY_derive_init(ctx.get()) != 1) { ++ return "Error initializing context"; ++ } ++ ++ if (EVP_PKEY_derive_set_peer(ctx.get(), pkey.get()) != 1) { ++ return "Error setting peer"; ++ } ++ ++ /* Determine buffer length for shared secret */ ++ size_t secretLen = 0; ++ if (EVP_PKEY_derive(ctx.get(), NULL, &secretLen) != 1) { ++ return "Error determing length of shared secret"; ++ } ++ bytevec sharedSecret; ++ sharedSecret.resize(secretLen); ++ ++ if (EVP_PKEY_derive(ctx.get(), sharedSecret.data(), &secretLen) != 1) { ++ return "Error deriving shared secret"; ++ } ++ return sharedSecret; ++} ++ + } // namespace + + ErrMsgOr generateHmacSha256(const bytevec& key, const bytevec& data) { +@@ -134,6 +227,17 @@ ErrMsgOr verifyAndParseCoseMac0(const cppbor::Item* macIt + return payload->value(); + } + ++ErrMsgOr createECDSACoseSign1Signature(const bytevec& key, const bytevec& protectedParams, ++ const bytevec& payload, const bytevec& aad) { ++ bytevec signatureInput = cppbor::Array() ++ .add("Signature1") // ++ .add(protectedParams) ++ .add(aad) ++ .add(payload) ++ .encode(); ++ return signEcdsaDigest(key, sha256(signatureInput)); ++} ++ + ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec signatureInput = cppbor::Array() +@@ -152,6 +256,19 @@ ErrMsgOr createCoseSign1Signature(const bytevec& key, const bytevec& pr + return signature; + } + ++ErrMsgOr constructECDSACoseSign1(const bytevec& key, cppbor::Map protectedParams, ++ const bytevec& payload, const bytevec& aad) { ++ bytevec protParms = protectedParams.add(ALGORITHM, ES256).canonicalize().encode(); ++ auto signature = createECDSACoseSign1Signature(key, protParms, payload, aad); ++ if (!signature) return signature.moveMessage(); ++ ++ return cppbor::Array() ++ .add(std::move(protParms)) ++ .add(cppbor::Map() /* unprotected parameters */) ++ .add(std::move(payload)) ++ .add(std::move(*signature)); ++} ++ + ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map protectedParams, + const bytevec& payload, const bytevec& aad) { + bytevec protParms = protectedParams.add(ALGORITHM, EDDSA).canonicalize().encode(); +@@ -193,7 +310,8 @@ ErrMsgOr verifyAndParseCoseSign1(const cppbor::Array* coseSign1, + } + + auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM); +- if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) { ++ if (!algorithm || !algorithm->asInt() || ++ !(algorithm->asInt()->value() == EDDSA || algorithm->asInt()->value() == ES256)) { + return "Unsupported signature algorithm"; + } + +@@ -203,17 +321,30 @@ ErrMsgOr verifyAndParseCoseSign1(const cppbor::Array* coseSign1, + } + + bool selfSigned = signingCoseKey.empty(); +- auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey); +- if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty()) { +- return "Bad signing key: " + key.moveMessage(); +- } +- + bytevec signatureInput = + cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode(); +- +- if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), +- key->getBstrValue(CoseKey::PUBKEY_X)->data())) { +- return "Signature verification failed"; ++ if (algorithm->asInt()->value() == EDDSA) { ++ auto key = CoseKey::parseEd25519(selfSigned ? payload->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ ++ if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(), ++ key->getBstrValue(CoseKey::PUBKEY_X)->data())) { ++ return "Signature verification failed"; ++ } ++ } else { // P256 ++ auto key = CoseKey::parseP256(selfSigned ? payload->value() : signingCoseKey); ++ if (!key || key->getBstrValue(CoseKey::PUBKEY_X)->empty() || ++ key->getBstrValue(CoseKey::PUBKEY_Y)->empty()) { ++ return "Bad signing key: " + key.moveMessage(); ++ } ++ auto publicKey = key->getEcPublicKey(); ++ if (!publicKey) return publicKey.moveMessage(); ++ ++ if (!verifyEcdsaDigest(publicKey.moveValue(), sha256(signatureInput), signature->value())) { ++ return "Signature verification failed"; ++ } + } + + return payload->value(); +@@ -294,28 +425,47 @@ getSenderPubKeyFromCoseEncrypt(const cppbor::Item* coseEncrypt) { + if (!senderCoseKey || !senderCoseKey->asMap()) return "Invalid sender COSE_Key"; + + auto& keyType = senderCoseKey->asMap()->get(CoseKey::KEY_TYPE); +- if (!keyType || !keyType->asInt() || keyType->asInt()->value() != OCTET_KEY_PAIR) { ++ if (!keyType || !keyType->asInt() || (keyType->asInt()->value() != OCTET_KEY_PAIR && ++ keyType->asInt()->value() != EC2)) { + return "Invalid key type"; + } + + auto& curve = senderCoseKey->asMap()->get(CoseKey::CURVE); +- if (!curve || !curve->asInt() || curve->asInt()->value() != X25519) { ++ if (!curve || !curve->asInt() || ++ (keyType->asInt()->value() == OCTET_KEY_PAIR && curve->asInt()->value() != X25519) || ++ (keyType->asInt()->value() == EC2 && curve->asInt()->value() != P256)) { + return "Unsupported curve"; + } + +- auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); +- if (!pubkey || !pubkey->asBstr() || +- pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) { +- return "Invalid X25519 public key"; ++ bytevec publicKey; ++ if (keyType->asInt()->value() == EC2) { ++ auto& pubX = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); ++ if (!pubX || !pubX->asBstr() || pubX->asBstr()->value().size() != kP256AffinePointSize) { ++ return "Invalid EC public key"; ++ } ++ auto& pubY = senderCoseKey->asMap()->get(CoseKey::PUBKEY_Y); ++ if (!pubY || !pubY->asBstr() || pubY->asBstr()->value().size() != kP256AffinePointSize) { ++ return "Invalid EC public key"; ++ } ++ auto key = CoseKey::getEcPublicKey(pubX->asBstr()->value(), pubY->asBstr()->value()); ++ if (!key) return key.moveMessage(); ++ publicKey = key.moveValue(); ++ } else { ++ auto& pubkey = senderCoseKey->asMap()->get(CoseKey::PUBKEY_X); ++ if (!pubkey || !pubkey->asBstr() || ++ pubkey->asBstr()->value().size() != X25519_PUBLIC_VALUE_LEN) { ++ return "Invalid X25519 public key"; ++ } ++ publicKey = pubkey->asBstr()->value(); + } + + auto& key_id = unprotParms->asMap()->get(KEY_ID); + if (key_id && key_id->asBstr()) { +- return std::make_pair(pubkey->asBstr()->value(), key_id->asBstr()->value()); ++ return std::make_pair(publicKey, key_id->asBstr()->value()); + } + + // If no key ID, just return an empty vector. +- return std::make_pair(pubkey->asBstr()->value(), bytevec{}); ++ return std::make_pair(publicKey, bytevec{}); + } + + ErrMsgOr decryptCoseEncrypt(const bytevec& key, const cppbor::Item* coseEncrypt, +@@ -367,6 +517,43 @@ ErrMsgOr decryptCoseEncrypt(const bytevec& key, const cppbor::Item* cos + return aesGcmDecrypt(key, nonce->asBstr()->value(), aad, ciphertext->asBstr()->value()); + } + ++ErrMsgOr ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, ++ const bytevec& pubKeyB, bool senderIsA) { ++ if (privKeyA.empty() || pubKeyA.empty() || pubKeyB.empty()) { ++ return "Missing input key parameters"; ++ } ++ ++ auto rawSharedKey = ecdh(pubKeyB, privKeyA); ++ if (!rawSharedKey) return rawSharedKey.moveMessage(); ++ ++ bytevec kdfContext = cppbor::Array() ++ .add(AES_GCM_256) ++ .add(cppbor::Array() // Sender Info ++ .add(cppbor::Bstr("client")) ++ .add(bytevec{} /* nonce */) ++ .add(senderIsA ? pubKeyA : pubKeyB)) ++ .add(cppbor::Array() // Recipient Info ++ .add(cppbor::Bstr("server")) ++ .add(bytevec{} /* nonce */) ++ .add(senderIsA ? pubKeyB : pubKeyA)) ++ .add(cppbor::Array() // SuppPubInfo ++ .add(kAesGcmKeySizeBits) // output key length ++ .add(bytevec{})) // protected ++ .encode(); ++ ++ bytevec retval(SHA256_DIGEST_LENGTH); ++ bytevec salt{}; ++ if (!HKDF(retval.data(), retval.size(), // ++ EVP_sha256(), // ++ rawSharedKey->data(), rawSharedKey->size(), // ++ salt.data(), salt.size(), // ++ kdfContext.data(), kdfContext.size())) { ++ return "ECDH HKDF failed"; ++ } ++ ++ return retval; ++} ++ + ErrMsgOr x25519_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, + const bytevec& pubKeyB, bool senderIsA) { + if (privKeyA.empty() || pubKeyA.empty() || pubKeyB.empty()) { +@@ -460,4 +647,43 @@ ErrMsgOr aesGcmDecrypt(const bytevec& key, const bytevec& nonce, const + return plaintext; + } + ++bytevec sha256(const bytevec& data) { ++ bytevec ret(SHA256_DIGEST_LENGTH); ++ SHA256_CTX ctx; ++ SHA256_Init(&ctx); ++ SHA256_Update(&ctx, data.data(), data.size()); ++ SHA256_Final((unsigned char*)ret.data(), &ctx); ++ return ret; ++} ++ ++bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature) { ++ const unsigned char* p = (unsigned char*)signature.data(); ++ auto sig = ECDSA_SIG_Ptr(d2i_ECDSA_SIG(nullptr, &p, signature.size())); ++ if (sig.get() == nullptr) { ++ return false; ++ } ++ ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (EC_POINT_oct2point(group.get(), point.get(), key.data(), key.size(), nullptr) != 1) { ++ return false; ++ } ++ auto ecKey = EC_KEY_Ptr(EC_KEY_new()); ++ if (ecKey.get() == nullptr) { ++ return false; ++ } ++ if (EC_KEY_set_group(ecKey.get(), group.get()) != 1) { ++ return false; ++ } ++ if (EC_KEY_set_public_key(ecKey.get(), point.get()) != 1) { ++ return false; ++ } ++ ++ int rc = ECDSA_do_verify(digest.data(), digest.size(), sig.get(), ecKey.get()); ++ if (rc != 1) { ++ return false; ++ } ++ return true; ++} ++ + } // namespace cppcose +diff --git a/include/keymaster/cppcose/cppcose.h b/include/keymaster/cppcose/cppcose.h +index 0f97388..03251f1 100644 +--- a/include/keymaster/cppcose/cppcose.h ++++ b/include/keymaster/cppcose/cppcose.h +@@ -24,17 +24,25 @@ + + #include + #include +- ++#include ++#include ++#include + #include + #include + #include + #include ++#include + #include + #include + #include + + namespace cppcose { + ++using BIGNUM_Ptr = bssl::UniquePtr; ++using EC_GROUP_Ptr = bssl::UniquePtr; ++using EC_POINT_Ptr = bssl::UniquePtr; ++using BN_CTX_Ptr = bssl::UniquePtr; ++ + template class ErrMsgOr; + using bytevec = std::vector; + using HmacSha256 = std::array; +@@ -203,6 +211,41 @@ class CoseKey { + return key; + } + ++ static ErrMsgOr getEcPublicKey(const bytevec& pubX, const bytevec& pubY) { ++ auto bnX = BIGNUM_Ptr(BN_bin2bn(pubX.data(), pubX.size(), nullptr)); ++ if (bnX.get() == nullptr) { ++ return "Error creating BIGNUM X Coordinate"; ++ } ++ auto bnY = BIGNUM_Ptr(BN_bin2bn(pubY.data(), pubY.size(), nullptr)); ++ if (bnY.get() == nullptr) { ++ return "Error creating BIGNUM Y Coordinate"; ++ } ++ auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); ++ auto point = EC_POINT_Ptr(EC_POINT_new(group.get())); ++ if (!point) return "Failed to create EC_POINT instance"; ++ BN_CTX_Ptr ctx(BN_CTX_new()); ++ if (!ctx.get()) return "Failed to create BN_CTX instance"; ++ if (!EC_POINT_set_affine_coordinates_GFp(group.get(), point.get(), bnX.get(), bnY.get(), ++ ctx.get())) { ++ return "Failed to set affine coordinates."; ++ } ++ int size = EC_POINT_point2oct(group.get(), point.get(), POINT_CONVERSION_UNCOMPRESSED, ++ nullptr, 0, nullptr); ++ if (size == 0) { ++ return "Error generating public key encoding"; ++ } ++ bytevec publicKey(size); ++ EC_POINT_point2oct(group.get(), point.get(), POINT_CONVERSION_UNCOMPRESSED, ++ publicKey.data(), publicKey.size(), nullptr); ++ return publicKey; ++ } ++ ++ ErrMsgOr getEcPublicKey() { ++ auto pubX = getBstrValue(PUBKEY_X).value(); ++ auto pubY = getBstrValue(PUBKEY_Y).value(); ++ return getEcPublicKey(pubX, pubY); ++ } ++ + std::optional getIntValue(Label label) { + const auto& value = key_->get(label); + if (!value || !value->asInt()) return {}; +@@ -252,6 +295,8 @@ ErrMsgOr constructCoseSign1(const bytevec& key, const bytevec& pa + const bytevec& aad); + ErrMsgOr constructCoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, + const bytevec& payload, const bytevec& aad); ++ErrMsgOr constructECDSACoseSign1(const bytevec& key, cppbor::Map extraProtectedFields, ++ const bytevec& payload, const bytevec& aad); + /** + * Verify and parse a COSE_Sign1 message, returning the payload. + * +@@ -282,7 +327,10 @@ decryptCoseEncrypt(const bytevec& key, const cppbor::Item* encryptItem, const by + + ErrMsgOr x25519_HKDF_DeriveKey(const bytevec& senderPubKey, const bytevec& senderPrivKey, + const bytevec& recipientPubKey, bool senderIsA); +- ++ErrMsgOr ECDH_HKDF_DeriveKey(const bytevec& pubKeyA, const bytevec& privKeyA, ++ const bytevec& pubKeyB, bool senderIsA); ++bool verifyEcdsaDigest(const bytevec& key, const bytevec& digest, const bytevec& signature); ++bytevec sha256(const bytevec& data); + ErrMsgOr aesGcmEncrypt(const bytevec& key, const bytevec& nonce, + const bytevec& aad, + const bytevec& plaintext); diff --git a/patches/keymint/aosp_integration_patches_aosp_12_r15/system_security.patch b/patches/keymint/aosp_integration_patches_aosp_12_r15/system_security.patch new file mode 100644 index 00000000..22956d5e --- /dev/null +++ b/patches/keymint/aosp_integration_patches_aosp_12_r15/system_security.patch @@ -0,0 +1,13 @@ +diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp +index 64849c1..40ca554 100644 +--- a/keystore2/src/km_compat/km_compat.cpp ++++ b/keystore2/src/km_compat/km_compat.cpp +@@ -1314,7 +1314,7 @@ KeymasterDevices initializeKeymasters() { + CHECK(serviceManager.get()) << "Failed to get ServiceManager"; + auto result = enumerateKeymasterDevices(serviceManager.get()); + auto softKeymaster = result[SecurityLevel::SOFTWARE]; +- if (!result[SecurityLevel::TRUSTED_ENVIRONMENT]) { ++ if ((!result[SecurityLevel::TRUSTED_ENVIRONMENT]) && (!result[SecurityLevel::STRONGBOX])) { + result = enumerateKeymasterDevices(serviceManager.get()); + } + if (softKeymaster) result[SecurityLevel::SOFTWARE] = softKeymaster; diff --git a/patches/keymint/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch b/patches/keymint/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch new file mode 100644 index 00000000..f533e8c7 --- /dev/null +++ b/patches/keymint/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch @@ -0,0 +1,40 @@ +diff --git a/prebuilts/api/31.0/public/hal_neverallows.te b/prebuilts/api/31.0/public/hal_neverallows.te +index 105689b8a..275f9a5c2 100644 +--- a/prebuilts/api/31.0/public/hal_neverallows.te ++++ b/prebuilts/api/31.0/public/hal_neverallows.te +@@ -9,6 +9,7 @@ neverallow { + -hal_wifi_supplicant_server + -hal_telephony_server + -hal_uwb_server ++ -hal_keymint_server + } self:global_capability_class_set { net_admin net_raw }; + + # Unless a HAL's job is to communicate over the network, or control network +@@ -27,6 +28,7 @@ neverallow { + -hal_wifi_supplicant_server + -hal_telephony_server + -hal_uwb_server ++ -hal_keymint_server + } domain:{ tcp_socket udp_socket rawip_socket } *; + + # The UWB HAL is not actually a networking HAL but may need to bring up and down +diff --git a/public/hal_neverallows.te b/public/hal_neverallows.te +index 105689b8a..275f9a5c2 100644 +--- a/public/hal_neverallows.te ++++ b/public/hal_neverallows.te +@@ -9,6 +9,7 @@ neverallow { + -hal_wifi_supplicant_server + -hal_telephony_server + -hal_uwb_server ++ -hal_keymint_server + } self:global_capability_class_set { net_admin net_raw }; + + # Unless a HAL's job is to communicate over the network, or control network +@@ -27,6 +28,7 @@ neverallow { + -hal_wifi_supplicant_server + -hal_telephony_server + -hal_uwb_server ++ -hal_keymint_server + } domain:{ tcp_socket udp_socket rawip_socket } *; + + # The UWB HAL is not actually a networking HAL but may need to bring up and down