diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index 10311463..3ee5b4be 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -2350,8 +2350,19 @@ public ResponseAPDU importWrappedKey() { Util.arrayCopyNonAtomic(output, (short) 0, encTransportKey, (short) 0, outlen); // Begn Import wrapped key. - short nullParams = KMArray.instance((short) 0); - nullParams = KMKeyParameters.instance(nullParams); + // Unwrapping params should have Digest: SHA256 and padding as RSA_OAEP + short unwrappingParamsArr = KMArray.instance((short) 2); + // RSA OAEP Padding + short paddingBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(paddingBlob).add((short) 0, KMType.RSA_OAEP); + short padding = KMEnumArrayTag.instance(KMType.PADDING, paddingBlob); + // SHA256 digest + short digestBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(digestBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, digestBlob); + KMArray.cast(unwrappingParamsArr).add((short) 0, padding); + KMArray.cast(unwrappingParamsArr).add((short) 1, digest); + short unwrappingParams = KMKeyParameters.instance(unwrappingParamsArr); short arr = KMArray.instance((short) 4); KMArray.cast(arr).add((short) 0, KMByteBlob.instance(encTransportKey, (short) 0, (short) encTransportKey.length)); // Encrypted Transport Key @@ -2359,7 +2370,7 @@ public ResponseAPDU importWrappedKey() { (short) wrappingKeyBlob.length)); // Wrapping Key KeyBlob KMArray.cast(arr).add((short) 2, KMByteBlob.instance(maskingKey, (short) 0, (short) maskingKey.length)); // Masking Key - KMArray.cast(arr).add((short) 3, nullParams); // unwrapping params + KMArray.cast(arr).add((short) 3, unwrappingParams); // unwrapping params CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, arr); ResponseAPDU response = simulator.transmitCommand(apdu); diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java index 16a093c4..23754ae3 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java @@ -17,34 +17,30 @@ package com.android.javacard.test; import com.android.javacard.keymaster.KMArray; -import com.android.javacard.keymaster.KMAsn1Parser; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMCose; import com.android.javacard.keymaster.KMCoseHeaders; import com.android.javacard.keymaster.KMCoseKey; +import com.android.javacard.keymaster.KMDecoder; +import com.android.javacard.keymaster.KMEncoder; +import com.android.javacard.keymaster.KMError; +import com.android.javacard.keymaster.KMInteger; +import com.android.javacard.keymaster.KMJCardSimApplet; +import com.android.javacard.keymaster.KMKeymasterApplet; import com.android.javacard.keymaster.KMMap; import com.android.javacard.keymaster.KMNInteger; import com.android.javacard.keymaster.KMRepository; import com.android.javacard.keymaster.KMSimpleValue; -import com.android.javacard.keymaster.KMJCardSimApplet; -import com.android.javacard.keymaster.KMKeymasterApplet; import com.android.javacard.keymaster.KMTextString; +import com.android.javacard.keymaster.KMType; import com.android.javacard.seprovider.KMJCardSimulator; import com.android.javacard.seprovider.KMSEProvider; -import com.android.javacard.keymaster.KMDecoder; -import com.android.javacard.keymaster.KMEncoder; -import com.android.javacard.keymaster.KMError; -import com.android.javacard.keymaster.KMInteger; -import com.android.javacard.keymaster.KMType; import com.licel.jcardsim.smartcardio.CardSimulator; import com.licel.jcardsim.utils.AIDUtil; - import javacard.framework.AID; import javacard.framework.Util; - import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; - import org.junit.Assert; import org.junit.Test; @@ -298,24 +294,26 @@ public void testGenerateCsr(short no_keys) { // challenge short challenge = KMByteBlob.instance(CSR_CHALLENGE, (short) 0, (short) CSR_CHALLENGE.length); - + // begin send data short arr = KMArray.instance((short) 3); KMArray.cast(arr).add((short) 0, KMInteger.uint_8((byte) no_keys)); KMArray.cast(arr).add((short) 1, KMInteger.uint_16(totalEncodedCoseKeysLen)); KMArray.cast(arr).add((short) 2, challenge); - + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_BEGIN_SEND_DATA_CMD, arr); ResponseAPDU response = simulator.transmitCommand(apdu); byte[] resp = response.getBytes(); - arr = KMArray.instance((short) 2); + arr = KMArray.instance((short) 4); KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK KMArray.cast(arr).add((short) 1, KMByteBlob.exp()); // deviceInfo + KMArray.cast(arr).add((short) 2, KMInteger.exp()); // version + KMArray.cast(arr).add((short) 3, KMTextString.exp()); // certType ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - + // get device info - short deviceInfo = KMArray.cast(ret).get((short) 1); + short deviceInfo = KMArray.cast(ret).get((short) 1); byte[] deviceInfoBytes = new byte[512]; Util.arrayCopyNonAtomic(KMByteBlob.cast(deviceInfo).getBuffer(), KMByteBlob.cast(deviceInfo).getStartOff(), @@ -324,12 +322,22 @@ public void testGenerateCsr(short no_keys) { KMByteBlob.cast(deviceInfo).length()); short deviceInfoBytesLen = KMByteBlob.cast(deviceInfo).length(); + short schemaVersion = KMArray.cast(ret).get((short) 2); + schemaVersion = KMInteger.cast(schemaVersion).getShort(); + + short certType = KMArray.cast(ret).get((short) 3); + short certTypeLen = KMTextString.cast(certType).length(); + byte[] cerTypeBytes = new byte[certTypeLen]; + Util.arrayCopyNonAtomic(KMTextString.cast(certType).getBuffer(), + KMTextString.cast(certType).getStartOff(), cerTypeBytes, (short) 0, + KMTextString.cast(certType).length()); + // update data. short cKey; - byte[]cosyKeyArrayBytes = new byte[2048]; + byte[] cosyKeyArrayBytes = new byte[2048]; short coseKeyBytesLen = 0; coseKeyBytesLen += - encoder.encodeArrayHeader(no_keys, cosyKeyArrayBytes, coseKeyBytesLen, (short) 3); + encoder.encodeArrayHeader(no_keys, cosyKeyArrayBytes, coseKeyBytesLen, (short) 3); for (short i = 0; i < no_keys; i++) { coseMacPtr = KMTestUtils.decodeCoseMac(decoder, mackedKeys[i], (short) 0, (short) mackedKeys[i].length); @@ -343,25 +351,25 @@ public void testGenerateCsr(short no_keys) { KMArray.cast(arr).add((short) 1, KMByteBlob.exp()); // cosekey ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - + // get cose keys in cosyKeyArrayBytes byte array cKey = KMArray.cast(ret).get((short) 1); Util.arrayCopyNonAtomic(KMByteBlob.cast(cKey).getBuffer(), - KMByteBlob.cast(cKey).getStartOff(), - cosyKeyArrayBytes, - coseKeyBytesLen, - KMByteBlob.cast(cKey).length()); + KMByteBlob.cast(cKey).getStartOff(), + cosyKeyArrayBytes, + coseKeyBytesLen, + KMByteBlob.cast(cKey).length()); coseKeyBytesLen += KMByteBlob.cast(cKey).length(); } //Clean the heap. KMRepository.instance().clean(); - + // finish // Extended length. apdu = new CommandAPDU(0x80, INS_FINISH_SEND_DATA_CMD, 0x50, 0x00, (byte[]) null, 65536); response = simulator.transmitCommand(apdu); - + short coseHeadersExp = KMCoseHeaders.exp(); arr = KMArray.instance((short) 5); KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK @@ -376,58 +384,72 @@ public void testGenerateCsr(short no_keys) { short protectedHeaders = KMArray.cast(ret).get((short) 1); short signatureData = KMArray.cast(ret).get((short) 2); short version = KMArray.cast(ret).get((short) 3); - + byte[] signature = new byte[512]; Util.arrayCopyNonAtomic(KMByteBlob.cast(signatureData).getBuffer(), - KMByteBlob.cast(signatureData).getStartOff(), - signature, - (short)0, - KMByteBlob.cast(signatureData).length()); + KMByteBlob.cast(signatureData).getStartOff(), + signature, + (short) 0, + KMByteBlob.cast(signatureData).length()); short signatureLen = KMByteBlob.cast(signatureData).length(); - - byte[] tmp = new byte[10]; + + byte[] tmp = new byte[10]; byte[] payload = new byte[2048]; short payloadLen = 0; - - short aad = KMByteBlob.instance(tmp, (short) 0, (short) 0); - // construct cose sign structure + + short aad = KMByteBlob.instance(tmp, (short) 0, (short) 0); + // construct cose sign structure short signStructure = - KMCose.constructCoseSignStructure(protectedHeaders, aad, KMType.INVALID_VALUE); + KMCose.constructCoseSignStructure(protectedHeaders, aad, KMType.INVALID_VALUE); //encode sign structure to paload byte array - payloadLen = KMKeymasterApplet.encodeToApduBuffer(signStructure, payload, - (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - - short payloadByteLen = (short)((short)1 /*Array of 3 elements occupies 1 byte */+ - deviceInfoBytesLen + - encoder.getEncodedBytesLength((short)CSR_CHALLENGE.length) + - (short)CSR_CHALLENGE.length + - coseKeyBytesLen); - + payloadLen = KMKeymasterApplet.encodeToApduBuffer(signStructure, payload, + (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + + short payloadByteLen = (short) ((short) 1 /*Array of 5 elements occupies 1 byte */ + + (short) 1 + // Version occupies 1 byte + encoder.getEncodedBytesLength(certTypeLen) + + certTypeLen + + deviceInfoBytesLen + + encoder.getEncodedBytesLength((short) CSR_CHALLENGE.length) + + (short) CSR_CHALLENGE.length + + coseKeyBytesLen); + payloadLen += encoder.encodeByteBlobHeader(payloadByteLen, payload, payloadLen, (short) 3); - payloadLen += encoder.encodeArrayHeader((short)3, payload, payloadLen, (short) 3); + payloadLen += encoder.encodeArrayHeader((short) 5, payload, payloadLen, (short) 3); + + short payloadSchemaVersion = KMInteger.uint_16(schemaVersion); + payloadLen += + encoder.encode(payloadSchemaVersion, payload, payloadLen, + KMKeymasterApplet.MAX_COSE_BUF_SIZE); + + short certTypeTstr = KMTextString.instance(cerTypeBytes, (short) 0, certTypeLen); + payloadLen += + encoder.encode(certTypeTstr, payload, payloadLen, KMKeymasterApplet.MAX_COSE_BUF_SIZE); + Util.arrayCopyNonAtomic(deviceInfoBytes, - (short)0, - payload, - payloadLen, - deviceInfoBytesLen); + (short) 0, + payload, + payloadLen, + deviceInfoBytesLen); payloadLen += deviceInfoBytesLen; - - payloadLen += encoder.encodeByteBlobHeader((short)CSR_CHALLENGE.length, payload, payloadLen, (short) 3); - + + payloadLen += encoder.encodeByteBlobHeader((short) CSR_CHALLENGE.length, payload, payloadLen, + (short) 3); + Util.arrayCopyNonAtomic(CSR_CHALLENGE, - (short)0, - payload, - payloadLen, - (short)CSR_CHALLENGE.length); - payloadLen += (short)CSR_CHALLENGE.length; - + (short) 0, + payload, + payloadLen, + (short) CSR_CHALLENGE.length); + payloadLen += (short) CSR_CHALLENGE.length; + Util.arrayCopyNonAtomic(cosyKeyArrayBytes, - (short)0, - payload, - payloadLen, - coseKeyBytesLen); + (short) 0, + payload, + payloadLen, + coseKeyBytesLen); payloadLen += coseKeyBytesLen; - + byte moreData; byte[] udsCerts = new byte[2500]; short offset = 0; @@ -443,10 +465,11 @@ public void testGenerateCsr(short no_keys) { ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); short partialData = KMArray.cast(ret).get((short) 1); - Util.arrayCopyNonAtomic(KMByteBlob.cast(partialData).getBuffer(), KMByteBlob.cast(partialData).getStartOff(), + Util.arrayCopyNonAtomic(KMByteBlob.cast(partialData).getBuffer(), + KMByteBlob.cast(partialData).getStartOff(), udsCerts, offset, KMByteBlob.cast(partialData).length()); offset += KMByteBlob.cast(partialData).length(); - + moreData = KMInteger.cast(KMArray.cast(ret).get((short) 2)).getByte(); } while (moreData != 0); short x509Arr = KMArray.exp(KMByteBlob.exp()); @@ -460,54 +483,56 @@ public void testGenerateCsr(short no_keys) { byte[] diceCerts = new byte[2500]; offset = 0; do { - apdu = new CommandAPDU(0x80, INS_GET_DICE_CERT_CHAIN_CMD, 0x50, 0x00, (byte[]) null, 65536);// BCC - response = simulator.transmitCommand(apdu); - arr = KMArray.instance((short) 3); - KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK - KMArray.cast(arr).add((short) 1, KMByteBlob.exp()); // data - KMArray.cast(arr).add((short) 2, KMInteger.exp()); // more data - resp = response.getBytes(); - ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - short partialData = KMArray.cast(ret).get((short) 1); - Util.arrayCopyNonAtomic(KMByteBlob.cast(partialData).getBuffer(), KMByteBlob.cast(partialData).getStartOff(), - diceCerts, offset, KMByteBlob.cast(partialData).length()); - offset += KMByteBlob.cast(partialData).length(); - - moreData = KMInteger.cast(KMArray.cast(ret).get((short) 2)).getByte(); - } while (moreData != 0); - - short coseKeyExp = KMCoseKey.exp(); - short signedMacArr = KMArray.instance((short) 4); - short headersExp = KMCoseHeaders.exp(); - KMArray.cast(signedMacArr).add((short) 0, KMByteBlob.exp()); - KMArray.cast(signedMacArr).add((short) 1, headersExp); - KMArray.cast(signedMacArr).add((short) 2, KMByteBlob.exp()); - KMArray.cast(signedMacArr).add((short) 3, KMByteBlob.exp()); - short dccArr = KMArray.instance((short) 2); - KMArray.cast(dccArr).add((short) 0, coseKeyExp); - KMArray.cast(dccArr).add((short) 1, signedMacArr); - - short dccPtr = decoder.decode(dccArr, diceCerts, (short)0, offset); - - byte[] pubKey = new byte[100]; - short pubLen = KMTestUtils.getDccPublicKey(cryptoProvider, encoder, decoder, - dccPtr, pubKey, (short) 0); - - byte[] encodedSignBuf = new byte[512]; - short encodedSignLen = - KMTestUtils.encodeES256CoseSignSignature( - signature, - (short)0, - signatureLen, - encodedSignBuf, - (short) 0); - // Verify the signature of cose sign1. - KMTestUtils.print(payload, (short) 0, payloadLen); - Assert.assertTrue( - cryptoProvider.ecVerify256(pubKey, (short)0, pubLen, payload, (short) 0, payloadLen, - encodedSignBuf, (short) 0, encodedSignLen)); - + apdu = new CommandAPDU(0x80, INS_GET_DICE_CERT_CHAIN_CMD, 0x50, 0x00, (byte[]) null, + 65536);// BCC + response = simulator.transmitCommand(apdu); + arr = KMArray.instance((short) 3); + KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK + KMArray.cast(arr).add((short) 1, KMByteBlob.exp()); // data + KMArray.cast(arr).add((short) 2, KMInteger.exp()); // more data + resp = response.getBytes(); + ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); + Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); + short partialData = KMArray.cast(ret).get((short) 1); + Util.arrayCopyNonAtomic(KMByteBlob.cast(partialData).getBuffer(), + KMByteBlob.cast(partialData).getStartOff(), + diceCerts, offset, KMByteBlob.cast(partialData).length()); + offset += KMByteBlob.cast(partialData).length(); + + moreData = KMInteger.cast(KMArray.cast(ret).get((short) 2)).getByte(); + } while (moreData != 0); + + short coseKeyExp = KMCoseKey.exp(); + short signedMacArr = KMArray.instance((short) 4); + short headersExp = KMCoseHeaders.exp(); + KMArray.cast(signedMacArr).add((short) 0, KMByteBlob.exp()); + KMArray.cast(signedMacArr).add((short) 1, headersExp); + KMArray.cast(signedMacArr).add((short) 2, KMByteBlob.exp()); + KMArray.cast(signedMacArr).add((short) 3, KMByteBlob.exp()); + short dccArr = KMArray.instance((short) 2); + KMArray.cast(dccArr).add((short) 0, coseKeyExp); + KMArray.cast(dccArr).add((short) 1, signedMacArr); + + short dccPtr = decoder.decode(dccArr, diceCerts, (short) 0, offset); + + byte[] pubKey = new byte[100]; + short pubLen = KMTestUtils.getDccPublicKey(cryptoProvider, encoder, decoder, + dccPtr, pubKey, (short) 0); + + byte[] encodedSignBuf = new byte[512]; + short encodedSignLen = + KMTestUtils.encodeES256CoseSignSignature( + signature, + (short) 0, + signatureLen, + encodedSignBuf, + (short) 0); + // Verify the signature of cose sign1. + KMTestUtils.print(payload, (short) 0, payloadLen); + Assert.assertTrue( + cryptoProvider.ecVerify256(pubKey, (short) 0, pubLen, payload, (short) 0, payloadLen, + encodedSignBuf, (short) 0, encodedSignLen)); + } } diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java index 7b4129df..2ce4b83e 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java @@ -361,151 +361,6 @@ public static short getErrorCode(short arr) { return KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(); } - public static void validateProtectedData(KMSEProvider cryptoProvider, KMEncoder encoder, - KMDecoder decoder, - byte[] EEK_KEY_ID, KeyPair eekKey, byte[] CsrChallenge, byte[] encodedCoseKeysArray, - boolean testMode, short protectedDataArrPtr, - short deviceInfoMapPtr, - short pubKeysToSignMac) { - Assert.assertEquals(4, KMArray.cast(protectedDataArrPtr).length()); - //-------------------------------------------- - // Validate recipients structure and get the public key. - //-------------------------------------------- - byte[] ephemeralPub = new byte[100]; - byte[] eekKeyId = new byte[EEK_KEY_ID.length]; - short ephemeralPubLen = getSenderPublicKeyAndKeyIdFromRecipientStructure(decoder, EEK_KEY_ID, - protectedDataArrPtr, - ephemeralPub, (short) 0, - eekKeyId, (short) 0, (short) eekKeyId.length); - //-------------------------------------------- - // Derive session key using ECDH HKDF. Alg. - //-------------------------------------------- - byte[] eekPriv = new byte[100]; - byte[] eekPub = new byte[100]; - ECPublicKey ecPublicKey = (ECPublicKey) eekKey.getPublic(); - ECPrivateKey ecPrivateKey = (ECPrivateKey) eekKey.getPrivate(); - short eekPubLen = ecPublicKey.getW(eekPub, (short) 0); - short eekPrivLen = ecPrivateKey.getS(eekPriv, (short) 0); - byte[] sessionKey = new byte[100]; - short sessionKeyLen = - ecdhHkdfDeriveKey(cryptoProvider, encoder, eekPriv, (short) 0, eekPrivLen, eekPub, - (short) 0, eekPubLen, - ephemeralPub, (short) 0, - ephemeralPubLen, sessionKey, (short) 0); - //-------------------------------------------- - // Validate Protected Data and Decrypt the Cose_Encrypt structure using session Key. - // 1. Validate protected header. - // 2. Validate unprotected header. - // 3. Decrypt the protected data. - //-------------------------------------------- - short params = KMArray.cast(protectedDataArrPtr).get((short) 0); - short protectedHeader = params; - params = - decoder.decode(KMCoseHeaders.exp(), KMByteBlob.cast(params).getBuffer(), - KMByteBlob.cast(params).getStartOff(), - KMByteBlob.cast(params).length()); - params = KMCoseHeaders.cast(params).getVals(); - // The length of the protected params is 1 and the algorithm should be AES_GCM. - Assert.assertEquals(1, KMArray.cast(params).length()); - short param = KMArray.cast(params).get((short) 0); - Assert.assertEquals(KMCose.COSE_ALG_AES_GCM_256, - KMInteger.cast(KMCosePairIntegerTag.cast(param).getValuePtr()).getByte()); - // 2. Validate unprotected header. - params = KMArray.cast(protectedDataArrPtr).get((short) 1); - short iv = KMCoseHeaders.cast(params).getIV(); - Assert.assertEquals(AES_GCM_NONCE_LENGTH, KMByteBlob.cast(iv).length()); - // 3. Decrypt the protected data. - byte[] authData = new byte[256]; - short coseEncryptStr = - KMCose.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0)); - short authDataLen = encoder.encode(coseEncryptStr, authData, (short) 0, (short) 256); - short cipherText = KMArray.cast(protectedDataArrPtr).get((short) 2); - byte[] authTag = new byte[AES_GCM_AUTH_TAG_LENGTH]; - short encryptedDataLen = (short) (KMByteBlob.cast(cipherText).length() - - AES_GCM_AUTH_TAG_LENGTH); - byte[] encryptedData = new byte[encryptedDataLen]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherText).getBuffer(), - KMByteBlob.cast(cipherText).getStartOff(), - encryptedData, (short) 0, encryptedDataLen); - Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherText).getBuffer(), - (short) (encryptedDataLen + KMByteBlob.cast(cipherText).getStartOff()), - authTag, (short) 0, AES_GCM_AUTH_TAG_LENGTH); - byte[] plainText = new byte[encryptedDataLen]; - boolean valid = - cryptoProvider.aesGCMDecrypt( - sessionKey, - (short) 0, - sessionKeyLen, - encryptedData, - (short) 0, - encryptedDataLen, - plainText, - (short) 0, - KMByteBlob.cast(iv).getBuffer(), - KMByteBlob.cast(iv).getStartOff(), - KMByteBlob.cast(iv).length(), - authData, - (short) 0, - authDataLen, - authTag, - (short) 0, - AES_GCM_AUTH_TAG_LENGTH - ); - Assert.assertTrue(valid); - //-------------------------------------------- - // Validate the decrypted payload. - // payload = [signedMac + dcc + ? UdsCertChain] - //-------------------------------------------- - short payloadLength = testMode ? (short) 2 : (short) 3; - short udsCertChain = 0; - short headersExp = KMCoseHeaders.exp(); - short coseKeyExp = KMCoseKey.exp(); - short signedMacArr = KMArray.instance((short) 4); - KMArray.cast(signedMacArr).add((short) 0, KMByteBlob.exp()); - KMArray.cast(signedMacArr).add((short) 1, headersExp); - KMArray.cast(signedMacArr).add((short) 2, KMByteBlob.exp()); - KMArray.cast(signedMacArr).add((short) 3, KMByteBlob.exp()); - // dcc exp - short dccArr = KMArray.instance((short) 2); - KMArray.cast(dccArr).add((short) 0, coseKeyExp); - KMArray.cast(dccArr).add((short) 1, signedMacArr); - //if (!testMode) { - short headers = KMCoseHeaders.exp(); - short x509Arr = KMArray.exp(KMByteBlob.exp()); - udsCertChain = KMMap.instance((short) 1); - KMMap.cast(udsCertChain).add((short) 0, KMTextString.exp(), x509Arr); - // protected payload exp - short payload = KMArray.instance(payloadLength); - KMArray.cast(payload).add((short) 0, signedMacArr); - KMArray.cast(payload).add((short) 1, dccArr); - if (udsCertChain != 0) { - KMArray.cast(payload).add((short) 2, udsCertChain); - } - short payloadPtr = decoder.decode(payload, plainText, (short) 0, encryptedDataLen); - byte[] pub = new byte[100]; - //-------------------------------------------- - // Validate DCC and get public key. - //-------------------------------------------- - short pubLen = getDccPublicKey(cryptoProvider, encoder, decoder, - KMArray.cast(payloadPtr).get((short) 1), pub, (short) 0); - //-------------------------------------------- - // Validate Signed MacPtr. - //-------------------------------------------- - validateSignedMac(cryptoProvider, encoder, decoder, CsrChallenge, encodedCoseKeysArray, - KMArray.cast(payloadPtr).get((short) 0), pub, (short) 0, pubLen, - deviceInfoMapPtr, - pubKeysToSignMac); - //-------------------------------------------- - // Validate Uds certificate chain. - //-------------------------------------------- - //if (!testMode) { - { - short addCertChain = KMArray.cast(payloadPtr).get((short) 2); - addCertChain = KMMap.cast(addCertChain).getKeyValue((short) 0); - Assert.assertTrue(validateCertChain(addCertChain)); - } - } - public static X509Certificate decodeCert(byte[] cert, short certOff, short certLen) throws IOException { byte[] certificate = new byte[certLen]; diff --git a/Applet/src/com/android/javacard/keymaster/KMCose.java b/Applet/src/com/android/javacard/keymaster/KMCose.java index 5b1a2cdb..a1897c8a 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCose.java +++ b/Applet/src/com/android/javacard/keymaster/KMCose.java @@ -94,8 +94,6 @@ public class KMCose { public static final byte[] MAC_CONTEXT = {0x4d, 0x41, 0x43, 0x30}; // MAC0 public static final byte[] SIGNATURE1_CONTEXT = {0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31}; // Signature1 - public static final byte[] ENCRYPT_CONTEXT = - {0x45, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74}; // Encrypt //Empty strings public static final byte[] EMPTY_MAC_KEY = {0x45, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x4d, 0x41, 0x43, 0x20, 0x6b, 0x65, 0x79}; // "Empty MAC key" @@ -317,89 +315,6 @@ public static short constructHeaders(short []buff, short alg, short keyId, short return ptr; } - /** - * Construct Recipients structure for COSE_Encrypt message. - * - * @param protectedHeaders instance of KMByteBlob which contains encoded KMCoseHeaders. - * @param unprotectedHeaders instance of KMCoseHeaders. - * @param cipherText instance of KMSimple - * @return instance of KMArray. - */ - public static short constructRecipientsStructure(short protectedHeaders, short unprotectedHeaders, - short cipherText) { - // recipients : [+COSE_recipient] - // COSE_recipient = [ - // Headers, - // ciphertext : bstr / nil, - // ? recipients : [+COSE_recipient] - // ] - short arrPtr = KMArray.instance(COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT); - // 1 - protected headers - KMArray.cast(arrPtr).add((short) 0, protectedHeaders); - // 2 - unprotected headers - KMArray.cast(arrPtr).add((short) 1, unprotectedHeaders); - // 2 - payload - KMArray.cast(arrPtr).add((short) 2, cipherText); - - short recipientsArrayPtr = KMArray.instance((short) 1); - KMArray.cast(recipientsArrayPtr).add((short) 0, arrPtr); - return recipientsArrayPtr; - } - - /** - * Construct Encrypt structure required for COSE_Encrypt message. - * - * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders. - * @param aad instance of KMByteBlob. - * @return instance of KMArray. - */ - public static short constructCoseEncryptStructure(short protectedHeader, short aad) { - // Enc_structure = [ - // context : "Encrypt" / "Encrypt0" / "Enc_Recipient" / - // "Mac_Recipient" / "Rec_Recipient", - // protected : empty_or_serialized_map, - // external_aad : bstr - // ] - short arrPtr = KMArray.instance(COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT); - // 1 - protected headers - KMArray.cast(arrPtr).add((short) 0, KMTextString.instance(KMCose.ENCRYPT_CONTEXT, (short) 0, - (short) KMCose.ENCRYPT_CONTEXT.length)); - // 2 - unprotected headers - KMArray.cast(arrPtr).add((short) 1, protectedHeader); - // 2 - payload - KMArray.cast(arrPtr).add((short) 2, aad); - return arrPtr; - } - - /** - * Constructs COSE_Encrypt message. - * - * @param protectedHeader instance of KMByteBlob which wraps KMCoseHeaders. - * @param unProtectedHeader instance of KMCoseHeaders. - * @param cipherText instance of KMByteBlob containing the cipher text. - * @param recipients instance of KMArray containing the recipients instance - * @return instance of KMArray. - */ - public static short constructCoseEncrypt(short protectedHeader, short unProtectedHeader, short cipherText, - short recipients) { - // COSE_Encrypt = [ - // protectedHeader, - // unprotectedHeader, - // ciphertext : bstr / nil, - // recipients : [+COSE_recipient] - // ] - short arrPtr = KMArray.instance(KMCose.COSE_ENCRYPT_ENTRY_COUNT); - // 1 - protected headers - KMArray.cast(arrPtr).add((short) 0, protectedHeader); - // 2 - unprotected headers - KMArray.cast(arrPtr).add((short) 1, unProtectedHeader); - // 2 - payload - KMArray.cast(arrPtr).add((short) 2, cipherText); - // 3 - tag - KMArray.cast(arrPtr).add((short) 3, recipients); - return arrPtr; - } - /** * Constructs the instance of KMCosePair*Tag. * diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java index 599fb5a8..b6c1a67d 100644 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java @@ -41,6 +41,8 @@ public class RemotelyProvisionedComponentDevice { private static final byte FALSE = 0x00; // RKP Version private static final short RKP_VERSION = (short) 0x03; + // The CsrPayload CDDL Schema version. + private static final short CSR_PAYLOAD_CDDL_SCHEMA_VERSION = 1; // Boot params private static final byte OS_VERSION_ID = 0x00; private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01; @@ -260,10 +262,15 @@ public short getHeaderLen(short length) { } public void constructPartialSignedData(byte[] scratchPad, short coseKeysCount, - short totalCoseKeysLen, short challengeByteBlob, short deviceInfo) { + short totalCoseKeysLen, short challengeByteBlob, short deviceInfo, + short versionPtr, short certTypePtr) { // Initialize ECDSA operation initECDSAOperation(); + // Calculate the version Length including header + short versionLength = encoder.getEncodedLength(versionPtr); + // Calculate the CertificateType including header + short certTypeLen = encoder.getEncodedLength(certTypePtr); // Calculate the challenge length short challengeLen = (short) KMByteBlob.cast(challengeByteBlob).length(); if (challengeLen < 32 || challengeLen > 64) { @@ -281,10 +288,10 @@ public void constructPartialSignedData(byte[] scratchPad, short coseKeysCount, short coseKeysArrHeaderLen = getHeaderLen(coseKeysCount); short keysToSignLen = (short) (coseKeysArrHeaderLen + totalCoseKeysLen); - // Calcualte the payload array header len - short paylaodArrHeaderLen = 1; // Array of 3 elements occupies 1 byte. - short payloadLen = (short) (paylaodArrHeaderLen + deviceInfoLen + challengeHeaderLen - + challengeLen + keysToSignLen); + // Calculate the payload array header len + short paylaodArrHeaderLen = 1; // Array of 5 elements occupies 1 byte. + short payloadLen = (short) (paylaodArrHeaderLen + versionLength + certTypeLen + + deviceInfoLen + challengeHeaderLen + challengeLen + keysToSignLen); // Empty aad short aad = KMByteBlob.instance(scratchPad, (short) 0, (short) 0); @@ -315,10 +322,12 @@ public void constructPartialSignedData(byte[] scratchPad, short coseKeysCount, ((KMOperation) operation[0]).update(heap, heapIndex, byteBlobHeaderLen); // Construct partial payload array - short arr = KMArray.instance((short) 3); - KMArray.cast(arr).add((short) 0, deviceInfo); - KMArray.cast(arr).add((short) 1, challengeByteBlob); - KMArray.cast(arr).add((short) 2, KMType.INVALID_VALUE); + short arr = KMArray.instance((short) 5); + KMArray.cast(arr).add((short) 0, versionPtr); + KMArray.cast(arr).add((short) 1, certTypePtr); + KMArray.cast(arr).add((short) 2, deviceInfo); + KMArray.cast(arr).add((short) 3, challengeByteBlob); + KMArray.cast(arr).add((short) 4, KMType.INVALID_VALUE); short paritalPayloadArrayLen = encoder.encode(arr, heap, heapIndex, prevReclaimIndex); ((KMOperation) operation[0]).update(heap, heapIndex, paritalPayloadArrayLen); @@ -341,12 +350,14 @@ public void processBeginSendData(APDU apdu) throws Exception { byte[] scratchPad = apdu.getBuffer(); // Create DeviceInfo short deviceInfo = createDeviceInfo(scratchPad); + short versionPtr = KMInteger.uint_16(CSR_PAYLOAD_CDDL_SCHEMA_VERSION); + short certTypePtr = KMTextString.instance(DI_CERT_TYPE, (short) 0, (short) DI_CERT_TYPE.length); constructPartialSignedData(scratchPad, KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(), KMInteger.cast(KMArray.cast(arr).get((short) 1)).getShort(), KMArray.cast(arr).get((short) 2), - deviceInfo); + deviceInfo, versionPtr, certTypePtr); // Store the total keys in data table. short dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE); Util.setShort(data, dataEntryIndex, @@ -364,9 +375,11 @@ public void processBeginSendData(APDU apdu) throws Exception { //release memory repository.reclaimMemory(MAX_ENCODED_BUF_SIZE); // Send response. - short array = KMArray.instance((short) 2); + short array = KMArray.instance((short) 4); KMArray.cast(array).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(array).add((short) 1, encodedDeviceInfo); + KMArray.cast(array).add((short) 2, versionPtr); + KMArray.cast(array).add((short) 3, certTypePtr); KMKeymasterApplet.sendOutgoing(apdu, array); } catch (Exception e) { clearDataTable(); @@ -700,7 +713,6 @@ private void updateState(byte state) { * "vendor_patch_level" : uint, // YYYYMMDD * "security_level" : "tee" / "strongbox" * "fused": 1 / 0, - * "cert_type": "widevine" / "keymint" * } */ private short createDeviceInfo(byte[] scratchpad) { @@ -736,8 +748,6 @@ private short createDeviceInfo(byte[] scratchpad) { KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length)); updateItem(rkpTmpVariables, metaOffset, FUSED, KMInteger.uint_8((byte) storeDataInst.secureBootMode)); - updateItem(rkpTmpVariables, metaOffset, CERT_TYPE, - KMTextString.instance(DI_CERT_TYPE, (short) 0, (short) DI_CERT_TYPE.length)); // Create device info map. short map = KMMap.instance(rkpTmpVariables[1]); short mapIndex = 0; diff --git a/HAL/CborConverter.cpp b/HAL/CborConverter.cpp index 1d5f1824..6c26ce8b 100644 --- a/HAL/CborConverter.cpp +++ b/HAL/CborConverter.cpp @@ -271,6 +271,15 @@ std::optional> CborConverter::getCertificateChain(const std: return certChain; } +std::optional CborConverter::getTextStr(const unique_ptr& item, const uint32_t pos) { + auto textStrItem = getItemAtPos(item, pos); + if (!textStrItem || (MajorType::TSTR != getType(textStrItem.value()))) { + return std::nullopt; + } + const Tstr* tstr = textStrItem.value().get()->asTstr(); + return tstr->value(); +} + std::optional CborConverter::getByteArrayStr(const unique_ptr& item, const uint32_t pos) { auto optTemp = getByteArrayVec(item, pos); if (!optTemp) { diff --git a/HAL/CborConverter.h b/HAL/CborConverter.h index c00b852c..258af91d 100644 --- a/HAL/CborConverter.h +++ b/HAL/CborConverter.h @@ -73,6 +73,8 @@ class CborConverter { std::optional getSharedSecretParameters(const std::unique_ptr& item, const uint32_t pos); std::optional getByteArrayStr(const unique_ptr& item, const uint32_t pos); + + std::optional getTextStr(const unique_ptr& item, const uint32_t pos); std::optional> getByteArrayVec(const unique_ptr& item, const uint32_t pos); diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp index 4bd8db28..a549dc8a 100644 --- a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp +++ b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp @@ -130,7 +130,8 @@ JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(bool, ScopedAStatus JavacardRemotelyProvisionedComponentDevice::beginSendData(const std::vector& keysToSign, - DeviceInfo &deviceInfo, const std::vector& challenge) { + const std::vector& challenge, DeviceInfo* deviceInfo, uint32_t* version, + std::string* certificateType) { uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign); cppbor::Array array; array.add(keysToSign.size()); @@ -143,10 +144,22 @@ JavacardRemotelyProvisionedComponentDevice::beginSendData(const std::vectordeviceInfo = std::move(optDecodedDeviceInfo.value()); + auto optVersion = cbor_.getUint64(item, 2); + if (!optVersion) { + LOG(ERROR) << "Error in decoding version in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *version = optVersion.value(); + auto optCertType = cbor_.getTextStr(item, 3); + if (!optCertType) { + LOG(ERROR) << "Error in decoding cert type in beginSendData."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + *certificateType = std::move(optCertType.value()); return ScopedAStatus::ok(); } @@ -253,6 +266,8 @@ JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2( const std::vector& challenge, std::vector* csr) { uint32_t version; + uint32_t csrPayloadSchemaVersion; + std::string certificateType; uint32_t respFlag; DeviceInfo deviceInfo; Array coseKeys; @@ -263,7 +278,7 @@ JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2( std::vector udsCertChain; cppbor::Array payLoad; - auto ret = beginSendData(keysToSign, deviceInfo, challenge); + auto ret = beginSendData(keysToSign, challenge, &deviceInfo, &csrPayloadSchemaVersion, &certificateType); if (!ret.isOk()) return ret; ret = updateMacedKey(keysToSign, coseKeys); @@ -280,6 +295,8 @@ JavacardRemotelyProvisionedComponentDevice::generateCertificateRequestV2( if (!ret.isOk()) return ret; auto payload = cppbor::Array() + .add(csrPayloadSchemaVersion) + .add(certificateType) .add(EncodedItem(deviceInfo.deviceInfo)) // deviceinfo .add(challenge) // Challenge .add(std::move(coseKeys)) // KeysToSign diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.h b/HAL/JavacardRemotelyProvisionedComponentDevice.h index 3940cd94..ae2970e4 100644 --- a/HAL/JavacardRemotelyProvisionedComponentDevice.h +++ b/HAL/JavacardRemotelyProvisionedComponentDevice.h @@ -61,7 +61,8 @@ class JavacardRemotelyProvisionedComponentDevice private: ScopedAStatus beginSendData(const std::vector& keysToSign, - DeviceInfo &deviceInfo, const std::vector& challenge); + const std::vector& challenge, DeviceInfo* deviceInfo, uint32_t* version, + std::string* certificateType); ScopedAStatus updateMacedKey(const std::vector& keysToSign, Array& coseKeys); diff --git a/HAL/OmapiTransport.cpp b/HAL/OmapiTransport.cpp index 44a7fbe3..bfe2f2ad 100644 --- a/HAL/OmapiTransport.cpp +++ b/HAL/OmapiTransport.cpp @@ -27,8 +27,8 @@ namespace keymint::javacard { -constexpr uint8_t SELECTABLE_AID[] = {0xA0, 0x00, 0x00, 0x04, 0x76, 0x41, 0x6E, 0x64, - 0x72, 0x6F, 0x69, 0x64, 0x43, 0x54, 0x53, 0x31}; +constexpr uint8_t KEYMINT_APPLET_AID[] = {0xA0, 0x00, 0x00, 0x00, 0x62, 0x03, + 0x02, 0x0C, 0x01, 0x01, 0x01}; std::string const ESE_READER_PREFIX = "eSE"; constexpr const char omapiServiceName[] = "android.se.omapi.ISecureElementService/default"; @@ -49,7 +49,7 @@ keymaster_error_t OmapiTransport::initialize() { return static_cast(KM_ERROR_HARDWARE_NOT_YET_AVAILABLE); } - int size = sizeof(SELECTABLE_AID) / sizeof(SELECTABLE_AID[0]); + int size = sizeof(KEYMINT_APPLET_AID) / sizeof(KEYMINT_APPLET_AID[0]); // reset readers, clear readers if already existing if (mVSReaders.size() > 0) { closeConnection(); @@ -116,7 +116,7 @@ keymaster_error_t OmapiTransport::initialize() { return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; } - std::vector aid(SELECTABLE_AID, SELECTABLE_AID + size); + std::vector aid(KEYMINT_APPLET_AID, KEYMINT_APPLET_AID + size); auto mSEListener = ndk::SharedRefBase::make(); status = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); if (!status.isOk()) { @@ -171,9 +171,9 @@ bool OmapiTransport::internalTransmitApdu( } } - int size = sizeof(SELECTABLE_AID) / sizeof(SELECTABLE_AID[0]); - std::vector aid(SELECTABLE_AID, SELECTABLE_AID + size); - if(result) { + int size = sizeof(KEYMINT_APPLET_AID) / sizeof(KEYMINT_APPLET_AID[0]); + std::vector aid(KEYMINT_APPLET_AID, KEYMINT_APPLET_AID + size); + if (result) { auto mSEListener = ndk::SharedRefBase::make(); res = session->openLogicalChannel(aid, 0x00, mSEListener, &channel); if (!res.isOk()) {