diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java new file mode 100644 index 00000000..642775f1 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -0,0 +1,435 @@ +/* + * 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 com.android.javacard.seprovider.KMAndroidSEProvider; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMError; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMType; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.Util; + +public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeListener { + + private static final byte KM_BEGIN_STATE = 0x00; + private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; + private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + + // Provider specific Commands + private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 1; + private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 2; + private static final byte INS_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; + private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 4; + private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 5; + private static final byte INS_PROVISION_DEVICE_UNIQUE_KEY_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 6; + private static final byte INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 7; + + private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F; + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; + + // Provision reporting status + private static final 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_BOOT_PARAM = 0x20; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; + + public static final short SHARED_SECRET_KEY_SIZE = 32; + + private static byte keymasterState = ILLEGAL_STATE; + private static byte provisionStatus = NOT_PROVISIONED; + + 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 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()) { + super.powerReset(); + } + + if (((KMAndroidSEProvider) seProvider).isProvisionLocked()) { + switch (apduIns) { + case INS_SET_BOOT_PARAMS_CMD: + processSetBootParamsCmd(apdu); + break; + default: + super.process(apdu); + break; + } + return; + } + + if (apduIns == KMType.INVALID_VALUE) { + return; + } + switch (apduIns) { + case INS_PROVISION_ATTEST_IDS_CMD: + processProvisionAttestIdsCmd(apdu); + provisionStatus |= PROVISION_STATUS_ATTEST_IDS; + sendError(apdu, KMError.OK); + break; + + case INS_PROVISION_PRESHARED_SECRET_CMD: + processProvisionPreSharedSecretCmd(apdu); + provisionStatus |= PROVISION_STATUS_PRESHARED_SECRET; + sendError(apdu, KMError.OK); + break; + + case INS_GET_PROVISION_STATUS_CMD: + processGetProvisionStatusCmd(apdu); + break; + + case INS_LOCK_PROVISIONING_CMD: + processLockProvisioningCmd(apdu); + break; + + case INS_SET_BOOT_PARAMS_CMD: + + processSetBootParamsCmd(apdu); + provisionStatus |= PROVISION_STATUS_BOOT_PARAM; + break; + + case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: + processProvisionDeviceUniqueKey(apdu); + break; + + case INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD: + processProvisionAdditionalCertChain(apdu); + break; + + default: + super.process(apdu); + break; + } + } finally { + repository.clean(); + } + } + + private static 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.cast(arr).add((short) 0, coseKeyExp); //[ CoseKey ] + arr = receiveIncoming(apdu, arr); + // Get cose key. + short coseKey = KMArray.cast(arr).get((short) 0); + short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0); + short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen); + //Store the Device unique Key. + seProvider.createDeviceUniqueKey(false, scratchPad, (short) 0, pubKeyLen, scratchPad, + pubKeyLen, privKeyLen); + short bcc = generateBcc(false, scratchPad); + short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, + MAX_COSE_BUF_SIZE); + ((KMAndroidSEProvider) seProvider).persistBootCertificateChain(scratchPad, (short) 0, len); + sendError(apdu, KMError.OK); + } + + private static void processProvisionAdditionalCertChain(APDU apdu) { + // Prepare the expression to decode + short headers = KMCoseHeaders.exp(); + short arrInst = KMArray.instance((short) 4); + KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 1, headers); + KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); + KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); + short coseSignArr = KMArray.exp(arrInst); + short map = KMMap.instance((short) 1); + KMMap.cast(map).add((short) 0, KMTextString.exp(), coseSignArr); + // receive incoming data and decode it. + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = apdu.setIncomingAndReceive(); + short srcOffset = apdu.getOffsetCdata(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); + short index = bufferStartOffset; + byte[] buffer = repository.getHeap(); + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + // decode + map = decoder.decode(map, buffer, bufferStartOffset, bufferLength); + arrInst = KMMap.cast(map).getKeyValue((short) 0); + // Validate Additional certificate chain. + short leafCoseKey = + validateCertChain(false, KMCose.COSE_ALG_ES256, KMCose.COSE_ALG_ES256, arrInst, + srcBuffer, null); + // Compare the DK_Pub. + short pubKeyLen = KMCoseKey.cast(leafCoseKey).getEcdsa256PublicKey(srcBuffer, (short) 0); + KMDeviceUniqueKey uniqueKey = seProvider.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); + } + seProvider.persistAdditionalCertChain(buffer, bufferStartOffset, bufferLength); + //reclaim memory + repository.reclaimMemory(bufferLength); + sendError(apdu, KMError.OK); + } + + private void processProvisionAttestIdsCmd(APDU apdu) { + short keyparams = KMKeyParameters.exp(); + short cmd = KMArray.instance((short) 1); + KMArray.cast(cmd).add((short) 0, keyparams); + short args = receiveIncoming(apdu, cmd); + + short attData = KMArray.cast(args).get((short) 0); + // persist attestation Ids - if any is missing then exception occurs + setAttestationIds(attData); + } + + public void setAttestationIds(short attIdVals) { + KMKeyParameters instParam = KMKeyParameters.cast(attIdVals); + KMArray vals = KMArray.cast(instParam.getVals()); + short index = 0; + short length = vals.length(); + short key; + short type; + short obj; + while (index < length) { + obj = vals.get(index); + key = KMTag.getKey(obj); + type = KMTag.getTagType(obj); + + if (KMType.BYTES_TAG != type) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + obj = KMByteTag.cast(obj).getValue(); + ((KMAndroidSEProvider) seProvider).setAttestationId(key, KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); + index++; + } + } + + private void processProvisionPreSharedSecretCmd(APDU apdu) { + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, blob); + short args = receiveIncoming(apdu, argsProto); + + short val = KMArray.cast(args).get((short) 0); + + if (val != KMType.INVALID_VALUE + && KMByteBlob.cast(val).length() != SHARED_SECRET_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Persist shared Hmac. + ((KMAndroidSEProvider) seProvider).createPresharedKey( + KMByteBlob.cast(val).getBuffer(), + KMByteBlob.cast(val).getStartOff(), + KMByteBlob.cast(val).length()); + + } + + //This function masks the error code with POWER_RESET_MASK_FLAG + // in case if card reset event occurred. The clients of the Applet + // has to extract the power reset status from the error code and + // process accordingly. + private static short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 4); + short powerResetStatus = 0; + if (((KMAndroidSEProvider) seProvider).isPowerReset()) { + powerResetStatus = POWER_RESET_MASK_FLAG; + } + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + KMInteger.cast(int32Ptr).getStartOff(), + powerResetStatus); + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + (short) (KMInteger.cast(int32Ptr).getStartOff() + 2), + err); + // reset power reset status flag to its default value. + //repository.restorePowerResetStatus(); //TODO + return int32Ptr; + } + + private void processGetProvisionStatusCmd(APDU apdu) { + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK)); + KMArray.cast(resp).add((short) 1, KMInteger.uint_16(provisionStatus)); + sendOutgoing(apdu, resp); + } + + private void processSetBootParamsCmd(APDU apdu) { + short argsProto = KMArray.instance((short) 5); + + // Array of 4 expected arguments + // Argument 0 Boot Patch level + KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); + // Argument 1 Verified Boot Key + KMArray.cast(argsProto).add((short) 1, KMByteBlob.exp()); + // Argument 2 Verified Boot Hash + KMArray.cast(argsProto).add((short) 2, KMByteBlob.exp()); + // Argument 3 Verified Boot State + KMArray.cast(argsProto).add((short) 3, KMEnum.instance(KMType.VERIFIED_BOOT_STATE)); + // Argument 4 Device Locked + KMArray.cast(argsProto).add((short) 4, KMEnum.instance(KMType.DEVICE_LOCKED)); + + short args = receiveIncoming(apdu, argsProto); + + short bootParam = KMArray.cast(args).get((short) 0); + + ((KMAndroidSEProvider) seProvider).setBootPatchLevel(KMInteger.cast(bootParam).getBuffer(), + KMInteger.cast(bootParam).getStartOff(), + KMInteger.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 1); + if (KMByteBlob.cast(bootParam).length() > BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMAndroidSEProvider) seProvider).setBootKey(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 2); + if (KMByteBlob.cast(bootParam).length() > BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMAndroidSEProvider) seProvider).setVerifiedBootHash(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 3); + byte enumVal = KMEnum.cast(bootParam).getVal(); + ((KMAndroidSEProvider) seProvider).setBootState(enumVal); + + bootParam = KMArray.cast(args).get((short) 4); + enumVal = KMEnum.cast(bootParam).getVal(); + ((KMAndroidSEProvider) seProvider).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + + super.reboot(); + sendError(apdu, KMError.OK); + } + + private void processLockProvisioningCmd(APDU apdu) { + ((KMAndroidSEProvider) seProvider).setProvisionLocked(true); + sendError(apdu, KMError.OK); + } + + @Override + public void onCleanup() { + } + + @Override + public void onConsolidate() { + } + + @Override + public void onRestore(Element element) { + element.initRead(); + provisionStatus = element.readByte(); + keymasterState = element.readByte(); + repository.onRestore(element); + seProvider.onRestore(element); + } + + @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(provisionStatus); + element.write(keymasterState); + repository.onSave(element); + seProvider.onSave(element); + return element; + } + + private short computePrimitveDataSize() { + // provisionStatus + keymasterState + return (short) 2; + } + + private short computeObjectCount() { + return (short) 0; + } + + private short validateApdu(APDU apdu) { + // Read the apdu header and buffer. + byte[] apduBuffer = apdu.getBuffer(); + byte apduClass = apduBuffer[ISO7816.OFFSET_CLA]; + short P1P2 = Util.getShort(apduBuffer, ISO7816.OFFSET_P1); + + // Validate APDU Header. + if ((apduClass != CLA_ISO7816_NO_SM_NO_CHAN)) { + sendError(apdu, KMError.UNSUPPORTED_CLA); + return KMType.INVALID_VALUE; + } + + // Validate P1P2. + if (P1P2 != KMKeymasterApplet.KM_HAL_VERSION) { + sendError(apdu, KMError.INVALID_P1P2); + return KMType.INVALID_VALUE; + } + return apduBuffer[ISO7816.OFFSET_INS]; + } +} + diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java new file mode 100644 index 00000000..c1cdb91d --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -0,0 +1,1056 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.keymaster; + +import com.android.javacard.seprovider.KMAESKey; +import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMMasterKey; +import com.android.javacard.seprovider.KMSEProvider; + +import javacard.framework.JCSystem; +import javacard.framework.Util; + +// The class encodes strongbox generated amd signed attestation certificate. This only encodes +// required fields of the certificates. It is not meant to be generic X509 cert encoder. +// Whatever fields that are fixed are added as byte arrays. The Extensions are encoded as per +// the values. +// The certificate is assembled with leafs first and then the sequences. + +public class KMAttestationCertImpl implements KMAttestationCert { + + private static final byte MAX_PARAMS = 30; + // DER encoded object identifiers required by the cert. + // rsaEncryption - 1.2.840.113549.1.1.1 + private static final byte[] rsaEncryption = { + 0x06, 0x09, 0x2A, (byte) 0x86, 0x48, (byte) 0x86, (byte) 0xF7, 0x0D, 0x01, 0x01, 0x01 + }; + // ecPublicKey - 1.2.840.10045.2.1 + private static final byte[] eccPubKey = { + 0x06, 0x07, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x02, 0x01 + }; + // prime256v1 curve - 1.2.840.10045.3.1.7 + private static final byte[] prime256v1 = { + 0x06, 0x08, 0x2A, (byte) 0x86, 0x48, (byte) 0xCE, 0x3D, 0x03, 0x01, 0x07 + }; + // Key Usage Extn - 2.5.29.15 + private static final byte[] keyUsageExtn = {0x06, 0x03, 0x55, 0x1D, 0x0F}; + // Android Extn - 1.3.6.1.4.1.11129.2.1.17 + private static final byte[] androidExtn = { + 0x06, 0x0A, 0X2B, 0X06, 0X01, 0X04, 0X01, (byte) 0XD6, 0X79, 0X02, 0X01, 0X11 + }; + private static final short RSA_SIG_LEN = 256; + private static final short ECDSA_MAX_SIG_LEN = 72; + //Signature algorithm identifier - ecdsaWithSha256 - 1.2.840.10045.4.3.2 + //SEQUENCE of alg OBJ ID and parameters = NULL. + private static final byte[] X509EcdsaSignAlgIdentifier = { + 0x30, + 0x0A, + 0x06, + 0x08, + 0x2A, + (byte) 0x86, + 0x48, + (byte) 0xCE, + (byte) 0x3D, + 0x04, + 0x03, + 0x02 + }; + // Signature algorithm identifier - sha256WithRSAEncryption - 1.2.840.113549.1.1.11 + // SEQUENCE of alg OBJ ID and parameters = NULL. + private static final byte[] X509RsaSignAlgIdentifier = { + 0x30, + 0x0D, + 0x06, + 0x09, + 0x2A, + (byte) 0x86, + 0x48, + (byte) 0x86, + (byte) 0xF7, + 0x0D, + 0x01, + 0x01, + 0x0B, + 0x05, + 0x00 + }; + // 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 keyUsageKeyAgreement = (byte) 0x08; // 4th- bit + private static final byte keyUsageCertSign = (byte) 0x04; // 5th- bit + + private static final byte KEYMASTER_VERSION = 100; + private static final byte ATTESTATION_VERSION = 100; + 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 certLength; + private static short tbsStart; + private static short tbsLength; + private static byte[] stack; + 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; + 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 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 boolean certRsaSign; + private static final byte SERIAL_NUM_MAX_LEN = 20; + private static final byte SUBJECT_NAME_MAX_LEN = 32; + + private KMAttestationCertImpl() { + } + + public static KMAttestationCert instance(boolean rsaCert, KMSEProvider provider) { + if (inst == null) { + inst = new KMAttestationCertImpl(); + seProvider = provider; + } + init(); + KMAttestationCertImpl.rsaCert = rsaCert; + return inst; + } + + private static void init() { + stack = null; + stackPtr = 0; + certStart = 0; + certLength = 0; + bufStart = 0; + bufLength = 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; + certMode = KMType.NO_CERT; + certAttestKeySecret = KMType.INVALID_VALUE; + certRsaSign = true; + issuer = KMType.INVALID_VALUE; + subjectName = KMType.INVALID_VALUE; + serialNum = KMType.INVALID_VALUE; + } + + @Override + public KMAttestationCert verifiedBootHash(short obj) { + verifiedHash = obj; + return this; + } + + @Override + public KMAttestationCert verifiedBootKey(short obj) { + 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, boolean derEncoded, byte[] scratchpad) { + if(!derEncoded) { + // convert milliseconds to UTC date + notBefore = KMUtils.convertToDate(obj, scratchpad, true); + }else{ + notBefore = KMByteBlob.instance(KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); + } + return this; + } + + @Override + public KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad) { + if(!derEncoded) { + if (usageExpiryTimeObj != KMType.INVALID_VALUE) { + // compare if the expiry time is greater then 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 { + //notAfter = certExpirtyTimeObj; + } + }else{ + notAfter = KMByteBlob.instance(KMByteBlob.cast(usageExpiryTimeObj).getBuffer(), + KMByteBlob.cast(usageExpiryTimeObj).getStartOff(), + KMByteBlob.cast(usageExpiryTimeObj).length()); + } + 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) { + 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); + } else if (KMEnumArrayTag.cast(tag).get(index) == KMType.AGREE_KEY){ + keyUsage = (byte) (keyUsage | keyUsageKeyAgreement); + }else if (KMEnumArrayTag.cast(tag).get(index) == KMType.ATTEST_KEY){ + keyUsage = (byte) (keyUsage | keyUsageCertSign); + } + index++; + } + index = keyUsage; + while (index != 0) { + index = (byte) (index << 1); + unusedBits--; + } + } + +//TODO Serial number, X509Version needa to be passed as parameter + private static void pushTbsCert(boolean rsaCert, boolean rsa) { + short last = stackPtr; + if(certMode == KMType.ATTESTATION_CERT) { + pushExtensions(); + } + // subject public key info + if (rsaCert) { + pushRsaSubjectKeyInfo(); + } else { + pushEccSubjectKeyInfo(); + } + // subject + pushBytes(KMByteBlob.cast(subjectName).getBuffer(), KMByteBlob.cast(subjectName).getStartOff(), + KMByteBlob.cast(subjectName).length()); + pushValidity(); + // issuer - der encoded + pushBytes( + KMByteBlob.cast(issuer).getBuffer(), + KMByteBlob.cast(issuer).getStartOff(), + KMByteBlob.cast(issuer).length()); + // Algorithm Id + if(rsa) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + }else{ + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } + // Serial Number + pushBytes(KMByteBlob.cast(serialNum).getBuffer(), KMByteBlob.cast(serialNum).getStartOff(), + KMByteBlob.cast(serialNum).length()); + pushIntegerHeader(KMByteBlob.cast(serialNum).length()); + // 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 != 0) { + 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; + // 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; + do { + pushParams(swParams, swParamsIndex, tagIds[index]); + } while (++index < tagIds.length); + pushSequenceHeader((short) (last - stackPtr)); + } + + private static 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; + do { + if (tagIds[index] == KMType.ROOT_OF_TRUST) { + pushRoT(); + continue; + } + if (pushParams(hwParams, hwParamsIndex, tagIds[index])) { + continue; + } + } while (++index < tagIds.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); + // pushIntegerHeader((short) (last - stackPtr)); + 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 (bufStart > stackPtr) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + + @Override + public KMAttestationCert buffer(byte[] buf, short start, short maxLen) { + stack = buf; + bufStart = start; + bufLength = maxLen; + stackPtr = (short) (bufStart + bufLength); + return this; + } + + @Override + public short getCertStart() { + return certStart; + } + + @Override + public short getCertLength() { + return certLength; + } + +public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) { + stackPtr = (short)(bufStart + bufLength); + short last = 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)); + if (rsaSign) { + pushAlgorithmId(X509RsaSignAlgIdentifier); + } else { + pushAlgorithmId(X509EcdsaSignAlgIdentifier); + } + tbsLength = stackPtr; + pushTbsCert(rsaCert, rsaSign); + tbsStart = stackPtr; + tbsLength = (short) (tbsLength - tbsStart); + if(attSecret != KMType.INVALID_VALUE){ + // Sign with the attestation key + // The pubKey is the modulus. + if (rsaSign) { + sigLen = seProvider + .rsaSign256Pkcs1( + KMByteBlob.cast(attSecret).getBuffer(), + KMByteBlob.cast(attSecret).getStartOff(), + KMByteBlob.cast(attSecret).length(), + KMByteBlob.cast(attMod).getBuffer(), + KMByteBlob.cast(attMod).getStartOff(), + KMByteBlob.cast(attMod).length(), + stack, + tbsStart, + tbsLength, + stack, + signatureOffset); + if(sigLen > RSA_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR); + } else { + sigLen = seProvider + .ecSign256( + KMByteBlob.cast(attSecret).getBuffer(), + KMByteBlob.cast(attSecret).getStartOff(), + KMByteBlob.cast(attSecret).length(), + stack, + 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; + certLength = (short)(last - certStart); + //print(stack, getCertStart(), getCertLength()); + } + + @Override + public void build() { + if(certMode == KMType.FAKE_CERT) { + build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true); + }else { + build(certAttestKeySecret, certAttestKeyRsaPubModulus, certRsaSign, false); + } + } + + @Override + public KMAttestationCert makeUniqueId(byte[] scratchPad, short scratchPadOff, + byte[] creationTime, short timeOffset, short creationTimeLen, + byte[] attestAppId, short appIdOff, short attestAppIdLen, + byte resetSinceIdRotation, KMMasterKey masterKey) { + // Concatenate T||C||R + // temporal count T + short temp = KMUtils.countTemporalCount(creationTime, timeOffset, + creationTimeLen, scratchPad, scratchPadOff); + Util.setShort(scratchPad, (short) scratchPadOff, temp); + temp = scratchPadOff; + scratchPadOff += 2; + + // Application Id C + Util.arrayCopyNonAtomic(attestAppId, appIdOff, scratchPad, scratchPadOff, + attestAppIdLen); + scratchPadOff += attestAppIdLen; + + // Reset After Rotation R + scratchPad[scratchPadOff] = resetSinceIdRotation; + scratchPadOff++; + + //Get the key data from the master key + KMAESKey aesKey = (KMAESKey) masterKey; + short mKeyData = KMByteBlob.instance((short) (aesKey.getKeySizeBits() / 8)); + aesKey.getKey( + KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ + KMByteBlob.cast(mKeyData).getStartOff()); /* Key start*/ + timeOffset = KMByteBlob.instance((short) 32); + appIdOff = seProvider.hmacSign( + KMByteBlob.cast(mKeyData).getBuffer(), /* Key */ + KMByteBlob.cast(mKeyData).getStartOff(), /* Key start*/ + KMByteBlob.cast(mKeyData).length(), /* Key length*/ + scratchPad, /* data */ + temp, /* data start */ + scratchPadOff, /* data length */ + KMByteBlob.cast(timeOffset).getBuffer(), /* signature buffer */ + KMByteBlob.cast(timeOffset).getStartOff()); /* signature start */ + if (appIdOff != 32) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + return uniqueId(timeOffset); + } + + @Override + public boolean serialNumber(short number){ + short length = KMByteBlob.cast(number).length(); + if(length > SERIAL_NUM_MAX_LEN){ + return false; + } + byte msb = KMByteBlob.cast(number).get((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 = KMByteBlob.cast(sub).length(); + if(length > SUBJECT_NAME_MAX_LEN){ + return false; + } + Util.arrayCopyNonAtomic(KMByteBlob.cast(sub).getBuffer(), KMByteBlob.cast(sub).getStartOff(), + subjectName,(short)0,length); + subjectLen = length; + */ + if(sub == KMType.INVALID_VALUE || KMByteBlob.cast(sub).length() == 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; + } + + //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/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java new file mode 100644 index 00000000..82093450 --- /dev/null +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -0,0 +1,422 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.keymaster; + +import com.android.javacard.seprovider.KMException; + +import javacard.framework.Util; + +public class KMUtils { + + // 64 bit unsigned calculations for time + public static final byte[] oneSecMsec = { + 0, 0, 0, 0, 0, 0, 0x03, (byte) 0xE8}; // 1000 msec + public static final byte[] oneMinMsec = { + 0, 0, 0, 0, 0, 0, (byte) 0xEA, 0x60}; // 60000 msec + public static final byte[] oneHourMsec = { + 0, 0, 0, 0, 0, 0x36, (byte) 0xEE, (byte) 0x80}; // 3600000 msec + public static final byte[] oneDayMsec = { + 0, 0, 0, 0, 0x05, 0x26, 0x5C, 0x00}; // 86400000 msec + public static final byte[] oneMonthMsec = { + 0, 0, 0, 0, (byte) 0x9C, (byte) 0xBE, (byte) 0xBD, 0x50}; // 2629746000 msec + public static final byte[] leapYearMsec = { + 0, 0, 0, 0x07, (byte) 0x5C, (byte) 0xD7, (byte) 0x88, 0x00}; //31622400000; + public static final byte[] yearMsec = { + 0, 0, 0, 0x07, 0x57, (byte) 0xB1, 0x2C, 0x00}; //31536000000 + //Leap year(366) + 3 * 365 + public static final byte[] fourYrsMsec = { + 0, 0, 0, 0x1D, 0x63, (byte) 0xEB, 0x0C, 0x00};//126230400000 + public static final byte[] firstJan2020 = { + 0, 0, 0x01, 0x6F, 0x5E, 0x66, (byte) 0xE8, 0x00}; // 1577836800000 msec + public static final byte[] 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, (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, (byte) 8); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + // divide the given time with four yrs msec count + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, fourYrsMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(fourYrsMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + 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, (byte) 8); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + if (((short) (i + 1) == leapYrIdx)) { + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, leapYearMsec, + (short) 0, (short) 8) < 0) { + break; + } + } else { + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, yearMsec, + (short) 0, (short) 8) < 0) { + break; + } + } + } + } + + // total yrs from 1970 + if (from2020) { + yrsCount = (short) (year2020 + yrsCount); + } else { + yrsCount = (short) (year2051 + yrsCount); + } + + // divide the given time with one month msec count + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneMonthMsec, (short) 0, + (short) 8) >= 0) { + for (short i = 0; i < 12; i++) { + if (i == 1) { + // Feb month + if (isLeapYear(yrsCount)) { + // Leap year 29 days + Util.arrayCopyNonAtomic(febMonthLeapMSec, (short) 0, scratchPad, + (short) 8, (short) 8); + } else { + // 28 days + Util.arrayCopyNonAtomic(febMonthMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } + } else if (((i <= 6) && ((i % 2 == 0))) || ((i > 6) && ((i % 2 == 1)))) { + Util.arrayCopyNonAtomic(ThirtyOneDaysMonthMsec, (short) 0, + scratchPad, (short) 8, (short) 8); + } else { + // 30 Days + Util.arrayCopyNonAtomic(ThirtDaysMonthMsec, (short) 0, scratchPad, + (short) 8, (short) 8); + } + + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, scratchPad, (short) 8, + (short) 8) >= 0) { + subtract(scratchPad, (short) 0, (short) 8, (short) 16, (byte) 8); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } else { + break; + } + monthCount++; + } + } + + // divide the given time with one day msec count + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneDayMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneDayMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + dayCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + dayCount++; + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one hour msec count + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneHourMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneHourMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + hhCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one minute msec count + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneMinMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneMinMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + mmCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // divide the given time with one second msec count + if (KMInteger.unsignedByteArrayCompare(scratchPad, (short) 0, oneSecMsec, (short) 0, + (short) 8) >= 0) { + Util.arrayCopyNonAtomic(oneSecMsec, (short) 0, scratchPad, (short) 8, + (short) 8); + ssCount = divide(scratchPad, (short) 0, (short) 8, (short) 16); + Util.arrayCopyNonAtomic(scratchPad, (short) 16, scratchPad, (short) 0, + (short) 8); + } + + // Now convert to ascii string YYMMDDhhmmssZ or YYYYMMDDhhmmssZ + Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 256, (byte) 0); + short len = numberToString(yrsCount, scratchPad, (short) 0); // returns YYYY + len += numberToString(monthCount, scratchPad, len); + len += numberToString(dayCount, scratchPad, len); + len += numberToString(hhCount, scratchPad, len); + len += numberToString(mmCount, scratchPad, len); + len += numberToString(ssCount, scratchPad, len); + scratchPad[len] = Z; + len++; + if (utcFlag) { + return KMByteBlob.instance(scratchPad, (short) 2, (short) (len - 2)); // YY + } else { + return KMByteBlob.instance(scratchPad, (short) 0, len); // YYYY + } + } + + public static short numberToString(short number, byte[] scratchPad, + short offset) { + byte zero = 0x30; + byte len = 2; + byte digit; + if (number > 999) { + len = 4; + } + byte index = len; + while (index > 0) { + digit = (byte) (number % 10); + number = (short) (number / 10); + scratchPad[(short) (offset + index - 1)] = (byte) (digit + zero); + index--; + } + return len; + } + + // Use Euclid's formula: dividend = quotient*divisor + remainder + // i.e. dividend - quotient*divisor = remainder where remainder < divisor. + // so this is division by subtraction until remainder remains. + public static short divide(byte[] buf, short dividend, short divisor, + short remainder) { + short expCnt = 1; + short q = 0; + // first increase divisor so that it becomes greater then dividend. + while (compare(buf, divisor, dividend) < 0) { + shiftLeft(buf, divisor); + expCnt = (short) (expCnt << 1); + } + // Now subtract divisor from dividend if dividend is greater then divisor. + // Copy remainder in the dividend and repeat. + while (expCnt != 0) { + if (compare(buf, dividend, divisor) >= 0) { + subtract(buf, dividend, divisor, remainder, (byte) 8); + copy(buf, remainder, dividend); + q = (short) (q + expCnt); + } + expCnt = (short) (expCnt >> 1); + shiftRight(buf, divisor); + } + return q; + } + + public static void copy(byte[] buf, short from, short to) { + Util.arrayCopyNonAtomic(buf, from, buf, to, (short) 8); + } + + public static byte compare(byte[] buf, short lhs, short rhs) { + return KMInteger.unsignedByteArrayCompare(buf, lhs, buf, rhs, (short) 8); + } + + public static void shiftLeft(byte[] buf, short start) { + 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)] + buf[(short) (op2 + index)] + carry); + carry = 0; + if (tmp > 255) { + carry = 1; // max unsigned byte value is 255 + } + buf[(short) (result + index)] = (byte) (tmp & (byte) 0xFF); + index--; + } + } + + // subtraction by borrowing. + public static void subtract(byte[] buf, short op1, short op2, short result, byte sizeBytes) { + byte borrow = 0; + byte index = (byte) (sizeBytes - 1); + short r; + short x; + short y; + while (index >= 0) { + x = (short) (buf[(short) (op1 + index)] & 0xFF); + y = (short) (buf[(short) (op2 + index)] & 0xFF); + r = (short) (x - y - borrow); + borrow = 0; + if (r < 0) { + borrow = 1; + r = (short) (r + 256); // max unsigned byte value is 255 + } + buf[(short) (result + index)] = (byte) (r & 0xFF); + index--; + } + } + + public static short countTemporalCount(byte[] bufTime, short timeOff, + short timeLen, byte[] scratchPad, short offset) { + Util.arrayFillNonAtomic(scratchPad, (short) offset, (short) 24, (byte) 0); + Util.arrayCopyNonAtomic( + bufTime, + timeOff, + scratchPad, + (short) (offset + 8 - timeLen), + timeLen); + Util.arrayCopyNonAtomic(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; + } + + 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++; + } + } +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java new file mode 100644 index 00000000..7e34065e --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAESKey.java @@ -0,0 +1,60 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +import javacard.security.AESKey; + +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(); + } + + public static void onSave(Element element, KMAESKey kmKey) { + element.write(kmKey.aesKey); + } + + public static KMAESKey onRestore(Element element) { + AESKey aesKey = (AESKey) element.readObject(); + KMAESKey kmKey = new KMAESKey(aesKey); + return kmKey; + } + + public static short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } + +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java new file mode 100644 index 00000000..aaf93834 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -0,0 +1,1489 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; +import org.globalplatform.upgrade.UpgradeManager; + +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.RandomData; +import javacard.security.Signature; +import javacardx.crypto.AEADCipher; +import javacardx.crypto.Cipher; +import javacard.security.KeyAgreement; + +public class KMAndroidSEProvider implements KMSEProvider { + + // static final variables + // -------------------------------------------------------------- + // P-256 Curve Parameters + static byte[] secp256r1_P; + static byte[] secp256r1_A; + + static byte[] secp256r1_B; + static byte[] secp256r1_S; + + // Uncompressed form + static byte[] secp256r1_UCG; + static byte[] secp256r1_N; + static final short secp256r1_H = 1; + // -------------------------------------------------------------- + public static final short AES_GCM_TAG_LENGTH = 16; + public static final short AES_GCM_NONCE_LENGTH = 12; + public static final byte KEYSIZE_128_OFFSET = 0x00; + public static final byte KEYSIZE_256_OFFSET = 0x01; + public static final short TMP_ARRAY_SIZE = 300; + private static final short RSA_KEY_SIZE = 256; + public static final short CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. + private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 512;//First 2 bytes for length. + private static final short BCC_MAX_SIZE = 512; + public static final 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 static KeyAgreement keyAgreement; + + // AESKey + private AESKey aesKeys[]; + // DES3Key + private DESKey triDesKey; + // HMACKey + private HMACKey hmacKey; + // RSA Key Pair + private KeyPair rsaKeyPair; + // EC Key Pair. + private KeyPair ecKeyPair; + // Temporary array. + public byte[] tmpArray; + // This is used for internal encryption/decryption operations. + private static AEADCipher aesGcmCipher; + + private Signature kdf; + public static byte[] resetFlag; + + private Signature hmacSignature; + //For ImportwrappedKey operations. + private KMRsaOAEPEncoding rsaOaepDecipher; + private KMPoolManager poolMgr; + + // 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; + + // Entropy + private RandomData rng; + //For storing root certificate and intermediate certificates. + private byte[] certificateChain; + private KMAESKey masterKey; + private KMECPrivateKey attestationKey; + private KMECDeviceUniqueKey testKey; + private KMECDeviceUniqueKey deviceUniqueKey; + private KMHmacKey preSharedKey; + private byte[] additionalCertChain; + private byte[] bcc; + private boolean isProvisionLocked; + + private static KMAndroidSEProvider androidSEProvider = null; + + public static KMAndroidSEProvider getInstance() { + return androidSEProvider; + } + + public KMAndroidSEProvider() { + initStatics(); + // Re-usable AES,DES and HMAC keys in persisted memory. + aesKeys = new AESKey[2]; + aesKeys[KEYSIZE_128_OFFSET] = (AESKey) KeyBuilder.buildKey( + KeyBuilder.TYPE_AES, 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.LENGTH_DES3_3KEY, false); + hmacKey = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (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); + poolMgr = KMPoolManager.getInstance(); + //RsaOAEP Decipher + rsaOaepDecipher = new KMRsaOAEPEncoding(KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1); + + kdf = Signature.getInstance(Signature.ALG_AES_CMAC_128, false); + hmacSignature = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); + + // Temporary transient array created to use locally inside functions. + tmpArray = JCSystem.makeTransientByteArray(TMP_ARRAY_SIZE, + JCSystem.CLEAR_ON_DESELECT); + + // Random number generator initialisation. + rng = RandomData.getInstance(RandomData.ALG_KEYGENERATION); + //Allocate buffer for certificate chain. + if (!isUpgrading()) { + certificateChain = new byte[CERT_CHAIN_MAX_SIZE]; + additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; + bcc = new byte[BCC_MAX_SIZE]; + // 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) SHARED_SECRET_KEY_SIZE); + } + androidSEProvider = this; + resetFlag = JCSystem.makeTransientByteArray((short) 1, + JCSystem.CLEAR_ON_DESELECT); + resetFlag[0] = (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() { + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + } + + private void initECKey(KeyPair ecKeyPair) { + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) ecKeyPair.getPublic(); + pubkey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length); + pubkey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length); + pubkey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length); + pubkey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length); + pubkey.setK(secp256r1_H); + pubkey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length); + + privKey.setFieldFP(secp256r1_P, (short) 0, (short) secp256r1_P.length); + privKey.setA(secp256r1_A, (short) 0, (short) secp256r1_A.length); + privKey.setB(secp256r1_B, (short) 0, (short) secp256r1_B.length); + privKey.setG(secp256r1_UCG, (short) 0, (short) secp256r1_UCG.length); + privKey.setK(secp256r1_H); + privKey.setR(secp256r1_N, (short) 0, (short) secp256r1_N.length); + } + + public AESKey createAESKey(short keysize) { + try { + newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); + return createAESKey(tmpArray, (short) 0, (short) (keysize / 8)); + } finally { + clean(); + } + } + + public AESKey createAESKey(byte[] buf, short startOff, short length) { + AESKey key = null; + short keysize = (short) (length * 8); + if (keysize == 128) { + key = (AESKey) aesKeys[KEYSIZE_128_OFFSET]; + key.setKey(buf, (short) startOff); + } else if (keysize == 256) { + key = (AESKey) aesKeys[KEYSIZE_256_OFFSET]; + key.setKey(buf, (short) startOff); + } + return key; + } + + public DESKey createTDESKey() { + try { + newRandomNumber(tmpArray, (short) 0, + (short) (KeyBuilder.LENGTH_DES3_3KEY / 8)); + return createTDESKey(tmpArray, (short) 0, + (short) (KeyBuilder.LENGTH_DES3_3KEY / 8)); + } finally { + clean(); + } + } + + public DESKey createTDESKey(byte[] secretBuffer, short secretOff, + short secretLength) { + triDesKey.setKey(secretBuffer, secretOff); + return triDesKey; + } + + public HMACKey createHMACKey(short keysize) { + if ((keysize % 8 != 0) || !(keysize >= 64 && keysize <= 512)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + try { + newRandomNumber(tmpArray, (short) 0, (short) (keysize / 8)); + return createHMACKey(tmpArray, (short) 0, (short) (keysize / 8)); + } finally { + clean(); + } + } + + public HMACKey createHMACKey(byte[] secretBuffer, short secretOff, + short secretLength) { + hmacKey.setKey(secretBuffer, secretOff, secretLength); + return hmacKey; + } + + public KeyPair createRsaKeyPair() { + rsaKeyPair.genKeyPair(); + return rsaKeyPair; + } + + public RSAPrivateKey createRsaKey(byte[] modBuffer, short modOff, + short modLength, byte[] privBuffer, short privOff, short privLength) { + RSAPrivateKey privKey = (RSAPrivateKey) rsaKeyPair.getPrivate(); + privKey.setExponent(privBuffer, privOff, privLength); + privKey.setModulus(modBuffer, modOff, modLength); + return privKey; + } + + public KeyPair createECKeyPair() { + ecKeyPair.genKeyPair(); + return ecKeyPair; + } + + public ECPrivateKey createEcKey(byte[] privBuffer, short privOff, + short privLength) { + ECPrivateKey privKey = (ECPrivateKey) ecKeyPair.getPrivate(); + privKey.setS(privBuffer, privOff, privLength); + return privKey; + } + + @Override + public short createSymmetricKey(byte alg, short keysize, byte[] buf, + short startOff) { + switch (alg) { + case KMType.AES: + AESKey aesKey = createAESKey(keysize); + return aesKey.getKey(buf, startOff); + case KMType.DES: + DESKey desKey = createTDESKey(); + return desKey.getKey(buf, startOff); + case KMType.HMAC: + HMACKey hmacKey = createHMACKey(keysize); + return hmacKey.getKey(buf, startOff); + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return 0; + } + + @Override + public void createAsymmetricKey(byte alg, byte[] privKeyBuf, + short privKeyStart, short privKeyLength, byte[] pubModBuf, + short pubModStart, short pubModLength, short[] lengths) { + switch (alg) { + case KMType.RSA: + if (RSA_KEY_SIZE != privKeyLength || RSA_KEY_SIZE != pubModLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + KeyPair rsaKey = createRsaKeyPair(); + RSAPrivateKey privKey = (RSAPrivateKey) rsaKey.getPrivate(); + //Copy exponent. + Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0); + lengths[0] = privKey.getExponent(tmpArray, (short) 0); + if (lengths[0] > privKeyLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(privKeyBuf, privKeyStart, privKeyLength, (byte) 0); + Util.arrayCopyNonAtomic(tmpArray, (short) 0, + privKeyBuf, (short) (privKeyStart + privKeyLength - lengths[0]), lengths[0]); + //Copy modulus + Util.arrayFillNonAtomic(tmpArray, (short) 0, RSA_KEY_SIZE, (byte) 0); + lengths[1] = privKey.getModulus(tmpArray, (short) 0); + if (lengths[1] > pubModLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(pubModBuf, pubModStart, pubModLength, (byte) 0); + Util.arrayCopyNonAtomic(tmpArray, (short) 0, + pubModBuf, (short) (pubModStart + pubModLength - lengths[1]), lengths[1]); + break; + case KMType.EC: + KeyPair ecKey = createECKeyPair(); + ECPublicKey ecPubKey = (ECPublicKey) ecKey.getPublic(); + ECPrivateKey ecPrivKey = (ECPrivateKey) ecKey.getPrivate(); + lengths[0] = ecPrivKey.getS(privKeyBuf, privKeyStart); + lengths[1] = ecPubKey.getW(pubModBuf, pubModStart); + if (lengths[0] > privKeyLength || lengths[1] > pubModLength) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + } + + @Override + public boolean importSymmetricKey(byte alg, short keysize, byte[] buf, + short startOff, short length) { + switch (alg) { + case KMType.AES: + createAESKey(buf, startOff, length); + break; + case KMType.DES: + createTDESKey(buf, startOff, length); + break; + case KMType.HMAC: + createHMACKey(buf, startOff, length); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return true; + } + + @Override + public boolean importAsymmetricKey(byte alg, byte[] privKeyBuf, + short privKeyStart, short privKeyLength, byte[] pubModBuf, + short pubModStart, short pubModLength) { + switch (alg) { + case KMType.RSA: + createRsaKey(pubModBuf, pubModStart, pubModLength, privKeyBuf, + privKeyStart, privKeyLength); + break; + case KMType.EC: + createEcKey(privKeyBuf, privKeyStart, privKeyLength); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return true; + } + + @Override + public void getTrueRandomNumber(byte[] buf, short start, short length) { + newRandomNumber(buf, start, length); + } + + @Override + public void newRandomNumber(byte[] num, short startOff, short length) { + rng.nextBytes(num, startOff, length); + } + + @Override + public void addRngEntropy(byte[] num, short offset, short length) { + rng.setSeed(num, offset, length); + } + + public short aesGCMEncrypt(AESKey key, + byte[] secret, short secretStart, short secretLen, byte[] encSecret, + short encSecretStart, byte[] nonce, short nonceStart, short nonceLen, + byte[] authData, short authDataStart, short authDataLen, byte[] authTag, + short authTagStart, short authTagLen) { + if (authTagLen != AES_GCM_TAG_LENGTH) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (nonceLen != AES_GCM_NONCE_LENGTH) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, + false); + } + aesGcmCipher.init(key, Cipher.MODE_ENCRYPT, nonce, nonceStart, nonceLen); + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + short ciphLen = aesGcmCipher.doFinal(secret, secretStart, secretLen, + encSecret, encSecretStart); + aesGcmCipher.retrieveTag(authTag, authTagStart, authTagLen); + return ciphLen; + } + + @Override + public short aesGCMEncrypt(byte[] aesKey, short aesKeyStart, short aesKeyLen, + byte[] secret, short secretStart, short secretLen, byte[] encSecret, + short encSecretStart, byte[] nonce, short nonceStart, short nonceLen, + byte[] authData, short authDataStart, short authDataLen, byte[] authTag, + short authTagStart, short authTagLen) { + + AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); + return aesGCMEncrypt( + key, + secret, + secretStart, + secretLen, + encSecret, + encSecretStart, + nonce, + nonceStart, + nonceLen, + authData, + authDataStart, + authDataLen, + authTag, + authTagStart, + authTagLen); + } + + @Override + public boolean aesGCMDecrypt(byte[] aesKey, short aesKeyStart, + short aesKeyLen, byte[] encSecret, short encSecretStart, + short encSecretLen, byte[] secret, short secretStart, byte[] nonce, + short nonceStart, short nonceLen, byte[] authData, short authDataStart, + short authDataLen, byte[] authTag, short authTagStart, short authTagLen) { + if (aesGcmCipher == null) { + aesGcmCipher = (AEADCipher) Cipher.getInstance(AEADCipher.ALG_AES_GCM, + false); + } + boolean verification = false; + AESKey key = createAESKey(aesKey, aesKeyStart, aesKeyLen); + aesGcmCipher.init(key, Cipher.MODE_DECRYPT, nonce, nonceStart, nonceLen); + aesGcmCipher.updateAAD(authData, authDataStart, authDataLen); + // encrypt the secret + aesGcmCipher.doFinal(encSecret, encSecretStart, encSecretLen, secret, + secretStart); + verification = aesGcmCipher.verifyTag(authTag, authTagStart, (short) authTagLen, + (short) AES_GCM_TAG_LENGTH); + return verification; + } + + public HMACKey cmacKdf(KMPreSharedKey preSharedKey, byte[] label, short labelStart, + short labelLen, + byte[] context, short contextStart, short contextLength) { + try { + // This is hardcoded to requirement - 32 byte output with two concatenated + // 16 bytes K1 and K2. + final byte n = 2; // hardcoded + // [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; + //Convert Hmackey to AES Key as the algorithm is ALG_AES_CMAC_128. + KMHmacKey hmacKey = ((KMHmacKey) preSharedKey); + hmacKey.getKey(tmpArray, (short) 0); + aesKeys[KEYSIZE_256_OFFSET].setKey(tmpArray, (short) 0); + //Initialize the key derivation function. + kdf.init(aesKeys[KEYSIZE_256_OFFSET], Signature.MODE_SIGN); + //Clear the tmpArray buffer. + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + + Util.arrayFillNonAtomic(tmpArray, (short) 0, iBufLen, (byte) 0); + Util.arrayFillNonAtomic(tmpArray, (short) iBufLen, keyOutLen, (byte) 0); + + byte i = 1; + short pos = 0; + while (i <= n) { + tmpArray[3] = i; + // 4 bytes of iBuf with counter in it + kdf.update(tmpArray, (short) 0, (short) iBufLen); + kdf.update(label, labelStart, (short) labelLen); // label + kdf.update(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(L, (short) 0, (short) 4, tmpArray, + (short) (iBufLen + pos)); + i++; + } + return createHMACKey(tmpArray, (short) iBufLen, (short) keyOutLen); + } finally { + clean(); + } + } + + public short hmacSign(HMACKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart) { + hmacSignature.init(key, Signature.MODE_SIGN); + return hmacSignature.sign(data, dataStart, dataLength, mac, macStart); + } + + public boolean hmacVerify(HMACKey key, byte[] data, short dataStart, + short dataLength, byte[] mac, short macStart, short macLength) { + hmacSignature.init(key, 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 hmacKDF(KMMasterKey masterkey, byte[] data, short dataStart, + short dataLength, byte[] signature, short signatureStart) { + try { + 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(byte[] keyBuf, short keyStart, short keyLength, + byte[] data, short dataStart, short dataLength, byte[] mac, + short macStart, short macLength) { + HMACKey key = createHMACKey(keyBuf, keyStart, keyLength); + return hmacVerify(key, data, dataStart, dataLength, mac, macStart, + macLength); + } + + @Override + public short rsaDecipherOAEP256(byte[] secret, short secretStart, + short secretLength, byte[] modBuffer, short modOff, short modLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + key.setExponent(secret, (short) secretStart, (short) secretLength); + key.setModulus(modBuffer, (short) modOff, (short) modLength); + rsaOaepDecipher.init(key, Cipher.MODE_DECRYPT); + return rsaOaepDecipher.doFinal(inputDataBuf, (short) inputDataStart, (short) inputDataLength, + outputDataBuf, (short) outputDataStart); + } + + private byte mapSignature256Alg(byte alg, byte padding, byte digest) { + switch (alg) { + case KMType.RSA: + switch (padding) { + case KMType.RSA_PKCS1_1_5_SIGN: { + if (digest == KMType.DIGEST_NONE) { + return KMRsa2048NoDigestSignature.ALG_RSA_PKCS1_NODIGEST; + } else { + return Signature.ALG_RSA_SHA_256_PKCS1; + } + } + case KMType.RSA_PSS: + return Signature.ALG_RSA_SHA_256_PKCS1_PSS; + case KMType.PADDING_NONE: + return KMRsa2048NoDigestSignature.ALG_RSA_SIGN_NOPAD; + } + break; + case KMType.EC: + if (digest == KMType.DIGEST_NONE) { + return KMEcdsa256NoDigestSignature.ALG_ECDSA_NODIGEST; + } else { + return Signature.ALG_ECDSA_SHA_256; + } + case KMType.HMAC: + return Signature.ALG_HMAC_SHA_256; + } + return -1; + } + + private byte mapCipherAlg(byte alg, byte padding, byte blockmode, byte digest) { + switch (alg) { + case KMType.AES: + switch (blockmode) { + case KMType.ECB: + return Cipher.ALG_AES_BLOCK_128_ECB_NOPAD; + case KMType.CBC: + return Cipher.ALG_AES_BLOCK_128_CBC_NOPAD; + case KMType.CTR: + return Cipher.ALG_AES_CTR; + case KMType.GCM: + return AEADCipher.ALG_AES_GCM; + } + break; + case KMType.DES: + switch (blockmode) { + case KMType.ECB: + return Cipher.ALG_DES_ECB_NOPAD; + case KMType.CBC: + return Cipher.ALG_DES_CBC_NOPAD; + } + break; + case KMType.RSA: + switch (padding) { + case KMType.PADDING_NONE: + return Cipher.ALG_RSA_NOPAD; + case KMType.RSA_PKCS1_1_5_ENCRYPT: + return Cipher.ALG_RSA_PKCS1; + case KMType.RSA_OAEP: { + if (digest == KMType.SHA1) { /* MGF Digest is SHA1 */ + return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1; + } else if (digest == KMType.SHA2_256) { /* MGF Digest is SHA256 */ + return KMRsaOAEPEncoding.ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256; + } else { + KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); + } + } + } + break; + } + return -1; + } + + public KMOperation createSymmetricCipher(short alg, short purpose, short macLength, + short blockMode, short padding, byte[] secret, short secretStart, + short secretLength, byte[] ivBuffer, short ivStart, short ivLength) { + Key key = null; + switch (secretLength) { + case 32: + key = aesKeys[KEYSIZE_256_OFFSET]; + ((AESKey) key).setKey(secret, secretStart); + break; + case 16: + key = aesKeys[KEYSIZE_128_OFFSET]; + ((AESKey) key).setKey(secret, secretStart); + break; + case 24: + key = triDesKey; + ((DESKey) key).setKey(secret, secretStart); + break; + default: + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + break; + } + short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0); + KMOperation operation = + poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, ivBuffer, ivStart, ivLength); + return operation; + } + + public KMOperation createHmacSignerVerifier(short purpose, short digest, + byte[] secret, short secretStart, short secretLength) { + 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); + HMACKey key = createHMACKey(secret, secretStart, secretLength); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + @Override + public KMOperation initSymmetricOperation(byte purpose, byte alg, + byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, + short keyLength, byte[] ivBuf, short ivStart, short ivLength, + short macLength) { + KMOperation opr = null; + switch (alg) { + case KMType.AES: + case KMType.DES: + // Convert macLength to bytes + macLength = (short) (macLength / 8); + opr = createSymmetricCipher(alg, purpose, macLength, blockMode, padding, keyBuf, keyStart, + keyLength, ivBuf, ivStart, ivLength); + break; + case KMType.HMAC: + opr = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, keyLength); + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + break; + } + return opr; + } + + public KMOperation createRsaSigner(short digest, short padding, byte[] secret, + short secretStart, short secretLength, byte[] modBuffer, short modOff, + short modLength) { + byte alg = mapSignature256Alg(KMType.RSA, (byte) padding, (byte) digest); + KMOperation operation = poolMgr.getOperationImpl(KMType.SIGN, alg, KMType.RSA, padding, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuffer, modOff, modLength); + ((KMOperationImpl) operation).init(key, digest, null, (short) 0, (short) 0); + return operation; + } + + public KMOperation createRsaDecipher(short padding, short mgfDigest, byte[] secret, + short secretStart, short secretLength, byte[] modBuffer, short modOff, + short modLength) { + byte cipherAlg = mapCipherAlg(KMType.RSA, (byte) padding, (byte) 0, (byte) mgfDigest); + KMOperation operation = poolMgr.getOperationImpl(KMType.DECRYPT, cipherAlg, KMType.RSA, padding, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuffer, modOff, modLength); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; + } + + public KMOperation createEcSigner(short digest, byte[] secret, + short secretStart, short secretLength) { + byte alg = mapSignature256Alg(KMType.EC, (byte) 0, (byte) digest); + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + KMOperation operation = poolMgr + .getOperationImpl(KMType.SIGN, alg, KMType.EC, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); + ((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); + ((KMOperationImpl) operation).init(key, KMType.INVALID_VALUE, null, (short) 0, (short) 0); + return operation; + } + + @Override + public KMOperation initAsymmetricOperation(byte purpose, byte alg, + byte padding, byte digest, byte mgfDigest, byte[] privKeyBuf, short privKeyStart, + short privKeyLength, byte[] pubModBuf, short pubModStart, + short pubModLength) { + KMOperation opr = null; + if (alg == KMType.RSA) { + switch (purpose) { + case KMType.SIGN: + opr = createRsaSigner(digest, padding, privKeyBuf, + privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); + break; + case KMType.DECRYPT: + opr = createRsaDecipher(padding, mgfDigest, privKeyBuf, + privKeyStart, privKeyLength, pubModBuf, pubModStart, pubModLength); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + } + } else if (alg == KMType.EC) { + switch (purpose) { + case KMType.SIGN: + opr = createEcSigner(digest, privKeyBuf, privKeyStart, privKeyLength); + break; + + case KMType.AGREE_KEY: + opr = createKeyAgreement(privKeyBuf, privKeyStart, privKeyLength); + break; + default: + KMException.throwIt(KMError.UNSUPPORTED_PURPOSE); + break; + } + } else { + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return opr; + + } + + @Override + public short cmacKDF(KMPreSharedKey pSharedKey, byte[] label, + short labelStart, short labelLen, byte[] context, short contextStart, + short contextLength, byte[] keyBuf, short keyStart) { + HMACKey key = cmacKdf(pSharedKey, label, labelStart, labelLen, context, + contextStart, contextLength); + return key.getKey(keyBuf, keyStart); + } + + public void clearCertificateChain() { + JCSystem.beginTransaction(); + Util.arrayFillNonAtomic(certificateChain, (short) 0, CERT_CHAIN_MAX_SIZE, (byte) 0); + JCSystem.commitTransaction(); + } + + //This function supports multi-part request data. + public void persistPartialCertificateChain(byte[] buf, short offset, short len, short totalLen) { + // _____________________________________________________ + // | 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. + if (totalLen > (short) (CERT_CHAIN_MAX_SIZE - 2)) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + short persistedLen = Util.getShort(certificateChain, (short) 0); + if (persistedLen > totalLen) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(certificateChain, (short) 0, (short) (len + persistedLen)); + Util.arrayCopyNonAtomic(buf, offset, certificateChain, + (short) (persistedLen + 2), len); + JCSystem.commitTransaction(); + } + + public short readCertificateChain(byte[] buf, short offset) { + short len = Util.getShort(certificateChain, (short) 0); + Util.arrayCopyNonAtomic(certificateChain, (short) 2, buf, offset, len); + return len; + } + + public short getCertificateChainLength() { + return Util.getShort(certificateChain, (short) 0); + } + + @Override + public void onSave(Element element) { + element.write(certificateChain); + KMAESKey.onSave(element, masterKey); + KMECPrivateKey.onSave(element, attestationKey); + KMHmacKey.onSave(element, preSharedKey); + } + + @Override + public void onRestore(Element element) { + certificateChain = (byte[]) element.readObject(); + masterKey = KMAESKey.onRestore(element); + attestationKey = KMECPrivateKey.onRestore(element); + preSharedKey = KMHmacKey.onRestore(element); + } + + @Override + public short getBackupPrimitiveByteCount() { + short count = + (short) (KMAESKey.getBackupPrimitiveByteCount() + + KMECPrivateKey.getBackupPrimitiveByteCount() + + KMHmacKey.getBackupPrimitiveByteCount()); + return count; + } + + @Override + public short getBackupObjectCount() { + short count = + (short) (1 /*Certificate chain */ + + KMAESKey.getBackupObjectCount() + + KMECPrivateKey.getBackupObjectCount() + + KMHmacKey.getBackupObjectCount()); + return count; + } + + @Override + public boolean isUpgrading() { + return UpgradeManager.isUpgrading(); + } + + @Override + public KMMasterKey createMasterKey(short keySizeBits) { + try { + if (masterKey == null) { + AESKey key = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, + keySizeBits, false); + masterKey = new KMAESKey(key); + short keyLen = (short) (keySizeBits / 8); + getTrueRandomNumber(tmpArray, (short) 0, keyLen); + masterKey.setKey(tmpArray, (short) 0); + } + return (KMMasterKey) masterKey; + } finally { + clean(); + } + } + + 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 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 KMMasterKey getMasterKey() { + return (KMMasterKey) masterKey; + } + + public KMAttestationKey getAttestationKey() { + return (KMAttestationKey) attestationKey; + } + + @Override + public KMPreSharedKey getPresharedKey() { + return (KMPreSharedKey) preSharedKey; + } + + @Override + public short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + + ECPrivateKey key = (ECPrivateKey) ecKeyPair.getPrivate(); + key.setS(secret, secretStart, secretLength); + + Signature.OneShot signer = null; + try { + + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public short ecSign256(KMAttestationKey ecPrivKey, byte[] inputDataBuf, short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, short outputDataStart) { + Signature.OneShot signer = null; + try { + + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + signer.init(((KMECPrivateKey) ecPrivKey).getPrivateKey(), Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public short rsaSign256Pkcs1(byte[] secret, short secretStart, short secretLength, byte[] modBuf, + short modStart, + short modLength, byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, + short outputDataStart) { + + Signature.OneShot signer = null; + try { + + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_RSA, Cipher.PAD_PKCS1); + + RSAPrivateKey key = (RSAPrivateKey) rsaKeyPair.getPrivate(); + ; + key.setExponent(secret, secretStart, secretLength); + key.setModulus(modBuf, modStart, modLength); + + signer.init(key, Signature.MODE_SIGN); + return signer.sign(inputDataBuf, inputDataStart, inputDataLength, + outputDataBuf, outputDataStart); + } finally { + if (signer != null) { + signer.close(); + } + } + + } + + @Override + public boolean isAttestationKeyProvisioned() { + return false; + } + + @Override + public short getAttestationKeyAlgorithm() { + return KMType.INVALID_VALUE; + } + + @Override + public short getAttestationId(short tag, byte[] buffer, short start) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + Util.arrayCopyNonAtomic(attIdBrand, (short) 0, buffer, start, (short) attIdBrand.length); + return (short) attIdBrand.length; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + Util.arrayCopyNonAtomic(attIdDevice, (short) 0, buffer, start, (short) attIdDevice.length); + return (short) attIdDevice.length; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + Util.arrayCopyNonAtomic(attIdProduct, (short) 0, buffer, start, + (short) attIdProduct.length); + return (short) attIdProduct.length; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + Util.arrayCopyNonAtomic(attIdSerial, (short) 0, buffer, start, (short) attIdSerial.length); + return (short) attIdSerial.length; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + Util.arrayCopyNonAtomic(attIdImei, (short) 0, buffer, start, (short) attIdImei.length); + return (short) attIdImei.length; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + Util.arrayCopyNonAtomic(attIdMeId, (short) 0, buffer, start, (short) attIdMeId.length); + return (short) attIdMeId.length; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + Util.arrayCopyNonAtomic(attIdManufacturer, (short) 0, buffer, start, + (short) attIdManufacturer.length); + return (short) attIdManufacturer.length; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + Util.arrayCopyNonAtomic(attIdModel, (short) 0, buffer, start, (short) attIdModel.length); + return (short) attIdModel.length; + } + return (short) 0; + } + + public void setAttestationId(short tag, byte[] buffer, short start, short length) { + switch (tag) { + // Attestation Id Brand + case KMType.ATTESTATION_ID_BRAND: + JCSystem.beginTransaction(); + attIdBrand = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdBrand, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Device + case KMType.ATTESTATION_ID_DEVICE: + JCSystem.beginTransaction(); + attIdDevice = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdDevice, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Product + case KMType.ATTESTATION_ID_PRODUCT: + JCSystem.beginTransaction(); + attIdProduct = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdProduct, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Serial + case KMType.ATTESTATION_ID_SERIAL: + JCSystem.beginTransaction(); + attIdSerial = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdSerial, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id IMEI + case KMType.ATTESTATION_ID_IMEI: + JCSystem.beginTransaction(); + attIdImei = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdImei, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id MEID + case KMType.ATTESTATION_ID_MEID: + JCSystem.beginTransaction(); + attIdMeId = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdMeId, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Manufacturer + case KMType.ATTESTATION_ID_MANUFACTURER: + JCSystem.beginTransaction(); + attIdManufacturer = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdManufacturer, (short) 0, length); + JCSystem.commitTransaction(); + break; + // Attestation Id Model + case KMType.ATTESTATION_ID_MODEL: + JCSystem.beginTransaction(); + attIdModel = new byte[length]; + Util.arrayCopyNonAtomic(buffer, (short) start, attIdModel, (short) 0, length); + JCSystem.commitTransaction(); + break; + } + } + + @Override + public void deleteAttestationIds() { + attIdBrand = null; + attIdDevice = null; + attIdProduct = null; + attIdSerial = null; + attIdImei = null; + attIdMeId = null; + attIdManufacturer = null; + attIdModel = null; + } + + public boolean isPowerReset() { + boolean flag = false; + if (resetFlag[0] == POWER_RESET_TRUE) { + resetFlag[0] = POWER_RESET_FALSE; + flag = true; + if (poolMgr != null) { + poolMgr.powerReset(); + } + } + return flag; + } + + @Override + public short getVerifiedBootHash(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(verifiedHash, (short) 0, buffer, start, (short) verifiedHash.length); + return (short) verifiedHash.length; + } + + @Override + public short getBootKey(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootKey, (short) 0, buffer, start, (short) bootKey.length); + return (short) bootKey.length; + } + + @Override + public short getBootState() { + return bootState; + } + + @Override + public boolean isDeviceBootLocked() { + return deviceBootLocked; + } + + @Override + public short getBootPatchLevel(byte[] buffer, short start) { + Util.arrayCopyNonAtomic(bootPatchLevel, (short) 0, buffer, start, + (short) bootPatchLevel.length); + return (short) bootPatchLevel.length; + } + + 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, (short) 4); + } + + @Override + public short hkdf(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, + short saltOff, short saltLen, byte[] info, short infoOff, short infoLen, + byte[] out, short outOff, short outLen) { + // HMAC_extract + hkdfExtract(ikm, ikmOff, ikmLen, salt, saltOff, saltLen, tmpArray, (short) 0); + //HMAC_expand + return hkdfExpand(tmpArray, (short) 0, (short) 32, info, infoOff, infoLen, out, outOff, outLen); + } + + private short hkdfExtract(byte[] ikm, short ikmOff, short ikmLen, byte[] salt, short saltOff, + short saltLen, + byte[] out, short off) { + // https://tools.ietf.org/html/rfc5869#section-2.2 + HMACKey hmacKey = createHMACKey(salt, saltOff, saltLen); + hmacSignature.init(hmacKey, Signature.MODE_SIGN); + return hmacSignature.sign(ikm, ikmOff, ikmLen, out, off); + } + + private short hkdfExpand(byte[] prk, short prkOff, short prkLen, byte[] info, short infoOff, + short infoLen, + byte[] out, short outOff, short outLen) { + // https://tools.ietf.org/html/rfc5869#section-2.3 + short digestLen = (short) 32; // SHA256 digest length. + // Calculate no of iterations N. + short n = (short) ((short) (outLen + digestLen - 1) / digestLen); + if (n > 255) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + HMACKey hmacKey = createHMACKey(prk, prkOff, prkLen); + Util.arrayFill(tmpArray, (short) 0, (short) 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 short ecdhKeyAgreement(byte[] privKey, short privKeyOff, + short privKeyLen, byte[] publicKey, short publicKeyOff, + short publicKeyLen, byte[] secret, short secretOff) { + keyAgreement.init(createEcKey(privKey, privKeyOff, privKeyLen)); + return keyAgreement.generateSecret(publicKey, publicKeyOff, publicKeyLen, secret, secretOff); + } + + @Override + public boolean ecVerify256(byte[] pubKey, short pubKeyOffset, short pubKeyLen, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signatureDataBuf, short signatureDataStart, + short signatureDataLen) { + Signature.OneShot signer = null; + try { + signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, + Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); + ECPublicKey key = (ECPublicKey) ecKeyPair.getPublic(); + key.setW(pubKey, pubKeyOffset, pubKeyLen); + signer.init(key, Signature.MODE_VERIFY); + return signer.verify(inputDataBuf, inputDataStart, inputDataLength, + signatureDataBuf, signatureDataStart, + (short) (signatureDataBuf[(short) (signatureDataStart + 1)] + 2)); + } finally { + if (signer != null) { + signer.close(); + } + } + } + + @Override + public short ecSign256(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(); + } + } + } + + private KMDeviceUniqueKey createDeviceUniqueKey(KMECDeviceUniqueKey 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); + } + key.setS(privKey, privKeyOff, privKeyLen); + key.setW(pubKey, pubKeyOff, pubKeyLen); + return (KMDeviceUniqueKey) key; + } + + @Override + public KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, + byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, + short privKeyOff, short privKeyLen) { + KMDeviceUniqueKey key; + if (testMode) { + key = createDeviceUniqueKey(testKey, pubKey, pubKeyOff, + pubKeyLen, privKey, privKeyOff, privKeyLen); + if (testKey == null) { + testKey = (KMECDeviceUniqueKey) key; + } + } else { + key = createDeviceUniqueKey(deviceUniqueKey, pubKey, pubKeyOff, + pubKeyLen, privKey, privKeyOff, privKeyLen); + if (deviceUniqueKey == null) { + deviceUniqueKey = (KMECDeviceUniqueKey) key; + } + } + return key; + } + + @Override + public KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode) { + return ((KMDeviceUniqueKey) (testMode ? testKey : deviceUniqueKey)); + } + + @Override + public void persistAdditionalCertChain(byte[] buf, short offset, short len) { + // Input buffer contains encoded additional certificate chain as shown below. + // AdditionalDKSignatures = { + // + SignerName => DKCertChain + // } + // SignerName = tstr + // DKCertChain = [ + // 2* Certificate // Root -> Leaf. Root is the vendo r + // // self-signed cert, leaf contains DK_pu b + // ] + // Certificate = COSE_Sign1 of a public key + if ((short) (len + 2) >= ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(additionalCertChain, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, + (short) 2, len); + JCSystem.commitTransaction(); + + } + + @Override + public short getAdditionalCertChainLength() { + return Util.getShort(additionalCertChain, (short) 0); + } + + @Override + public byte[] getAdditionalCertChain() { + return additionalCertChain; + } + + + @Override + public byte[] getBootCertificateChain() { + return bcc; + } + + public void persistBootCertificateChain(byte[] buf, short offset, short len) { + if ((short) (len + 2) > BCC_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } + JCSystem.beginTransaction(); + Util.setShort(bcc, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, bcc, + (short) 2, len); + JCSystem.commitTransaction(); + } + + public void setProvisionLocked(boolean locked) { + JCSystem.beginTransaction(); + isProvisionLocked = locked; + JCSystem.commitTransaction(); + } + + public boolean isProvisionLocked() { + return isProvisionLocked; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java similarity index 99% rename from Applet/src/com/android/javacard/keymaster/KMAttestationCert.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java index f2bab41a..6cd8e7b0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMAttestationCert.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/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.seprovider; /** * The KMAttestationCert interface represents a X509 compliant attestation certificate required to diff --git a/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMAttestationKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java index 8843a954..1b8e334e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMAttestationKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/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.seprovider; /** * KMAttestationKey is a marker interface and the SE Provider has to implement this interface. diff --git a/Applet/src/com/android/javacard/keymaster/KMDeviceUniqueKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java similarity index 94% rename from Applet/src/com/android/javacard/keymaster/KMDeviceUniqueKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java index 02b22e57..08e60a3f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDeviceUniqueKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.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; public interface KMDeviceUniqueKey { diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECDeviceUniqueKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java similarity index 97% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECDeviceUniqueKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java index bfda51bc..0f68de61 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECDeviceUniqueKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.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.ECPrivateKey; import javacard.security.ECPublicKey; import javacard.security.KeyPair; diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java new file mode 100644 index 00000000..33372f97 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java @@ -0,0 +1,64 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +import javacard.security.AESKey; +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 short getS(byte[] buffer, short offset) { + ECPrivateKey ecPriv = (ECPrivateKey) ecKeyPair.getPrivate(); + return ecPriv.getS(buffer, offset); + } + + public ECPrivateKey getPrivateKey() { + return (ECPrivateKey) ecKeyPair.getPrivate(); + } + + 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 short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } + +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java new file mode 100644 index 00000000..4707f637 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java @@ -0,0 +1,126 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.security.CryptoException; +import javacard.framework.Util; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacard.security.Signature; +import javacardx.crypto.Cipher; + +public class KMEcdsa256NoDigestSignature extends Signature { + + public static final byte ALG_ECDSA_NODIGEST = (byte) 0x67; + public static final short MAX_NO_DIGEST_MSG_LEN = 32; + private byte algorithm; + private Signature inst; + + public KMEcdsa256NoDigestSignature(byte alg) { + algorithm = alg; + inst = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); + } + + @Override + public void init(Key key, byte b) throws CryptoException { + inst.init(key, b); + } + + @Override + public void init(Key key, byte b, byte[] bytes, short i, short i1) + throws CryptoException { + inst.init(key, b, bytes, i, i1); + } + + @Override + public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, + short i2, short i3) throws CryptoException { + + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getMessageDigestAlgorithm() { + return MessageDigest.ALG_NULL; + } + + @Override + public byte getCipherAlgorithm() { + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + return Cipher.PAD_NULL; + } + + @Override + public short getLength() throws CryptoException { + return inst.getLength(); + } + + @Override + public void update(byte[] message, short msgStart, short messageLength) + throws CryptoException { + // HAL accumulates the data and send it at finish operation. + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + try { + if (i1 > MAX_NO_DIGEST_MSG_LEN) { + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + } + // add zeros to the left + if (i1 < MAX_NO_DIGEST_MSG_LEN) { + Util.arrayFillNonAtomic(KMAndroidSEProvider.getInstance().tmpArray, + (short) 0, (short) MAX_NO_DIGEST_MSG_LEN, (byte) 0); + } + Util.arrayCopyNonAtomic(bytes, i, + KMAndroidSEProvider.getInstance().tmpArray, + (short) (MAX_NO_DIGEST_MSG_LEN - i1), i1); + return inst.signPreComputedHash(KMAndroidSEProvider.getInstance().tmpArray, + (short) 0, (short) MAX_NO_DIGEST_MSG_LEN, bytes1, i2); + } finally { + KMAndroidSEProvider.getInstance().clean(); + } + } + + @Override + public short signPreComputedHash(byte[] bytes, short i, short i1, + byte[] bytes1, short i2) throws CryptoException { + return inst.sign(bytes, i, i1, bytes1, i2); + } + + @Override + public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, + short i2, short i3) throws CryptoException { + //Verification is handled inside HAL + return false; + } + + @Override + public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, + byte[] bytes1, short i2, short i3) throws CryptoException { + //Verification is handled inside HAL + return false; + } +} \ No newline at end of file diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java new file mode 100644 index 00000000..5754abe9 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMError.java @@ -0,0 +1,134 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMError includes all the error codes from android keymaster hal specifications. The values are + * positive unlike negative values in keymaster hal. + */ +public class KMError { + + public static final short OK = 0; + public static final short UNSUPPORTED_PURPOSE = 2; + public static final short 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 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 DEVICE_LOCKED = 72; + public static final short EARLY_BOOT_ENDED = 73; + public static final short ATTESTATION_KEYS_NOT_PROVISIONED =74; + public static final short INCOMPATIBLE_MGF_DIGEST = 78; + public static final short UNSUPPORTED_MGF_DIGEST = 79; + public static final short MISSING_NOT_BEFORE = 80; + public static final short MISSING_NOT_AFTER = 81; + public static final short MISSING_ISSUER_SUBJECT_NAME = 82; + public static final short INVALID_ISSUER_SUBJECT_NAME = 83; + + public static final short UNIMPLEMENTED = 100; + public static final short UNKNOWN_ERROR = 1000; + + //Extended errors + public static final short SW_CONDITIONS_NOT_SATISFIED = 10001; + public static final short UNSUPPORTED_CLA = 10002; + public static final short INVALID_P1P2 = 10003; + public static final short UNSUPPORTED_INSTRUCTION = 10004; + public static final short CMD_NOT_ALLOWED = 10005; + public static final short SW_WRONG_LENGTH = 10006; + public static final short INVALID_DATA = 10007; + + //Crypto errors + public static final short CRYPTO_ILLEGAL_USE = 10008; + public static final short CRYPTO_ILLEGAL_VALUE = 10009; + public static final short CRYPTO_INVALID_INIT = 10010; + public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011; + public static final short CRYPTO_UNINITIALIZED_KEY = 10012; + //Generic Unknown error. + public static final short GENERIC_UNKNOWN_ERROR = 10013; + + // Remote key provisioning error codes. + public static final short STATUS_FAILED = 32000; + public static final short STATUS_INVALID_MAC = 32001; + public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; + public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; + public static final short STATUS_INVALID_EEK = 32004; + public static final short INVALID_STATE = 32005; + + public static short translate(short err) { + switch(err) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + case 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/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java similarity index 97% rename from Applet/src/com/android/javacard/keymaster/KMException.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java index 3ffb9f7a..c0b2431f 100644 --- a/Applet/src/com/android/javacard/keymaster/KMException.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import javacard.framework.JCSystem; diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java new file mode 100644 index 00000000..2eaeeeb9 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -0,0 +1,59 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +import javacard.security.HMACKey; + +public class KMHmacKey implements KMPreSharedKey { + + 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 short getKeySizeBits() { + return hmacKey.getSize(); + } + + 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 short getBackupPrimitiveByteCount() { + return (short) 0; + } + + public static short getBackupObjectCount() { + return (short) 1; + } +} diff --git a/Applet/src/com/android/javacard/keymaster/KMMasterKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMMasterKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java index 7a88778e..6eab5e56 100644 --- a/Applet/src/com/android/javacard/keymaster/KMMasterKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/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.seprovider; /** * KMMasterKey is a marker interface and the SE Provider has to implement this interface. Internally diff --git a/Applet/src/com/android/javacard/keymaster/KMOperation.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java similarity index 98% rename from Applet/src/com/android/javacard/keymaster/KMOperation.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperation.java index f21977db..b73d58d1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperation.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/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.seprovider; /** * KMOperation represents a persistent operation started by keymaster hal's beginOperation function. diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java new file mode 100644 index 00000000..2c447fd4 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java @@ -0,0 +1,359 @@ +/* + * 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.seprovider.KMError; +import com.android.javacard.seprovider.KMException; +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; + //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() { + 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/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java new file mode 100644 index 00000000..9938cd59 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -0,0 +1,304 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; +import javacard.security.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; + // Cipher pool + private Object[] cipherPool; + // Signature pool + private Object[] signerPool; + // Keyagreement pool + private Object[] keyAgreementPool; + // KMOperationImpl pool + private Object[] operationPool; + + 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)]; + signerPool = new Object[(short) (SIG_ALGS.length * 4)]; + keyAgreementPool = new Object[(short) (KEY_AGREE_ALGS.length * 4)]; + operationPool = new Object[4]; + /* Initialize pools */ + initializeOperationPool(); + initializeSignerPool(); + initializeCipherPool(); + initializeKeyAgreementPool(); + } + + private void initializeOperationPool() { + short index = 0; + while (index < MAX_OPERATION_INSTANCES) { + operationPool[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() { + short index = 0; + KMOperationImpl impl; + while (index < operationPool.length) { + impl = (KMOperationImpl) operationPool[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)) { + 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) { + KMOperation operation; + // Throw exception if no resource from operation pool is available. + if (null == (operation = getResourceFromOperationPool())) { + 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; + + while (index < pool.length) { + if (usageCount >= MAX_OPERATION_INSTANCES) { + 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(); + index++; + } + } + +} diff --git a/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java index 273aeb4a..86bb1df9 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPreSharedKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/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.seprovider; /** * KMPreSharedKey is a marker interface and the SE Provider has to implement this interface. diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java new file mode 100644 index 00000000..303e2b98 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java @@ -0,0 +1,144 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacard.security.Signature; +import javacardx.crypto.Cipher; + +public class KMRsa2048NoDigestSignature extends Signature { + + public static final byte ALG_RSA_SIGN_NOPAD = (byte) 0x65; + public static final byte ALG_RSA_PKCS1_NODIGEST = (byte) 0x66; + private byte algorithm; + private Cipher inst; + + public KMRsa2048NoDigestSignature(byte alg) { + algorithm = alg; + inst = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + } + + @Override + public void init(Key key, byte b) throws CryptoException { + inst.init(key, b); + } + + @Override + public void init(Key key, byte b, byte[] bytes, short i, short i1) + throws CryptoException { + inst.init(key, b, bytes, i, i1); + } + + @Override + public void setInitialDigest(byte[] bytes, short i, short i1, byte[] bytes1, + short i2, short i3) throws CryptoException { + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getMessageDigestAlgorithm() { + return MessageDigest.ALG_NULL; + } + + @Override + public byte getCipherAlgorithm() { + return algorithm; + } + + @Override + public byte getPaddingAlgorithm() { + return Cipher.PAD_NULL; + } + + @Override + public short getLength() throws CryptoException { + return 0; + } + + @Override + public void update(byte[] bytes, short i, short i1) throws CryptoException { + // HAL accumulates the data and send it at finish operation. + } + + @Override + public short sign(byte[] bytes, short i, short i1, byte[] bytes1, short i2) + throws CryptoException { + padData(bytes, i, i1, KMAndroidSEProvider.getInstance().tmpArray, (short) 0); + return inst.doFinal(KMAndroidSEProvider.getInstance().tmpArray, (short) 0, + (short) 256, bytes1, i2); + } + + @Override + public short signPreComputedHash(byte[] bytes, short i, short i1, + byte[] bytes1, short i2) throws CryptoException { + return 0; + } + + @Override + public boolean verify(byte[] bytes, short i, short i1, byte[] bytes1, + short i2, short i3) throws CryptoException { + //Verification is handled inside HAL + return false; + } + + @Override + public boolean verifyPreComputedHash(byte[] bytes, short i, short i1, + byte[] bytes1, short i2, short i3) throws CryptoException { + //Verification is handled inside HAL + return false; + } + + private void padData(byte[] buf, short start, short len, byte[] outBuf, + short outBufStart) { + if (!isValidData(buf, start, len)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + Util.arrayFillNonAtomic(outBuf, (short) outBufStart, (short) 256, + (byte) 0x00); + if (algorithm == ALG_RSA_SIGN_NOPAD) { // add zero to right + } else if (algorithm == ALG_RSA_PKCS1_NODIGEST) {// 0x00||0x01||PS||0x00 + outBuf[0] = 0x00; + outBuf[1] = 0x01; + Util.arrayFillNonAtomic(outBuf, (short) 2, (short) (256 - len - 3), + (byte) 0xFF); + outBuf[(short) (256 - len - 1)] = 0x00; + } else { + CryptoException.throwIt(CryptoException.ILLEGAL_USE); + } + Util.arrayCopyNonAtomic(buf, start, outBuf, (short) (256 - len), len); + } + + private boolean isValidData(byte[] buf, short start, short len) { + if (algorithm == ALG_RSA_SIGN_NOPAD) { + if (len > 256) { + return false; + } + } else { // ALG_RSA_PKCS1_NODIGEST + if (len > 245) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + return false; + } + } + return true; + } +} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java new file mode 100644 index 00000000..2f2da22f --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRsaOAEPEncoding.java @@ -0,0 +1,260 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import javacard.framework.JCSystem; +import javacard.framework.Util; +import javacard.security.CryptoException; +import javacard.security.Key; +import javacard.security.MessageDigest; +import javacardx.crypto.Cipher; + +public class KMRsaOAEPEncoding extends Cipher { + + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1 = (byte) 0x1E; + public static final byte ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256 = (byte) 0x1F; + + final short MGF1_BUF_SIZE = 256; + static byte[] mgf1Buf; + private Cipher cipher; + private byte hash; + private byte mgf1Hash; + private byte algorithm; + + public KMRsaOAEPEncoding(byte alg) { + setDigests(alg); + cipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false); + algorithm = alg; + if (null == mgf1Buf) { + mgf1Buf = JCSystem.makeTransientByteArray(MGF1_BUF_SIZE, + JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT); + } + } + + private void setDigests(byte alg) { + switch (alg) { + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA1: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA; + break; + case ALG_RSA_PKCS1_OAEP_SHA256_MGF1_SHA256: + hash = MessageDigest.ALG_SHA_256; + mgf1Hash = MessageDigest.ALG_SHA_256; + break; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + } + + private short getDigestLength() { + switch (hash) { + case MessageDigest.ALG_SHA: + return MessageDigest.LENGTH_SHA; + case MessageDigest.ALG_SHA_224: + return MessageDigest.LENGTH_SHA_224; + case MessageDigest.ALG_SHA_256: + return MessageDigest.LENGTH_SHA_256; + case MessageDigest.ALG_SHA_384: + return MessageDigest.LENGTH_SHA_384; + case MessageDigest.ALG_SHA3_512: + return MessageDigest.LENGTH_SHA_512; + default: + CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); + } + return 0; + } + + @Override + public void init(Key theKey, byte theMode) throws CryptoException { + cipher.init(theKey, theMode); + + } + + @Override + public void init(Key theKey, byte theMode, byte[] bArray, short bOff, + short bLen) throws CryptoException { + cipher.init(theKey, theMode, bArray, bOff, bLen); + } + + @Override + public byte getAlgorithm() { + return algorithm; + } + + @Override + public byte getCipherAlgorithm() { + return 0; + } + + @Override + public byte getPaddingAlgorithm() { + return 0; + } + + @Override + public short doFinal(byte[] inBuff, short inOffset, short inLength, + byte[] outBuff, short outOffset) throws CryptoException { + short len = cipher.doFinal(inBuff, inOffset, inLength, outBuff, outOffset); + + // https://tools.ietf.org/html/rfc8017#section-7.1 + // https://www.inf.pucrs.br/~calazans/graduate/TPVLSI_I/RSA-oaep_spec.pdf + // RSA OAEP Encoding and Decoding Mechanism for a 2048 bit RSA Key. + // Msg -> RSA-OAEP-ENCODE -> RSAEncryption -> RSADecryption -> + // RSA-OAEP-DECODE -> Msg + // RSA-OAEP-ENCODE generates an output length of 255, but RSAEncryption + // requires and input of length 256 so we pad 0 to the left of the input + // message and make the length equal to 256 and pass to RSAEncryption. + // RSADecryption takes input length equal to 256 and generates an + // output of length 256. After decryption the first byte of the output + // should be 0(left padding we did in encryption). + // RSA-OAEP-DECODE takes input of length 255 so remove the left padding of 1 + // byte. + if (len != 256 || outBuff[0] != 0) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + inBuff = outBuff; + inOffset = (short) (outOffset + 1); + return rsaOAEPDecode(inBuff, inOffset, (short) (len - 1), outBuff, + outOffset); + + } + + @Override + public short update(byte[] inBuff, short inOffset, short inLength, + byte[] outBuff, short outOffset) throws CryptoException { + return cipher.update(inBuff, inOffset, inLength, outBuff, outOffset); + } + + private void maskGenerationFunction1(byte[] input, short inputOffset, + short inputLen, short expectedOutLen, byte[] outBuf, short outOffset) { + short counter = 0; + MessageDigest.OneShot md = null; + try { + md = MessageDigest.OneShot.open(mgf1Hash); + short digestLen = md.getLength(); + + Util.arrayCopyNonAtomic(input, inputOffset, mgf1Buf, (short) 0, inputLen); + while (counter < (short) (expectedOutLen / digestLen)) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal(mgf1Buf, (short) 0, (short) (4 + inputLen), outBuf, + (short) (outOffset + (counter * digestLen))); + counter++; + } + + if ((short) (counter * digestLen) < expectedOutLen) { + I2OS(counter, mgf1Buf, (short) inputLen); + md.doFinal(mgf1Buf, (short) 0, (short) (4 + inputLen), outBuf, + (short) (outOffset + (counter * digestLen))); + } + + } finally { + if (md != null) { + md.close(); + } + Util.arrayFillNonAtomic(mgf1Buf, (short) 0, (short) MGF1_BUF_SIZE, + (byte) 0); + } + } + + // Integer to Octet String conversion. + private void I2OS(short i, byte[] out, short offset) { + Util.arrayFillNonAtomic(out, (short) offset, (short) 4, (byte) 0); + out[(short) (offset + 3)] = (byte) (i >>> 0); + out[(short) (offset + 2)] = (byte) (i >>> 8); + } + + private short rsaOAEPDecode(byte[] encodedMsg, short encodedMsgOff, + short encodedMsgLen, byte[] msg, short offset) { + MessageDigest.OneShot md = null; + byte[] tmpArray = KMAndroidSEProvider.getInstance().tmpArray; + + try { + short hLen = getDigestLength(); + + if (encodedMsgLen < (short) (2 * hLen + 1)) { + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + // encodedMsg will be in the format of maskedSeed||maskedDB. + // maskedSeed length is hLen and maskedDB length is (encodedMsgLen - hLen) + // Now retrieve the seedMask by calling MGF(maskedDB, hLen). The length + // of the seedMask is hLen. + // seedMask = MGF(maskedDB, hLen) + maskGenerationFunction1(encodedMsg, (short) (encodedMsgOff + hLen), + (short) (encodedMsgLen - hLen), hLen, tmpArray, (short) 0); + + // Get the seed by doing XOR of (maskedSeed ^ seedMask). + // seed = (maskedSeed ^ seedMask) + for (short i = 0; i < hLen; i++) { + // Store the seed in encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i)] ^= tmpArray[i]; + } + + // Now get the dbMask by calling MGF(seed , (emLen-hLen)). + // dbMask = MGF(seed , (emLen-hLen)). + maskGenerationFunction1(encodedMsg, (short) encodedMsgOff, hLen, + (short) (encodedMsgLen - hLen), tmpArray, (short) 0); + + // Get the DB value. DB = (maskedDB ^ dbMask) + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + for (short i = 0; i < (short) (encodedMsgLen - hLen); i++) { + // Store the DB inside encodeMsg itself. + encodedMsg[(short) (encodedMsgOff + i + hLen)] ^= tmpArray[i]; + } + + // Verify Hash. + md = MessageDigest.OneShot.open(hash); + Util.arrayFillNonAtomic(tmpArray, (short) 0, (short) 256, (byte) 0); + md.doFinal(tmpArray, (short) 0, (short) 0, tmpArray, (short) 0); + if (0 != Util.arrayCompare(encodedMsg, (short) (encodedMsgOff + hLen), + tmpArray, (short) 0, hLen)) { + // Verification failed. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + + // Find the Message block in DB. + // DB = Hash(P)||00||01||Msg, where P is encoding parameters. (P = NULL) + // The message will be located at the end of the Data block (DB). + // The DB block is first constructed by keeping the message at the end and + // to the message 0x01 byte is prepended. The hash of the + // encoding parameters is calculated and then copied from the + // starting of the block and a variable length of 0's are + // appended to the end of the hash till the 0x01 byte. + short start = 0; + for (short i = (short) (encodedMsgOff + 2 * hLen); + i < (short) (encodedMsgOff + encodedMsgLen); i++) { + if (i == (short) ((encodedMsgOff + encodedMsgLen) - 1)) { + // Bad Padding. + CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); + } + if (encodedMsg[i] != 0) { + start = i; + break; + } + } + // Copy the message + Util.arrayCopyNonAtomic(encodedMsg, (short) (start + 1), msg, offset, + (short) (encodedMsgLen - ((start - encodedMsgOff) + 1))); + return (short) (encodedMsgLen - ((start - encodedMsgOff) + 1)); + + } finally { + if (md != null) { + md.close(); + } + Util.arrayFillNonAtomic(tmpArray, (short) 0, + KMAndroidSEProvider.TMP_ARRAY_SIZE, (byte) 0); + } + } +} \ No newline at end of file diff --git a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java similarity index 98% rename from Applet/src/com/android/javacard/keymaster/KMSEProvider.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 9dbc0413..0c3051d1 100644 --- a/Applet/src/com/android/javacard/keymaster/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/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.seprovider; /** * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to @@ -557,7 +557,7 @@ KMOperation initAsymmetricOperation( * is for ec public key. * @return An empty instance of KMAttestationCert implementation. */ - KMAttestationCert getAttestationCert(boolean rsaCert); + //KMAttestationCert getAttestationCert(boolean rsaCert); /** * This function tells if applet is upgrading or not. @@ -689,14 +689,6 @@ KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, */ byte[] getAdditionalCertChain(); - /** - * Generate boot certificate chain. - * - * @param testMode to indicate if current execution is for test or production. - * @param scratchPad buffer to store temporary results. - * @return instance of the boot certificate chin. - */ - short generateBcc(boolean testMode, byte[] scratchPad); /** * Returns the boot certificate chain. @@ -704,6 +696,8 @@ KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, * @return boot certificate chain. */ byte[] getBootCertificateChain(); + + public boolean isProvisionLocked(); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java new file mode 100644 index 00000000..d696e332 --- /dev/null +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java @@ -0,0 +1,354 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.seprovider; + +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; + // 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; + // 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/src/com/android/javacard/keymaster/KMUpgradable.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java similarity index 95% rename from Applet/src/com/android/javacard/keymaster/KMUpgradable.java rename to Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java index 0a241652..9bca1c8c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMUpgradable.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/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.seprovider; import org.globalplatform.upgrade.Element; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 02b9c6b5..b0d633b2 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -15,6 +15,12 @@ */ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMAESKey; +import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMJCardSimulator; +import com.android.javacard.seprovider.KMMasterKey; +import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -113,6 +119,7 @@ public class KMAttestationCertImpl implements KMAttestationCert { private static short stackPtr; private static short bufStart; private static short bufLength; + private static KMSEProvider seProvider; private static short uniqueId; private static short attChallenge; @@ -147,9 +154,10 @@ public class KMAttestationCertImpl implements KMAttestationCert { 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; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index ec36af0c..63c00331 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -15,15 +15,20 @@ */ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMJCardSimulator; import javacard.framework.APDU; import javacard.framework.ISO7816; import javacard.framework.Util; public class KMJCardSimApplet extends KMKeymasterApplet { + // Provider specific Commands private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 1; - private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_KEYMINT_PROVIDER_APDU_START + 2; + private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 2; private static final byte INS_LOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 4; private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 5; @@ -33,11 +38,29 @@ public class KMJCardSimApplet extends KMKeymasterApplet { INS_KEYMINT_PROVIDER_APDU_START + 7; private static final byte INS_KEYMINT_PROVIDER_APDU_END = 0x1F; + //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_BOOT_PARAM = 0x20; + private static final byte PROVISION_STATUS_PROVISIONING_LOCKED = 0x40; + private static final byte PROVISION_STATUS_DEVICE_UNIQUE_KEY = 0x60; + private static final byte PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = (byte) 0x80; + private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; + public static final short SHARED_SECRET_KEY_SIZE = 32; + public static final byte BOOT_KEY_MAX_SIZE = 32; + public static final byte BOOT_HASH_MAX_SIZE = 32; + + private static byte provisionStatus = NOT_PROVISIONED; + KMJCardSimApplet() { super(new KMJCardSimulator()); - setDummyBootParams(); - setDummyPresharedKey(); - setDummyAttestationIds(); + // setDummyBootParams(); + // setDummyPresharedKey(); + // setDummyAttestationIds(); } /** @@ -75,14 +98,19 @@ public void process(APDU apdu) { } return; } - if (apduIns == KMType.INVALID_VALUE) + if (apduIns == KMType.INVALID_VALUE) { return; + } switch (apduIns) { case INS_PROVISION_ATTEST_IDS_CMD: processProvisionAttestIdsCmd(apdu); + provisionStatus |= PROVISION_STATUS_ATTEST_IDS; + sendError(apdu, KMError.OK); break; case INS_PROVISION_PRESHARED_SECRET_CMD: processProvisionPreSharedSecretCmd(apdu); + provisionStatus |= PROVISION_STATUS_PRESHARED_SECRET; + sendError(apdu, KMError.OK); break; case INS_GET_PROVISION_STATUS_CMD: processGetProvisionStatusCmd(apdu); @@ -92,12 +120,15 @@ public void process(APDU apdu) { break; case INS_SET_BOOT_PARAMS_CMD: processSetBootParamsCmd(apdu); + provisionStatus |= PROVISION_STATUS_BOOT_PARAM; break; case INS_PROVISION_DEVICE_UNIQUE_KEY_CMD: processProvisionDeviceUniqueKey(apdu); + provisionStatus |= PROVISION_STATUS_DEVICE_UNIQUE_KEY; break; case INS_PROVISION_ADDITIONAL_CERT_CHAIN_CMD: processProvisionAdditionalCertChain(apdu); + provisionStatus |= PROVISION_STATUS_ADDITIONAL_CERT_CHAIN; break; default: super.process(apdu); @@ -109,23 +140,118 @@ public void process(APDU apdu) { } private void processProvisionAttestIdsCmd(APDU apdu) { - sendError(apdu, KMError.OK); + + short keyparams = KMKeyParameters.exp(); + short cmd = KMArray.instance((short) 1); + KMArray.cast(cmd).add((short) 0, keyparams); + short args = receiveIncoming(apdu, cmd); + short attData = KMArray.cast(args).get((short) 0); + // persist attestation Ids - if any is missing then exception occurs + setAttestationIds(attData); + } + + public void setAttestationIds(short attIdVals) { + KMKeyParameters instParam = KMKeyParameters.cast(attIdVals); + KMArray vals = KMArray.cast(instParam.getVals()); + short index = 0; + short length = vals.length(); + short key; + short type; + short obj; + while (index < length) { + obj = vals.get(index); + key = KMTag.getKey(obj); + type = KMTag.getTagType(obj); + + if (KMType.BYTES_TAG != type) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + obj = KMByteTag.cast(obj).getValue(); + ((KMJCardSimulator) seProvider).setAttestationId(key, KMByteBlob.cast(obj).getBuffer(), + KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); + index++; + } } private void processProvisionPreSharedSecretCmd(APDU apdu) { - sendError(apdu, KMError.OK); + + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 1); + KMArray.cast(argsProto).add((short) 0, blob); + short args = receiveIncoming(apdu, argsProto); + + short val = KMArray.cast(args).get((short) 0); + + if (val != KMType.INVALID_VALUE + && KMByteBlob.cast(val).length() != SHARED_SECRET_KEY_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Persist shared Hmac. + ((KMJCardSimulator) seProvider).createPresharedKey( + KMByteBlob.cast(val).getBuffer(), + KMByteBlob.cast(val).getStartOff(), + KMByteBlob.cast(val).length()); } private void processGetProvisionStatusCmd(APDU apdu) { - sendError(apdu, KMError.OK); + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, buildErrorStatus(KMError.OK)); + KMArray.cast(resp).add((short) 1, KMInteger.uint_16(provisionStatus)); + sendOutgoing(apdu, resp); + } private void processSetBootParamsCmd(APDU apdu) { + short argsProto = KMArray.instance((short) 5); + // Array of 4 expected arguments + // Argument 0 Boot Patch level + KMArray.cast(argsProto).add((short) 0, KMInteger.exp()); + // Argument 1 Verified Boot Key + KMArray.cast(argsProto).add((short) 1, KMByteBlob.exp()); + // Argument 2 Verified Boot Hash + KMArray.cast(argsProto).add((short) 2, KMByteBlob.exp()); + // Argument 3 Verified Boot State + KMArray.cast(argsProto).add((short) 3, KMEnum.instance(KMType.VERIFIED_BOOT_STATE)); + // Argument 4 Device Locked + KMArray.cast(argsProto).add((short) 4, KMEnum.instance(KMType.DEVICE_LOCKED)); + + short args = receiveIncoming(apdu, argsProto); + short bootParam = KMArray.cast(args).get((short) 0); + + ((KMJCardSimulator) seProvider).setBootPatchLevel(KMInteger.cast(bootParam).getBuffer(), + KMInteger.cast(bootParam).getStartOff(), + KMInteger.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 1); + if (KMByteBlob.cast(bootParam).length() > BOOT_KEY_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMJCardSimulator) seProvider).setBootKey(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 2); + if (KMByteBlob.cast(bootParam).length() > BOOT_HASH_MAX_SIZE) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + ((KMJCardSimulator) seProvider).setVerifiedBootHash(KMByteBlob.cast(bootParam).getBuffer(), + KMByteBlob.cast(bootParam).getStartOff(), + KMByteBlob.cast(bootParam).length()); + + bootParam = KMArray.cast(args).get((short) 3); + byte enumVal = KMEnum.cast(bootParam).getVal(); + ((KMJCardSimulator) seProvider).setBootState(enumVal); + + bootParam = KMArray.cast(args).get((short) 4); + enumVal = KMEnum.cast(bootParam).getVal(); + ((KMJCardSimulator) seProvider).setDeviceLocked(enumVal == KMType.DEVICE_LOCKED_TRUE); + + super.reboot(); sendError(apdu, KMError.OK); } private void processLockProvisioningCmd(APDU apdu) { - ((KMJCardSimulator)seProvider).setProvisionLocked(true); + ((KMJCardSimulator) seProvider).setProvisionLocked(true); sendError(apdu, KMError.OK); } @@ -149,10 +275,10 @@ private short validateApdu(APDU apdu) { return apduBuffer[ISO7816.OFFSET_INS]; } - private void setDummyBootParams(){ - short osVersion = KMInteger.uint_16(((short)0)); - short osPatchLevel = KMInteger.uint_16((short)0); - short vendorPatchLevel = KMInteger.uint_16((short)0); + private void setDummyBootParams() { + short osVersion = KMInteger.uint_16(((short) 0)); + short osPatchLevel = KMInteger.uint_16((short) 0); + short vendorPatchLevel = KMInteger.uint_16((short) 0); short bootPatchLevel = KMInteger.uint_16((short) 0); super.setOsVersion(osVersion); @@ -166,51 +292,64 @@ private void setDummyBootParams(){ (short) bootBlob.length); short bootState = KMType.UNVERIFIED_BOOT; - ((KMJCardSimulator)seProvider).setBootPatchLevel( + ((KMJCardSimulator) seProvider).setBootPatchLevel( KMInteger.cast(bootPatchLevel).getBuffer(), KMInteger.cast(bootPatchLevel).getStartOff(), KMInteger.cast(bootPatchLevel).length()); - ((KMJCardSimulator)seProvider).setBootKey( + ((KMJCardSimulator) seProvider).setBootKey( KMByteBlob.cast(bootKey).getBuffer(), KMByteBlob.cast(bootKey).getStartOff(), KMByteBlob.cast(bootKey).length()); - ((KMJCardSimulator)seProvider).setVerifiedBootHash( + ((KMJCardSimulator) seProvider).setVerifiedBootHash( KMByteBlob.cast(verifiedHash).getBuffer(), KMByteBlob.cast(verifiedHash).getStartOff(), KMByteBlob.cast(verifiedHash).length()); - ((KMJCardSimulator)seProvider).setBootState((byte)bootState); - ((KMJCardSimulator)seProvider).setDeviceLocked(true); + ((KMJCardSimulator) seProvider).setBootState((byte) bootState); + ((KMJCardSimulator) seProvider).setDeviceLocked(true); super.reboot(); } - private void setDummyPresharedKey(){ - final byte[] presharedKey = {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}; - ((KMJCardSimulator)seProvider).createPresharedKey(presharedKey, (short)0, (short)presharedKey.length); + private void setDummyPresharedKey() { + final byte[] presharedKey = {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}; + ((KMJCardSimulator) seProvider) + .createPresharedKey(presharedKey, (short) 0, (short) presharedKey.length); } - private void setDummyAttestationIds(){ - final byte[] brand = {'g','e','n','e','r','i','c'}; - final byte[] device = {'v','s','o','c','_','x','8','6','_','6','4'};//vsoc_x86_64 + private void setDummyAttestationIds() { + final byte[] brand = {'g', 'e', 'n', 'e', 'r', 'i', 'c'}; + final byte[] device = {'v', 's', 'o', 'c', '_', 'x', '8', '6', '_', '6', '4'};//vsoc_x86_64 final byte[] product = //aosp_cf_x86_64_phone - {'a','o','s','p','_','c','f','_','x','8','6','_','6','4','_','p','h','o','n','e'}; + {'a', 'o', 's', 'p', '_', 'c', 'f', '_', 'x', '8', '6', '_', '6', '4', '_', 'p', 'h', 'o', + 'n', 'e'}; final byte[] serial = {}; - final byte[] imei = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; - final byte[] meid = {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; - final byte[] manufacturer = {'G','o','o','g','l', 'e'}; + final byte[] imei = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; + final byte[] meid = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; + final byte[] manufacturer = {'G', 'o', 'o', 'g', 'l', 'e'}; final byte[] model = //"Cuttlefish x86_64 phone" - {'C','u','t','t','l', 'e','f','i','s','h',' ','x','8','6','_','6','4', - ' ','p','h','o','n','e'}; - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_BRAND,brand,(short)0,(short)brand.length); - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_IMEI,imei,(short)0,(short)imei.length); - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_DEVICE,device,(short)0,(short)device.length); - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_MEID,meid,(short)0,(short)meid.length); - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_MODEL,model,(short)0,(short)model.length); - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_MANUFACTURER,manufacturer,(short)0,(short)manufacturer.length); - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_PRODUCT,product,(short)0,(short)product.length); - ((KMJCardSimulator)seProvider).setAttestationId(KMType.ATTESTATION_ID_SERIAL,serial,(short)0,(short)serial.length); + {'C', 'u', 't', 't', 'l', 'e', 'f', 'i', 's', 'h', ' ', 'x', '8', '6', '_', '6', '4', + ' ', 'p', 'h', 'o', 'n', 'e'}; + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_BRAND, brand, (short) 0, (short) brand.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_IMEI, imei, (short) 0, (short) imei.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_DEVICE, device, (short) 0, (short) device.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_MEID, meid, (short) 0, (short) meid.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_MODEL, model, (short) 0, (short) model.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, manufacturer, (short) 0, + (short) manufacturer.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_PRODUCT, product, (short) 0, + (short) product.length); + ((KMJCardSimulator) seProvider) + .setAttestationId(KMType.ATTESTATION_ID_SERIAL, serial, (short) 0, (short) serial.length); } private static void processProvisionDeviceUniqueKey(APDU apdu) { @@ -228,7 +367,7 @@ private static void processProvisionDeviceUniqueKey(APDU apdu) { seProvider.createDeviceUniqueKey(false, scratchPad, (short) 0, pubKeyLen, scratchPad, pubKeyLen, privKeyLen); // Newly added code 30/07/2021 - short bcc = ((KMJCardSimulator) seProvider).generateBcc(false, scratchPad); + short bcc = generateBcc(false, scratchPad); short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, MAX_COSE_BUF_SIZE); ((KMJCardSimulator) seProvider).persistBootCertificateChain(scratchPad, (short) 0, len); @@ -244,7 +383,7 @@ private static void processProvisionAdditionalCertChain(APDU apdu) { KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); short coseSignArr = KMArray.exp(arrInst); - short map = KMMap.instance((short) 1); + short map = KMMap.instance((short) 1); KMMap.cast(map).add((short) 0, KMTextString.exp(), coseSignArr); // TODO duplicate code. // receive incoming data and decode it. @@ -270,8 +409,9 @@ private static void processProvisionAdditionalCertChain(APDU apdu) { // Compare the DK_Pub. short pubKeyLen = KMCoseKey.cast(leafCoseKey).getEcdsa256PublicKey(srcBuffer, (short) 0); KMDeviceUniqueKey uniqueKey = seProvider.getDeviceUniqueKey(false); - if (uniqueKey == null) + 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))) { @@ -283,4 +423,23 @@ private static void processProvisionAdditionalCertChain(APDU apdu) { sendError(apdu, KMError.OK); } + private static short buildErrorStatus(short err) { + short int32Ptr = KMInteger.instance((short) 4); + short powerResetStatus = 0; + if (((KMJCardSimulator) seProvider).isPowerReset()) { + powerResetStatus = POWER_RESET_MASK_FLAG; + } + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + KMInteger.cast(int32Ptr).getStartOff(), + powerResetStatus); + + Util.setShort(KMInteger.cast(int32Ptr).getBuffer(), + (short) (KMInteger.cast(int32Ptr).getStartOff() + 2), + err); + // reset power reset status flag to its default value. + //repository.restorePowerResetStatus(); //TODO + return int32Ptr; + } + } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java index 5b178143..06c77ff6 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMUtils.java @@ -15,6 +15,7 @@ */ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.Util; public class KMUtils { diff --git a/Applet/JCardSimProvider/lib/gpapi-upgrade.jar b/Applet/JCardSimProviderLib/lib/gpapi-upgrade.jar similarity index 100% rename from Applet/JCardSimProvider/lib/gpapi-upgrade.jar rename to Applet/JCardSimProviderLib/lib/gpapi-upgrade.jar diff --git a/Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar b/Applet/JCardSimProviderLib/lib/hamcrest-core-1.3.jar similarity index 100% rename from Applet/JCardSimProvider/lib/hamcrest-core-1.3.jar rename to Applet/JCardSimProviderLib/lib/hamcrest-core-1.3.jar diff --git a/Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar b/Applet/JCardSimProviderLib/lib/jcardsim-3.0.5-SNAPSHOT.jar similarity index 100% rename from Applet/JCardSimProvider/lib/jcardsim-3.0.5-SNAPSHOT.jar rename to Applet/JCardSimProviderLib/lib/jcardsim-3.0.5-SNAPSHOT.jar diff --git a/Applet/JCardSimProvider/lib/junit-4.13.jar b/Applet/JCardSimProviderLib/lib/junit-4.13.jar similarity index 100% rename from Applet/JCardSimProvider/lib/junit-4.13.jar rename to Applet/JCardSimProviderLib/lib/junit-4.13.jar diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAESKey.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAESKey.java index 258dc461..bbbb27f2 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAESKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAESKey.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.AESKey; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java new file mode 100644 index 00000000..6cd8e7b0 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java @@ -0,0 +1,194 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * The KMAttestationCert interface represents a X509 compliant attestation certificate required to + * support keymaster's attestKey function. This cert will be created according to the specifications + * given in android keymaster hal documentation. KMSeProvider has to provide the instance of this + * certificate. This interface is designed based on builder pattern and hence each method returns + * instance of cert. + */ +public interface KMAttestationCert { + + /** + * Set verified boot hash. + * + * @param obj This is a KMByteBlob containing hash + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootHash(short obj); + + /** + * Set verified boot key received during booting up. + * + * @param obj This is a KMByteBlob containing verified boot key. + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootKey(short obj); + + /** + * Set verified boot state received during booting up. + * + * @param val This is a byte containing verified boot state value. + * @return instance of KMAttestationCert + */ + KMAttestationCert verifiedBootState(byte val); + + /** + * Set uniqueId received from CA certificate during provisioning. + * + * @param scratchpad Buffer to store intermediate results. + * @param scratchPadOff Start offset of the scratchpad buffer. + * @param creationTime This buffer contains the CREATION_TIME value. + * @param creationTimeOff Start offset of creattionTime buffer. + * @param creationTimeLen Length of the creationTime buffer. + * @param attestAppId This buffer contains the ATTESTATION_APPLICATION_ID value. + * @param attestAppIdOff Start offset of the attestAppId buffer. + * @param attestAppIdLen Length of the attestAppId buffer. + * @param resetSinceIdRotation This holds the information of RESET_SINCE_ID_ROTATION. + * @param masterKey + * @return instance of KMAttestationCert. + */ + KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] creationTime, + short creationTimeOff, short creationTimeLen, byte[] attestAppId, + short attestAppIdOff, short attestAppIdLen, byte resetSinceIdRotation, + KMMasterKey masterKey); + + /** + * Set start time received from creation/activation time tag. Used for certificate's valid + * period. + * + * @param obj This is a KMByteBlob object containing start time. + * @param scratchpad Buffer to store intermediate results. + * @return instance of KMAttestationCert. + */ + KMAttestationCert notBefore(short obj, boolean derEncoded, byte[] scratchpad); + + + /** + * Set expiry time received from expiry time tag or ca certificates expiry time. Used for + * certificate's valid period. + * + * @param usageExpiryTimeObj This is a KMByteBlob containing expiry time. + * certificate. + * @param scratchPad Buffer to store intermediate results. + * @return instance of KMAttestationCert + */ + KMAttestationCert notAfter(short usageExpiryTimeObj, boolean derEncoded, byte[] scratchPad); + + /** + * Set device lock status received during booting time or due to device lock command. + * + * @param val This is true if device is locked. + * @return instance of KMAttestationCert + */ + KMAttestationCert deviceLocked(boolean val); + + /** + * Set public key to be attested received from attestKey command. + * + * @param obj This is KMByteBlob containing the public key. + * @return instance of KMAttestationCert + */ + KMAttestationCert publicKey(short obj); + + /** + * Set attestation challenge received from attestKey command. + * + * @param obj This is KMByteBlob containing the attestation challenge. + * @return instance of KMAttestationCert + */ + KMAttestationCert attestationChallenge(short obj); + + /** + * Set extension tag received from key characteristics which needs to be added to android + * extension. This method will called once for each tag. + * + * @param tag is the KMByteBlob containing KMTag. + * @param hwEnforced is true if the tag has to be added to hw enforced list or else added to sw + * enforced list. + * @return instance of KMAttestationCert + */ + KMAttestationCert extensionTag(short tag, boolean hwEnforced); + + /** + * Set ASN.1 encoded X509 issuer field received from attestation key CA cert. + * + * @param obj This is KMByteBlob containing the issuer. + * @return instance of KMAttestationCert + */ + KMAttestationCert issuer(short obj); + + /** + * Set byte buffer to be used to generate certificate. + * + * @param buf This is byte[] buffer. + * @param bufStart This is short start offset. + * @param maxLen This is short length of the buffer. + * @return instance of KMAttestationCert + */ + KMAttestationCert buffer(byte[] buf, short bufStart, short maxLen); + + /** + * Get the start of the certificate + * + * @return start of the attestation cert. + */ + short getCertStart(); + + /** + * Get the length of the certificate + * + * @return length of the attestation cert. + */ + short getCertLength(); + + + /** + * Build a fake signed certificate. After this method executes the certificate is ready with the + * signature equal to 1 byte which is 0 and with rsa signature algorithm. + */ + void build(); + + /** + * Set the Serial number in the certificate. If no serial number is set then serial number is 1. + * + * @param serialNumber + */ + boolean serialNumber(short serialNumber); + + /** + * Set the Subject Name in the certificate. + * + * @param subject + */ + boolean subjectName(short subject); + + /** + * Set attestation key and mode. + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert ecAttestKey(short attestKey, byte mode); + /** + * Set attestation key and mode. + * @param attestKey KMByteBlob of the key + * @param mode + */ + KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte mode); + +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java new file mode 100644 index 00000000..1b8e334e --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMAttestationKey.java @@ -0,0 +1,25 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMAttestationKey is a marker interface and the SE Provider has to implement this interface. + * Internally attestation key is stored as a Javacard EC key pair object, which will provide + * additional security. The attestation key is maintained by the SEProvider. + */ +public interface KMAttestationKey { +} + diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipher.java similarity index 94% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipher.java index 420a775c..31389eee 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipher.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipher.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; public abstract class KMCipher { diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipherImpl.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipherImpl.java index c4162b12..4340cd4b 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMCipherImpl.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMCipherImpl.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.Util; import javacard.security.CryptoException; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java new file mode 100644 index 00000000..08e60a3f --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKey.java @@ -0,0 +1,21 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +public interface KMDeviceUniqueKey { + + short getPublicKey(byte[] buf, short offset); +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java new file mode 100644 index 00000000..0f68de61 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java @@ -0,0 +1,53 @@ +/* + * Copyright(C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; +import javacard.security.ECPrivateKey; +import javacard.security.ECPublicKey; +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(); + } + +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java index 62b26cce..f45c55ba 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMECPrivateKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.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.ECPrivateKey; import javacard.security.KeyPair; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java similarity index 99% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMEcdsa256NoDigestSignature.java index c382f3cb..e53a8963 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMEcdsa256NoDigestSignature.java +++ b/Applet/JCardSimProviderLib/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 java.math.BigInteger; import java.security.AlgorithmParameters; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMError.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMError.java new file mode 100644 index 00000000..5754abe9 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMError.java @@ -0,0 +1,134 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMError includes all the error codes from android keymaster hal specifications. The values are + * positive unlike negative values in keymaster hal. + */ +public class KMError { + + public static final short OK = 0; + public static final short UNSUPPORTED_PURPOSE = 2; + public static final short 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 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 DEVICE_LOCKED = 72; + public static final short EARLY_BOOT_ENDED = 73; + public static final short ATTESTATION_KEYS_NOT_PROVISIONED =74; + public static final short INCOMPATIBLE_MGF_DIGEST = 78; + public static final short UNSUPPORTED_MGF_DIGEST = 79; + public static final short MISSING_NOT_BEFORE = 80; + public static final short MISSING_NOT_AFTER = 81; + public static final short MISSING_ISSUER_SUBJECT_NAME = 82; + public static final short INVALID_ISSUER_SUBJECT_NAME = 83; + + public static final short UNIMPLEMENTED = 100; + public static final short UNKNOWN_ERROR = 1000; + + //Extended errors + public static final short SW_CONDITIONS_NOT_SATISFIED = 10001; + public static final short UNSUPPORTED_CLA = 10002; + public static final short INVALID_P1P2 = 10003; + public static final short UNSUPPORTED_INSTRUCTION = 10004; + public static final short CMD_NOT_ALLOWED = 10005; + public static final short SW_WRONG_LENGTH = 10006; + public static final short INVALID_DATA = 10007; + + //Crypto errors + public static final short CRYPTO_ILLEGAL_USE = 10008; + public static final short CRYPTO_ILLEGAL_VALUE = 10009; + public static final short CRYPTO_INVALID_INIT = 10010; + public static final short CRYPTO_NO_SUCH_ALGORITHM = 10011; + public static final short CRYPTO_UNINITIALIZED_KEY = 10012; + //Generic Unknown error. + public static final short GENERIC_UNKNOWN_ERROR = 10013; + + // Remote key provisioning error codes. + public static final short STATUS_FAILED = 32000; + public static final short STATUS_INVALID_MAC = 32001; + public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; + public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; + public static final short STATUS_INVALID_EEK = 32004; + public static final short INVALID_STATE = 32005; + + public static short translate(short err) { + switch(err) { + case SW_CONDITIONS_NOT_SATISFIED: + case UNSUPPORTED_CLA: + case INVALID_P1P2: + case INVALID_DATA: + case CRYPTO_ILLEGAL_USE: + case CRYPTO_ILLEGAL_VALUE: + case CRYPTO_INVALID_INIT: + case CRYPTO_UNINITIALIZED_KEY: + case GENERIC_UNKNOWN_ERROR: + case 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/JCardSimProviderLib/src/com/android/javacard/seprovider/KMException.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMException.java new file mode 100644 index 00000000..c0b2431f --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMException.java @@ -0,0 +1,55 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.seprovider; + +import javacard.framework.JCSystem; + +/** + * KMException is shared instance of exception used for all exceptions in the applet. It is used to + * throw EMError errors. + */ +public class KMException extends RuntimeException { + + private static short[] reason; + private static KMException exception; + + private KMException() { + } + public static short reason(){ + return reason[0]; + } + public static void throwIt(short e) { + if(reason == null) { + reason = JCSystem.makeTransientShortArray((short)1,JCSystem.CLEAR_ON_DESELECT); + } + if(exception == null){ + exception = new KMException(); + } + reason[0] = e; + throw exception; + } +/* + public static KMException instance() { + if (exception == null) { + exception = new KMException(); + } + return exception; + } +*/ +} + + diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java similarity index 96% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index 65f1d02a..8473f1bf 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMHmacKey.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMHmacKey.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.HMACKey; diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java similarity index 93% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java index 513beec7..1d0c426e 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimulator.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.javacard.keymaster; +package com.android.javacard.seprovider; import java.math.BigInteger; import java.security.InvalidAlgorithmParameterException; @@ -41,7 +41,6 @@ import javacard.security.KeyPair; import javacard.security.RSAPrivateKey; import javacard.security.KeyAgreement; -import javacard.security.RSAPublicKey; import javacard.security.RandomData; import javacard.security.Signature; import javacardx.crypto.AEADCipher; @@ -75,6 +74,7 @@ public class KMJCardSimulator implements KMSEProvider { private static final short RSA_KEY_SIZE = 256; public static final byte POWER_RESET_FALSE = (byte)0xAA; public static final byte POWER_RESET_TRUE = (byte)0x00; + public static final byte AES_BLOCK_SIZE = 16; public static byte[] resetFlag; public static boolean jcardSim = false; @@ -1202,11 +1202,9 @@ private void initEntropyPool(byte[] pool) { // 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; + byte[] countBuf = new byte[AES_BLOCK_SIZE]; + byte[] randBuf = new byte[AES_BLOCK_SIZE]; + short len = AES_BLOCK_SIZE; aesRngKey.setKey(entropyPool, (short) 0); aesRngCipher.init(aesRngKey, Cipher.MODE_ENCRYPT, aesICV, (short) 0, (short) 16); while (length > 0) { @@ -1216,12 +1214,12 @@ public void newRandomNumber(byte[] num, short startOff, short 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); + Util.arrayCopy(rngCounter, (short) 0, countBuf, (short) 0, (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); + countBuf, (short) 0, AES_BLOCK_SIZE, randBuf, (short) 0); // copy the encrypted rngCounter block to buffer passed in the argument - Util.arrayCopy(bufPtr, randBufInd, num, startOff, len); + Util.arrayCopy(randBuf, (short) 0, num, startOff, len); length = (short) (length - len); startOff = (short) (startOff + len); } @@ -1276,11 +1274,6 @@ public void addRngEntropy(byte[] num, short offset, short length) { } } - @Override - public KMAttestationCert getAttestationCert(boolean rsaCert) { - return KMAttestationCertImpl.instance(rsaCert); - } - /** * The operation reads the certificate chain from persistent memory. * @@ -1312,91 +1305,6 @@ public byte[] getAdditionalCertChain() { // return length; } - @Override - public short generateBcc(boolean testMode, byte[] scratchPad) { - if (!testMode && isProvisionLocked) { - KMException.throwIt(KMError.STATUS_FAILED); - } - KMDeviceUniqueKey deviceUniqueKey = getDeviceUniqueKey(testMode); - short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0); - short coseKey = - KMCose.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 = KMKeymasterApplet.encodeToApduBuffer(coseKey, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - // Construct payload. - short payload = - KMCose.constructCoseCertPayload( - KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.ISSUER), - KMTextString.instance(KMCose.TEST_ISSUER_NAME, (short) 0, - (short) KMCose.TEST_ISSUER_NAME.length)), - KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.SUBJECT), - KMTextString.instance(KMCose.TEST_SUBJECT_NAME, (short) 0, - (short) KMCose.TEST_SUBJECT_NAME.length)), - KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0), - KMByteBlob.instance(scratchPad, (short) 0, temp)), - KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0), - KMByteBlob.instance(KMCose.KEY_USAGE_SIGN, (short) 0, - (short) KMCose.KEY_USAGE_SIGN.length)) - ); - // temp temporarily holds the length of encoded cert payload. - temp = KMKeymasterApplet.encodeToApduBuffer(payload, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - payload = KMByteBlob.instance(scratchPad, (short) 0, temp); - - // protected header - short protectedHeader = - KMCose.constructHeaders(KMNInteger.uint_8(KMCose.COSE_ALG_ES256), KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); - // temp temporarily holds the length of encoded headers. - temp = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp); - - //unprotected headers. - short arr = KMArray.instance((short) 0); - short unprotectedHeader = KMCoseHeaders.instance(arr); - - // construct cose sign structure. - short coseSignStructure = - KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), payload); - // temp temporarily holds the length of encoded sign structure. - // Encode cose Sign_Structure. - temp = KMKeymasterApplet.encodeToApduBuffer(coseSignStructure, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - // do sign - short len = - ecSign256( - deviceUniqueKey, - scratchPad, - (short) 0, - temp, - scratchPad, - temp - ); - coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); - - // construct cose_sign1 - short coseSign1 = - KMCose.constructCoseSign1(protectedHeader, unprotectedHeader, payload, coseSignStructure); - - // [Cose_Key, Cose_Sign1] - short bcc = KMArray.instance((short) 2); - KMArray.cast(bcc).add((short) 0, coseKey); - KMArray.cast(bcc).add((short) 1, coseSign1); - return bcc; - } - public void persistBootCertificateChain(byte[] buf, short offset, short len) { if ((short) (len + 2) > BCC_MAX_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java new file mode 100644 index 00000000..6eab5e56 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMMasterKey.java @@ -0,0 +1,25 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMMasterKey is a marker interface and the SE Provider has to implement this interface. Internally + * Masterkey is stored as a Javacard AES key object, which will provide additional security. The + * master key is maintained by the SEProvider. + */ +public interface KMMasterKey { + +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperation.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperation.java new file mode 100644 index 00000000..b73d58d1 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperation.java @@ -0,0 +1,54 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMOperation represents a persistent operation started by keymaster hal's beginOperation function. + * This operation is persistent i.e. it will be stored in non volatile memory of se card. It will be + * returned back to KMSEProvider for the reuse when the operation is finished. + */ +public interface KMOperation { + + // Used for cipher operations + short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + // Used for signature operations + short update(byte[] inputDataBuf, short inputDataStart, short inputDataLength); + + // Used for finishing cipher operations or ecdh keyAgreement. + short finish(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + // Used for finishing signing operations. + short sign(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signBuf, short signStart); + + // Used for finishing verifying operations. + boolean verify(byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] signBuf, short signStart, short signLength); + + // Used for aborting the ongoing operations. + void abort(); + + // Used for AES GCM cipher operation. + void updateAAD(byte[] dataBuf, short dataStart, short dataLength); + + // Used for getting output size before finishing a AES GCM cipher operation. For encryption this will + // include the auth tag which is appended at the end of the encrypted data. For decryption this will be + // size of the decrypted data only. + short getAESGCMOutputSize(short dataSize, short macLength); +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java similarity index 98% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.java index a66fc6ab..715092af 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMOperationImpl.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMOperationImpl.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.KeyAgreement; import javacard.security.Signature; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java new file mode 100644 index 00000000..86bb1df9 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMPreSharedKey.java @@ -0,0 +1,25 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +/** + * KMPreSharedKey is a marker interface and the SE Provider has to implement this interface. + * Internally Preshared key is stored as a Javacard HMac key object, which will provide additional + * security. The pre-shared key is maintained by the SEProvider. + */ +public interface KMPreSharedKey { + +} diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java similarity index 84% rename from Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java rename to Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.java index df8c856b..f9e8875d 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMRsa2048NoDigestSignature.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMRsa2048NoDigestSignature.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.Util; import javacard.security.CryptoException; @@ -130,7 +130,7 @@ private boolean isValidData(byte[] buf, short start, short len) { if (len > 256) { return false; } else if (len == 256) { - short v = KMInteger.unsignedByteArrayCompare(buf, start, rsaModulus, (short) 0, len); + short v = unsignedByteArrayCompare(buf, start, rsaModulus, (short) 0, len); if (v > 0) { return false; } @@ -143,4 +143,24 @@ private boolean isValidData(byte[] buf, short start, short len) { } return true; } + + private byte unsignedByteArrayCompare(byte[] a1, short offset1, byte[] a2, short offset2, + short length) { + byte count = (byte) 0; + short val1 = (short) 0; + short val2 = (short) 0; + + for (; count < length; count++) { + val1 = (short) (a1[(short) (count + offset1)] & 0x00FF); + val2 = (short) (a2[(short) (count + offset2)] & 0x00FF); + + if (val1 < val2) { + return -1; + } + if (val1 > val2) { + return 1; + } + } + return 0; + } } diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java new file mode 100644 index 00000000..d23d7f15 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -0,0 +1,691 @@ +/* + * 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; + +/** + * KMSEProvider is facade to use SE specific methods. The main intention of this interface is to + * abstract the cipher, signature and backup and restore related functions. The instance of this + * interface is created by the singleton KMSEProviderImpl class for each provider. At a time there + * can be only one provider in the applet package. + */ +public interface KMSEProvider extends KMUpgradable { + + /** + * Create a symmetric key instance. If the algorithm and/or keysize are not supported then it + * should throw a CryptoException. + * + * @param alg will be KMType.AES, KMType.DES or KMType.HMAC. + * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for + * HMAC. + * @param buf is the buffer in which key has to be returned + * @param startOff is the start offset. + * @return length of the data in the buf. This should match the keysize (in bytes). + */ + short createSymmetricKey(byte alg, short keysize, byte[] buf, short startOff); + + /** + * Create a asymmetric key pair. If the algorithms are not supported then it should throw a + * CryptoException. For RSA the public key exponent must always be 0x010001. The key size of RSA + * key pair must be 2048 bits and key size of EC key pair must be for p256 curve. + * + * @param alg will be KMType.RSA or KMType.EC. + * @param privKeyBuf is the buffer to return the private key exponent in case of RSA or private + * key in case of EC. + * @param privKeyStart is the start offset. + * @param privKeyMaxLength is the maximum length of this private key buffer. + * @param pubModBuf is the buffer to return the modulus in case of RSA or public key in case of + * EC. + * @param pubModStart is the start of offset. + * @param pubModMaxLength is the maximum length of this public key buffer. + * @param lengths is the actual length of the key pair - lengths[0] should be private key and + * lengths[1] should be public key. + */ + void createAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyMaxLength, + byte[] pubModBuf, + short pubModStart, + short pubModMaxLength, + short[] lengths); + + /** + * Verify that the imported key is valid. If the algorithm and/or keysize are not supported then + * it should throw a CryptoException. + * + * @param alg will be KMType.AES, KMType.DES or KMType.HMAC. + * @param keysize will be 128 or 256 for AES or DES. It can be 64 to 512 (multiple of 8) for + * HMAC. + * @param buf is the buffer that contains the symmetric key. + * @param startOff is the start offset. + * @param length of the data in the buf. This should match the keysize (in bytes). + * @return true if the symmetric key is supported and valid. + */ + boolean importSymmetricKey(byte alg, short keysize, byte[] buf, short startOff, short length); + + /** + * Validate that the imported asymmetric key pair is valid. For RSA the public key exponent must + * always be 0x010001. The key size of RSA key pair must be 2048 bits and key size of EC key pair + * must be for p256 curve. If the algorithms are not supported then it should throw a + * CryptoException. + * + * @param alg will be KMType.RSA or KMType.EC. + * @param privKeyBuf is the buffer that contains the private key exponent in case of RSA or + * private key in case of EC. + * @param privKeyStart is the start offset. + * @param privKeyLength is the length of this private key buffer. + * @param pubModBuf is the buffer that contains the modulus in case of RSA or public key in case + * of EC. + * @param pubModStart is the start of offset. + * @param pubModLength is the length of this public key buffer. + * @return true if the key pair is supported and valid. + */ + boolean importAsymmetricKey( + byte alg, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + /** + * This is a oneshot operation that generates random number of desired length. + * + * @param num is the buffer in which random number is returned to the applet. + * @param offset is start of the buffer. + * @param length indicates the size of buffer and desired length of random number in bytes. + */ + void newRandomNumber(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that adds the entropy to the entropy pool. This operation + * corresponds to addRndEntropy command. This method may ignore the added entropy value if the SE + * provider does not support it. + * + * @param num is the buffer in which entropy value is given. + * @param offset is start of the buffer. + * @param length length of the buffer. + */ + void addRngEntropy(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that generates and returns back a true random number. + * + * @param num is the buffer in which entropy value is returned. + * @param offset is start of the buffer. + * @param length length of the buffer. + */ + void getTrueRandomNumber(byte[] num, short offset, short length); + + /** + * This is a oneshot operation that performs encryption operation using AES GCM algorithm. It + * throws CryptoException if algorithm is not supported or if tag length is not equal to 16 or + * nonce length is not equal to 12. + * + * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt. + * @param aesKeyStart is the start in aes key buffer. + * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes). + * @param data is the buffer that contains data to encrypt. + * @param dataStart is the start of the data buffer. + * @param dataLen is the length of the data buffer. + * @param encData is the buffer of the output encrypted data. + * @param encDataStart is the start of the encrypted data buffer. + * @param nonce is the buffer of nonce. + * @param nonceStart is the start of the nonce buffer. + * @param nonceLen is the length of the nonce buffer. + * @param authData is the authentication data buffer. + * @param authDataStart is the start of the authentication buffer. + * @param authDataLen is the length of the authentication buffer. + * @param authTag is the buffer to output authentication tag. + * @param authTagStart is the start of the buffer. + * @param authTagLen is the length of the buffer. + * @return length of the encrypted data. + */ + short aesGCMEncrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] data, + short dataStart, + short dataLen, + byte[] encData, + short encDataStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + /** + * This is a oneshot operation that performs decryption operation using AES GCM algorithm. It + * throws CryptoException if algorithm is not supported. + * + * @param aesKey is the buffer that contains 128 bit or 256 bit aes key used to encrypt. + * @param aesKeyStart is the start in aes key buffer. + * @param aesKeyLen is the length of aes key buffer in bytes (16 or 32 bytes). + * @param encData is the buffer of the input encrypted data. + * @param encDataStart is the start of the encrypted data buffer. + * @param encDataLen is the length of the data buffer. + * @param data is the buffer that contains output decrypted data. + * @param dataStart is the start of the data buffer. + * @param nonce is the buffer of nonce. + * @param nonceStart is the start of the nonce buffer. + * @param nonceLen is the length of the nonce buffer. + * @param authData is the authentication data buffer. + * @param authDataStart is the start of the authentication buffer. + * @param authDataLen is the length of the authentication buffer. + * @param authTag is the buffer to output authentication tag. + * @param authTagStart is the start of the buffer. + * @param authTagLen is the length of the buffer. + * @return true if the authentication is valid. + */ + boolean aesGCMDecrypt( + byte[] aesKey, + short aesKeyStart, + short aesKeyLen, + byte[] encData, + short encDataStart, + short encDataLen, + byte[] data, + short dataStart, + byte[] nonce, + short nonceStart, + short nonceLen, + byte[] authData, + short authDataStart, + short authDataLen, + byte[] authTag, + short authTagStart, + short authTagLen); + + /** + * This is a oneshot operation that performs key derivation function using cmac kdf (CKDF) as + * defined in android keymaster hal definition. + * + * @param hmacKey of pre-shared key. + * @param label is the label to be used for ckdf. + * @param labelStart is the start of label. + * @param labelLen is the length of the label. + * @param context is the context to be used for ckdf. + * @param contextStart is the start of the context + * @param contextLength is the length of the context + * @param key is the output buffer to return the derived key + * @param keyStart is the start of the output buffer. + * @return length of the derived key buffer in bytes. + */ + short cmacKDF( + KMPreSharedKey hmacKey, + byte[] label, + short labelStart, + short labelLen, + byte[] context, + short contextStart, + short contextLength, + byte[] key, + short keyStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. + * + * @param keyBuf is the buffer with hmac key. + * @param keyStart is the start of the buffer. + * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacSign( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + + /** + * This is a oneshot operation that signs the data using hmac algorithm. This is used to derive + * the key, which is used to encrypt the keyblob. + * + * @param masterkey of masterkey. + * @param data is the buffer containing data to be signed. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the output signature buffer + * @param signatureStart is the start of the signature + * @return length of the signature buffer in bytes. + */ + short hmacKDF( + KMMasterKey masterkey, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart); + + /** + * This is a oneshot operation that verifies the signature using hmac algorithm. + * + * @param keyBuf is the buffer with hmac key. + * @param keyStart is the start of the buffer. + * @param keyLength is the length of the buffer which will be in bytes from 8 to 64. + * @param data is the buffer containing data. + * @param dataStart is the start of the data. + * @param dataLength is the length of the data. + * @param signature is the signature buffer. + * @param signatureStart is the start of the signature buffer. + * @param signatureLen is the length of the signature buffer in bytes. + * @return true if the signature matches. + */ + boolean hmacVerify( + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] data, + short dataStart, + short dataLength, + byte[] signature, + short signatureStart, + short signatureLen); + + /** + * This is a oneshot operation that decrypts the data using RSA algorithm with oaep256 padding. + * The public exponent is always 0x010001. It throws CryptoException if OAEP encoding validation + * fails. + * + * @param privExp is the private exponent (2048 bit) buffer. + * @param privExpStart is the start of the private exponent buffer. + * @param privExpLength is the length of the private exponent buffer in bytes. + * @param modBuffer is the modulus (2048 bit) buffer. + * @param modOff is the start of the modulus buffer. + * @param modLength is the length of the modulus buffer in bytes. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the decrypted data. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short rsaDecipherOAEP256( + byte[] privExp, + short privExpStart, + short privExpLength, + byte[] modBuffer, + short modOff, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * This is a oneshot operation that signs the data using EC private key. + * + * @param ecPrivKey of KMAttestationKey. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the inpur data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the signature. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short ecSign256( + KMAttestationKey ecPrivKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * Implementation of HKDF as per RFC5869 https://datatracker.ietf.org/doc/html/rfc5869#section-2 + * + * @param ikm is the buffer containing input key material. + * @param ikmOff is the start of the input key. + * @param ikmLen is the length of the input key. + * @param salt is the buffer containing the salt. + * @param saltOff is the start of the salt buffer. + * @param saltLen is the length of the salt buffer. + * @param info is the buffer containing the application specific information + * @param infoOff is the start of the info buffer. + * @param infoLen is the length of the info buffer. + * @param out is the output buffer. + * @param outOff is the start of the output buffer. + * @param outLen is the length of the expected out buffer. + * @return Length of the out buffer which is outLen. + */ + short hkdf( + byte[] ikm, + short ikmOff, + short ikmLen, + byte[] salt, + short saltOff, + short saltLen, + byte[] info, + short infoOff, + short infoLen, + byte[] out, + short outOff, + short outLen); + + /** + * This function performs ECDH key agreement and generates a secret. + * + * @param privKey is the buffer containing the private key from first party. + * @param privKeyOff is the offset of the private key buffer. + * @param privKeyLen is the length of the private key buffer. + * @param publicKey is the buffer containing the public key from second party. + * @param publicKeyOff is the offset of the public key buffer. + * @param publicKeyLen is the length of the public key buffer. + * @param secret is the output buffer. + * @param secretOff is the offset of the output buffer. + * @return The length of the secret. + */ + short ecdhKeyAgreement( + byte[] privKey, + short privKeyOff, + short privKeyLen, + byte[] publicKey, + short publicKeyOff, + short publicKeyLen, + byte[] secret, + short secretOff); + + /** + * This is a oneshort operation that verifies the data using EC public key + * + * @param pubKey is the public key buffer. + * @param pubKeyOffset is the start of the public key buffer. + * @param pubKeyLen is the length of the public key. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param signatureDataBuf is the buffer the signature input data. + * @param signatureDataStart is the start of the signature input data. + * @param signatureDataLen is the length of the signature input data. + * @return true if verification is successful, otherwise false. + */ + boolean ecVerify256( + byte[] pubKey, + short pubKeyOffset, + short pubKeyLen, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] signatureDataBuf, + short signatureDataStart, + short signatureDataLen); + + /** + * This is a oneshot operation that signs the data using device unique key. + * + * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data. + * @param inputDataBuf is the buffer of the input data. + * @param inputDataStart is the start of the input data buffer. + * @param inputDataLength is the length of the input data buffer in bytes. + * @param outputDataBuf is the output buffer that contains the signature. + * @param outputDataStart is the start of the output data buffer. + * @return length of the decrypted data. + */ + short ecSign256( + KMDeviceUniqueKey ecPrivKey, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + short ecSign256(byte[] secret, short secretStart, short secretLength, + byte[] inputDataBuf, short inputDataStart, short inputDataLength, + byte[] outputDataBuf, short outputDataStart); + + short rsaSign256Pkcs1( + byte[] secret, + short secretStart, + short secretLength, + byte[] modBuf, + short modStart, + short modLength, + byte[] inputDataBuf, + short inputDataStart, + short inputDataLength, + byte[] outputDataBuf, + short outputDataStart); + + /** + * This creates a persistent operation for signing, verify, encryption and decryption using HMAC, + * AES and DES algorithms when keymaster hal's beginOperation function is executed. The + * KMOperation instance can be reclaimed by the seProvider when KMOperation is finished or + * aborted. It throws CryptoException if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be + * KMType.SIGN and KMType.VERIFY for HMAC algorithm + * @param alg is KMType.HMAC, KMType.AES or KMType.DES. + * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. + * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). + * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is + * 0. + * @param keyBuf is aes, des or hmac key buffer. + * @param keyStart is the start of the key buffer. + * @param keyLength is the length of the key buffer. + * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) + * @param ivStart is the start of the iv buffer. + * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES + * with ECB mode. + * @param macLength is the mac length in case of signing operation for hmac algorithm. + * @return KMOperation instance. + */ + KMOperation initSymmetricOperation( + byte purpose, + byte alg, + byte digest, + byte padding, + byte blockMode, + byte[] keyBuf, + short keyStart, + short keyLength, + byte[] ivBuf, + short ivStart, + short ivLength, + short macLength); + + /** + * This creates a persistent operation for signing, verify, encryption and decryption using RSA + * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public + * exponent is always 0x0100101. For EC the curve is always p256. The KMOperation instance can be + * reclaimed by the seProvider when KMOperation is finished or aborted. It throws CryptoException + * if algorithm is not supported. + * + * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for RSA. It will be * KMType.SIGN and + * KMType.VERIFY for RSA and EC algorithms. + * @param alg is KMType.RSA or KMType.EC algorithms. + * @param padding is KMType.PADDING_NONE or KMType.RSA_OAEP, KMType.RSA_PKCS1_1_5_ENCRYPT, + * KMType.RSA_PKCS1_1_5_SIGN or KMType.RSA_PSS. + * @param digest is KMType.DIGEST_NONE or KMType.SHA2_256. + * @param mgfDigest is the MGF digest. + * @param privKeyBuf is the private key in case of EC or private key exponent is case of RSA. + * @param privKeyStart is the start of the private key. + * @param privKeyLength is the length of the private key. + * @param pubModBuf is the modulus (in case of RSA) or public key (in case of EC). + * @param pubModStart is the start of the modulus. + * @param pubModLength is the length of the modulus. + * @return KMOperation instance that can be executed. + */ + KMOperation initAsymmetricOperation( + byte purpose, + byte alg, + byte padding, + byte digest, + byte mgfDigest, + byte[] privKeyBuf, + short privKeyStart, + short privKeyLength, + byte[] pubModBuf, + short pubModStart, + short pubModLength); + + /** + * This function tells if applet is upgrading or not. + * + * @return true if upgrading, otherwise false. + */ + boolean isUpgrading(); + + /** + * This function generates an AES Key of keySizeBits, which is used as an master key. This + * generated key is maintained by the SEProvider. This function should be called only once at the + * time of installation. + * + * @param keySizeBits key size in bits. + * @return An instance of KMMasterKey. + */ + KMMasterKey createMasterKey(short keySizeBits); + + /** + * Returns the master key. + * + * @return Instance of the KMMasterKey + */ + KMMasterKey getMasterKey(); + + /** + * Returns true if factory provisioned attestation key is supported. + */ + boolean isAttestationKeyProvisioned(); + + /** + * Returns algorithm type of the attestation key. It can be KMType.EC or KMType.RSA if the + * attestation key is provisioned in the factory. + */ + short getAttestationKeyAlgorithm(); + + /** + * Returns the preshared key. + * + * @return Instance of the KMPreSharedKey. + */ + KMPreSharedKey getPresharedKey(); + + /** + * Returns the value of the attestation id. + * + * @param tag - attestation id tag key as defined KMType. + * @param buffer - memorey buffer in which value of the id must be copied + * @param start - start offset in the buffer + * @return length - length of the returned attestation id value. + */ + short getAttestationId(short tag, byte[] buffer, short start); + + /** + * Delete the attestation ids permanently. + */ + void deleteAttestationIds(); + + /** + * 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); + + /** + * Creates an ECKey instance and sets the public and private keys to it. + * + * @param testMode to indicate if current execution is for test or production. + * @param pubKey buffer containing the public key. + * @param pubKeyOff public key buffer start offset. + * @param pubKeyLen public key buffer length. + * @param privKey buffer containing the private key. + * @param privKeyOff private key buffer start offset. + * @param privKeyLen private key buffer length. + * @return instance of KMDeviceUniqueKey. + */ + KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, + byte[] pubKey, short pubKeyOff, short pubKeyLen, + byte[] privKey, short privKeyOff, short privKeyLen); + + /** + * Returns the instance KMDeviceUnique if it is created. + * + * @param testMode Indicates if current execution is for test or production. + * @return instance of KMDeviceUniqueKey if present; null otherwise. + */ + KMDeviceUniqueKey getDeviceUniqueKey(boolean testMode); + + /** + * Persists the additional certificate chain in persistent memory. + * + * @param buf buffer containing the cbor encoded additional certificate chain. + * @param offset start offset of the buffer. + * @param len length of the buffer. + */ + void persistAdditionalCertChain(byte[] buf, short offset, short len); + + /** + * Returns the additional certificate chain length. + * + * @return length of the encoded additional certificate chain. + */ + short getAdditionalCertChainLength(); + + /** + * Returns the additional certificate chain. + * + * @return additional cert chain. + */ + byte[] getAdditionalCertChain(); + + + /** + * Returns the boot certificate chain. + * + * @return boot certificate chain. + */ + byte[] getBootCertificateChain(); + + public boolean isProvisionLocked(); + +} diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMType.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMType.java new file mode 100644 index 00000000..acae1362 --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMType.java @@ -0,0 +1,348 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.javacard.seprovider; + +/** + * This class declares all types, tag types, and tag keys. It also establishes basic structure of + * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also, + * KMType refers to transient memory heap in the repository. Finally KMType's subtypes are singleton + * prototype objects which just cast the structure over contiguous memory buffer. + */ +public abstract class KMType { + + public static final short INVALID_VALUE = (short) 0x8000; + 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; + // 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; + // 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/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java new file mode 100644 index 00000000..9bca1c8c --- /dev/null +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMUpgradable.java @@ -0,0 +1,30 @@ +/* + * Copyright(C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" (short)0IS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.javacard.seprovider; + +import org.globalplatform.upgrade.Element; + +public interface KMUpgradable { + + void onSave(Element ele); + + void onRestore(Element ele); + + short getBackupPrimitiveByteCount(); + + short getBackupObjectCount(); + +} diff --git a/Applet/README.md b/Applet/README.md index 9b66ce6f..ace69502 100644 --- a/Applet/README.md +++ b/Applet/README.md @@ -10,8 +10,6 @@ which mediates between Android Keystore and this applet. - Keymint 1.0 supported functions for required VTS compliance. - SharedSecret 1.0 supported functions for required VTS compliance. -# Exceptions - - Generate and Import key with client supplied attestation key is not yet supported. # 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/src/com/android/javacard/keymaster/KMBoolTag.java b/Applet/src/com/android/javacard/keymaster/KMBoolTag.java index dec737ad..d1e277a0 100644 --- a/Applet/src/com/android/javacard/keymaster/KMBoolTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMBoolTag.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index 63603f39..e68ecaa7 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java index fc2a3ecd..281ba543 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; diff --git a/Applet/src/com/android/javacard/keymaster/KMInteger.java b/Applet/src/com/android/javacard/keymaster/KMInteger.java index 73c88203..8ae8c8c9 100644 --- a/Applet/src/com/android/javacard/keymaster/KMInteger.java +++ b/Applet/src/com/android/javacard/keymaster/KMInteger.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index a6222638..21dd7f5c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 32554fb1..b8483275 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -16,7 +16,10 @@ package com.android.javacard.keymaster; -import com.android.javacard.rkp.RemotelyProvisionedComponentDevice; +import com.android.javacard.seprovider.KMAttestationCert; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMSEProvider; import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.AppletEvent; @@ -334,8 +337,7 @@ private short mapISOErrorToKMError(short reason) { return KMError.SW_WRONG_LENGTH; case ISO7816.SW_UNKNOWN: default: -// return KMError.UNKNOWN_ERROR; - return KMError.INVALID_NONCE; + return KMError.UNKNOWN_ERROR; } } @@ -352,8 +354,7 @@ private short mapCryptoErrorToKMError(short reason) { case CryptoException.UNINITIALIZED_KEY: return KMError.CRYPTO_UNINITIALIZED_KEY; default: - // return KMError.UNKNOWN_ERROR; - return KMError.INVALID_NONCE; + return KMError.UNKNOWN_ERROR; } } @@ -483,7 +484,7 @@ public void process(APDU apdu) { resetWrappingKey(); sendError(apdu, KMException.reason()); } catch (ISOException exp) { - // sendError(apdu, mapISOErrorToKMError(exp.getReason())); + sendError(apdu, mapISOErrorToKMError(exp.getReason())); freeOperations(); resetWrappingKey(); sendError(apdu, mapISOErrorToKMError(exp.getReason())); @@ -492,7 +493,7 @@ public void process(APDU apdu) { resetWrappingKey(); sendError(apdu, mapCryptoErrorToKMError(e.getReason())); } catch (Exception e) { -// sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); + sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); freeOperations(); resetWrappingKey(); sendError(apdu, KMError.GENERIC_UNKNOWN_ERROR); @@ -1192,7 +1193,7 @@ private void processFinishImportWrappedKeyCmd(APDU apdu){ private KMAttestationCert makeCommonCert(byte[] scratchPad) { short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); boolean rsaCert = KMEnumTag.cast(alg).getValue() == KMType.RSA; - KMAttestationCert cert = seProvider.getAttestationCert(rsaCert); + KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider); short subject = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]); @@ -2100,16 +2101,10 @@ private void processUpdateOperationCmd(APDU apdu) { short len = KMByteBlob.cast(data[INPUT_DATA]).length(); short additionalExpOutLen = 0; if (op.getAlgorithm() == KMType.AES) { - if (op.getBlockMode() == KMType.GCM) { + if (op.getBlockMode() == KMType.GCM || op.getBlockMode() == KMType.CTR) { if(op.isAesGcmUpdateAllowed()){ op.setAesGcmUpdateComplete(); } - // if input data present then it should be block aligned. - if (len > 0) { - if (len % AES_BLOCK_SIZE != 0) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - } additionalExpOutLen = 16; } else { // input data must be block aligned. @@ -2331,11 +2326,12 @@ private void processBeginOperationCmd(APDU apdu) { } short params = KMKeyParameters.instance(iv); - short resp = KMArray.instance((short) 4); + short resp = KMArray.instance((short) 5); KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(resp).add((short) 1, params); KMArray.cast(resp).add((short) 2, data[OP_HANDLE]); KMArray.cast(resp).add((short) 3, KMInteger.uint_8(op.getBufferingMode())); + KMArray.cast(resp).add((short) 4, KMInteger.uint_16((short) (op.getMacLength() / 8))); sendOutgoing(apdu, resp); } @@ -3144,6 +3140,12 @@ private void importTDESKey(byte[] scratchPad) { 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]; @@ -3153,12 +3155,12 @@ private void importAESKey(byte[] scratchPad) { short keysize = KMIntegerTag.getShortValue(KMType.UINT_TAG, KMType.KEYSIZE, data[KEY_PARAMETERS]); if (keysize != KMType.INVALID_VALUE) { - if (keysize != 128 && keysize != 256) { - KMException.throwIt(KMError.UNSUPPORTED_KEY_SIZE); - } + validateAesKeySize(keysize); } else { // add the key size to scratchPad - keysize = KMInteger.uint_16(KMByteBlob.cast(data[SECRET]).length()); + keysize = (short) ( 8 * KMByteBlob.cast(data[SECRET]).length()); + validateAesKeySize(keysize); + keysize = KMInteger.uint_16(keysize); short keysizeTag = KMIntegerTag.instance(KMType.UINT_TAG, KMType.KEYSIZE, keysize); Util.setShort(scratchPad, index, keysizeTag); index += 2; @@ -4034,6 +4036,8 @@ private void add(byte[] buf, short op1, short op2, short result) { public void powerReset() { //TODO handle power reset signal. + releaseAllOperations(); + resetWrappingKey(); } public static void generateRkpKey(byte[] scratchPad, short keyParams) { @@ -4100,7 +4104,7 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET); ptr2 = decoder.decode(coseKeyExp, KMByteBlob.cast(ptr2).getBuffer(), KMByteBlob.cast(ptr2).getStartOff(), KMByteBlob.cast(ptr2).length()); - if (index == (short) (len - 1)) { + if ((index == (short) (len - 1)) && len > 1) { alg = expLeafCertAlg; } if (!KMCoseKey.cast(ptr2).isDataValid(KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, alg, @@ -4148,4 +4152,89 @@ public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, } return prevCoseKey; } + + + public static short generateBcc(boolean testMode, byte[] scratchPad) { + if (!testMode && seProvider.isProvisionLocked()) { + KMException.throwIt(KMError.STATUS_FAILED); + } + KMDeviceUniqueKey deviceUniqueKey = seProvider.getDeviceUniqueKey(testMode); + short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0); + short coseKey = + KMCose.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 = KMKeymasterApplet.encodeToApduBuffer(coseKey, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + // Construct payload. + short payload = + KMCose.constructCoseCertPayload( + KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.ISSUER), + KMTextString.instance(KMCose.TEST_ISSUER_NAME, (short) 0, + (short) KMCose.TEST_ISSUER_NAME.length)), + KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.SUBJECT), + KMTextString.instance(KMCose.TEST_SUBJECT_NAME, (short) 0, + (short) KMCose.TEST_SUBJECT_NAME.length)), + KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0), + KMByteBlob.instance(scratchPad, (short) 0, temp)), + KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0), + KMByteBlob.instance(KMCose.KEY_USAGE_SIGN, (short) 0, + (short) KMCose.KEY_USAGE_SIGN.length)) + ); + // temp temporarily holds the length of encoded cert payload. + temp = KMKeymasterApplet.encodeToApduBuffer(payload, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + payload = KMByteBlob.instance(scratchPad, (short) 0, temp); + + // protected header + short protectedHeader = + KMCose.constructHeaders(KMNInteger.uint_8(KMCose.COSE_ALG_ES256), KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE); + // temp temporarily holds the length of encoded headers. + temp = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp); + + //unprotected headers. + short arr = KMArray.instance((short) 0); + short unprotectedHeader = KMCoseHeaders.instance(arr); + + // construct cose sign structure. + short coseSignStructure = + KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), payload); + // temp temporarily holds the length of encoded sign structure. + // Encode cose Sign_Structure. + temp = KMKeymasterApplet.encodeToApduBuffer(coseSignStructure, scratchPad, (short) 0, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + // do sign + short len = + seProvider.ecSign256( + deviceUniqueKey, + scratchPad, + (short) 0, + temp, + scratchPad, + temp + ); + coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); + + // construct cose_sign1 + short coseSign1 = + KMCose.constructCoseSign1(protectedHeader, unprotectedHeader, payload, coseSignStructure); + + // [Cose_Key, Cose_Sign1] + short bcc = KMArray.instance((short) 2); + KMArray.cast(bcc).add((short) 0, coseKey); + KMArray.cast(bcc).add((short) 1, coseSign1); + return bcc; + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMOperationState.java b/Applet/src/com/android/javacard/keymaster/KMOperationState.java index 5bd532c8..df2bcbb3 100644 --- a/Applet/src/com/android/javacard/keymaster/KMOperationState.java +++ b/Applet/src/com/android/javacard/keymaster/KMOperationState.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMOperation; import javacard.framework.JCSystem; import javacard.framework.Util; @@ -336,15 +337,33 @@ 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; } - if(alg == KMType.AES || alg == KMType.DES){ - return KMType.BUF_BLOCK_ALIGN; + + switch(alg) { + case KMType.AES: + if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN; + } else if (purpose == KMType.DECRYPT && blockMode == KMType.GCM) { + return KMType.BUF_AES_GCM_DECRYPT_BLOCK_ALIGN; + } else if (blockMode == KMType.CBC || blockMode == KMType.ECB) { + return KMType.BUF_AES_BLOCK_ALIGN; + } + break; + case KMType.DES: + if (purpose == KMType.DECRYPT && padding == KMType.PKCS7) { + return KMType.BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN; + } else { + return KMType.BUF_DES_BLOCK_ALIGN; + } } return KMType.BUF_NONE; } diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java index efc7d66d..eb860e2d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java @@ -1,5 +1,6 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.Util; public class KMPKCS8Decoder { diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 1be6c37d..39340daf 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -16,6 +16,8 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMUpgradable; import org.globalplatform.upgrade.Element; import javacard.framework.ISO7816; diff --git a/Applet/src/com/android/javacard/keymaster/KMTag.java b/Applet/src/com/android/javacard/keymaster/KMTag.java index 8ef6136e..0cac460e 100644 --- a/Applet/src/com/android/javacard/keymaster/KMTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMTag.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import com.android.javacard.seprovider.KMException; import javacard.framework.Util; /** diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index a1352c78..499c7636 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -349,7 +349,11 @@ public abstract class KMType { 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; + public static final byte BUF_AES_BLOCK_ALIGN = 3; + public static final byte BUF_AES_PKCS7_DECRYPT_BLOCK_ALIGN = 4; + public static final byte BUF_DES_BLOCK_ALIGN = 5; + public static final byte BUF_DES_PKCS7_DECRYPT_BLOCK_ALIGN = 6; + public static final byte BUF_AES_GCM_DECRYPT_BLOCK_ALIGN = 7; protected static KMRepository repository; protected static byte[] heap; diff --git a/Applet/src/com/android/javacard/rkp/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java similarity index 96% rename from Applet/src/com/android/javacard/rkp/RemotelyProvisionedComponentDevice.java rename to Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 3b2efb90..adc1684a 100644 --- a/Applet/src/com/android/javacard/rkp/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -13,10 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.javacard.rkp; +package com.android.javacard.keymaster; -import com.android.javacard.keymaster.*; -import javacard.framework.*; +import com.android.javacard.seprovider.KMDeviceUniqueKey; +import com.android.javacard.seprovider.KMException; +import com.android.javacard.seprovider.KMOperation; +import com.android.javacard.seprovider.KMSEProvider; + +import javacard.framework.APDU; +import javacard.framework.ISO7816; +import javacard.framework.ISOException; +import javacard.framework.JCSystem; +import javacard.framework.Util; /* * This class handles the remote key provisioning. Generates an RKP key and generates a certificate signing @@ -66,6 +74,8 @@ public class RemotelyProvisionedComponentDevice { {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E}; public static final byte[] SECURITY_LEVEL = {0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C}; + public static final byte[] ATTEST_ID_STATE = + {0x61, 0x74, 0x74, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65}; // Verified boot state values public static final byte[] VB_STATE_GREEN = {0x67, 0x72, 0x65, 0x65, 0x6E}; public static final byte[] VB_STATE_YELLOW = {0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77}; @@ -78,6 +88,8 @@ public class RemotelyProvisionedComponentDevice { public static final byte DI_SCHEMA_VERSION = 1; public static final byte[] DI_SECURITY_LEVEL = {0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, 0x78}; + public static final byte[] ATTEST_ID_LOCKED = {0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64}; + public static final byte[] ATTEST_ID_OPEN = {0x6f, 0x70, 0x65, 0x6e}; private static final short MAX_SEND_DATA = 1024; // more data or no data private static final byte MORE_DATA = 0x01; // flag to denote more data to retrieve @@ -245,7 +257,7 @@ public void processGenerateRkpKey(APDU apdu) { KMKeymasterApplet.sendOutgoing(apdu, arr); } - public void processBeginSendData(APDU apdu) { + public void processBeginSendData(APDU apdu) throws Exception { try { initializeDataTable(); short arr = KMArray.instance((short) 3); @@ -285,7 +297,7 @@ public void processBeginSendData(APDU apdu) { } } - public void processUpdateKey(APDU apdu) { + public void processUpdateKey(APDU apdu) throws Exception { try { // The prior state can be BEGIN or UPDATE validateState((byte) (BEGIN | UPDATE)); @@ -328,7 +340,7 @@ public void processUpdateKey(APDU apdu) { } } - public void processUpdateEekChain(APDU apdu) { + public void processUpdateEekChain(APDU apdu) throws Exception { try { // The prior state can be BEGIN or UPDATE validateState((byte) (BEGIN | UPDATE)); @@ -371,7 +383,7 @@ public void processUpdateEekChain(APDU apdu) { } } - public void processUpdateChallenge(APDU apdu) { + public void processUpdateChallenge(APDU apdu) throws Exception { try { // The prior state can be BEGIN or UPDATE validateState((byte) (BEGIN | UPDATE)); @@ -400,7 +412,7 @@ public void processUpdateChallenge(APDU apdu) { // 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) { + public void processFinishSendData(APDU apdu) throws Exception { try { // The prior state should be UPDATE. validateState(UPDATE); @@ -410,8 +422,9 @@ public void processFinishSendData(APDU apdu) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } // PubKeysToSignMac + byte[] empty = {}; short len = - ((KMOperation) operation[0]).sign(null, (short) 0, + ((KMOperation) operation[0]).sign(empty, (short) 0, (short) 0, scratchPad, (short) 0); // release operation releaseOperation(); @@ -451,7 +464,7 @@ public void processFinishSendData(APDU apdu) { } } - public void processGetResponse(APDU apdu) { + public void processGetResponse(APDU apdu) throws Exception { try { // The prior state should be FINISH. validateState((byte) (FINISH | GET_RESPONSE)); @@ -496,7 +509,7 @@ public void processGetResponse(APDU apdu) { } } - public void process(short ins, APDU apdu) { + public void process(short ins, APDU apdu) throws Exception { switch (ins) { case KMKeymasterApplet.INS_GET_RKP_HARDWARE_INFO: processGetRkpHwInfoCmd(apdu); @@ -857,14 +870,23 @@ private KMDeviceUniqueKey createDeviceUniqueKey(boolean testMode, byte[] scratch /** * 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, // The - * CDDL schema version. "security_level" : "tee" / "strongbox" } + * 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. @@ -883,6 +905,7 @@ private short createDeviceInfo(byte[] scratchpad) { KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, + KMType.INVALID_VALUE, KMType.INVALID_VALUE, }; short[] out = {0/* index */, 0 /* length */}; updateItem(deviceIds, out, BRAND, getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad)); @@ -903,6 +926,7 @@ private short createDeviceInfo(byte[] 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; @@ -1220,13 +1244,11 @@ private short processBcc(byte[] scratchPad) { boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; short len; if (testMode) { - short bcc = seProvider.generateBcc(true, scratchPad); + short bcc = KMKeymasterApplet.generateBcc(true, scratchPad); len = KMKeymasterApplet .encodeToApduBuffer(bcc, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); } else { - //len = seProvider.getBootCertificateChainLength(); byte[] bcc = seProvider.getBootCertificateChain(); - //len = seProvider.readBootCertificateChain(scratchPad, (short) 0); len = Util.getShort(bcc, (short) 0); Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len); } diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp index 43606534..402504b3 100644 --- a/HAL/JavacardKeyMintDevice.cpp +++ b/HAL/JavacardKeyMintDevice.cpp @@ -296,16 +296,18 @@ ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector // return the result uint64_t opHandle; uint8_t bufMode; + uint16_t macLength; if (!cbor_.getKeyParameters(item, 1, result->params) || !cbor_.getUint64(item, 2, opHandle) || - !cbor_.getUint64(item, 3, bufMode)) { + !cbor_.getUint64(item, 3, bufMode) || + !cbor_.getUint64(item, 4, macLength)) { LOG(ERROR) << "Error in decoding the response in begin."; return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); } result->challenge = opHandle; result->operation = ndk::SharedRefBase::make( static_cast(opHandle), static_cast(bufMode), - card_); + macLength, card_); return ScopedAStatus::ok(); } diff --git a/HAL/JavacardKeyMintOperation.cpp b/HAL/JavacardKeyMintOperation.cpp index 20e02297..d4952aa0 100644 --- a/HAL/JavacardKeyMintOperation.cpp +++ b/HAL/JavacardKeyMintOperation.cpp @@ -104,12 +104,9 @@ ScopedAStatus JavacardKeyMintOperation::abort() { return km_utils::kmError2ScopedAStatus(err); } -void JavacardKeyMintOperation::blockAlign(DataView& view, short blockSize) { +void JavacardKeyMintOperation::blockAlign(DataView& view, uint16_t blockSize) { appendBufferedData(view); - short offset = ((view.length / blockSize) - 1) * blockSize; - if (offset <= 0) { - offset = 0; - } + uint16_t offset = getDataViewOffset(view, blockSize); if (view.buffer.empty() && view.data.empty()) { offset = 0; } else if (view.buffer.empty()) { @@ -129,6 +126,33 @@ void JavacardKeyMintOperation::blockAlign(DataView& view, short blockSize) { view.length = view.length - buffer_.size(); } +uint16_t JavacardKeyMintOperation::getDataViewOffset(DataView& view, uint16_t blockSize) { + uint16_t offset = 0; + uint16_t remaining = 0; + switch(bufferingMode_) { + case BufferingMode::BUF_AES_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + break; + case BufferingMode::BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + offset = ((view.length / blockSize)) * blockSize; + remaining = (view.length % blockSize); + if (offset >= blockSize && remaining == 0) { + offset -= 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 JavacardKeyMintOperation::bufferData(DataView& view) { if (view.data.empty()) return KM_ERROR_OK; // nothing to buffer switch (bufferingMode_) { @@ -152,8 +176,16 @@ keymaster_error_t JavacardKeyMintOperation::bufferData(DataView& view) { view.start = 0; view.length = 0; break; - case BufferingMode::BLOCK_ALIGNED: - blockAlign(view, BLOCK_SIZE); + case BufferingMode::BUF_AES_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_BLOCK_ALIGNED: + case BufferingMode::BUF_DES_DECRYPT_PKCS7_BLOCK_ALIGNED: + blockAlign(view, DES_BLOCK_SIZE); break; case BufferingMode::NONE: break; diff --git a/HAL/JavacardKeyMintOperation.h b/HAL/JavacardKeyMintOperation.h index 0e04b778..4f5fc046 100644 --- a/HAL/JavacardKeyMintOperation.h +++ b/HAL/JavacardKeyMintOperation.h @@ -24,7 +24,8 @@ #include #include -#define BLOCK_SIZE 16 +#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 @@ -45,7 +46,12 @@ enum class BufferingMode : int32_t { // 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. - BLOCK_ALIGNED = 3, // Buffer the atlest 16 bytes and reminder to make input data block aligned. + BUF_AES_BLOCK_ALIGNED = 3, // Buffer 15 bytes and reminder to make input data block aligned. + BUF_AES_DECRYPT_PKCS7_BLOCK_ALIGNED = 4, // Buffer 16 bytes. + BUF_DES_BLOCK_ALIGNED = 5, // Buffer 7 bytes and reminder to make input data block aligned. + 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. @@ -61,9 +67,10 @@ class JavacardKeyMintOperation : public BnKeyMintOperation { public: explicit JavacardKeyMintOperation(keymaster_operation_handle_t opHandle, BufferingMode bufferingMode, + uint16_t macLength, shared_ptr card) - : buffer_(vector()), bufferingMode_(bufferingMode), card_(card), - opHandle_(opHandle) {} + : buffer_(vector()), bufferingMode_(bufferingMode), macLength_(macLength), + card_(card), opHandle_(opHandle) {} virtual ~JavacardKeyMintOperation(); ScopedAStatus updateAad(const vector& input, @@ -109,10 +116,12 @@ class JavacardKeyMintOperation : public BnKeyMintOperation { std::tuple, keymaster_error_t> sendRequest(Instruction ins, Array& request); keymaster_error_t bufferData(DataView& data); - void blockAlign(DataView& data, short blockSize); + void blockAlign(DataView& data, uint16_t blockSize); + uint16_t getDataViewOffset(DataView& view, uint16_t blockSize); vector buffer_; BufferingMode bufferingMode_; + uint16_t macLength_; const shared_ptr card_; keymaster_operation_handle_t opHandle_; CborConverter cbor_; diff --git a/HAL/keymint_utils.cpp b/HAL/keymint_utils.cpp index c19c5fd7..a98de129 100644 --- a/HAL/keymint_utils.cpp +++ b/HAL/keymint_utils.cpp @@ -31,10 +31,11 @@ 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 char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$"; constexpr size_t kYearMatch = 1; constexpr size_t kMonthMatch = 2; -constexpr size_t kPatchlevelMatchCount = kMonthMatch + 1; +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; @@ -46,14 +47,12 @@ uint32_t match_to_uint32(const char* expression, const regmatch_t& match) { std::string wait_and_get_property(const char* prop) { std::string prop_value; - // while (!::android::base::WaitForPropertyCreation(prop)) + while (!::android::base::WaitForPropertyCreation(prop)) ; prop_value = ::android::base::GetProperty(prop, "" /* default */); return prop_value; } -} // anonymous namespace - uint32_t getOsVersion(const char* version_str) { regex_t regex; if (regcomp(®ex, kPlatformVersionRegex, REG_EXTENDED)) { @@ -75,12 +74,9 @@ uint32_t getOsVersion(const char* version_str) { return (major * 100 + minor) * 100 + subminor; } -uint32_t getOsVersion() { - std::string version = wait_and_get_property(kPlatformVersionProp); - return getOsVersion(version.c_str()); -} +enum class PatchlevelOutput { kYearMonthDay, kYearMonth }; -uint32_t getPatchlevel(const char* patchlevel_str) { +uint32_t getPatchlevel(const char* patchlevel_str, PatchlevelOutput detail) { regex_t regex; if (regcomp(®ex, kPatchlevelRegex, REG_EXTENDED) != 0) { return 0; @@ -99,17 +95,35 @@ uint32_t getPatchlevel(const char* patchlevel_str) { if (month < 1 || month > 12) { return 0; } - return year * 100 + month; + + 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 + +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()); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonth); } uint32_t getVendorPatchlevel() { std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp); - return getPatchlevel(patchlevel.c_str()); + return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonthDay); } } // namespace keymint::javacard diff --git a/README.md b/README.md index d87ad351..55d34d74 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,5 @@ 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) -**Exceptions** -1) [Remote Key Provisiong HAL](https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl) -is not supported in the release. diff --git a/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch b/aosp_integration_patches_aosp_12_r15/device_google_cuttlefish.patch new file mode 100644 index 00000000..b0fca48f --- /dev/null +++ b/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/aosp_integration_patches_aosp_12_r15/hardware_interfaces.patch b/aosp_integration_patches_aosp_12_r15/hardware_interfaces.patch new file mode 100644 index 00000000..bf456260 --- /dev/null +++ b/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/aosp_integration_patches_aosp_12_r15/system_keymaster.patch b/aosp_integration_patches_aosp_12_r15/system_keymaster.patch new file mode 100644 index 00000000..b994b768 --- /dev/null +++ b/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/aosp_integration_patches_aosp_12_r15/system_security.patch b/aosp_integration_patches_aosp_12_r15/system_security.patch new file mode 100644 index 00000000..22956d5e --- /dev/null +++ b/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/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch b/aosp_integration_patches_aosp_12_r15/system_sepolicy.patch new file mode 100644 index 00000000..f533e8c7 --- /dev/null +++ b/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