diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 3a3d4983..d024fb83 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -288,6 +288,9 @@ public void setAttestationIds(short attIdVals) { KMException.throwIt(KMError.INVALID_ARGUMENT); } obj = KMByteTag.cast(obj).getValue(); + if (KMByteBlob.cast(obj).length() > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) { + KMException.throwIt(KMError.INVALID_INPUT_LENGTH); + } kmDataStore.setAttestationId(key, KMByteBlob.cast(obj).getBuffer(), KMByteBlob.cast(obj).getStartOff(), KMByteBlob.cast(obj).length()); index++; diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index a5e4953b..0da291eb 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -110,7 +110,7 @@ public class KMAttestationCertImpl implements KMAttestationCert { 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.UNLOCKED_DEVICE_REQUIRED, + KMType.ORIGIN, KMType.UNLOCKED_DEVICE_REQUIRED, KMType.TRUSTED_CONFIRMATION_REQUIRED, KMType.AUTH_TIMEOUT, KMType.USER_AUTH_TYPE, KMType.NO_AUTH_REQUIRED, KMType.EARLY_BOOT_ONLY, diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java index 3eb4a2e4..b706d5ed 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -20,4 +20,7 @@ public class KMConfigurations { public static final byte LITTLE_ENDIAN = 0x00; public static final byte BIG_ENDIAN = 0x01; public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN; + // If the size of the attestation ids is known and lesser than 64 + // then reduce the size here. It reduces the heap memory usage. + public static final byte MAX_ATTESTATION_IDS_SIZE = 64; } diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java index 3eb4a2e4..b706d5ed 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMConfigurations.java @@ -20,4 +20,7 @@ public class KMConfigurations { public static final byte LITTLE_ENDIAN = 0x00; public static final byte BIG_ENDIAN = 0x01; public static final byte TEE_MACHINE_TYPE = LITTLE_ENDIAN; + // If the size of the attestation ids is known and lesser than 64 + // then reduce the size here. It reduces the heap memory usage. + public static final byte MAX_ATTESTATION_IDS_SIZE = 64; } diff --git a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java b/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java similarity index 66% rename from Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java rename to Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java index 3040d480..bb0c82ba 100644 --- a/Applet/src/com/android/javacard/keymaster/KMPKCS8Decoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMAsn1Parser.java @@ -3,13 +3,22 @@ import com.android.javacard.seprovider.KMException; import javacard.framework.Util; -public class KMPKCS8Decoder { +public class KMAsn1Parser { public static final byte ASN1_OCTET_STRING= 0x04; public static final byte ASN1_SEQUENCE= 0x30; + public static final byte ASN1_SET= 0x31; public static final byte ASN1_INTEGER= 0x02; + public static final byte OBJECT_IDENTIFIER = 0x06; public static final byte ASN1_A0_TAG = (byte) 0xA0; public static final byte ASN1_A1_TAG = (byte) 0xA1; public static final byte ASN1_BIT_STRING = 0x03; + + public static final byte ASN1_UTF8_STRING = 0x0C; + public static final byte ASN1_TELETEX_STRING = 0x14; + public static final byte ASN1_PRINTABLE_STRING = 0x13; + public static final byte ASN1_UNIVERSAL_STRING = 0x1C; + public static final byte ASN1_BMP_STRING = 0x1E; + public static final byte[] EC_CURVE = { 0x06,0x08,0x2a,(byte)0x86,0x48,(byte)0xce,0x3d,0x03, 0x01,0x07 @@ -23,12 +32,55 @@ public class KMPKCS8Decoder { 0x3d,0x02,0x01,0x06,0x08,0x2a,(byte)0x86,0x48, (byte)0xce,0x3d,0x03,0x01,0x07 }; + + //https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 21 + // 2.5.4 + public byte[] COMMON_OID = new byte[] { + 0x06, 0x03, 0x55, 0x04 + }; + // This array contains the last byte of OID for each oid type. + // The first 4 bytes are common as shown above in COMMON_OID + private static final byte[] attributeOIds = { + 0x03, /* commonName COMMON_OID.3 */ + 0x04, /* surName COMMON_OID.4*/ + 0x05, /* serialNumber COMMON_OID.5 */ + 0x06, /* countryName COMMON_OID.6 */ + 0x07, /* locality COMMON_OID.7 */ + 0x08, /* stateOrProviince COMMON_OID.8 */ + 0x0A, /* organizationName COMMON_OID.10 */ + 0x0B, /* organizationalUnitName COMMON_OID.11 */ + 0x0C, /* title COMMON_OID.10 */ + 0x2A, /* givenName COMMON_OID.42 */ + 0x2B, /* initials COMMON_OID.43 */ + 0x2C, /* generationQualifier COMMON_OID.44 */ + 0x2E, /* dnQualifer COMMON_OID.46 */ + 0x41, /* pseudonym COMMON_OID.65 */ + }; + // https://datatracker.ietf.org/doc/html/rfc5280, RFC 5280, Page 124 + // TODO Specification does not mention about the DN_QUALIFIER_OID max length. + // So the max limit is set at 64. + private static final byte[] attributeValueMaxLen = { + 0x40, /* 1-64 commonName */ + 0x28, /* 1-40 surname */ + 0x40, /* 1-64 serial */ + 0x02, /* 1-2 country */ + (byte) 0x80, /* 1-128 locality */ + (byte) 0x80, /* 1-128 state */ + 0x40, /* 1-64 organization */ + 0x40, /* 1-64 organization unit*/ + 0x40, /* 1-64 title */ + 0x10, /* 1-16 givenName */ + 0x05, /* 1-5 initials */ + 0x03, /* 1-3 gen qualifier */ + 0x40, /* 1-64 dn-qualifier */ + (byte) 0x80 /* 1-128 pseudonym */ + }; private byte[] data; private short start; private short length; private short cur; - private static KMPKCS8Decoder inst; - private KMPKCS8Decoder(){ + private static KMAsn1Parser inst; + private KMAsn1Parser(){ start = 0; length = 0; cur = 0; @@ -45,6 +97,18 @@ public short decodeEc(short blob){ decodeCommon((short)0, EC_ALGORITHM); return decodeEcPrivateKey((short)1); } + + public void validateDerSubject(short blob) { + init(blob); + header(ASN1_SEQUENCE); + while (cur < ((short) (start + length))) { + header(ASN1_SET); + header(ASN1_SEQUENCE); + // Parse and validate OBJECT-IDENTIFIER and Value fields + // Cursor is incremented in validateAttributeTypeAndValue. + validateAttributeTypeAndValue(); + } + } public short decodeEcSubjectPublicKeyInfo(short blob) { init(blob); @@ -183,6 +247,46 @@ private void validateTag0IfPresent(){ if(Util.arrayCompare(data, cur, EC_CURVE, (short)0, len) != 0) KMException.throwIt(KMError.UNKNOWN_ERROR); incrementCursor(len); } + + private void validateAttributeTypeAndValue() { + short start = cur; + if (getByte() != OBJECT_IDENTIFIER) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + short length = getLength(); + if (length != 3) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + cur = start; + boolean found = false; + for(short i = 0; i < (short) attributeOIds.length; i++) { + if ((Util.arrayCompare(data, cur, COMMON_OID, (short)0, (short) COMMON_OID.length) == 0) && + (attributeOIds[i] == data[(short)(cur + COMMON_OID.length)])) { + incrementCursor((short) (COMMON_OID.length + 1)); + // Validate the length of the attribute value. + short tag = getByte(); + if(tag != ASN1_UTF8_STRING && + tag != ASN1_TELETEX_STRING && + tag != ASN1_PRINTABLE_STRING && + tag != ASN1_UNIVERSAL_STRING && + tag != ASN1_BMP_STRING) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + length = getLength(); + if (length <= 0 && length > attributeValueMaxLen[i]) { + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + incrementCursor(length); + found = true; + break; + } + } + if (!found) { + // None of the attributes matches. + KMException.throwIt(KMError.UNKNOWN_ERROR); + } + } + private short header(short tag){ short t = getByte(); if(t != tag) KMException.throwIt(KMError.UNKNOWN_ERROR); @@ -222,9 +326,9 @@ private short getLength(){ else KMException.throwIt(KMError.UNKNOWN_ERROR); return KMType.INVALID_VALUE; //should not come here } - public static KMPKCS8Decoder instance() { + public static KMAsn1Parser instance() { if (inst == null) { - inst = new KMPKCS8Decoder(); + inst = new KMAsn1Parser(); } return inst; } diff --git a/Applet/src/com/android/javacard/keymaster/KMByteTag.java b/Applet/src/com/android/javacard/keymaster/KMByteTag.java index b7b8decd..70fa8959 100644 --- a/Applet/src/com/android/javacard/keymaster/KMByteTag.java +++ b/Applet/src/com/android/javacard/keymaster/KMByteTag.java @@ -98,23 +98,19 @@ public short length() { private static boolean validateKey(short key, short byteBlob) { short valueLen = KMByteBlob.cast(byteBlob).length(); switch (key) { - case ROOT_OF_TRUST: - case UNIQUE_ID: case ATTESTATION_APPLICATION_ID: - case ATTESTATION_ID_BRAND: - case ATTESTATION_ID_DEVICE: - case ATTESTATION_ID_PRODUCT: - case ATTESTATION_ID_SERIAL: - case ATTESTATION_ID_IMEI: - case ATTESTATION_ID_MEID: - case ATTESTATION_ID_MANUFACTURER: - case ATTESTATION_ID_MODEL: - case ASSOCIATED_DATA: - case NONCE: - case CONFIRMATION_TOKEN: - case VERIFIED_BOOT_KEY: - case VERIFIED_BOOT_HASH: + if (valueLen > MAX_ATTESTATION_APP_ID_SIZE) { + return false; + } + break; case CERTIFICATE_SUBJECT_NAME: + { + if (valueLen > MAX_SUBJECT_DER_LEN) { + return false; + } + KMAsn1Parser asn1Decoder = KMAsn1Parser.instance(); + asn1Decoder.validateDerSubject(byteBlob); + } break; case APPLICATION_ID: case APPLICATION_DATA: @@ -127,6 +123,21 @@ private static boolean validateKey(short key, short byteBlob) { return false; } break; + case ATTESTATION_ID_BRAND: + case ATTESTATION_ID_DEVICE: + case ATTESTATION_ID_PRODUCT: + case ATTESTATION_ID_SERIAL: + case ATTESTATION_ID_IMEI: + case ATTESTATION_ID_MEID: + case ATTESTATION_ID_MANUFACTURER: + case ATTESTATION_ID_MODEL: + if (valueLen > KMConfigurations.MAX_ATTESTATION_IDS_SIZE) { + return false; + } + break; + case ROOT_OF_TRUST: + case NONCE: + break; default: return false; } diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseMap.java b/Applet/src/com/android/javacard/keymaster/KMCoseMap.java index ceb67fb2..2f57be40 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCoseMap.java +++ b/Applet/src/com/android/javacard/keymaster/KMCoseMap.java @@ -127,8 +127,8 @@ private static boolean compareAndSwap(short ptr, short index) { firstKey = KMMap.cast(ptr).getKey(index); secondKey = KMMap.cast(ptr).getKey((short) (index + 1)); } - firstKeyLen = KMKeymasterApplet.encoder.encode(firstKey, scratchpad, (short) 0); - secondKeyLen = KMKeymasterApplet.encoder.encode(secondKey, scratchpad, firstKeyLen); + firstKeyLen = KMKeymasterApplet.encoder.encode(firstKey, scratchpad, (short) 0, (short) scratchpad.length); + secondKeyLen = KMKeymasterApplet.encoder.encode(secondKey, scratchpad, firstKeyLen, (short) scratchpad.length); if ((firstKeyLen > secondKeyLen) || ((firstKeyLen == secondKeyLen) && (0 < Util.arrayCompare(scratchpad, (short) 0, scratchpad, firstKeyLen, firstKeyLen)))) { diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index ac17b9eb..e3c7bc45 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -363,10 +363,13 @@ private short decodeKeyParam(short exp) { obj = decode(tagClass); KMArray.cast(vals).add(arrPos++, obj); break; - }catch(KMException e){ - if(KMException.reason() == KMError.INVALID_TAG && - !ignoreInvalidTags){ - KMException.throwIt(KMError.INVALID_TAG); + } catch(KMException e){ + if (KMException.reason() == KMError.INVALID_TAG) { + if(!ignoreInvalidTags){ + KMException.throwIt(KMError.INVALID_TAG); + } + }else { + KMException.throwIt(KMException.reason()); } break; } diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java index e47d799d..25158a33 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -77,70 +77,56 @@ private void encode(short obj) { push(obj); } - public short encode(short object, byte[] buffer, short startOff) { + /** + * This functions encodes the given object into the provider buffer space + * in cbor format. + * + * @param object Object to be encoded into cbor data. + * @param buffer Output where cbor data is copied. + * @param startOff is the start offset of the buffer. + * @param bufLen length of the buffer + * @param encoderOutLimitLen excepted encoded output length. + * @return length of the encoded buffer. + */ + public short encode(short object, byte[] buffer, short startOff, short bufLen, + short encoderOutLimitLen) { scratchBuf[STACK_PTR_OFFSET] = 0; bufferRef[0] = buffer; scratchBuf[START_OFFSET] = startOff; - short len = (short) (buffer.length - startOff); - if ((len < 0) || len > KMRepository.HEAP_SIZE) { - scratchBuf[LEN_OFFSET] = KMRepository.HEAP_SIZE; - } else { - scratchBuf[LEN_OFFSET] = (short) buffer.length; + if ((short) (startOff + encoderOutLimitLen) > bufLen) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - //this.length = (short)(startOff + length); + scratchBuf[LEN_OFFSET] = (short) (startOff + encoderOutLimitLen); push(object); encode(); return (short) (scratchBuf[START_OFFSET] - startOff); } - // array{KMError.OK,Array{KMByteBlobs}} - public void encodeCertChain(byte[] buffer, short offset, short length, short errInt32Ptr) { - bufferRef[0] = buffer; - scratchBuf[START_OFFSET] = offset; - scratchBuf[LEN_OFFSET] = (short) (offset + 1); - //Total length is ArrayHeader + [UIntHeader + length(errInt32Ptr)] - scratchBuf[LEN_OFFSET] += (short) (1 + getEncodedIntegerLength(errInt32Ptr)); - - writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - encodeUnsignedInteger(errInt32Ptr); + public short encode(short object, byte[] buffer, short startOff, short bufLen) { + return encode(object, buffer, startOff, bufLen, (short) (bufLen - startOff)); } //array{KMError.OK,Array{KMByteBlobs}} - public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength, short errInt32Ptr) { + public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, short certLength) { + if (bufferStart > certStart) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } bufferRef[0] = certBuffer; scratchBuf[START_OFFSET] = certStart; scratchBuf[LEN_OFFSET] = (short) (certStart + 1); - //Array header - 2 elements i.e. 1 byte - scratchBuf[START_OFFSET]--; - // errInt32Ptr - PowerResetStatus + ErrorCode - 4 bytes - // Integer header - 1 byte - scratchBuf[START_OFFSET] -= getEncodedIntegerLength(errInt32Ptr); - //Array header - 2 elements i.e. 1 byte + // Byte Header + cert length + scratchBuf[START_OFFSET] -= getEncodedBytesLength(certLength); + //Array header - 1 elements i.e. 1 byte scratchBuf[START_OFFSET]--; - // Cert Byte blob - typically 2 bytes length i.e. 3 bytes header - scratchBuf[START_OFFSET] -= 2; - if (certLength >= SHORT_PAYLOAD) { - scratchBuf[START_OFFSET]--; - } if (scratchBuf[START_OFFSET] < bufferStart) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } bufferStart = scratchBuf[START_OFFSET]; - writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements - encodeUnsignedInteger(errInt32Ptr); //PowerResetStatus + ErrorCode - writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 element + writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 elements writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length return bufferStart; } - public short encodeError(short errInt32Ptr, byte[] buffer, short startOff, short length) { - bufferRef[0] = buffer; - scratchBuf[START_OFFSET] = startOff; - scratchBuf[LEN_OFFSET] = (short) (startOff + length + 1); - encodeUnsignedInteger(errInt32Ptr); - return (short) (scratchBuf[START_OFFSET] - startOff); - } - private void encode() { while (scratchBuf[STACK_PTR_OFFSET] > 0) { short exp = pop(); @@ -637,7 +623,7 @@ private short getEncodedArrayLen(short obj) { return len; } - private short getEncodedBytesLength(short len) { + public short getEncodedBytesLength(short len) { short ret = 0; if (len < KMEncoder.UINT8_LENGTH && len >= 0) { ret = 1; diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java b/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java index be073140..654dae3d 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyCharacteristics.java @@ -37,15 +37,13 @@ private KMKeyCharacteristics() { } public static short exp() { - short sb = KMKeyParameters.exp(); - short tee = KMKeyParameters.exp(); - short keystore = KMKeyParameters.exp(); + short keyParamExp = KMKeyParameters.exp(); short arrPtr = KMArray.instance((short) 3); KMArray arr = KMArray.cast(arrPtr); - arr.add(STRONGBOX_ENFORCED, sb); - arr.add(TEE_ENFORCED, tee); - arr.add(KEYSTORE_ENFORCED, keystore); + arr.add(STRONGBOX_ENFORCED, keyParamExp); + arr.add(TEE_ENFORCED, keyParamExp); + arr.add(KEYSTORE_ENFORCED, keyParamExp); return instance(arrPtr); } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java index 73eee8e1..9b2e4dad 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeyParameters.java @@ -403,11 +403,17 @@ public static short makeHidden(short keyParamsPtr, short rootOfTrustBlob, byte[] short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, keyParamsPtr); if (appId != KMTag.INVALID_VALUE) { appId = KMByteTag.cast(appId).getValue(); + if (KMByteBlob.cast(appId).length() == 0) { + appId = KMTag.INVALID_VALUE; + } } short appData = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, keyParamsPtr); if (appData != KMTag.INVALID_VALUE) { appData = KMByteTag.cast(appData).getValue(); + if (KMByteBlob.cast(appData).length() == 0) { + appData = KMTag.INVALID_VALUE; + } } return makeHidden(appId, appData, rootOfTrustBlob, scratchPad); } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index e78e7bc6..c4632d39 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -146,9 +146,7 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; //0x40 public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; //0x41 public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; //0x42 - private static final byte INS_GET_HEAP_PROFILE_DATA = KEYMINT_CMD_APDU_START + 35; //0x43 - private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 36; //0x44 - + private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 35; //0x43 private static final byte INS_END_KM_CMD = 0x7F; // Data Dictionary items @@ -210,7 +208,9 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final byte AES_GCM_NONCE_LENGTH = 12; // ComputeHMAC constants private static final short HMAC_SHARED_PARAM_MAX_SIZE = 64; - protected static final short MAX_CERT_SIZE = 2048; + protected static final short MAX_CERT_SIZE = 3000; + protected static final short MAX_KEY_CHARS_SIZE = 512; + protected static final short MAX_KEYBLOB_SIZE = 1024; // KEYBLOB_CURRENT_VERSION goes into KeyBlob and will affect all // the KeyBlobs if it is changed. please increment this // version number whenever you change anything related to @@ -415,9 +415,6 @@ public void process(APDU apdu) { case INS_GENERATE_KEY_CMD: processGenerateKey(apdu); break; - case INS_ATTEST_KEY_CMD: - processAttestKeyCmd(apdu); - break; case INS_IMPORT_KEY_CMD: processImportKeyCmd(apdu); break; @@ -491,9 +488,6 @@ public void process(APDU apdu) { case INS_GET_RKP_HARDWARE_INFO: rkp.process(apduIns, apdu); break; - case INS_GET_HEAP_PROFILE_DATA: - processGetHeapProfileData(apdu); - break; default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } @@ -607,16 +601,67 @@ protected void resetData() { */ public static void sendOutgoing(APDU apdu, short resp) { //TODO handle the extended buffer stuff. We can reuse this. - short usedHeap = repository.getHeapIndex(); short bufferStartOffset = repository.allocAvailableMemory(); byte[] buffer = repository.getHeap(); // TODO we can change the following to incremental send. - short bufferLength = encoder.encode(resp, buffer, bufferStartOffset); + short bufferLength = encoder.encode(resp, buffer, bufferStartOffset, repository.getHeapReclaimIndex()); if (((short) (bufferLength + bufferStartOffset)) > ((short) repository .getHeap().length)) { ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } - repository.updateHeapProfileData((short)(usedHeap + bufferLength)); + // Send data + apdu.setOutgoing(); + apdu.setOutgoingLength(bufferLength); + apdu.sendBytesLong(buffer, bufferStartOffset, bufferLength); + } + + public void sendOutgoing(APDU apdu, KMAttestationCert cert, short certStart, short keyblob, + short keyChars) { + // This is the special case where the output is encoded manually without using + // the encoder algorithm. Encoder creates a duplicate copy for each KMType Object. + // The output of the generateKey, importKey and importWrappedKey commands are huge so + // by manually encoding we can avoid duplicate copies. + // The output data is directly written to the end of heap in the below order + // output = [ + // errorCode : uint // ErrorCode + // keyBlob : bstr // KeyBlob. + // keyChars + // certifcate + // ] + // certificate = [ + // x509_cert : bstr // X509 certificate + // ] + // keyChars = { // Map + // } + byte[] buffer = repository.getHeap(); + + if (cert == null) { + // This happens for Symmetric keys. + short bufferStart = repository.allocReclaimableMemory((short) 1); + buffer[bufferStart] = (byte) 0x80; // Array of 0 length. + } else { + // Encode the certificate into cbor data at the end of the heap + // certData = [ + // x509_cert : bstr // X509 certificate + // ] + short bufferStart = encoder.encodeCert( + repository.getHeap(), + certStart, + cert.getCertStart(), + cert.getCertLength()); + // reclaim the unused memory in the certificate. + repository.reclaimMemory((short) (bufferStart - certStart)); + } + + // Encode KeyCharacteristics at the end of heap just before data[CERTIFICATE] + encodeKeyCharacteristics(keyChars); + // and encode it to the end of the buffer before KEY_CHARACTERISTICS + encodeKeyBlob(keyblob); + // Write Array header and ErrorCode before data[KEY_BLOB] + short bufferStartOffset = repository.allocReclaimableMemory((short) 2); + Util.setShort(buffer, bufferStartOffset, (short) 0x8400); + + short bufferLength = (short) (KMRepository.HEAP_SIZE - bufferStartOffset); // Send data apdu.setOutgoing(); apdu.setOutgoingLength(bufferLength); @@ -1027,6 +1072,12 @@ private void processUpgradeKeyCmd(APDU apdu) { upgradeKeyBlobKeyCharacteristics(data[HW_PARAMETERS], scratchPad); // create new key blob with current os version etc. createEncryptedKeyBlob(scratchPad); + short prevReclaimIndex = repository.getHeapReclaimIndex(); + short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE); + data[KEY_BLOB] = encoder.encode(data[KEY_BLOB], repository.getHeap(), offset, + prevReclaimIndex, MAX_KEYBLOB_SIZE); + data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]); + repository.reclaimMemory(MAX_KEYBLOB_SIZE); } else { data[KEY_BLOB] = KMByteBlob.instance((short) 0); } @@ -1231,9 +1282,7 @@ private void processFinishImportWrappedKeyCmd(APDU apdu){ } private KMAttestationCert makeCommonCert(byte[] scratchPad) { - // The Algorithm value can be read from either data[HW_PARAMETERS] or data[KEY_PARAMETERS] - // as the values will be same, and they are cryptographically bound. - short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[HW_PARAMETERS]); + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); boolean rsaCert = KMEnumTag.cast(alg).getValue() == KMType.RSA; KMAttestationCert cert = KMAttestationCertImpl.instance(rsaCert, seProvider); @@ -1291,28 +1340,44 @@ private KMAttestationCert makeCommonCert(byte[] scratchPad) { } private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyParam, - short attChallenge, short issuer, short hwParameters, byte[] scratchPad) { + short attChallenge, short issuer, byte[] scratchPad) { KMAttestationCert cert = makeCommonCert(scratchPad); - // App Id and App Data, + // Read App Id and App Data. short appId = getApplicationId(attKeyParam); short appData = getApplicationData(attKeyParam); + // Take backup of the required global variables KEY_BLOB, PUB_KEY, SECRET, KEY_CHAR + // and HW_PARAMS before they get overridden by isKeyUpgradeRequired() function. short origBlob = data[KEY_BLOB]; short pubKey = data[PUB_KEY]; short privKey = data[SECRET]; - // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired - // function itself. + short hwParams = data[HW_PARAMETERS]; + short keyChars = data[KEY_CHARACTERISTICS]; + // Check if key requires upgrade for attestKeyBlob. The KeyBlob is parsed inside + // isKeyUpgradeRequired function itself. if (isKeyUpgradeRequired(attKeyBlob, appId, appData, scratchPad)) { KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); } + // Get the private key of the attest key. short attestationKeySecret = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_SECRET); + // Get the KeyCharacteristics and SB param of the attest key short attestKeyCharacteristics = KMArray.cast(data[KEY_BLOB]).get(KEY_BLOB_PARAMS); short attestKeySbParams = KMKeyCharacteristics.cast(attestKeyCharacteristics).getStrongboxEnforced(); - short attKeyPurpose = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestKeySbParams); // If the attest key's purpose is not "attest key" then error. + short attKeyPurpose = KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, attestKeySbParams); if (!KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY)) { KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); } + KMAsn1Parser asn1Decoder = KMAsn1Parser.instance(); + short length = 0; + try { + asn1Decoder.validateDerSubject(issuer); + } catch (KMException e) { + KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME); + } + if (KMByteBlob.cast(issuer).length() > KMType.MAX_SUBJECT_DER_LEN) { + KMException.throwIt(KMError.INVALID_ISSUER_SUBJECT_NAME); + } // If issuer is not present then it is an error if (KMByteBlob.cast(issuer).length() <= 0) { KMException.throwIt(KMError.MISSING_ISSUER_SUBJECT_NAME); @@ -1328,9 +1393,16 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara } cert.attestationChallenge(attChallenge); cert.issuer(issuer); + + // Restore back the global variables. data[PUB_KEY] = pubKey; data[SECRET] = privKey; data[KEY_BLOB] = origBlob; + data[HW_PARAMETERS] = hwParams; + data[KEY_CHARACTERISTICS] = keyChars; + data[SW_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getKeystoreEnforced(); + data[TEE_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); + data[SB_PARAMETERS] = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getStrongboxEnforced(); cert.publicKey(data[PUB_KEY]); // Save attestation application id - must be present. @@ -1348,26 +1420,21 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara addAttestationIds(cert, scratchPad); // Add Tags - addTags(hwParameters, true, cert); - short swParams = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS], scratchPad); - addTags(swParams, false, cert); + addTags(data[HW_PARAMETERS], true, cert); + addTags(data[SW_PARAMETERS], false, cert); // Add Device Boot locked status cert.deviceLocked(kmDataStore.isDeviceBootLocked()); // VB data cert.verifiedBootHash(getVerifiedBootHash(scratchPad)); cert.verifiedBootKey(getBootKey(scratchPad)); cert.verifiedBootState((byte) kmDataStore.getBootState()); - data[SECRET] = privKey; - data[KEY_BLOB] = origBlob; return cert; } private KMAttestationCert makeSelfSignedCert(short attPrivKey, short attPubKey, short mode, byte[] scratchPad) { KMAttestationCert cert = makeCommonCert(scratchPad); - // The Algorithm value can be read from either data[HW_PARAMETERS] or data[KEY_PARAMETERS] - // as the values will be same, and they are cryptographically bound. - short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); short subject = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.CERTIFICATE_SUBJECT_NAME, data[KEY_PARAMETERS]); // If no subject name is specified then use the default subject name. @@ -1649,7 +1716,7 @@ private void finishAesDesOperation(KMOperationState op){ private void finishKeyAgreementOperation(KMOperationState op, byte[] scratchPad) { try { - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + KMAsn1Parser pkcs8 = KMAsn1Parser.instance(); short blob = pkcs8.decodeEcSubjectPublicKeyInfo(data[INPUT_DATA]); short len = op.getOperation().finish( KMByteBlob.cast(blob).getBuffer(), @@ -2892,12 +2959,15 @@ private boolean validateHwToken(short hwToken, byte[] scratchPad) { } private short importKeyCmd(APDU apdu){ - short cmd = KMArray.instance((short) 3); + short cmd = KMArray.instance((short) 6); // Arguments short params = KMKeyParameters.expAny(); KMArray.cast(cmd).add((short) 0, params); KMArray.cast(cmd).add((short) 1, KMEnum.instance(KMType.KEY_FORMAT)); KMArray.cast(cmd).add((short) 2, KMByteBlob.exp()); + KMArray.cast(cmd).add((short) 3, KMByteBlob.exp()); //attest key + KMArray.cast(cmd).add((short) 4, params); //attest key params + KMArray.cast(cmd).add((short) 5, KMByteBlob.exp()); //issuer return receiveIncoming(apdu, cmd); } @@ -2908,6 +2978,9 @@ private void processImportKeyCmd(APDU apdu) { data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0); short keyFmt = KMArray.cast(cmd).get((short) 1); data[IMPORTED_KEY_BLOB] = KMArray.cast(cmd).get((short) 2); + data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 3); + data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 4); + data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 5); keyFmt = KMEnum.cast(keyFmt).getVal(); data[CERTIFICATE] = KMArray.instance((short)0); //by default the cert is empty. @@ -2973,26 +3046,19 @@ private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) { break; } makeKeyCharacteristics( scratchPad); + KMAttestationCert cert = generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS],scratchPad); createEncryptedKeyBlob(scratchPad); - // MAC the KeyParameters. - short keyParamsMac = macKeyParams(keyParams, scratchPad); // Remove custom tags from key characteristics short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); if(teeParams != KMType.INVALID_VALUE) { KMKeyParameters.cast(teeParams).deleteCustomTags(); } - // prepare the response - short resp = KMArray.instance((short) 4); - KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(resp).add((short) 1, data[KEY_BLOB]); - KMArray.cast(resp).add((short) 2, data[KEY_CHARACTERISTICS]); - KMArray.cast(resp).add((short) 3, keyParamsMac); - sendOutgoing(apdu, resp); + sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]); } private void importECKeys(byte[] scratchPad) { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + KMAsn1Parser pkcs8 = KMAsn1Parser.instance(); short keyBlob = pkcs8.decodeEc(data[IMPORTED_KEY_BLOB]); data[PUB_KEY] = KMArray.cast(keyBlob).get((short) 0); data[SECRET] = KMArray.cast(keyBlob).get((short) 1); @@ -3184,7 +3250,7 @@ private void importAESKey(byte[] scratchPad) { private void importRSAKey(byte[] scratchPad) { // Decode key material - KMPKCS8Decoder pkcs8 = KMPKCS8Decoder.instance(); + KMAsn1Parser pkcs8 = KMAsn1Parser.instance(); short keyblob = pkcs8.decodeRsa(data[IMPORTED_KEY_BLOB]); data[PUB_KEY] = KMArray.cast(keyblob).get((short) 0); short pubKeyExp = KMArray.cast(keyblob).get((short)1); @@ -3363,9 +3429,13 @@ protected void setVendorPatchLevel(short patch){ private short generateKeyCmd(APDU apdu){ short params = KMKeyParameters.expAny(); + short blob = KMByteBlob.exp(); // Array of expected arguments - short cmd = KMArray.instance((short) 1); + short cmd = KMArray.instance((short) 4); KMArray.cast(cmd).add((short) 0, params); //key params + KMArray.cast(cmd).add((short) 1, blob); //attest key + KMArray.cast(cmd).add((short) 2, params); //attest key params + KMArray.cast(cmd).add((short) 3, blob); //issuer return receiveIncoming(apdu, cmd); } @@ -3375,25 +3445,27 @@ private void processGenerateKey(APDU apdu) { // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0); + data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 1); + data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 2); + data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 3); + data[CERTIFICATE] = KMType.INVALID_VALUE; // by default the cert is empty. // ROLLBACK_RESISTANCE not supported. - KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, - KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); + KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); // As per specification Early boot keys may be created after early boot ended. // Algorithm must be present - KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, - KMError.INVALID_ARGUMENT); + KMTag.assertPresence(data[KEY_PARAMETERS], KMType.ENUM_TAG, KMType.ALGORITHM, KMError.INVALID_ARGUMENT); //Check if the tags are supported. if (KMKeyParameters.hasUnsupportedTags(data[KEY_PARAMETERS])) { KMException.throwIt(KMError.UNSUPPORTED_TAG); } short attKeyPurpose = - KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); // ATTEST_KEY cannot be combined with any other purpose. if (attKeyPurpose != KMType.INVALID_VALUE - && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) - && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { + && KMEnumArrayTag.cast(attKeyPurpose).contains(KMType.ATTEST_KEY) + && KMEnumArrayTag.cast(attKeyPurpose).length() > 1) { KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); } short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); @@ -3421,21 +3493,15 @@ private void processGenerateKey(APDU apdu) { // create key blob and associated attestation. data[ORIGIN] = KMType.GENERATED; makeKeyCharacteristics(scratchPad); + // construct the certificate and place the encoded data in data[CERTIFICATE] + KMAttestationCert cert = generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad); createEncryptedKeyBlob(scratchPad); - // MAC the KeyParameters. - short keyParamsMac = macKeyParams(data[KEY_PARAMETERS], scratchPad); // Remove custom tags from key characteristics short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); if(teeParams != KMType.INVALID_VALUE) { KMKeyParameters.cast(teeParams).deleteCustomTags(); } - // prepare the response - short resp = KMArray.instance((short) 4); - KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(resp).add((short) 1, data[KEY_BLOB]); - KMArray.cast(resp).add((short) 2, data[KEY_CHARACTERISTICS]); - KMArray.cast(resp).add((short) 3, keyParamsMac); - sendOutgoing(apdu, resp); + sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]); } private short generateAttestKeyCmd(APDU apdu) { @@ -3453,16 +3519,6 @@ private short generateAttestKeyCmd(APDU apdu) { return receiveIncoming(apdu, cmd); } - public void getAttestKeyInputParameters(short arrPtr, short[] data, byte keyBlobOff, - byte keyParametersOff, - byte attestKeyBlobOff, byte attestKeyParamsOff, byte attestKeyIssuerOff) { - data[keyBlobOff] = KMArray.cast(arrPtr).get((short) 0); - data[keyParametersOff] = KMArray.cast(arrPtr).get((short) 1); - data[attestKeyBlobOff] = KMType.INVALID_VALUE; - data[attestKeyParamsOff] = KMType.INVALID_VALUE; - data[attestKeyIssuerOff] = KMType.INVALID_VALUE; - } - private short getApplicationId(short params) { short appId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, params); if (appId != KMTag.INVALID_VALUE) { @@ -3487,52 +3543,13 @@ private short getApplicationData(short params) { return appData; } - private void processAttestKeyCmd(APDU apdu) { - // Receive the incoming request fully from the master into buffer. - short cmd = generateAttestKeyCmd(apdu); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); - data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1); - data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 2); - data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 3); - data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 4); - short keyParamsMac = KMArray.cast(cmd).get((short) 5); - - data[CERTIFICATE] = KMArray.instance((short) 0); // by default the cert is empty. - - // Check for app id and app data. - data[APP_ID] = getApplicationId(data[KEY_PARAMETERS]); - data[APP_DATA] = getApplicationData(data[KEY_PARAMETERS]); - // Check if key requires upgrade. The KeyBlob is parsed inside isKeyUpgradeRequired - // function itself. - if (isKeyUpgradeRequired(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad)) { - // This condition occurs if either any of the system properties (OsVersion, OsPatchLevel, - // VendorPatchLevel or BootPatchLevel) changes or KeyBlob format changed. So return - // KEY_REQUIRES_UPGRADE error as this scenario is application to ATTEST_KEY_BLOB as well - // as ATTEST_KEY_BLOB got generated before the KEY_BLOB. - KMException.throwIt(KMError.KEY_REQUIRES_UPGRADE); - } - // Validate KeyParams Mac - if (!validateKeyParamsMac(data[KEY_PARAMETERS], keyParamsMac, scratchPad)) { - KMException.throwIt(KMError.INVALID_KEY_BLOB); - } - // The key which is being attested should be asymmetric i.e. RSA or EC - // The Algorithm value can be read from either data[HW_PARAMETERS] or data[KEY_PARAMETERS] - // as the values will be same, and they are cryptographically bound. - short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); - if (alg == KMType.RSA || alg == KMType.EC) { - // Build certificate - generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad); - } - short resp = KMArray.instance((short) 2); - KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(resp).add((short) 1, data[CERTIFICATE]); - sendOutgoing(apdu, resp); - } - private short getAttestationMode(short attKeyBlob, short attChallenge) { + short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); short mode = KMType.NO_CERT; + if(KMEnumTag.cast(alg).getValue() != KMType.RSA && + KMEnumTag.cast(alg).getValue() != KMType.EC) { + return mode; + } // If attestation keyblob preset if (attKeyBlob != KMType.INVALID_VALUE && KMByteBlob.cast(attKeyBlob).length() > 0) { // No attestation challenge present then it is an error @@ -3557,8 +3574,15 @@ private short getAttestationMode(short attKeyBlob, short attChallenge) { return mode; } - private void generateAttestation(short attKeyBlob, short attKeyParam, byte[] scratchPad){ + private KMAttestationCert generateAttestation(short attKeyBlob, short attKeyParam, byte[] scratchPad){ + // 1) If attestation key is present and attestation challenge is absent then it is an error. + // 2) If attestation key is absent and attestation challenge is present then it is an error as + // factory provisioned attestation key is not supported. + // 3) If both are present and issuer is absent or attest key purpose is not ATTEST_KEY then it is an error. + // 4) If the generated/imported keys are RSA or EC then validity period must be specified. + // Device Unique Attestation is not supported. // Device unique attestation not supported + short heapStart = repository.getHeapIndex(); KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG, KMType.DEVICE_UNIQUE_ATTESTATION, KMError.CANNOT_ATTEST_IDS); // Read attestation challenge if present @@ -3574,7 +3598,7 @@ private void generateAttestation(short attKeyBlob, short attKeyParam, byte[] sc switch (mode){ case KMType.ATTESTATION_CERT: cert = makeAttestationCert(attKeyBlob, attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER], - data[HW_PARAMETERS], scratchPad); + scratchPad); break; case KMType.SELF_SIGNED_CERT: cert = makeSelfSignedCert(data[SECRET], data[PUB_KEY], mode, scratchPad); @@ -3583,35 +3607,22 @@ private void generateAttestation(short attKeyBlob, short attKeyParam, byte[] sc cert = makeSelfSignedCert(KMType.INVALID_VALUE, data[PUB_KEY], mode, scratchPad); break; default: - data[CERTIFICATE] = KMArray.instance((short)0); - return; + data[CERTIFICATE] = KMType.INVALID_VALUE; + return null; } - // Allocate memory - short certData = KMByteBlob.instance(MAX_CERT_SIZE); - - cert.buffer(KMByteBlob.cast(certData).getBuffer(), - KMByteBlob.cast(certData).getStartOff(), - KMByteBlob.cast(certData).length()); - + // Certificate Data is converted to cbor and written to the end of the stack. + short certData = repository.allocReclaimableMemory(MAX_CERT_SIZE); + // Leave first 4 bytes for Array header and ByteBlob header. + cert.buffer(repository.getHeap(), (short) (certData + 4), (short) (MAX_CERT_SIZE - 4)); // Build the certificate - this will sign the cert cert.build(); - // Adjust the start and length of the certificate in the blob - KMByteBlob.cast(certData).setStartOff(cert.getCertStart()); - KMByteBlob.cast(certData).setLength(cert.getCertLength()); - // Initialize the certificate as array of blob - data[CERTIFICATE] = KMArray.instance((short)1); - KMArray.cast(data[CERTIFICATE]).add((short)0, certData); + // Certificate is now built so the data in the heap starting from heapStart to the current + // heap index can be reused. So resetting the heap index to heapStart. + repository.setHeapIndex(heapStart); + data[CERTIFICATE] = certData; + return cert; } - /** - * 1) If attestation key is present and attestation challenge is absent then it is an error. - * 2) If attestation key is absent and attestation challenge is present then it is an error as - * factory provisioned attestation key is not supported. - * 3) If both are present and issuer is absent or attest key purpose is not ATTEST_KEY then it is an error. - * 4) If the generated/imported keys are RSA or EC then validity period must be specified. - * Device Unique Attestation is not supported. - */ - private static void validateRSAKey(byte[] scratchPad) { // Read key size if(!KMTag.isValidKeySize(data[KEY_PARAMETERS])){ @@ -3800,50 +3811,6 @@ private static void makeKeyCharacteristics(byte[] scratchPad) { KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).setTeeEnforced(data[TEE_PARAMETERS]); } - private short macKeyParams(short keyParams, byte[] scratchPad) { - // For Symmetric Keys no need to Mac the Key parameters. As for symmetric keys - // attestation is not done. - if (SYM_KEY_TYPE == getKeyType(keyParams)) { - return KMByteBlob.instance((short) 0); - } - short offset = repository.allocReclaimableMemory(MAX_KEY_PARAMS_BUF_SIZE); - short len = encoder.encode(keyParams, repository.getHeap(), offset); - if (len > MAX_KEY_PARAMS_BUF_SIZE) { - // KeyParamters exceeded the maximum allowed size. - KMException.throwIt(KMError.INSUFFICIENT_BUFFER_SPACE); - } - - short signLen = seProvider.hmacSign( - KMByteBlob.cast(data[AUTH_TAG]).getBuffer(), - KMByteBlob.cast(data[AUTH_TAG]).getStartOff(), - KMByteBlob.cast(data[AUTH_TAG]).length(), - repository.getHeap(), - offset, - len, - scratchPad, - (short) 0); - //release memory - repository.reclaimMemory(MAX_KEY_PARAMS_BUF_SIZE); - - return KMByteBlob.instance(scratchPad, (short) 0, signLen); - } - - private boolean validateKeyParamsMac(short keyParams, short keyParamsMac, byte[] scratchPad) { - short ptr = macKeyParams(keyParams, scratchPad); - if (KMByteBlob.cast(ptr).length() != KMByteBlob.cast(keyParamsMac).length()) { - return false; - } - if (0 != Util.arrayCompare( - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(keyParamsMac).getBuffer(), - KMByteBlob.cast(keyParamsMac).getStartOff(), - KMByteBlob.cast(keyParamsMac).length())) { - return false; - } - return true; - } - private static void createEncryptedKeyBlob(byte[] scratchPad) { // make root of trust blob data[ROT] = readROT(scratchPad); @@ -3871,11 +3838,37 @@ private static void createEncryptedKeyBlob(byte[] scratchPad) { KMKeyCharacteristics.cast(tempChar).setKeystoreEnforced(emptyParam); KMKeyCharacteristics.cast(tempChar).setTeeEnforced(data[TEE_PARAMETERS]); KMArray.cast(data[KEY_BLOB]).add(KEY_BLOB_PARAMS, tempChar); + } + + // Encodes KeyCharacteristics at the end of the heap + private void encodeKeyCharacteristics(short keyChars) { + byte[] buffer = repository.getHeap(); + short prevReclaimIndex = repository.getHeapReclaimIndex(); + short ptr = repository.allocReclaimableMemory(MAX_KEY_CHARS_SIZE); + short len = encoder.encode(keyChars, buffer, ptr, prevReclaimIndex, MAX_KEY_CHARS_SIZE); + // shift the encoded KeyCharacteristics data towards the right till the data[CERTIFICATE] offset. + Util.arrayCopyNonAtomic(buffer, ptr, buffer, (short) (ptr + (MAX_KEY_CHARS_SIZE - len)), len); + // Reclaim the unused memory. + repository.reclaimMemory((short) (MAX_KEY_CHARS_SIZE - len)); + } + // Encodes KeyBlob at the end of the heap + private void encodeKeyBlob(short keyBlobPtr) { // allocate reclaimable memory. - short buffer = repository.alloc((short) 1024); - short keyBlob = encoder.encode(data[KEY_BLOB], repository.getHeap(), buffer); - data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), buffer, keyBlob); + byte[] buffer = repository.getHeap(); + short prevReclaimIndex = repository.getHeapReclaimIndex(); + short top = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE); + short keyBlob = encoder.encode(keyBlobPtr, buffer, top, prevReclaimIndex, MAX_KEYBLOB_SIZE); + Util.arrayCopyNonAtomic(repository.getHeap(), top, repository.getHeap(), + (short) (top + MAX_KEYBLOB_SIZE - keyBlob), keyBlob); + short newTop = (short) (top + MAX_KEYBLOB_SIZE - keyBlob); + // Encode the KeyBlob array inside a ByteString. Get the length of + // the ByteString header. + short encodedBytesLength = encoder.getEncodedBytesLength(keyBlob); + newTop -= encodedBytesLength; + encoder.encodeByteBlobHeader(keyBlob, buffer, newTop, encodedBytesLength); + // Reclaim unused memory. + repository.reclaimMemory((short) (newTop - top)); } private short readKeyBlobVersion(short keyBlob) { @@ -4100,13 +4093,14 @@ private static void makeAuthData(short version, byte[] scratchPad) { default: KMException.throwIt(KMError.INVALID_KEY_BLOB); } + short prevReclaimIndex = repository.getHeapReclaimIndex(); short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE); index = 0; short len = 0; Util.arrayFillNonAtomic(repository.getHeap(), authIndex, MAX_AUTH_DATA_SIZE, (byte) 0); while (index < numParams) { short tag = Util.getShort(scratchPad, (short) (index * 2)); - len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32)); + len = encoder.encode(tag, repository.getHeap(), (short) (authIndex + 32), prevReclaimIndex); Util.arrayCopyNonAtomic(repository.getHeap(), authIndex, repository.getHeap(), (short) (authIndex + len + 32), (short) 32); len = seProvider.messageDigest256(repository.getHeap(), @@ -4202,6 +4196,12 @@ public static void generateRkpKey(byte[] scratchPad, short keyParams) { data[ORIGIN] = KMType.GENERATED; makeKeyCharacteristics(scratchPad); createEncryptedKeyBlob(scratchPad); + short prevReclaimIndex = repository.getHeapReclaimIndex(); + short offset = repository.allocReclaimableMemory(MAX_KEYBLOB_SIZE); + data[KEY_BLOB] = encoder.encode(data[KEY_BLOB], repository.getHeap(), offset, + prevReclaimIndex, MAX_KEYBLOB_SIZE); + data[KEY_BLOB] = KMByteBlob.instance(repository.getHeap(), offset, data[KEY_BLOB]); + repository.reclaimMemory(MAX_KEYBLOB_SIZE); } public static short getPubKey() { return data[PUB_KEY]; @@ -4222,8 +4222,9 @@ public static short getPivateKey() { */ public static short encodeToApduBuffer(short object, byte[] apduBuf, short apduOff, short maxLen) { + short prevReclaimIndex = repository.getHeapReclaimIndex(); short offset = repository.allocReclaimableMemory(maxLen); - short len = encoder.encode(object, repository.getHeap(), offset); + short len = encoder.encode(object, repository.getHeap(), offset, prevReclaimIndex, maxLen); Util.arrayCopyNonAtomic(repository.getHeap(), offset, apduBuf, apduOff, len); //release memory repository.reclaimMemory(maxLen); @@ -4386,7 +4387,7 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { scratchPad, temp ); - len = KMPKCS8Decoder.instance(). + len = KMAsn1Parser.instance(). decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, temp, len), scratchPad, temp); coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); @@ -4402,36 +4403,30 @@ public static short generateBcc(boolean testMode, byte[] scratchPad) { } private void updateTrustedConfirmationOperation(KMOperationState op) { - if (op.isTrustedConfirmationRequired()) { - op.getTrustedConfirmationSigner().update(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length()); - } + if (op.isTrustedConfirmationRequired()) { + op.getTrustedConfirmationSigner().update(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length()); + } } private void finishTrustedConfirmationOperation(KMOperationState op) { - // Perform trusted confirmation if required - if (op.isTrustedConfirmationRequired()) { - if (0 == KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()) { - KMException.throwIt(KMError.NO_USER_CONFIRMATION); - } - - boolean verified = op.getTrustedConfirmationSigner().verify(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), - KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), KMByteBlob.cast(data[INPUT_DATA]).length(), - KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getBuffer(), - KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getStartOff(), - KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()); - if (!verified) { - KMException.throwIt(KMError.NO_USER_CONFIRMATION); - } - } - } - - private void processGetHeapProfileData(APDU apdu) { - // No Arguments - // prepare the response - short resp = KMArray.instance((short) 2); - KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(resp).add((short) 1, KMInteger.uint_16(repository.getMaxHeapUsed())); - sendOutgoing(apdu, resp); + // Perform trusted confirmation if required + if (op.isTrustedConfirmationRequired()) { + if (0 == KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + + boolean verified = op.getTrustedConfirmationSigner() + .verify(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), + KMByteBlob.cast(data[INPUT_DATA]).getStartOff(), + KMByteBlob.cast(data[INPUT_DATA]).length(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getBuffer(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).getStartOff(), + KMByteBlob.cast(data[CONFIRMATION_TOKEN]).length()); + if (!verified) { + KMException.throwIt(KMError.NO_USER_CONFIRMATION); + } + } } } diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index e5065d47..380d63dc 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -16,6 +16,7 @@ package com.android.javacard.keymaster; +import java.util.Base64.Decoder; import org.globalplatform.upgrade.Element; import com.android.javacard.seprovider.KMException; import com.android.javacard.seprovider.KMUpgradable; @@ -37,9 +38,7 @@ public class KMRepository { // Class Attributes private byte[] heap; private short[] heapIndex; - private short reclaimIndex; - //used for heap profiling - public static short[] maxHeapUsage; + private static short[] reclaimIndex; // Singleton instance private static KMRepository repository; @@ -51,8 +50,8 @@ public static KMRepository instance() { public KMRepository(boolean isUpgrading) { heap = JCSystem.makeTransientByteArray(HEAP_SIZE, JCSystem.CLEAR_ON_RESET); heapIndex = JCSystem.makeTransientShortArray((short)1, JCSystem.CLEAR_ON_RESET); - maxHeapUsage = JCSystem.makeTransientShortArray((short)1, JCSystem.CLEAR_ON_RESET); - reclaimIndex = HEAP_SIZE; + reclaimIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); + reclaimIndex[0] = HEAP_SIZE; repository = this; } @@ -65,9 +64,9 @@ public void onProcess() { } public void clean() { - Util.arrayFillNonAtomic(heap, (short) 0, heapIndex[0], (byte) 0); + Util.arrayFillNonAtomic(heap, (short) 0, HEAP_SIZE, (byte) 0); heapIndex[0] = 0; - reclaimIndex = HEAP_SIZE; + reclaimIndex[0] = HEAP_SIZE; } public void onDeselect() { @@ -80,21 +79,31 @@ public void onSelect() { // This function uses memory from the back of the heap(transient memory). Call // reclaimMemory function immediately after the use. public short allocReclaimableMemory(short length) { - if ((((short) (reclaimIndex - length)) <= heapIndex[0]) + if ((((short) (reclaimIndex[0] - length)) <= heapIndex[0]) || (length >= HEAP_SIZE / 2)) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - reclaimIndex -= length; - return reclaimIndex; + reclaimIndex[0] -= length; + return reclaimIndex[0]; + } + + // Use this function to reset the heapIndex to its previous state. + // Some of the data might be lost so use it carefully. + public void setHeapIndex(short offset) { + if (offset > heapIndex[0] || offset < 0) { + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); + } + Util.arrayFillNonAtomic(heap, offset, (short) (heapIndex[0] - offset), (byte) 0); + heapIndex[0] = offset; } // Reclaims the memory back. public void reclaimMemory(short length) { - if (reclaimIndex < heapIndex[0]) { + if (reclaimIndex[0] < heapIndex[0]) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } - Util.arrayFillNonAtomic(heap, reclaimIndex, length, (byte) 0); - reclaimIndex += length; + Util.arrayFillNonAtomic(heap, reclaimIndex[0], length, (byte) 0); + reclaimIndex[0] += length; } public short allocAvailableMemory() { @@ -102,13 +111,13 @@ public short allocAvailableMemory() { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } short index = heapIndex[0]; - heapIndex[0] = (short) heap.length; + heapIndex[0] = reclaimIndex[0]; return index; } public short alloc(short length) { if ((((short) (heapIndex[0] + length)) > heap.length) || - (((short) (heapIndex[0] + length)) > reclaimIndex)) { + (((short) (heapIndex[0] + length)) > reclaimIndex[0])) { ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } heapIndex[0] += length; @@ -122,15 +131,8 @@ public byte[] getHeap() { public short getHeapIndex() { return heapIndex[0]; } - - public void updateHeapProfileData(short size) { - if(size > maxHeapUsage[0]) { - maxHeapUsage[0] = size; - } - } - public short getMaxHeapUsed() { - return maxHeapUsage[0]; + public short getHeapReclaimIndex() { + return reclaimIndex[0]; } - } diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index df78ba4e..95806ad3 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -145,12 +145,6 @@ public abstract class KMType { 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; @@ -364,6 +358,10 @@ public abstract class KMType { public static final short MAX_ATTESTATION_CHALLENGE_SIZE = 128; // Max certificate serial size. public static final short MAX_CERTIFICATE_SERIAL_SIZE = 20; + // Attestation Application ID + public static final short MAX_ATTESTATION_APP_ID_SIZE = 1024; + // DER subject max length. + public static final short MAX_SUBJECT_DER_LEN = 1095; protected static KMRepository repository; diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 19ac6a82..87ad8541 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -737,7 +737,7 @@ private void constructPartialPubKeysToSignMac(byte[] scratchPad, short arrayLeng bufIndex, (short) 3); partialPayloadLen += - encoder.encode(arrPtr, repository.getHeap(), (short) (bufIndex + partialPayloadLen)); + encoder.encode(arrPtr, repository.getHeap(), (short) (bufIndex + partialPayloadLen), repository.getHeapReclaimIndex()); Util.arrayCopyNonAtomic(repository.getHeap(), bufIndex, scratchPad, len, partialPayloadLen); ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen)); } @@ -785,7 +785,7 @@ private short createSignedMac(KMDeviceUniqueKeyPair deviceUniqueKeyPair, byte[] scratchPad, signStructure ); - len = KMPKCS8Decoder.instance(). + len = KMAsn1Parser.instance(). decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, signStructure, len), scratchPad, signStructure); signStructure = KMByteBlob.instance(scratchPad, signStructure, len); diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp index 03a1b004..d8cb427c 100644 --- a/HAL/JavacardKeyMintDevice.cpp +++ b/HAL/JavacardKeyMintDevice.cpp @@ -72,10 +72,11 @@ ScopedAStatus JavacardKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) ScopedAStatus JavacardKeyMintDevice::generateKey(const vector& keyParams, const optional& attestationKey, KeyCreationResult* creationResult) { - vector keyParamsMac; cppbor::Array array; // add key params cbor_.addKeyparameters(array, keyParams); + // add attestation key if any + cbor_.addAttestationKey(array, attestationKey); auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_KEY_CMD, array); if (err != KM_ERROR_OK) { LOG(ERROR) << "Error in sending generateKey."; @@ -83,32 +84,10 @@ ScopedAStatus JavacardKeyMintDevice::generateKey(const vector& key } if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || - !cbor_.getBinaryArray(item, 3, keyParamsMac)) { + !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { LOG(ERROR) << "Error in decoding og response in generateKey."; return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); } - - AuthorizationSet paramSet; - paramSet.Reinitialize(KmParamSet(keyParams)); - // Call attestKey only Asymmetric algorithms. - keymaster_algorithm_t algorithm; - paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); - if (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC) { - cppbor::Array attestKeyArray; - attestKeyArray.add(creationResult->keyBlob); - cbor_.addKeyparameters(attestKeyArray, keyParams); - cbor_.addAttestationKey(attestKeyArray, attestationKey); - attestKeyArray.add(keyParamsMac); - auto [certItem, error] = card_->sendRequest(Instruction::INS_ATTEST_KEY_CMD, attestKeyArray); - if (error != KM_ERROR_OK) { - LOG(ERROR) << "Failed in attestKey err: " << error; - return km_utils::kmError2ScopedAStatus(error); - } - if (!cbor_.getCertificateChain(certItem, 1, creationResult->certificateChain)) { - LOG(ERROR) << "Error in decoding og response in generateKey."; - return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); - } - } return ScopedAStatus::ok(); } @@ -129,7 +108,6 @@ ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyPa const optional& attestationKey, KeyCreationResult* creationResult) { - vector keyParamsMac; cppbor::Array request; // add key params cbor_.addKeyparameters(request, keyParams); @@ -137,6 +115,8 @@ ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyPa request.add(Uint(static_cast(keyFormat))); // add key data request.add(Bstr(keyData)); + // add attestation key if any + cbor_.addAttestationKey(request, attestationKey); auto [item, err] = card_->sendRequest(Instruction::INS_IMPORT_KEY_CMD, request); if (err != KM_ERROR_OK) { @@ -145,32 +125,10 @@ ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyPa } if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || - !cbor_.getBinaryArray(item, 3, keyParamsMac)) { + !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { LOG(ERROR) << "Error in decoding response in importKey."; return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); } - - AuthorizationSet paramSet; - paramSet.Reinitialize(KmParamSet(keyParams)); - // Call attestKey only Asymmetric algorithms. - keymaster_algorithm_t algorithm; - paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); - if (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC) { - cppbor::Array attestKeyArray; - attestKeyArray.add(creationResult->keyBlob); - cbor_.addKeyparameters(attestKeyArray, keyParams); - cbor_.addAttestationKey(attestKeyArray, attestationKey); - attestKeyArray.add(keyParamsMac); - auto [certItem, error] = card_->sendRequest(Instruction::INS_ATTEST_KEY_CMD, attestKeyArray); - if (error != KM_ERROR_OK) { - LOG(ERROR) << "Failed in attestKey err: " << error; - return km_utils::kmError2ScopedAStatus(error); - } - if (!cbor_.getCertificateChain(certItem, 1, creationResult->certificateChain)) { - LOG(ERROR) << "Error in decoding of response in importKey."; - return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); - } - } return ScopedAStatus::ok(); } @@ -192,7 +150,6 @@ ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector& wra std::vector tag; vector authList; KeyFormat keyFormat; - vector keyParamsMac; std::vector wrappedKeyDescription; keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, authList, keyFormat, wrappedKeyDescription); @@ -217,32 +174,10 @@ ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector& wra } if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || - !cbor_.getBinaryArray(item, 3, keyParamsMac)) { + !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { LOG(ERROR) << "Error in decoding the response in importWrappedKey."; return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); } - - AuthorizationSet paramSet; - paramSet.Reinitialize(KmParamSet(authList)); - // Call attestKey only Asymmetric algorithms. - keymaster_algorithm_t algorithm; - paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); - if (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC) { - cppbor::Array attestKeyArray; - attestKeyArray.add(creationResult->keyBlob); - cbor_.addKeyparameters(attestKeyArray, authList); - attestKeyArray.add(keyParamsMac); - cbor_.addAttestationKey(attestKeyArray, std::nullopt); - auto [certItem, error] = card_->sendRequest(Instruction::INS_ATTEST_KEY_CMD, attestKeyArray); - if (error != KM_ERROR_OK) { - LOG(ERROR) << "Failed in attestKey err: " << error; - return km_utils::kmError2ScopedAStatus(error); - } - if (!cbor_.getCertificateChain(certItem, 1, creationResult->certificateChain)) { - LOG(ERROR) << "Error in decoding of response in importWrappedKey."; - return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); - } - } return ScopedAStatus::ok(); }