diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java index 6c2a5a77..f3788eab 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAndroidSEApplet.java @@ -20,7 +20,6 @@ import org.globalplatform.upgrade.UpgradeManager; import com.android.javacard.seprovider.KMAndroidSEProvider; -import com.android.javacard.seprovider.KMDeviceUniqueKeyPair; import com.android.javacard.seprovider.KMError; import com.android.javacard.seprovider.KMException; import com.android.javacard.seprovider.KMType; @@ -38,12 +37,12 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis // MSB byte is for Major version and LSB byte is for Minor version. public static final short KM_APPLET_PACKAGE_VERSION = 0x0300; - private static final byte KM_BEGIN_STATE = 0x00; - private static final byte ILLEGAL_STATE = KM_BEGIN_STATE + 1; private static final short POWER_RESET_MASK_FLAG = (short) 0x4000; // Provider specific Commands private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_KEYMINT_PROVIDER_APDU_START + 1; //0x01 + private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = INS_KEYMINT_PROVIDER_APDU_START + 2; //0x02 private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; // Commands 4, 5 and 6 are reserved for vendor usage. private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7; @@ -52,10 +51,7 @@ public class KMAndroidSEApplet extends KMKeymasterApplet implements OnUpgradeLis private static final byte INS_SE_FACTORY_PROVISIONING_LOCK_CMD = INS_KEYMINT_PROVIDER_APDU_START + 10; private static final byte INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD = INS_KEYMINT_PROVIDER_APDU_START + 11; private static final byte INS_OEM_UNLOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 12; - private static final byte INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD = - INS_KEYMINT_PROVIDER_APDU_START + 13; - private static final byte INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD = - INS_KEYMINT_PROVIDER_APDU_START + 14; + // 13 and 14 are reserved for RKP Device UniqueKeyPair and Cert chain. private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_KEYMINT_PROVIDER_APDU_START + 15; private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 16; // Unused @@ -131,12 +127,12 @@ public void process(APDU apdu) { processGetProvisionStatusCmd(apdu); break; - case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: - processProvisionRkpDeviceUniqueKeyPair(apdu); + case INS_PROVISION_ATTESTATION_KEY_CMD: + processProvisionAttestationKey(apdu); break; - case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD: - processProvisionRkpAdditionalCertChain(apdu); + case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: + processProvisionAttestationCertDataCmd(apdu); break; case INS_SE_FACTORY_PROVISIONING_LOCK_CMD: @@ -217,8 +213,8 @@ && isSeFactoryProvisioningLocked())) { } break; - case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: - case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD: + case INS_PROVISION_ATTESTATION_KEY_CMD: + case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: if(isSeFactoryProvisioningLocked()) { result = false; } @@ -247,8 +243,10 @@ private boolean isSeFactoryProvisioningLocked() { private boolean isSeFactoryProvisioningComplete() { short pStatus = kmDataStore.getProvisionStatus(); - if (PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR == - (pStatus & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) { + short completeStatus = PROVISION_STATUS_ATTESTATION_CERT_CHAIN | + PROVISION_STATUS_ATTESTATION_KEY | + PROVISION_STATUS_ATTESTATION_CERT_PARAMS; + if (completeStatus == (pStatus & completeStatus)) { return true; } return false; @@ -304,7 +302,6 @@ private void authenticateOEM(byte[] plainMsg, APDU apdu) { private void processProvisionOEMRootPublicKeyCmd(APDU apdu) { // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); // Arguments short keyparams = KMKeyParameters.exp(); short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT); @@ -366,72 +363,6 @@ private void processProvisionOEMRootPublicKeyCmd(APDU apdu) { KMByteBlob.cast(tmpVariables[0]).length()); } - private static void processProvisionRkpDeviceUniqueKeyPair(APDU apdu) { - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - short arr = KMArray.instance((short) 1); - short coseKeyExp = KMCoseKey.exp(); - KMArray.cast(arr).add((short) 0, coseKeyExp); //[ CoseKey ] - arr = receiveIncoming(apdu, arr); - // Get cose key. - short coseKey = KMArray.cast(arr).get((short) 0); - short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0); - short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen); - //Store the Device unique Key. - kmDataStore.createRkpDeviceUniqueKeyPair(scratchPad, (short) 0, pubKeyLen, scratchPad, - pubKeyLen, privKeyLen); - short bcc = generateBcc(false, scratchPad); - short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, - MAX_COSE_BUF_SIZE); - kmDataStore.persistBootCertificateChain(scratchPad, (short) 0, len); - kmDataStore.setProvisionStatus(PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR); - sendResponse(apdu, KMError.OK); - } - - private void processProvisionRkpAdditionalCertChain(APDU apdu) { - // X509 certificate chain is received as shown below: - /** - * x509CertChain = bstr .cbor UdsCerts - * - * AdditionalDKSignatures = { - * * SignerName => DKCertChain - * } - * ; SignerName is a string identifier that indicates both the signing authority as - * ; well as the format of the DKCertChain - * SignerName = tstr - * - * DKCertChain = [ - * 2* X509Certificate ; Root -> ... -> Leaf. "Root" is the vendor self-signed - * ; cert, "Leaf" contains DK_pub. There may also be - * ; intermediate certificates between Root and Leaf. - * ] - * ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA) - * X509Certificate = bstr - */ - // Store the cbor encoded UdsCerts as it is in the persistent memory so cbor decoding is - // required here. - byte[] srcBuffer = apdu.getBuffer(); - short recvLen = apdu.setIncomingAndReceive(); - short srcOffset = apdu.getOffsetCdata(); - short bufferLength = apdu.getIncomingLength(); - short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); - short index = bufferStartOffset; - byte[] buffer = repository.getHeap(); - while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); - index += recvLen; - recvLen = apdu.receiveBytes(srcOffset); - } - short byteHeaderLen = decoder.readCertificateChainHeaderLen(buffer, bufferStartOffset, - bufferLength); - kmDataStore.persistAdditionalCertChain(buffer, (short) (bufferStartOffset + byteHeaderLen), - (short) (bufferLength - byteHeaderLen)); - kmDataStore.setProvisionStatus(PROVISION_STATUS_ADDITIONAL_CERT_CHAIN); - // reclaim memory - repository.reclaimMemory(bufferLength); - sendResponse(apdu, KMError.OK); - } - private void processProvisionAttestIdsCmd(APDU apdu) { short keyparams = KMKeyParameters.exp(); short cmd = KMArray.instance((short) 1); @@ -488,6 +419,113 @@ private void processProvisionPreSharedSecretCmd(APDU apdu) { KMByteBlob.cast(val).length()); } + + private void processProvisionAttestationKey(APDU apdu) { + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + short keyparams = KMKeyParameters.exp(); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT); + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 3); + KMArray.cast(argsProto).add((short) 0, keyparams); + KMArray.cast(argsProto).add((short) 1, keyFormatPtr); + KMArray.cast(argsProto).add((short) 2, blob); + short args = receiveIncoming(apdu, argsProto); + + // key params should have os patch, os version and verified root of trust + data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); + tmpVariables[0] = KMArray.cast(args).get((short) 1); + data[IMPORTED_KEY_BLOB] = KMArray.cast(args).get((short) 2); + // Key format must be RAW format + byte keyFormat = KMEnum.cast(tmpVariables[0]).getVal(); + if (keyFormat != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + data[ORIGIN] = KMType.IMPORTED; + + // get algorithm - only EC keys expected + tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.EC) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // get digest - only SHA256 supported + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0); + if (tmpVariables[0] != KMType.SHA2_256) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + } else { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Purpose should be ATTEST_KEY + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0); + if (tmpVariables[0] != KMType.ATTEST_KEY) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } else { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Import EC Key - initializes data[SECRET] data[PUB_KEY] + importECKeys(scratchPad, keyFormat); + // persist key + kmDataStore.createAttestationKeyPair( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTESTATION_KEY); + sendResponse(apdu, KMError.OK); + } + + private void processProvisionAttestationCertDataCmd(APDU apdu) { + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = apdu.setIncomingAndReceive(); + short srcOffset = apdu.getOffsetCdata(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); + short index = bufferStartOffset; + byte[] buffer = repository.getHeap(); + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + // CertificateData = [ + // X509CertifcateChain: bstr, ; DER encoded X.509 certificate chain ordered from Leaf to Root. + // Issuer: bstr, ; DER encoded subject field + // ] + + // srcBuffer holds the corresponding offsets and lengths of the certChain, certIssuer and certExpiry + // respectively in the input buffer. + decoder.decodeCertificateData((short) 2, /* Length of the input array */ + buffer, bufferStartOffset, bufferLength, srcBuffer, (short) 0); + // Persist data. + // The offset and length of the CertChain in the input buffer are stored in the srcBuffer + // at 0th and 2nd positions respectively. + kmDataStore.persistAttestationCertChain(buffer, + Util.getShort(srcBuffer, (short) 0), Util.getShort(srcBuffer, (short) 2)); + // The offset and length of the CertIssuer in the input buffer are stored in the srcBuffer + // at 4th and 6th positions respectively. + kmDataStore.persistAttestationCertIssuer(buffer, + Util.getShort(srcBuffer, (short) 4), Util.getShort(srcBuffer, (short) 6)); + + // reclaim memory + repository.reclaimMemory(bufferLength); + kmDataStore.setProvisionStatus( + (short) (PROVISION_STATUS_ATTESTATION_CERT_CHAIN | PROVISION_STATUS_ATTESTATION_CERT_PARAMS)); + sendResponse(apdu, KMError.OK); + } //This function masks the error code with POWER_RESET_MASK_FLAG // in case if card reset event occurred. The clients of the Applet @@ -524,10 +562,9 @@ private void processGetProvisionStatusCmd(APDU apdu) { private boolean isProvisioningComplete() { short pStatus = kmDataStore.getProvisionStatus(); - short pCompleteStatus = - PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR | PROVISION_STATUS_PRESHARED_SECRET - | PROVISION_STATUS_ATTEST_IDS | PROVISION_STATUS_OEM_PUBLIC_KEY | - PROVISION_STATUS_SECURE_BOOT_MODE; + short pCompleteStatus = PROVISION_STATUS_ATTESTATION_CERT_CHAIN | PROVISION_STATUS_ATTESTATION_CERT_PARAMS + | PROVISION_STATUS_ATTESTATION_KEY | PROVISION_STATUS_PRESHARED_SECRET | PROVISION_STATUS_ATTEST_IDS + | PROVISION_STATUS_OEM_PUBLIC_KEY | PROVISION_STATUS_SECURE_BOOT_MODE; if (kmDataStore.isProvisionLocked() || (pCompleteStatus == (pStatus & pCompleteStatus))) { return true; } diff --git a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 3050fa32..9ac796da 100644 --- a/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/AndroidSEProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -382,7 +382,8 @@ private static void pushExtensions() { if (states[KEY_USAGE] != 0) { pushKeyUsage(states[KEY_USAGE], states[UNUSED_BITS]); } - if (states[CERT_MODE] == KMType.ATTESTATION_CERT) { + if (states[CERT_MODE] == KMType.ATTESTATION_CERT || + states[CERT_MODE] == KMType.FACTORY_ATTESTATION_CERT) { pushKeyDescription(); } pushSequenceHeader((short) (last - indexes[STACK_PTR])); @@ -907,12 +908,16 @@ else if (rsaSign) { if (sigLen > ECDSA_MAX_SIG_LEN) KMException.throwIt(KMError.UNKNOWN_ERROR); } - // Adjust signature length - indexes[STACK_PTR] = signatureOffset; - pushBitStringHeader((byte) 0, sigLen); - } else if (!fakeCert) { // No attestation key provisioned in the factory - KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } else if (!fakeCert) { + // Sign with Factory provisioned attestation key. + sigLen = seProvider.ecSign256(KMKeymintDataStore.instance().getAttestationKeyPair(), + stack, indexes[TBS_START], indexes[TBS_LENGTH], stack, signatureOffset); + if (sigLen > ECDSA_MAX_SIG_LEN) + KMException.throwIt(KMError.UNKNOWN_ERROR); } + // Adjust signature length + indexes[STACK_PTR] = signatureOffset; + pushBitStringHeader((byte) 0, sigLen); last = (short) (signatureOffset + sigLen); // Add certificate sequence header indexes[STACK_PTR] = indexes[TBS_START]; @@ -925,6 +930,8 @@ else if (rsaSign) { public void build() { if(states[CERT_MODE] == KMType.FAKE_CERT) { build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true); + } else if(states[CERT_MODE] == KMType.FACTORY_ATTESTATION_CERT) { + build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, false, false); } else { build(indexes[CERT_ATT_KEY_SECRET], indexes[CERT_ATT_KEY_RSA_PUB_MOD], (states[CERT_RSA_SIGN] == 0 ? false: true), false); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java index 4142494e..e9da2c6d 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAndroidSEProvider.java @@ -595,11 +595,7 @@ public KMOperation createSymmetricCipher(short alg, short purpose, short macLeng short cipherAlg = mapCipherAlg((byte) alg, (byte) padding, (byte) blockMode, (byte) 0); KMOperation operation = null; - if (isRkp) { - operation = poolMgr.getRKpOperation(purpose, cipherAlg, alg, padding, blockMode, macLength); - } else { - operation = poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength, secretLength, false); - } + operation = poolMgr.getOperationImpl(purpose, cipherAlg, alg, padding, blockMode, macLength, secretLength, false); // Get the KeyObject from the operation and update the key with the secret key material. KMKeyObject keyObj = operation.getKeyObject(); Key key = (Key)keyObj.keyObjectInst; @@ -625,13 +621,9 @@ public KMOperation createHmacSignerVerifier(short purpose, short digest, if (digest != KMType.SHA2_256) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } - if (isRkp) { - operation = poolMgr.getRKpOperation(purpose, Signature.ALG_HMAC_SHA_256, KMType.HMAC, - KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE); - } else { - operation = poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, + + operation = poolMgr.getOperationImpl(purpose, Signature.ALG_HMAC_SHA_256, KMType.HMAC, KMType.INVALID_VALUE, KMType.INVALID_VALUE, KMType.INVALID_VALUE, (short)0, false); - } // Get the KeyObject from the operation and update the key with the secret key material. KMKeyObject keyObj = operation.getKeyObject(); HMACKey key = (HMACKey)keyObj.keyObjectInst; @@ -656,29 +648,6 @@ private KMOperation createHmacSignerVerifier(short purpose, short digest, HMACKe return operation; } - @Override - public KMOperation getRkpOperation(byte purpose, byte alg, - byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, - short keyLength, byte[] ivBuf, short ivStart, short ivLength, - short macLength) { - KMOperation opr = null; - switch (alg) { - case KMType.AES: - // Convert macLength to bytes - macLength = (short) (macLength / 8); - opr = createSymmetricCipher(alg, purpose, macLength, blockMode, padding, keyBuf, keyStart, keyLength, ivBuf, - ivStart, ivLength, true/* isRKP */); - break; - case KMType.HMAC: - opr = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, keyLength, true/* isRKP */); - break; - default: - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - return opr; - } - @Override public KMOperation initSymmetricOperation(byte purpose, byte alg, byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, @@ -974,7 +943,7 @@ public short rsaSign256Pkcs1(byte[] secret, short secretStart, short secretLengt @Override public boolean isAttestationKeyProvisioned() { - return false; + return true; } @Override @@ -1064,49 +1033,16 @@ public boolean ecVerify256(byte[] pubKey, short pubKeyOffset, short pubKeyLen, } @Override - public short ecSign256(KMDeviceUniqueKeyPair ecPrivKey, byte[] inputDataBuf, - short inputDataStart, short inputDataLength, byte[] outputDataBuf, - short outputDataStart) { - Signature.OneShot signer = null; - try { - signer = Signature.OneShot.open(MessageDigest.ALG_SHA_256, - Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL); - signer.init(((KMECDeviceUniqueKey) ecPrivKey).ecKeyPair.getPrivate(), Signature.MODE_SIGN); - return signer.sign(inputDataBuf, inputDataStart, inputDataLength, - outputDataBuf, outputDataStart); - } finally { - if (signer != null) { - signer.close(); - } - } - } - - @Override - public KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(KMDeviceUniqueKeyPair key, - byte[] pubKey, short pubKeyOff, short pubKeyLen, byte[] privKey, - short privKeyOff, short privKeyLen) { + public KMAttestationKey createAttestationKey(KMAttestationKey key, + byte[] keyData, short offset, short length) { if (key == null) { KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); poolMgr.initECKey(ecKeyPair); - key = new KMECDeviceUniqueKey(ecKeyPair); - } - ECPrivateKey ecKeyPair = (ECPrivateKey) ((KMECDeviceUniqueKey) key).ecKeyPair.getPrivate(); - ECPublicKey ecPublicKey = (ECPublicKey) ((KMECDeviceUniqueKey) key).ecKeyPair.getPublic(); - ecKeyPair.setS(privKey, privKeyOff, privKeyLen); - ecPublicKey.setW(pubKey, pubKeyOff, pubKeyLen); - return (KMDeviceUniqueKeyPair) key; - } - - @Override - public KMRkpMacKey createRkpMacKey(KMRkpMacKey rkpMacKey, byte[] keyData, - short offset, short length) { - if (rkpMacKey == null) { - HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), - false); - rkpMacKey = new KMHmacKey(key); + key = new KMECPrivateKey(ecKeyPair); } - ((KMHmacKey) rkpMacKey).hmacKey.setKey(keyData, offset, length); - return rkpMacKey; + ECPrivateKey ecKeyPair = (ECPrivateKey) ((KMECPrivateKey) key).ecKeyPair.getPrivate(); + ecKeyPair.setS(keyData, offset, length); + return (KMAttestationKey) key; } @Override @@ -1152,12 +1088,9 @@ public void onSave(Element element, byte interfaceType, Object object) { case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: KMHmacKey.onSave(element, (KMHmacKey) object); break; - case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: - KMECDeviceUniqueKey.onSave(element, (KMECDeviceUniqueKey) object); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + KMECPrivateKey.onSave(element, (KMECPrivateKey) object); break; - case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: - KMHmacKey.onSave(element, (KMHmacKey) object); - break; default: ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } @@ -1176,16 +1109,14 @@ public Object onRestore(Element element) { return KMAESKey.onRestore((AESKey) element.readObject()); case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: return KMHmacKey.onRestore((HMACKey) element.readObject()); - case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: - return KMECDeviceUniqueKey.onRestore((KeyPair) element.readObject()); - case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: - return KMHmacKey.onRestore((HMACKey) element.readObject()); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.onRestore((KeyPair) element.readObject()); default: ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } return null; } - + @Override public short getBackupPrimitiveByteCount(byte interfaceType) { short primitiveCount = 1; // interface type @@ -1196,11 +1127,8 @@ public short getBackupPrimitiveByteCount(byte interfaceType) { case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: primitiveCount += KMHmacKey.getBackupPrimitiveByteCount(); break; - case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: - primitiveCount += KMECDeviceUniqueKey.getBackupPrimitiveByteCount(); - break; - case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: - primitiveCount += KMHmacKey.getBackupPrimitiveByteCount(); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + primitiveCount += KMECPrivateKey.getBackupPrimitiveByteCount(); break; default: ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); @@ -1215,10 +1143,8 @@ public short getBackupObjectCount(byte interfaceType) { return KMAESKey.getBackupObjectCount(); case KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY: return KMHmacKey.getBackupObjectCount(); - case KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR: - return KMECDeviceUniqueKey.getBackupObjectCount(); - case KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY: - return KMHmacKey.getBackupObjectCount(); + case KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY: + return KMECPrivateKey.getBackupObjectCount(); default: ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java index 6cd8e7b0..e2dee08a 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMAttestationCert.java @@ -157,7 +157,6 @@ KMAttestationCert makeUniqueId(byte[] scratchpad, short scratchPadOff, byte[] cr */ short getCertLength(); - /** * Build a fake signed certificate. After this method executes the certificate is ready with the * signature equal to 1 byte which is 0 and with rsa signature algorithm. diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java index feb7d170..f1ffd5d0 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDataStoreConstants.java @@ -4,9 +4,7 @@ public class KMDataStoreConstants { // INTERFACE Types public static final byte INTERFACE_TYPE_COMPUTED_HMAC_KEY = 0x01; public static final byte INTERFACE_TYPE_ATTESTATION_KEY = 0x02; - public static final byte INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR = 0x03; public static final byte INTERFACE_TYPE_MASTER_KEY = 0x04; public static final byte INTERFACE_TYPE_PRE_SHARED_KEY = 0x05; - public static final byte INTERFACE_TYPE_RKP_MAC_KEY = 0x06; } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKeyPair.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKeyPair.java deleted file mode 100644 index 9bbccd8f..00000000 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMDeviceUniqueKeyPair.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.seprovider; - -public interface KMDeviceUniqueKeyPair { - - short getPublicKey(byte[] buf, short offset); -} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java deleted file mode 100644 index 41df6f48..00000000 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECDeviceUniqueKey.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.seprovider; -import org.globalplatform.upgrade.Element; - -import javacard.security.ECPrivateKey; -import javacard.security.ECPublicKey; -import javacard.security.KeyPair; - -public class KMECDeviceUniqueKey implements KMDeviceUniqueKeyPair { - - public KeyPair ecKeyPair; - - @Override - public short getPublicKey(byte[] buf, short offset) { - ECPublicKey publicKey = (ECPublicKey) ecKeyPair.getPublic(); - return publicKey.getW(buf, offset); - } - - public KMECDeviceUniqueKey(KeyPair ecPair) { - ecKeyPair = ecPair; - } - - public static void onSave(Element element, KMECDeviceUniqueKey kmKey) { - element.write(kmKey.ecKeyPair); - } - - public static KMECDeviceUniqueKey onRestore(KeyPair ecKey) { - if (ecKey == null) { - return null; - } - return new KMECDeviceUniqueKey(ecKey); - } - - public static short getBackupPrimitiveByteCount() { - return (short) 0; - } - - public static short getBackupObjectCount() { - return (short) 1; - } -} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java index 1ce36ee9..8816adb1 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMECPrivateKey.java @@ -17,8 +17,6 @@ import org.globalplatform.upgrade.Element; -import javacard.security.AESKey; -import javacard.security.ECPrivateKey; import javacard.security.KeyPair; public class KMECPrivateKey implements KMAttestationKey { diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java index cbb17e07..8cdaac86 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMHmacKey.java @@ -19,7 +19,7 @@ import javacard.security.HMACKey; -public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey, KMRkpMacKey { +public class KMHmacKey implements KMPreSharedKey, KMComputedHmacKey { public HMACKey hmacKey; diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java index 1a00886b..f9504f69 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMPoolManager.java @@ -67,12 +67,6 @@ public class KMPoolManager { private Object[] hmacSignOperationPool; private Object[] keysPool; - // RKP uses AESGCM and HMAC in generateCSR flow. - KMOperation rkpOPeration; - Cipher rkpAesGcm; - Signature rkpHmac; - KMKeyObject rkpHmacKey; - KMKeyObject rkpAesKey; final byte[] KEY_ALGS = { AES_128, @@ -200,16 +194,6 @@ private KMPoolManager() { initializeCipherPool(); initializeKeyAgreementPool(); initializeKeysPool(); - // Initialize the Crypto and Key objects required for RKP flow. - initializeRKpObjects(); - } - - private void initializeRKpObjects() { - rkpOPeration = new KMOperationImpl(); - rkpAesGcm = Cipher.getInstance(AEADCipher.ALG_AES_GCM, false); - rkpHmac = Signature.getInstance(Signature.ALG_HMAC_SHA_256, false); - rkpAesKey = createKeyObjectInstance(AES_256); - rkpHmacKey = createKeyObjectInstance(KMType.HMAC); } private void initializeKeysPool() { @@ -437,35 +421,6 @@ private void reserveOperation(KMOperation operation, short purpose, short strong ((KMOperationImpl) operation).setKeyObject(keyObject); setObject(purpose, operation, obj); } - - public KMOperation getRKpOperation(short purpose, short alg, short strongboxAlgType, - short padding, short blockMode, short macLength) { - if (((KMOperationImpl) rkpOPeration).getPurpose() != KMType.INVALID_VALUE) { - // Should not come here. - KMException.throwIt(KMError.UNKNOWN_ERROR); - } - Object cryptoObj = null; - KMKeyObject keyObject = null; - - switch (alg) { - case AEADCipher.ALG_AES_GCM: - cryptoObj = rkpAesGcm; - keyObject = rkpAesKey; - break; - case Signature.ALG_HMAC_SHA_256: - cryptoObj = rkpHmac; - keyObject = rkpHmacKey; - break; - default: - // Should not come here. - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - break; - } - reserveOperation(rkpOPeration, purpose, strongboxAlgType, padding, blockMode, macLength, - cryptoObj, keyObject); - return rkpOPeration; - } - public KMOperation getOperationImpl(short purpose, short alg, short strongboxAlgType, short padding, @@ -610,8 +565,6 @@ public void powerReset() { ((KMOperationImpl) hmacSignOperationPool[index]).abort(); index++; } - // release rkp operation - rkpOPeration.abort(); } } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java deleted file mode 100644 index 62116d24..00000000 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMRkpMacKey.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.android.javacard.seprovider; - - -public interface KMRkpMacKey { -} diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index 2a99f81b..bb4cf83c 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -489,24 +489,6 @@ boolean ecVerify256( short signatureDataStart, short signatureDataLen); - /** - * This is a oneshot operation that signs the data using device unique key. - * - * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data. - * @param inputDataBuf is the buffer of the input data. - * @param inputDataStart is the start of the input data buffer. - * @param inputDataLength is the length of the input data buffer in bytes. - * @param outputDataBuf is the output buffer that contains the signature. - * @param outputDataStart is the start of the output data buffer. - * @return length of the decrypted data. - */ - short ecSign256( - KMDeviceUniqueKeyPair ecPrivKey, - byte[] inputDataBuf, - short inputDataStart, - short inputDataLength, - byte[] outputDataBuf, - short outputDataStart); short ecSign256(byte[] secret, short secretStart, short secretLength, byte[] inputDataBuf, short inputDataStart, short inputDataLength, @@ -599,39 +581,6 @@ KMOperation initSymmetricOperation( short macLength, boolean oneShot); - /** - * This function creates an Operation instance only for RKP module. - * - * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be - * KMType.SIGN and KMType.VERIFY for HMAC algorithm - * @param alg is KMType.HMAC, KMType.AES or KMType.DES. - * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. - * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). - * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is - * 0. - * @param keyBuf is aes, des or hmac key buffer. - * @param keyStart is the start of the key buffer. - * @param keyLength is the length of the key buffer. - * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) - * @param ivStart is the start of the iv buffer. - * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES - * with ECB mode. - * @param macLength is the mac length in case of signing operation for hmac algorithm. - * @return KMOperation instance. - */ - KMOperation getRkpOperation(byte purpose, - byte alg, - byte digest, - byte padding, - byte blockMode, - byte[] keyBuf, - short keyStart, - short keyLength, - byte[] ivBuf, - short ivStart, - short ivLength, - short macLength); - /** * This creates a persistent operation for signing, verify, encryption and decryption using RSA * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public @@ -707,20 +656,18 @@ KMOperation initAsymmetricOperation( short getAttestationKeyAlgorithm(); /** - * Creates an ECKey instance and sets the public and private keys to it. + * This function creates an ECKey and initializes the ECPrivateKey with the provided input key + * data. The initialized Key is maintained by the SEProvider. This function should be called only + * while provisioning the attestation key. * - * @param testMode to indicate if current execution is for test or production. - * @param pubKey buffer containing the public key. - * @param pubKeyOff public key buffer start offset. - * @param pubKeyLen public key buffer length. - * @param privKey buffer containing the private key. - * @param privKeyOff private key buffer start offset. - * @param privKeyLen private key buffer length. - * @return instance of KMDeviceUniqueKey. + * @param key instance of the KMAttestationKey. + * @param keyData buffer containing the ec private key. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of KMAttestationKey. */ - KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(KMDeviceUniqueKeyPair key, - byte[] pubKey, short pubKeyOff, short pubKeyLen, - byte[] privKey, short privKeyOff, short privKeyLen); + KMAttestationKey createAttestationKey(KMAttestationKey key, + byte[] keyData, short offset, short length); /** * This is a one-shot operation the does digest of the input mesage. @@ -781,16 +728,4 @@ KMPreSharedKey createPreSharedKey(KMPreSharedKey presharedKey, byte[] key, short * @return count of the objects. */ short getBackupObjectCount(byte interfaceType); - - /** - * This function creates an HMACKey and initializes the key with the provided input key data. - * - * @param keyData buffer containing the key data. - * @param offset start of the buffer. - * @param length length of the buffer. - * @return An instance of the KMRkpMacKey. - */ - KMRkpMacKey createRkpMacKey(KMRkpMacKey createComputedHmacKey, byte[] keyData, - short offset, short length); - } diff --git a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java index d696e332..8e6e35f1 100644 --- a/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java +++ b/Applet/AndroidSEProviderLib/src/com/android/javacard/seprovider/KMType.java @@ -16,11 +16,6 @@ package com.android.javacard.seprovider; -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; - /** * This class declares all types, tag types, and tag keys. It also establishes basic structure of * any KMType i.e. struct{byte type, short length, value} where value can any of the KMType. Also, diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java index 45a88dcd..df100af3 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMAttestationCertImpl.java @@ -384,7 +384,8 @@ private static void pushExtensions() { if (states[KEY_USAGE] != 0) { pushKeyUsage(states[KEY_USAGE], states[UNUSED_BITS]); } - if (states[CERT_MODE] == KMType.ATTESTATION_CERT) { + if (states[CERT_MODE] == KMType.ATTESTATION_CERT || + states[CERT_MODE] == KMType.FACTORY_ATTESTATION_CERT) { pushKeyDescription(); } pushSequenceHeader((short) (last - indexes[STACK_PTR])); @@ -864,7 +865,7 @@ public short getCertStart() { public short getCertLength() { return indexes[CERT_LENGTH]; } - + public void build(short attSecret, short attMod, boolean rsaSign, boolean fakeCert) { indexes[STACK_PTR] = (short) (indexes[BUF_START] + indexes[BUF_LENGTH]); short last = indexes[STACK_PTR]; @@ -927,12 +928,16 @@ else if (rsaSign) { KMException.throwIt(KMError.UNKNOWN_ERROR); } } - // Adjust signature length - indexes[STACK_PTR] = signatureOffset; - pushBitStringHeader((byte) 0, sigLen); - } else if (!fakeCert) { // No attestation key provisioned in the factory - KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + } else if (!fakeCert) { + // Sign with Factory provisioned attestation key. + sigLen = seProvider.ecSign256(KMKeymintDataStore.instance().getAttestationKeyPair(), + stack, indexes[TBS_START], indexes[TBS_LENGTH], stack, signatureOffset); + if (sigLen > ECDSA_MAX_SIG_LEN) + KMException.throwIt(KMError.UNKNOWN_ERROR); } + // Adjust signature length + indexes[STACK_PTR] = signatureOffset; + pushBitStringHeader((byte) 0, sigLen); last = (short) (signatureOffset + sigLen); // Add certificate sequence header indexes[STACK_PTR] = indexes[TBS_START]; @@ -945,6 +950,8 @@ else if (rsaSign) { public void build() { if (states[CERT_MODE] == KMType.FAKE_CERT) { build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, true, true); + } else if (states[CERT_MODE] == KMType.FACTORY_ATTESTATION_CERT) { + build(KMType.INVALID_VALUE, KMType.INVALID_VALUE, false, false); } else { build(indexes[CERT_ATT_KEY_SECRET], indexes[CERT_ATT_KEY_RSA_PUB_MOD], (states[CERT_RSA_SIGN] == 0 ? false : true), false); @@ -1038,7 +1045,6 @@ public KMAttestationCert rsaAttestKey(short attestPrivExp, short attestMod, byte return this; } - private void print(byte[] buf, short start, short length) { StringBuilder sb = new StringBuilder(length * 2); for (short i = start; i < (start + length); i++) { diff --git a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java index 29536c25..772f13de 100644 --- a/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java +++ b/Applet/JCardSimProvider/src/com/android/javacard/keymaster/KMJCardSimApplet.java @@ -33,6 +33,8 @@ public class KMJCardSimApplet extends KMKeymasterApplet { // Provider specific Commands private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = INS_KEYMINT_PROVIDER_APDU_START + 1; //0x01 + private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = INS_KEYMINT_PROVIDER_APDU_START + 2; //0x02 private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; // Commands 4, 5 and 6 are reserved for vendor usage. private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7; @@ -41,10 +43,7 @@ public class KMJCardSimApplet extends KMKeymasterApplet { private static final byte INS_SE_FACTORY_PROVISIONING_LOCK_CMD = INS_KEYMINT_PROVIDER_APDU_START + 10; private static final byte INS_PROVISION_OEM_ROOT_PUBLIC_KEY_CMD = INS_KEYMINT_PROVIDER_APDU_START + 11; private static final byte INS_OEM_UNLOCK_PROVISIONING_CMD = INS_KEYMINT_PROVIDER_APDU_START + 12; - private static final byte INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD = - INS_KEYMINT_PROVIDER_APDU_START + 13; - private static final byte INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD = - INS_KEYMINT_PROVIDER_APDU_START + 14; + // 13 and 14 are reserved for RKP Device UniqueKeyPair and Cert chain. private static final byte INS_PROVISION_PRESHARED_SECRET_CMD = INS_KEYMINT_PROVIDER_APDU_START + 15; private static final byte INS_SET_BOOT_PARAMS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 16; // Unused @@ -123,12 +122,12 @@ public void process(APDU apdu) { processSetBootParamsCmd(apdu); break; - case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: - processProvisionRkpDeviceUniqueKeyPair(apdu); + case INS_PROVISION_ATTESTATION_KEY_CMD: + processProvisionAttestationKey(apdu); break; - case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD: - processProvisionRkpAdditionalCertChain(apdu); + case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: + processProvisionAttestationCertDataCmd(apdu); break; case INS_SE_FACTORY_PROVISIONING_LOCK_CMD: @@ -209,8 +208,8 @@ && isSeFactoryProvisioningLocked())) { } break; - case INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD: - case INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD: + case INS_PROVISION_ATTESTATION_KEY_CMD: + case INS_PROVISION_ATTESTATION_CERT_DATA_CMD: if(isSeFactoryProvisioningLocked()) { result = false; } @@ -240,8 +239,10 @@ private boolean isSeFactoryProvisioningLocked() { private boolean isSeFactoryProvisioningComplete() { short pStatus = kmDataStore.getProvisionStatus(); - if (PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR == (pStatus - & PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR)) { + short completeStatus = PROVISION_STATUS_ATTESTATION_CERT_CHAIN | + PROVISION_STATUS_ATTESTATION_KEY | + PROVISION_STATUS_ATTESTATION_CERT_PARAMS; + if (completeStatus == (pStatus & completeStatus)) { return true; } return false; @@ -297,7 +298,6 @@ private void authenticateOEM(byte[] plainMsg, APDU apdu) { private void processProvisionOEMRootPublicKeyCmd(APDU apdu) { // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); // Arguments short keyparams = KMKeyParameters.exp(); short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT); @@ -359,72 +359,6 @@ private void processProvisionOEMRootPublicKeyCmd(APDU apdu) { KMByteBlob.cast(tmpVariables[0]).length()); } - private static void processProvisionRkpDeviceUniqueKeyPair(APDU apdu) { - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - short arr = KMArray.instance((short) 1); - short coseKeyExp = KMCoseKey.exp(); - KMArray.cast(arr).add((short) 0, coseKeyExp); //[ CoseKey ] - arr = receiveIncoming(apdu, arr); - // Get cose key. - short coseKey = KMArray.cast(arr).get((short) 0); - short pubKeyLen = KMCoseKey.cast(coseKey).getEcdsa256PublicKey(scratchPad, (short) 0); - short privKeyLen = KMCoseKey.cast(coseKey).getPrivateKey(scratchPad, pubKeyLen); - //Store the Device unique Key. - kmDataStore.createRkpDeviceUniqueKeyPair(scratchPad, (short) 0, pubKeyLen, scratchPad, - pubKeyLen, privKeyLen); - short bcc = generateBcc(false, scratchPad); - short len = KMKeymasterApplet.encodeToApduBuffer(bcc, scratchPad, (short) 0, - MAX_COSE_BUF_SIZE); - kmDataStore.persistBootCertificateChain(scratchPad, (short) 0, len); - kmDataStore.setProvisionStatus(PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR); - sendResponse(apdu, KMError.OK); - } - - private void processProvisionRkpAdditionalCertChain(APDU apdu) { - // X509 certificate chain is received as shown below: - /** - * x509CertChain = bstr .cbor UdsCerts - * - * UdsCerts = { - * * SignerName => UdsCertChain - * } - * ; SignerName is a string identifier that indicates both the signing authority as - * ; well as the format of the UdsCertChain - * SignerName = tstr - * - * UdsCertChain = [ - * 2* X509Certificate ; Root -> ... -> Leaf. "Root" is the vendor self-signed - * ; cert, "Leaf" contains UDS_Public. There may also be - * ; intermediate certificates between Root and Leaf. - * ] - * ; A bstr containing a DER-encoded X.509 certificate (RSA, NIST P-curve, or edDSA) - * X509Certificate = bstr - */ - // Store the cbor encoded UdsCerts as it is in the persistent memory so cbor decoding is - // required here. - byte[] srcBuffer = apdu.getBuffer(); - short recvLen = apdu.setIncomingAndReceive(); - short srcOffset = apdu.getOffsetCdata(); - short bufferLength = apdu.getIncomingLength(); - short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); - short index = bufferStartOffset; - byte[] buffer = repository.getHeap(); - while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { - Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); - index += recvLen; - recvLen = apdu.receiveBytes(srcOffset); - } - short byteHeaderLen = decoder.readCertificateChainHeaderLen(buffer, bufferStartOffset, - bufferLength); - kmDataStore.persistAdditionalCertChain(buffer, (short) (bufferStartOffset + byteHeaderLen), - (short) (bufferLength - byteHeaderLen)); - kmDataStore.setProvisionStatus(PROVISION_STATUS_ADDITIONAL_CERT_CHAIN); - // reclaim memory - repository.reclaimMemory(bufferLength); - sendResponse(apdu, KMError.OK); - } - private void processProvisionAttestIdsCmd(APDU apdu) { short keyparams = KMKeyParameters.exp(); short cmd = KMArray.instance((short) 1); @@ -481,6 +415,113 @@ private void processProvisionPreSharedSecretCmd(APDU apdu) { KMByteBlob.cast(val).length()); } + + private void processProvisionAttestationKey(APDU apdu) { + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + // Arguments + short keyparams = KMKeyParameters.exp(); + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT); + short blob = KMByteBlob.exp(); + short argsProto = KMArray.instance((short) 3); + KMArray.cast(argsProto).add((short) 0, keyparams); + KMArray.cast(argsProto).add((short) 1, keyFormatPtr); + KMArray.cast(argsProto).add((short) 2, blob); + short args = receiveIncoming(apdu, argsProto); + + // key params should have os patch, os version and verified root of trust + data[KEY_PARAMETERS] = KMArray.cast(args).get((short) 0); + tmpVariables[0] = KMArray.cast(args).get((short) 1); + data[IMPORTED_KEY_BLOB] = KMArray.cast(args).get((short) 2); + // Key format must be RAW format + byte keyFormat = KMEnum.cast(tmpVariables[0]).getVal(); + if (keyFormat != KMType.RAW) { + KMException.throwIt(KMError.UNIMPLEMENTED); + } + data[ORIGIN] = KMType.IMPORTED; + + // get algorithm - only EC keys expected + tmpVariables[0] = KMEnumTag.getValue(KMType.ALGORITHM, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.EC) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // get digest - only SHA256 supported + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.DIGEST, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0); + if (tmpVariables[0] != KMType.SHA2_256) { + KMException.throwIt(KMError.INCOMPATIBLE_DIGEST); + } + } else { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Purpose should be ATTEST_KEY + tmpVariables[0] = + KMKeyParameters.findTag(KMType.ENUM_ARRAY_TAG, KMType.PURPOSE, data[KEY_PARAMETERS]); + if (tmpVariables[0] != KMType.INVALID_VALUE) { + if (KMEnumArrayTag.cast(tmpVariables[0]).length() != 1) { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + tmpVariables[0] = KMEnumArrayTag.cast(tmpVariables[0]).get((short) 0); + if (tmpVariables[0] != KMType.ATTEST_KEY) { + KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); + } + } else { + KMException.throwIt(KMError.INVALID_ARGUMENT); + } + // Import EC Key - initializes data[SECRET] data[PUB_KEY] + importECKeys(scratchPad, keyFormat); + // persist key + kmDataStore.createAttestationKeyPair( + KMByteBlob.cast(data[SECRET]).getBuffer(), + KMByteBlob.cast(data[SECRET]).getStartOff(), + KMByteBlob.cast(data[SECRET]).length()); + kmDataStore.setProvisionStatus(PROVISION_STATUS_ATTESTATION_KEY); + sendResponse(apdu, KMError.OK); + } + + private void processProvisionAttestationCertDataCmd(APDU apdu) { + byte[] srcBuffer = apdu.getBuffer(); + short recvLen = apdu.setIncomingAndReceive(); + short srcOffset = apdu.getOffsetCdata(); + short bufferLength = apdu.getIncomingLength(); + short bufferStartOffset = repository.allocReclaimableMemory(bufferLength); + short index = bufferStartOffset; + byte[] buffer = repository.getHeap(); + while (recvLen > 0 && ((short) (index - bufferStartOffset) < bufferLength)) { + Util.arrayCopyNonAtomic(srcBuffer, srcOffset, buffer, index, recvLen); + index += recvLen; + recvLen = apdu.receiveBytes(srcOffset); + } + // CertificateData = [ + // X509CertifcateChain: bstr, ; DER encoded X.509 certificate chain ordered from Leaf to Root. + // Issuer: bstr, ; DER encoded subject field + // ] + + // srcBuffer holds the corresponding offsets and lengths of the certChain, certIssuer and certExpiry + // respectively in the input buffer. + decoder.decodeCertificateData((short) 2, /* Length of the input array */ + buffer, bufferStartOffset, bufferLength, srcBuffer, (short) 0); + // Persist data. + // The offset and length of the CertChain in the input buffer are stored in the srcBuffer + // at 0th and 2nd positions respectively. + kmDataStore.persistAttestationCertChain(buffer, + Util.getShort(srcBuffer, (short) 0), Util.getShort(srcBuffer, (short) 2)); + // The offset and length of the CertIssuer in the input buffer are stored in the srcBuffer + // at 4th and 6th positions respectively. + kmDataStore.persistAttestationCertIssuer(buffer, + Util.getShort(srcBuffer, (short) 4), Util.getShort(srcBuffer, (short) 6)); + + // reclaim memory + repository.reclaimMemory(bufferLength); + kmDataStore.setProvisionStatus( + (short) (PROVISION_STATUS_ATTESTATION_CERT_CHAIN | PROVISION_STATUS_ATTESTATION_CERT_PARAMS)); + sendResponse(apdu, KMError.OK); + } //This function masks the error code with POWER_RESET_MASK_FLAG // in case if card reset event occurred. The clients of the Applet @@ -580,10 +621,9 @@ private void processSetBootParamsCmd(APDU apdu) { private boolean isProvisioningComplete() { short pStatus = kmDataStore.getProvisionStatus(); - short pCompleteStatus = - PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR | PROVISION_STATUS_PRESHARED_SECRET - | PROVISION_STATUS_ATTEST_IDS | PROVISION_STATUS_OEM_PUBLIC_KEY | - PROVISION_STATUS_SECURE_BOOT_MODE; + short pCompleteStatus = PROVISION_STATUS_ATTESTATION_CERT_CHAIN | PROVISION_STATUS_ATTESTATION_CERT_PARAMS + | PROVISION_STATUS_ATTESTATION_KEY | PROVISION_STATUS_PRESHARED_SECRET | PROVISION_STATUS_ATTEST_IDS + | PROVISION_STATUS_OEM_PUBLIC_KEY | PROVISION_STATUS_SECURE_BOOT_MODE; if (kmDataStore.isProvisionLocked() || (pCompleteStatus == (pStatus & pCompleteStatus))) { return true; } diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java index f420b8f4..144f05f7 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMFunctionalTest.java @@ -22,8 +22,6 @@ import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; import com.android.javacard.keymaster.KMConfigurations; -import com.android.javacard.keymaster.KMCose; -import com.android.javacard.keymaster.KMCoseHeaders; import com.android.javacard.keymaster.KMDecoder; import com.android.javacard.keymaster.KMEncoder; import com.android.javacard.keymaster.KMEnum; @@ -1480,9 +1478,9 @@ public void testVerifyOemLockWithOutSeLockFailure() { // Select applet simulator.selectApplet(appletAID1); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationKey(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationCertificateData(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, KMProvision.provisionSharedSecret(simulator, encoder, decoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, @@ -1503,9 +1501,9 @@ public void testVerifyOemUnLockAfterOemLockSuccess() { simulator.selectApplet(appletAID1); // provision attest key Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationKey(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationCertificateData(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, KMProvision.provisionSeLocked(simulator, decoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, @@ -1550,9 +1548,9 @@ public void testVerifyOemLockWithOutOemRootKeyFailure() { simulator.selectApplet(appletAID1); // provision attest key Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationKey(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationCertificateData(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, KMProvision.provisionSeLocked(simulator, decoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, @@ -1572,8 +1570,8 @@ public void testVerifySeLockWithOutCertDataSuccess() { simulator.selectApplet(appletAID1); // provision attest key Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); - Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, + KMProvision.provisionAttestationKey(simulator, encoder))); + Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, KMProvision.provisionSeLocked(simulator, decoder))); cleanUp(); } @@ -1586,15 +1584,15 @@ public void testVerifyProvisionSeDataAfterSeLockFailure() { simulator.selectApplet(appletAID1); // provision attest key Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationKey(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationCertificateData(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, KMProvision.provisionSeLocked(simulator, decoder))); Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, - KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationKey(simulator, encoder))); Assert.assertEquals(KMError.UNKNOWN_ERROR, KMTestUtils.decodeError(decoder, - KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationCertificateData(simulator, encoder))); cleanUp(); } @@ -1606,9 +1604,9 @@ public void testVerifyOemProvisionAfterOemLockFailure() { simulator.selectApplet(appletAID1); // provision attest key Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationKey(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - KMProvision.provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + KMProvision.provisionAttestationCertificateData(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, KMProvision.provisionSeLocked(simulator, decoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, @@ -2359,12 +2357,12 @@ 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 + // 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); - // Unwrapping params should have Digest: SHA256 and padding as RSA_OAEP - short unwrappingParamsArr = KMArray.instance((short) 2); // SHA256 digest short digestBlob = KMByteBlob.instance((short) 1); KMByteBlob.cast(digestBlob).add((short) 0, KMType.SHA2_256); diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMProvision.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMProvision.java index 6331539e..dd998259 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMProvision.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMProvision.java @@ -4,7 +4,6 @@ import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMByteTag; import com.android.javacard.keymaster.KMCose; -import com.android.javacard.keymaster.KMCoseHeaders; import com.android.javacard.keymaster.KMDecoder; import com.android.javacard.keymaster.KMEncoder; import com.android.javacard.keymaster.KMEnum; @@ -40,6 +39,10 @@ public class KMProvision { // Provision Instructions private static final byte INS_KEYMINT_PROVIDER_APDU_START = 0x00; + private static final byte INS_PROVISION_ATTESTATION_KEY_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 1; //0x01 + private static final byte INS_PROVISION_ATTESTATION_CERT_DATA_CMD = + INS_KEYMINT_PROVIDER_APDU_START + 2; //0x02 private static final byte INS_PROVISION_ATTEST_IDS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 3; private static final byte INS_GET_PROVISION_STATUS_CMD = INS_KEYMINT_PROVIDER_APDU_START + 7; //0x08 was reserved for INS_INIT_STRONGBOX_CMD @@ -293,6 +296,40 @@ public class KMProvision { (byte) 0x71, (byte) 0xcc, (byte) 0x55, (byte) 0xfc, (byte) 0x6a, (byte) 0x0b, (byte) 0x84, (byte) 0x28, (byte) 0x88, (byte) 0xa2, (byte) 0xca, (byte) 0x19, (byte) 0xe0}; + private static final byte[] X509Issuer = { + (byte) 0x30, (byte) 0x81, (byte) 0x88, (byte) 0x31, (byte) 0x0b, + (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, + (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, + (byte) 0x53, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, + (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, + (byte) 0x0c, (byte) 0x0a, (byte) 0x43, (byte) 0x61, (byte) 0x6c, + (byte) 0x69, (byte) 0x66, (byte) 0x6f, (byte) 0x72, (byte) 0x6e, + (byte) 0x69, (byte) 0x61, (byte) 0x31, (byte) 0x15, (byte) 0x30, + (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, + (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x47, (byte) 0x6f, + (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x2c, + (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x63, (byte) 0x2e, + (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, + (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, + (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, + (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x3b, + (byte) 0x30, (byte) 0x39, (byte) 0x06, (byte) 0x03, (byte) 0x55, + (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x32, (byte) 0x41, + (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, + (byte) 0x64, (byte) 0x20, (byte) 0x4b, (byte) 0x65, (byte) 0x79, + (byte) 0x73, (byte) 0x74, (byte) 0x6f, (byte) 0x72, (byte) 0x65, + (byte) 0x20, (byte) 0x53, (byte) 0x6f, (byte) 0x66, (byte) 0x74, + (byte) 0x77, (byte) 0x61, (byte) 0x72, (byte) 0x65, (byte) 0x20, + (byte) 0x41, (byte) 0x74, (byte) 0x74, (byte) 0x65, (byte) 0x73, + (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x69, (byte) 0x6f, + (byte) 0x6e, (byte) 0x20, (byte) 0x49, (byte) 0x6e, (byte) 0x74, + (byte) 0x65, (byte) 0x72, (byte) 0x6d, (byte) 0x65, (byte) 0x64, + (byte) 0x69, (byte) 0x61, (byte) 0x74, (byte) 0x65}; + + private static final byte[] expiryTime = {(byte) 0x32, (byte) 0x36, (byte) 0x30, (byte) 0x31, + (byte) 0x30, (byte) 0x38, (byte) 0x30, (byte) 0x30, (byte) 0x34, (byte) 0x36, (byte) 0x30, + (byte) 0x39, (byte) 0x5a}; + // OEM lock / unlock verification constants. private static final byte[] OEM_LOCK_PROVISION_VERIFICATION_LABEL = { // "OEM Provisioning Lock" 0x4f, 0x45, 0x4d, 0x20, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e, @@ -366,60 +403,76 @@ public static ResponseAPDU setBootParams(CardSimulator simulator, KMEncoder enco return simulator.transmitCommand(apdu); } - public static ResponseAPDU provisionAdditionalCertChain(CardSimulator simulator, - KMSEProvider cryptoProvider, KMEncoder encoder, KMDecoder decoder) { - short innerArrPtr = KMArray.instance((short) 2); + public static ResponseAPDU provisionAttestationKey(CardSimulator simulator, KMEncoder encoder) { + // KeyParameters. + short arrPtr = KMArray.instance((short) 4); + short ecCurve = KMEnumTag.instance(KMType.ECCURVE, KMType.P_256); + short byteBlob = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); + short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); + short byteBlob2 = KMByteBlob.instance((short) 1); + KMByteBlob.cast(byteBlob2).add((short) 0, KMType.ATTEST_KEY); + short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob2); + KMArray.cast(arrPtr).add((short) 0, ecCurve); + KMArray.cast(arrPtr).add((short) 1, digest); + KMArray.cast(arrPtr).add((short) 2, + KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); + KMArray.cast(arrPtr).add((short) 3, purpose); + short keyParams = KMKeyParameters.instance(arrPtr); + // Note: VTS uses PKCS8 KeyFormat RAW + short keyFormatPtr = KMEnum.instance(KMType.KEY_FORMAT, KMType.RAW); - short byteBlobPtr1 = KMByteBlob.instance(kEcAttestRootCert, (short) 0, (short) kEcAttestRootCert.length); - short byteBlobPtr2 = KMByteBlob.instance(kEcAttestCert, (short) 0, (short) kEcAttestCert.length); + // Key + short signKeyPtr = KMArray.instance((short) 2); + KMArray.cast(signKeyPtr).add((short) 0, KMByteBlob.instance(kEcPrivKey, + (short) 0, (short) kEcPrivKey.length)); + KMArray.cast(signKeyPtr).add((short) 1, KMByteBlob.instance(kEcPubKey, + (short) 0, (short) kEcPubKey.length)); + byte[] keyBuf = new byte[120]; + short len = encoder.encode(signKeyPtr, keyBuf, (short) 0, (short) 120); + short signKeyBstr = KMByteBlob.instance(keyBuf, (short) 0, len); - KMArray.cast(innerArrPtr).add((short) 0, byteBlobPtr1); - KMArray.cast(innerArrPtr).add((short) 1, byteBlobPtr2); - short map = KMMap.instance((short) 1); - byte[] signerName = "TestSigner".getBytes(); - KMMap.cast(map) - .add((short) 0, KMTextString.instance(signerName, (short) 0, (short) signerName.length), - innerArrPtr); - byte[] output = new byte[2048]; - short encodedLen = encoder.encode(map, output, (short) 0, (short) 2048); - short encodedData = KMByteBlob.instance(output, (short) 0, encodedLen); + short finalArrayPtr = KMArray.instance((short) 3); + KMArray.cast(finalArrayPtr).add((short) 0, keyParams); + KMArray.cast(finalArrayPtr).add((short) 1, keyFormatPtr); + KMArray.cast(finalArrayPtr).add((short) 2, signKeyBstr); - CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, - (byte) INS_PROVISION_RKP_ADDITIONAL_CERT_CHAIN_CMD, encodedData); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_PROVISION_ATTESTATION_KEY_CMD, + finalArrayPtr); // print(commandAPDU.getBytes()); - return simulator.transmitCommand(apdu); + ResponseAPDU response = simulator.transmitCommand(apdu); + return response; } - public static ResponseAPDU provisionDeviceUniqueKeyPair(CardSimulator simulator, - KMSEProvider cryptoProvider, KMEncoder encoder, - KMDecoder decoder) { - short[] lengths = new short[2]; - byte[] privKey = new byte[128]; - byte[] pubKey = new byte[128]; - cryptoProvider.createAsymmetricKey(KMType.EC, privKey, (short) 0, (short) 128, - pubKey, (short) 0, (short) 128, lengths); - short coseKey = - KMTestUtils.constructCoseKey( - KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), - KMType.INVALID_VALUE, - KMNInteger.uint_8(KMCose.COSE_ALG_ES256), - KMInteger.uint_8(KMCose.COSE_KEY_OP_SIGN), - KMInteger.uint_8(KMCose.COSE_ECCURVE_256), - pubKey, (short) 0, lengths[1], - privKey, (short) 0, lengths[0], - false - ); - Assert.assertEquals(lengths[1], 65); - Assert.assertTrue("Private key length should not be > 32", (lengths[0] <= 32)); - Util.arrayCopyNonAtomic(privKey, (short) 0, RKP_DK_PRIV, (short) (32 - lengths[0]), lengths[0]); - Util.arrayCopyNonAtomic(pubKey, (short) 0, RKP_DK_PUB, (short) 0, lengths[1]); - short arr = KMArray.instance((short) 1); - KMArray.cast(arr).add((short) 0, coseKey); + public static ResponseAPDU provisionAttestationCertificateData(CardSimulator simulator, + KMEncoder encoder) { + short arrPtr = KMArray.instance((short) 2); + + short byteBlobPtr = KMByteBlob.instance( + (short) (kEcAttestCert.length + kEcAttestRootCert.length)); + Util.arrayCopyNonAtomic(kEcAttestCert, (short) 0, + KMByteBlob.cast(byteBlobPtr).getBuffer(), + KMByteBlob.cast(byteBlobPtr).getStartOff(), + (short) kEcAttestCert.length); + Util.arrayCopyNonAtomic(kEcAttestRootCert, (short) 0, + KMByteBlob.cast(byteBlobPtr).getBuffer(), + (short) (KMByteBlob.cast(byteBlobPtr).getStartOff() + + kEcAttestCert.length), + (short) kEcAttestRootCert.length); + KMArray.cast(arrPtr).add((short) 0, byteBlobPtr); + + short byteBlob1 = KMByteBlob.instance(X509Issuer, (short) 0, + (short) X509Issuer.length); + KMArray.cast(arrPtr).add((short) 1, byteBlob1); + CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, - (byte) INS_PROVISION_RKP_DEVICE_UNIQUE_KEYPAIR_CMD, arr); - return simulator.transmitCommand(apdu); + (byte) INS_PROVISION_ATTESTATION_CERT_DATA_CMD, arrPtr); + // print(commandAPDU.getBytes()); + ResponseAPDU response = simulator.transmitCommand(apdu); + return response; } + public static ResponseAPDU provisionOEMRootPublicKey(CardSimulator simulator, KMEncoder encoder, KMDecoder decoder) { // KeyParameters. @@ -626,9 +679,9 @@ public static void provisionCmd(CardSimulator simulator, KMSEProvider cryptoProvider, KMEncoder encoder, KMDecoder decoder) { Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - provisionDeviceUniqueKeyPair(simulator, cryptoProvider, encoder, decoder))); + provisionAttestationKey(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, - provisionAdditionalCertChain(simulator, cryptoProvider, encoder, decoder))); + provisionAttestationCertificateData(simulator, encoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, provisionSeLocked(simulator, decoder))); Assert.assertEquals(KMError.OK, KMTestUtils.decodeError(decoder, @@ -689,17 +742,14 @@ public static void sendRootOfTrust(CardSimulator simulator, KMSEProvider cryptoP // Payload short payload = constructRotPayload(encoder); // Protected Header - short headerPtr = KMCose.constructHeaders(scratchBuffer, - KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); + short pH = KMMap.instance((short) 1); + KMMap.cast(pH).add((short) 0, KMInteger.uint_8(KMCose.COSE_LABEL_ALGORITHM), + KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256)); // Encode the protected header as byte blob. - short len = encoder.encode(headerPtr, scratchPad, (short) 0, (short) 500); + short len = encoder.encode(pH, scratchPad, (short) 0, (short) 500); short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); // Unprotected Header - short unprotectedHeader = KMArray.instance((short) 0); - unprotectedHeader = KMCoseHeaders.instance(unprotectedHeader); + short unprotectedHeader = KMMap.instance((short) 0); // Construct Mac_Structure short macStructure = diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java deleted file mode 100644 index aa4d1714..00000000 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMRKPFunctionalTest.java +++ /dev/null @@ -1,514 +0,0 @@ -/* - * Copyright(C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.test; - -import com.android.javacard.keymaster.KMArray; -import com.android.javacard.keymaster.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.KMCosePairByteBlobTag; -import com.android.javacard.keymaster.KMCosePairIntegerTag; -import com.android.javacard.keymaster.KMCosePairNegIntegerTag; -import com.android.javacard.keymaster.KMCosePairSimpleValueTag; -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.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.bouncycastle.util.encoders.Hex; -import com.licel.jcardsim.smartcardio.CardSimulator; -import com.licel.jcardsim.utils.AIDUtil; - -import java.util.ArrayList; -import java.util.Vector; -import javacard.framework.AID; -import javacard.framework.Util; -import javacard.security.KeyPair; - -import javax.smartcardio.CommandAPDU; -import javax.smartcardio.ResponseAPDU; - -import org.junit.Assert; -import org.junit.Test; - -public class KMRKPFunctionalTest { - - // Provider specific Commands - private static final byte KEYMINT_CMD_APDU_START = 0x20; - private static final byte INS_GENERATE_KEY_CMD = KEYMINT_CMD_APDU_START + 1; //0x21 - private static final byte INS_IMPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 2; //0x22 - private static final byte INS_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 3; //0x23 - private static final byte INS_EXPORT_KEY_CMD = KEYMINT_CMD_APDU_START + 4; //0x24 - private static final byte INS_ATTEST_KEY_CMD = KEYMINT_CMD_APDU_START + 5; //0x25 - private static final byte INS_UPGRADE_KEY_CMD = KEYMINT_CMD_APDU_START + 6; //0x26 - private static final byte INS_DELETE_KEY_CMD = KEYMINT_CMD_APDU_START + 7; //0x27 - private static final byte INS_DELETE_ALL_KEYS_CMD = KEYMINT_CMD_APDU_START + 8; //0x28 - private static final byte INS_ADD_RNG_ENTROPY_CMD = KEYMINT_CMD_APDU_START + 9; //0x29 - private static final byte INS_COMPUTE_SHARED_HMAC_CMD = KEYMINT_CMD_APDU_START + 10; //0x2A - private static final byte INS_DESTROY_ATT_IDS_CMD = KEYMINT_CMD_APDU_START + 11; //0x2B - private static final byte INS_VERIFY_AUTHORIZATION_CMD = KEYMINT_CMD_APDU_START + 12; //0x2C - private static final byte INS_GET_HMAC_SHARING_PARAM_CMD = KEYMINT_CMD_APDU_START + 13; //0x2D - private static final byte INS_GET_KEY_CHARACTERISTICS_CMD = KEYMINT_CMD_APDU_START + 14; //0x2E - private static final byte INS_GET_HW_INFO_CMD = KEYMINT_CMD_APDU_START + 15; //0x2F - private static final byte INS_BEGIN_OPERATION_CMD = KEYMINT_CMD_APDU_START + 16; //0x30 - private static final byte INS_UPDATE_OPERATION_CMD = KEYMINT_CMD_APDU_START + 17; //0x31 - private static final byte INS_FINISH_OPERATION_CMD = KEYMINT_CMD_APDU_START + 18; //0x32 - private static final byte INS_ABORT_OPERATION_CMD = KEYMINT_CMD_APDU_START + 19; //0x33 - private static final byte INS_DEVICE_LOCKED_CMD = KEYMINT_CMD_APDU_START + 20;//0x34 - private static final byte INS_EARLY_BOOT_ENDED_CMD = KEYMINT_CMD_APDU_START + 21; //0x35 - private static final byte INS_GET_CERT_CHAIN_CMD = KEYMINT_CMD_APDU_START + 22; //0x36 - private static final byte INS_UPDATE_AAD_OPERATION_CMD = KEYMINT_CMD_APDU_START + 23; //0x37 - private static final byte INS_BEGIN_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 24; //0x38 - private static final byte INS_FINISH_IMPORT_WRAPPED_KEY_CMD = KEYMINT_CMD_APDU_START + 25; //0x39 - private static final byte INS_INIT_STRONGBOX_CMD = KEYMINT_CMD_APDU_START + 26; //0x3A - // RKP - public static final byte INS_GET_RKP_HARDWARE_INFO = KEYMINT_CMD_APDU_START + 27; //0x3B - public static final byte INS_GENERATE_RKP_KEY_CMD = KEYMINT_CMD_APDU_START + 28; //0x3C - public static final byte INS_BEGIN_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 29; //0x3D - public static final byte INS_UPDATE_KEY_CMD = KEYMINT_CMD_APDU_START + 30; //0x3E - public static final byte INS_UPDATE_EEK_CHAIN_CMD = KEYMINT_CMD_APDU_START + 31; //0x3F - public static final byte INS_UPDATE_CHALLENGE_CMD = KEYMINT_CMD_APDU_START + 32; //0x40 - public static final byte INS_FINISH_SEND_DATA_CMD = KEYMINT_CMD_APDU_START + 33; //0x41 - public static final byte INS_GET_RESPONSE_CMD = KEYMINT_CMD_APDU_START + 34; //0x42 - - private static final byte KEYMINT_CMD_APDU_END = KEYMINT_CMD_APDU_START + 48; //0x50 - private static final byte INS_END_KM_CMD = 0x7F; - - public static byte[] CSR_CHALLENGE = new byte[32]; - - private CardSimulator simulator; - private KMEncoder encoder; - private KMDecoder decoder; - private KMSEProvider cryptoProvider; - private KMAsn1Parser asn1Parser; - - public KMRKPFunctionalTest() { - cryptoProvider = new KMJCardSimulator(); - simulator = new CardSimulator(); - encoder = new KMEncoder(); - decoder = new KMDecoder(); - } - - private void init() { - // Create simulator - AID appletAID = AIDUtil.create("A000000062"); - simulator.installApplet(appletAID, KMJCardSimApplet.class); - // Select applet - simulator.selectApplet(appletAID); - // provision attest key - KMProvision.provisionCmd(simulator, cryptoProvider, encoder, decoder); - } - - private void cleanUp() { - AID appletAID = AIDUtil.create("A000000062"); - // Delete i.e. uninstall applet - simulator.deleteApplet(appletAID); - } - - //---------------------------------------------------------------------------------------------- - // RKP Tests - //---------------------------------------------------------------------------------------------- - @Test - public void testNegativeInteger() { - init(); - short ptr = KMArray.instance((short) 3); - int a = 0xF0000056; - byte[] a_b1 = {(byte) 0xF0, 0x00, 0x00, 0x56}; - KMArray.cast(ptr).add((short) 0, KMNInteger.uint_32(a_b1, (short) 0)); - byte[] a_b2 = new byte[]{(byte) 0xF0, 0x00, 0x01, 0x56}; - KMArray.cast(ptr).add((short) 1, KMNInteger.uint_32(a_b2, (short) 0)); - byte[] a_b3 = new byte[]{(byte) 0xF0, 0x10, 0x01, 0x56}; - KMArray.cast(ptr).add((short) 2, KMNInteger.uint_32(a_b3, (short) 0)); - byte[] blob = new byte[256]; - short len = encoder.encode(ptr, blob, (short) 0, (short) 256); - - ptr = KMArray.instance((short) 3); - KMArray.cast(ptr).add((short) 0, KMNInteger.exp()); - KMArray.cast(ptr).add((short) 1, KMNInteger.exp()); - KMArray.cast(ptr).add((short) 2, KMNInteger.exp()); - ptr = decoder.decode(ptr, blob, (short) 0, len); - short a_b1_ptr = KMArray.cast(ptr).get((short) 0); - Assert.assertEquals(0, - Util.arrayCompare(a_b1, (short) 0, - KMNInteger.cast(a_b1_ptr).getBuffer(), - KMNInteger.cast(a_b1_ptr).getStartOff(), (short) 4)); - short a_b2_ptr = KMArray.cast(ptr).get((short) 1); - Assert.assertEquals(0, - Util.arrayCompare(a_b2, (short) 0, - KMNInteger.cast(a_b2_ptr).getBuffer(), - KMNInteger.cast(a_b2_ptr).getStartOff(), (short) 4)); - short a_b3_ptr = KMArray.cast(ptr).get((short) 2); - Assert.assertEquals(0, - Util.arrayCompare(a_b3, (short) 0, - KMNInteger.cast(a_b3_ptr).getBuffer(), - KMNInteger.cast(a_b3_ptr).getStartOff(), (short) 4)); - cleanUp(); - } - - @Test - public void testGetRkpHwInfo() { - init(); - short arrPtr = KMArray.instance((short) 0); - CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GET_RKP_HARDWARE_INFO, arrPtr); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] resp = response.getBytes(); - KMTestUtils.print(resp, (short) 0, (short) resp.length); - short blobExp = KMByteBlob.exp(); - arrPtr = KMArray.instance((short) 5); - KMArray.cast(arrPtr).add((short) 0, KMInteger.exp()); // ErrorCode - KMArray.cast(arrPtr).add((short) 1, KMInteger.exp()); // Version - KMArray.cast(arrPtr).add((short) 2, blobExp); // Text string - KMArray.cast(arrPtr).add((short) 3, KMInteger.exp()); // support Eek Curve. - KMArray.cast(arrPtr).add((short) 4, blobExp); // unique id - byte[] output = new byte[100]; - arrPtr = decoder.decode(arrPtr, resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(arrPtr).get((short) 0)).getShort()); - byte[] authorName = new byte[6]; - KMByteBlob.cast(KMArray.cast(arrPtr).get((short) 2)).getValue(authorName, (short) 0, (short) 6); - // Validate the author and Eek Curve - byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - Assert.assertArrayEquals(google, authorName); - Assert.assertEquals(KMType.RKP_CURVE_P256, - KMInteger.cast(KMArray.cast(arrPtr).get((short) 3)).getShort()); - Assert.assertEquals(2, KMInteger.cast(KMArray.cast(arrPtr).get((short) 1)).getShort()); - cleanUp(); - } - - @Test - public void testRkpGeneratedEcdsaKeyPair() { - init(); - // Running this test case in test mode. - byte[] testHmacKey = new byte[32]; - short ret = generateRkpEcdsaKeyPair(true); - // Prepare exp() for coseMac. - short coseMacArrPtr = KMArray.instance((short) 4); - short coseHeadersExp = KMCoseHeaders.exp(); - KMArray.cast(coseMacArrPtr).add((short) 0, KMByteBlob.exp()); - KMArray.cast(coseMacArrPtr).add((short) 1, coseHeadersExp); - KMArray.cast(coseMacArrPtr).add((short) 2, KMByteBlob.exp()); - KMArray.cast(coseMacArrPtr).add((short) 3, KMByteBlob.exp()); - short byteBlobMac = KMArray.cast(ret).get((short) 1); - short arrPtr = - decoder.decode(coseMacArrPtr, KMByteBlob.cast(byteBlobMac).getBuffer(), - KMByteBlob.cast(byteBlobMac).getStartOff(), - KMByteBlob.cast(byteBlobMac).length()); - // Decode CoseMac0 - short bstrPayloadPtr = KMArray.cast(arrPtr).get((short) 2); - short bstrTagPtr = KMArray.cast(arrPtr).get((short) 3); - short bstrProtectedHptr = KMArray.cast(arrPtr).get((short) 0); - short unprotectedHptr = KMArray.cast(arrPtr).get((short) 1); - // Verify algorithm inside protected header. - arrPtr = KMCoseHeaders.exp();//KMMap.instance((short) 1); - ret = decoder.decode(arrPtr, KMByteBlob.cast(bstrProtectedHptr).getBuffer(), - KMByteBlob.cast(bstrProtectedHptr).getStartOff(), - KMByteBlob.cast(bstrProtectedHptr).length()); - short[] scratchBuffer = new short[10]; - Assert.assertTrue(KMCoseHeaders.cast(ret) - .isDataValid(scratchBuffer, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)); - // Verify that unprotected header length is 0. - Assert.assertEquals(0, KMCoseHeaders.cast(unprotectedHptr).length()); - // Generate Cose_Mac0 structure and verify the tag. - byte[] output = new byte[256]; - short len = KMTestUtils.generateCoseMac0Mac(cryptoProvider, encoder, testHmacKey, (short) 0, - (short) testHmacKey.length, KMByteBlob.instance((short) 0), bstrPayloadPtr, - bstrProtectedHptr, output, (short) 0, (short) output.length); - if (len != 32) { - Assert.fail("Hmac sign len is not 32"); - } - // Compare the tag values. - Assert.assertEquals(0, - Util.arrayCompare(output, (short) 0, KMByteBlob.cast(bstrTagPtr).getBuffer(), - KMByteBlob.cast(bstrTagPtr).getStartOff(), KMByteBlob.cast(bstrTagPtr).length())); - cleanUp(); - } - - @Test - public void testGenerateCsrTestMode() { - init(); - short[] eekLengths = {2, 3, 9}; - short[] noOfKeys = {0, 5, 10}; - for (int i = 0; i < eekLengths.length; i++) { - testGenerateCsr(noOfKeys[i] /*no_keys*/, eekLengths[i] /*eek_chain_len*/, true /*testMode*/); - KMRepository.instance().clean(); - } - cleanUp(); - } - - @Test - public void testGenerateCsrProdMode() { - init(); - short[] noOfKeys = {0, 5, 10}; - for (int i = 0; i < noOfKeys.length; i++) { - testGenerateCsr(noOfKeys[i] /*no_keys*/, (short) 2 /*eek_chain_len*/, false /*testMode*/); - KMRepository.instance().clean(); - } - cleanUp(); - } - - //---------------------------------------------------------------------------------------------- - // Helper functions - //---------------------------------------------------------------------------------------------- - public short generateRkpEcdsaKeyPair(boolean testMode) { - short arrPtr = KMArray.instance((short) 1); - KMArray.cast(arrPtr).add((short) 0, - KMSimpleValue.instance(testMode ? KMSimpleValue.TRUE : KMSimpleValue.FALSE)); - CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_GENERATE_RKP_KEY_CMD, arrPtr); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] resp = response.getBytes(); - KMTestUtils.print(resp, (short) 0, (short) resp.length); - // Prepare exp for output. - arrPtr = KMArray.instance((short) 3); - KMArray.cast(arrPtr).add((short) 0, KMInteger.exp()); - KMArray.cast(arrPtr).add((short) 1, KMByteBlob.exp()); // bstr of cose mac0 - KMArray.cast(arrPtr).add((short) 2, KMByteBlob.exp()); // keyblob - short ret = decoder.decode(arrPtr, resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMError.OK, KMInteger.cast(KMArray.cast(ret).get((short) 0)).getShort()); - return ret; - } - - public void testGenerateCsr(short no_keys, short no_eek, boolean testMode) { - byte[][] mackedKeys = new byte[no_keys][]; - short ret; - short totalEncodedCoseKeysLen = 0; - for (short i = 0; i < no_keys; i++) { - // Generate RKP Key - ret = generateRkpEcdsaKeyPair(testMode); - // Store CoseMac0 in buffer. - short byteBlobCoseMac0 = KMArray.cast(ret).get((short) 1); - mackedKeys[i] = new byte[KMByteBlob.cast(byteBlobCoseMac0).length()]; - Util.arrayCopy(KMByteBlob.cast(byteBlobCoseMac0).getBuffer(), - KMByteBlob.cast(byteBlobCoseMac0).getStartOff(), mackedKeys[i], (short) 0, - KMByteBlob.cast(byteBlobCoseMac0).length()); - } - short coseKeyArr = KMArray.instance(no_keys); - short coseMacPtr; - short coseKey; - for (short i = 0; i < no_keys; i++) { - coseMacPtr = KMTestUtils.decodeCoseMac(decoder, mackedKeys[i], (short) 0, - (short) mackedKeys[i].length); - coseKey = KMTestUtils.getCoseKeyFromCoseMac(decoder, coseMacPtr); - short payload = KMArray.cast(coseMacPtr).get((short) 2); - totalEncodedCoseKeysLen += KMByteBlob.cast(payload).length(); - KMArray.cast(coseKeyArr).add(i, coseKey); - } - byte[] coseKeyArrBuf = new byte[1024]; - short coseKeyArrBufLen = encoder.encode(coseKeyArr, coseKeyArrBuf, (short) 0, (short) 1024); - byte[] encodedCoseKeysArray = new byte[coseKeyArrBufLen]; - Util.arrayCopy(coseKeyArrBuf, (short) 0, encodedCoseKeysArray, (short) 0, coseKeyArrBufLen); - - // 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, - KMSimpleValue.instance(testMode ? KMSimpleValue.TRUE : KMSimpleValue.FALSE)); - CommandAPDU apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_BEGIN_SEND_DATA_CMD, arr); - ResponseAPDU response = simulator.transmitCommand(apdu); - byte[] resp = response.getBytes(); - ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - - // update data. - for (short i = 0; i < no_keys; i++) { - coseMacPtr = KMTestUtils.decodeCoseMac(decoder, mackedKeys[i], (short) 0, - (short) mackedKeys[i].length); - short coseMacContainer = KMArray.instance((short) 1); - KMArray.cast(coseMacContainer).add((short) 0, coseMacPtr); - apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_KEY_CMD, coseMacContainer); - response = simulator.transmitCommand(apdu); - resp = response.getBytes(); - ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - } - - // update EEK - byte[] pub = new byte[65]; - byte[] priv = new byte[32]; - short[] lengths = new short[2]; - KeyPair eekKey = null; - short eekArr = KMType.INVALID_VALUE; - byte[] eekId; - if (testMode) { - eekId = new byte[]{0x01, 0x02, 0x03, 0x04}; - eekKey = KMTestUtils.generateEcKeyPair(cryptoProvider, pub, priv, lengths); - eekArr = KMTestUtils.generateEEk(cryptoProvider, encoder, eekKey, eekId, no_eek); - apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_EEK_CHAIN_CMD, eekArr); - } else { /* Production Mode */ - eekId = Hex.decode(KMTestUtils.PROD_EEK_ID); - byte[] eekPub = Hex.decode(KMTestUtils.PROD_PUB_KEY); - eekKey = - KMTestUtils.getEcKeyPair(eekPub, (short) 0, (short) eekPub.length, null, (short) 0, - (short) 0); - short length = (short) (KMTestUtils.kCoseEncodedEcdsa256GeekCert.length + - KMTestUtils.kCoseEncodedEcdsa256RootCert.length); - length += 1; // Array of 2. - byte[] encodedBuf = new byte[length]; - encodedBuf[0] = (byte) 0x82; - Util.arrayCopyNonAtomic( - KMTestUtils.kCoseEncodedEcdsa256RootCert, - (short) 0, - encodedBuf, - (short) 1, - (short) KMTestUtils.kCoseEncodedEcdsa256RootCert.length - ); - Util.arrayCopyNonAtomic( - KMTestUtils.kCoseEncodedEcdsa256GeekCert, - (short) 0, - encodedBuf, - (short) (1 + KMTestUtils.kCoseEncodedEcdsa256RootCert.length), - (short) KMTestUtils.kCoseEncodedEcdsa256GeekCert.length - ); - System.out.println(" PRODUCTION CHAIN"); - KMTestUtils.print(encodedBuf, (short) 0, (short) encodedBuf.length); - apdu = KMTestUtils.encodeApdu(encoder, INS_UPDATE_EEK_CHAIN_CMD, encodedBuf); - } - - //Clean the heap. - KMRepository.instance().clean(); - response = simulator.transmitCommand(apdu); - resp = response.getBytes(); - ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - - // update Challenge - short challenge = KMByteBlob.instance(CSR_CHALLENGE, (short) 0, (short) CSR_CHALLENGE.length); - short challengeArr = KMArray.instance((short) 1); - KMArray.cast(challengeArr).add((short) 0, challenge); - apdu = KMTestUtils.encodeApdu(encoder, (byte) INS_UPDATE_CHALLENGE_CMD, challengeArr); - response = simulator.transmitCommand(apdu); - resp = response.getBytes(); - ret = decoder.decode(KMTestUtils.receiveErrorCodeExp(), resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - - // 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) 7); - KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK - KMArray.cast(arr).add((short) 1, KMByteBlob.exp()); // pubKeysToSignMac - KMArray.cast(arr).add((short) 2, KMByteBlob.exp()); // deviceInfo - KMArray.cast(arr).add((short) 3, KMByteBlob.exp()); // CoseEncrypt ProtectedHeader - KMArray.cast(arr).add((short) 4, coseHeadersExp); // CoseEncrypt UnProtectedHeader - KMArray.cast(arr).add((short) 5, KMByteBlob.exp()); // CoseEncrypt partial cipher text. - KMArray.cast(arr).add((short) 6, KMInteger.exp()); // more data - resp = response.getBytes(); - //KMTestUtils.print(resp, (short) 0, (short) resp.length); - ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - short pubKeysToSignMac = KMArray.cast(ret).get((short) 1); - byte[] pubKeysToSignMacBytes = new byte[KMByteBlob.cast(pubKeysToSignMac).length()]; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(pubKeysToSignMac).getBuffer(), - KMByteBlob.cast(pubKeysToSignMac).getStartOff(), - pubKeysToSignMacBytes, - (short) 0, - KMByteBlob.cast(pubKeysToSignMac).length() - ); - short deviceInfo = KMArray.cast(ret).get((short) 2); - byte[] deviceInfoBytes = new byte[512]; - Util.arrayCopyNonAtomic(KMByteBlob.cast(deviceInfo).getBuffer(), - KMByteBlob.cast(deviceInfo).getStartOff(), - deviceInfoBytes, - (short) 0, - KMByteBlob.cast(deviceInfo).length()); - short deviceInfoBytesLen = KMByteBlob.cast(deviceInfo).length(); - short protectedHeader = KMArray.cast(ret).get((short) 3); - byte[] protectedHeaderBytes = new byte[KMByteBlob.cast(protectedHeader).length()]; - Util.arrayCopyNonAtomic( - KMByteBlob.cast(protectedHeader).getBuffer(), - KMByteBlob.cast(protectedHeader).getStartOff(), - protectedHeaderBytes, - (short) 0, - KMByteBlob.cast(protectedHeader).length() - ); - short UnProtectedHeader = KMArray.cast(ret).get((short) 4); - byte[] unProtectedHeaderBytes = new byte[256]; - short unProtectedHeaderBytesLen = encoder.encode(UnProtectedHeader, unProtectedHeaderBytes, - (short) 0, (short) 256); - short cipherObj = KMArray.cast(ret).get((short) 5); - byte[] cipher = new byte[5000]; - short startOffset = 0; - Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherObj).getBuffer(), - KMByteBlob.cast(cipherObj).getStartOff(), cipher, - (short) 0, KMByteBlob.cast(cipherObj).length()); - startOffset = KMByteBlob.cast(cipherObj).length(); - byte moreData = KMInteger.cast(KMArray.cast(ret).get((short) 6)).getByte(); - - short recipientStruct = KMType.INVALID_VALUE; - while (moreData != 0) { - apdu = new CommandAPDU(0x80, INS_GET_RESPONSE_CMD, 0x50, 0x00, (byte[]) null, 65536); - response = simulator.transmitCommand(apdu); - arr = KMArray.instance((short) 4); - // Prepare recipients expression. - short byteBlobExp = KMByteBlob.exp(); - coseHeadersExp = KMCoseHeaders.exp(); - short recipient = KMArray.instance((short) 3); - KMArray.cast(recipient).add((short) 0, byteBlobExp); - KMArray.cast(recipient).add((short) 1, coseHeadersExp); - KMArray.cast(recipient).add((short) 2, KMSimpleValue.exp()); - short recipientsExp = KMArray.exp(recipient); - KMArray.cast(arr).add((short) 0, KMInteger.exp()); // OK - KMArray.cast(arr).add((short) 1, byteBlobExp); // partial cipherText - KMArray.cast(arr).add((short) 2, recipientsExp); // recipient structure - KMArray.cast(arr).add((short) 3, KMInteger.exp()); // more data - resp = response.getBytes(); - ret = decoder.decode(arr, resp, (short) 0, (short) resp.length); - Assert.assertEquals(KMTestUtils.getErrorCode(ret), KMError.OK); - moreData = KMInteger.cast(KMArray.cast(ret).get((short) 3)).getByte(); - recipientStruct = KMArray.cast(ret).get((short) 2); - cipherObj = KMArray.cast(ret).get((short) 1); - Util.arrayCopyNonAtomic(KMByteBlob.cast(cipherObj).getBuffer(), - KMByteBlob.cast(cipherObj).getStartOff(), cipher, - startOffset, KMByteBlob.cast(cipherObj).length()); - startOffset += KMByteBlob.cast(cipherObj).length(); - } - short cipherLength = startOffset; - short cipherByteBlob = KMByteBlob.instance(cipher, (short) 0, cipherLength); - protectedHeader = KMByteBlob.instance(protectedHeaderBytes, (short) 0, - (short) protectedHeaderBytes.length); - UnProtectedHeader = decoder.decode(KMCoseHeaders.exp(), unProtectedHeaderBytes, (short) 0, - unProtectedHeaderBytesLen); - short protectedDataArrPtr = - KMCose.constructCoseEncrypt(protectedHeader, UnProtectedHeader, cipherByteBlob, - recipientStruct); - - //Verify code - KMTestUtils.print(deviceInfoBytes, (short) 0, deviceInfoBytesLen); - deviceInfo = decoder.decode(KMTestUtils.getDeviceInfoExp(), deviceInfoBytes, (short) 0, - deviceInfoBytesLen); - pubKeysToSignMac = KMByteBlob.instance(pubKeysToSignMacBytes, (short) 0, - (short) pubKeysToSignMacBytes.length); - // In Production mode we cannot validate the protected data since we don't have the - // EEK Private key. - if (testMode) { - KMTestUtils.validateProtectedData(cryptoProvider, encoder, decoder, eekId, eekKey, - CSR_CHALLENGE, encodedCoseKeysArray, - testMode, protectedDataArrPtr, deviceInfo, pubKeysToSignMac); - } - } - -} diff --git a/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java b/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java index d5828bc1..c3a1ba00 100644 --- a/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java +++ b/Applet/JCardSimProvider/test/com/android/javacard/test/KMTestUtils.java @@ -4,11 +4,6 @@ import com.android.javacard.keymaster.KMAsn1Parser; import com.android.javacard.keymaster.KMByteBlob; import com.android.javacard.keymaster.KMCose; -import com.android.javacard.keymaster.KMCoseCertPayload; -import com.android.javacard.keymaster.KMCoseHeaders; -import com.android.javacard.keymaster.KMCoseKey; -import com.android.javacard.keymaster.KMCosePairIntegerTag; -import com.android.javacard.keymaster.KMCosePairNegIntegerTag; import com.android.javacard.keymaster.KMDecoder; import com.android.javacard.keymaster.KMEncoder; import com.android.javacard.keymaster.KMError; @@ -18,7 +13,6 @@ import com.android.javacard.keymaster.KMKeymasterApplet; import com.android.javacard.keymaster.KMMap; import com.android.javacard.keymaster.KMNInteger; -import com.android.javacard.keymaster.KMSemanticTag; import com.android.javacard.keymaster.KMSimpleValue; import com.android.javacard.keymaster.KMTextString; import com.android.javacard.keymaster.KMType; @@ -192,25 +186,6 @@ public static short readMajorTypeWithPayloadLength(byte[] resp, short majorType) return payloadLength; } - public static short decodeCoseMac(KMDecoder decoder, byte[] coseMac, short coseMacOff, - short coseMacLen) { - short arrPtr = KMArray.instance((short) 4); - short coseHeadersExp = KMCoseHeaders.exp(); - KMArray.cast(arrPtr).add((short) 0, KMByteBlob.exp()); - KMArray.cast(arrPtr).add((short) 1, coseHeadersExp); - KMArray.cast(arrPtr).add((short) 2, KMByteBlob.exp()); - KMArray.cast(arrPtr).add((short) 3, KMByteBlob.exp()); - short ret = decoder.decode(arrPtr, coseMac, coseMacOff, coseMacLen); - return ret; - } - - public static short getCoseKeyFromCoseMac(KMDecoder decoder, short coseMacPtr) { - short payload = KMArray.cast(coseMacPtr).get((short) 2); - return decoder.decode(KMCoseKey.exp(), KMByteBlob.cast(payload).getBuffer(), - KMByteBlob.cast(payload).getStartOff(), - KMByteBlob.cast(payload).length()); - } - public static short getDeviceInfoExp() { short textStrExp = KMTextString.exp(); short byteBlobExp = KMByteBlob.exp(); @@ -250,111 +225,6 @@ public static short getDeviceInfoExp() { return map; } - public static short generateEEk(KMSEProvider cryptoProvider, KMEncoder encoder, KeyPair eekKey, - byte[] eekId, short length) { - byte[] pub = new byte[65]; // EC Public key - byte[] priv = new byte[32]; // EC Private Key - short[] lengths = new short[2]; - KeyPair signingKey = null; - short alg = KMNInteger.uint_8(KMCose.COSE_ALG_ES256); - boolean testMode = true; - short xPtr = 0; - short yPtr = 0; - short keyId = KMType.INVALID_VALUE; - short eekChainArr = KMArray.instance(length); - - for (short i = 0; i < length; i++) { - KeyPair keyPair; - if (i == (length - 1)) { - keyPair = eekKey; - getEcKeys(keyPair, pub, priv, lengths); - } else { - keyPair = generateEcKeyPair(cryptoProvider, pub, priv, lengths); - } - if (i == 0) { // First key is self signed. - signingKey = keyPair; - } - // prepare coseKey and encode it. - if (pub[0] == 0x04) { // uncompressed - short pubLen = lengths[1]; - pubLen = (short) ((pubLen - 1) / 2); - xPtr = KMByteBlob.instance(pub, (short) 1, pubLen); - yPtr = KMByteBlob.instance(pub, (short) (pubLen + 1), pubLen); - } else { - Assert.fail("Not in uncompressed form."); - } - if (i == length - 1) { - alg = KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256); - keyId = KMByteBlob.instance(eekId, (short) 0, (short) eekId.length); - } - short[] scratchBufferEncode = new short[20]; - short coseKey = - KMCose.constructCoseKey(scratchBufferEncode, - KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), - keyId, - alg, - KMType.INVALID_VALUE, - KMInteger.uint_8(KMCose.COSE_ECCURVE_256), - xPtr, - yPtr, - KMType.INVALID_VALUE, - testMode); - byte[] scratchpad = new byte[200]; - short coseKeyEncodedLen = encoder.encode(coseKey, scratchpad, (short) 0, (short) 200); - short payload = KMByteBlob.instance(scratchpad, (short) 0, coseKeyEncodedLen); - //print(KMByteBlob.cast(payload).getBuffer(), KMByteBlob.cast(payload).getStartOff(), - // KMByteBlob.cast(payload).length()); - - // Prepare protectedHeader - short headerPtr = KMCose.constructHeaders(scratchBufferEncode, - KMNInteger.uint_8(KMCose.COSE_ALG_ES256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - byte[] coseHeaders = new byte[200]; - short coseHeadersLen = encoder.encode(headerPtr, coseHeaders, (short) 0, (short) 200); - short protectedHeader = KMByteBlob.instance(coseHeadersLen); - Util.arrayCopyNonAtomic(coseHeaders, (short) 0, KMByteBlob.cast(protectedHeader).getBuffer(), - KMByteBlob.cast(protectedHeader).getStartOff(), coseHeadersLen); - - // prepare Cose Sign_Structure - byte[] coseSignStructureEncoded = new byte[200]; - short coseSignStructureEncodedLen; - short coseSignStructure = - KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), - payload); - coseSignStructureEncodedLen = encoder.encode(coseSignStructure, coseSignStructureEncoded, - (short) 0, (short) 200); - - // Sign the Sign_structure with signingKey. - KMECPrivateKey privateKey = new KMECPrivateKey(signingKey); - short signLen = - cryptoProvider.ecSign256(privateKey, - coseSignStructureEncoded, (short) 0, coseSignStructureEncodedLen, scratchpad, - (short) 0); - short signPtr = KMByteBlob.instance(scratchpad, (short) 0, signLen); - KMAsn1Parser asn1Parser = KMAsn1Parser.instance(); - signLen = asn1Parser.decodeEcdsa256Signature(signPtr, scratchpad, (short) 0); - KMByteBlob.cast(signPtr).setValue(scratchpad, (short) 0, signLen); - - // prepare Cose_Sign1 - short emptyArr = KMArray.instance((short) 0); - KMCoseHeaders.instance(emptyArr); - short coseSign1 = - KMCose.constructCoseSign1(protectedHeader, - KMCoseHeaders.instance(emptyArr), - payload, - signPtr); - - KMArray.cast(eekChainArr).add(i, coseSign1); - - // copy signing key - signingKey = keyPair; - } - return eekChainArr; - } - public static short receiveErrorCodeExp() { short arr = KMArray.instance((short) 1); KMArray.cast(arr).add((short) 0, KMInteger.exp()); @@ -365,162 +235,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 + bcc + ? AdditionalCertChain] - //-------------------------------------------- - short payloadLength = testMode ? (short) 2 : (short) 3; - short additionalCertChain = 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()); - // bcc exp - short bccArr = KMArray.instance((short) 2); - KMArray.cast(bccArr).add((short) 0, coseKeyExp); - KMArray.cast(bccArr).add((short) 1, signedMacArr); - short headers = KMCoseHeaders.exp(); - short arrInst = KMArray.instance((short) 4); - KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); - KMArray.cast(arrInst).add((short) 1, headers); - KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); - KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); - short x509CertChainArr = KMArray.exp(KMByteBlob.exp()); - if (!testMode) { - additionalCertChain = KMMap.instance((short) 1); - KMMap.cast(additionalCertChain).add((short) 0, KMTextString.exp(), x509CertChainArr); - } - // protected payload exp - short payload = KMArray.instance(payloadLength); - KMArray.cast(payload).add((short) 0, signedMacArr); - KMArray.cast(payload).add((short) 1, bccArr); - if (additionalCertChain != 0) { - KMArray.cast(payload).add((short) 2, additionalCertChain); - } - short payloadPtr = decoder.decode(payload, plainText, (short) 0, encryptedDataLen); - byte[] pub = new byte[100]; - //-------------------------------------------- - // Validate BCC and get public key. - //-------------------------------------------- - short pubLen = getBccPublicKey(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 Additional certificate chain. - //-------------------------------------------- - if (!testMode) { - short addCertChain = KMArray.cast(payloadPtr).get((short) 2); - addCertChain = KMMap.cast(addCertChain).getKeyValue((short) 0); - try { - Assert.assertTrue(validateCertChain(cryptoProvider, encoder, decoder, KMCose.COSE_ALG_ES256, - KMCose.COSE_ALG_ES256, addCertChain)); - } catch (IOException e) { - e.printStackTrace(); - Assert.fail("Certificate chain validation failed."); - } - } - } - public static X509Certificate decodeCert(byte[] cert, short certOff, short certLen) throws IOException { System.out.println("Certificate=>"); @@ -641,300 +355,6 @@ public static short encodeES256CoseSignSignature(byte[] input, short offset, sho return length; } - public static short getBccPublicKey(KMSEProvider cryptoProvider, KMEncoder encoder, - KMDecoder decoder, short bccPtr, byte[] pub, short pubOff) { - short len = KMArray.cast(bccPtr).length(); - short pubKeyLen = 0; - short prevCoseKey = KMArray.cast(bccPtr).get((short) 0); - for (short index = 1; index < len; index++) { - //-------------------------------------------- - // Validate Cose_Sign1 - //-------------------------------------------- - short coseSign1Arr = KMArray.cast(bccPtr).get(index); - // Validate protected Header. - short headers = KMArray.cast(coseSign1Arr).get((short) 0); - short protectedHeader = headers; - headers = - decoder.decode(KMCoseHeaders.exp(), KMByteBlob.cast(headers).getBuffer(), - KMByteBlob.cast(headers).getStartOff(), KMByteBlob.cast(headers).length()); - Assert.assertEquals(KMCose.COSE_ALG_ES256, - (byte) KMNInteger.cast(KMCoseHeaders.cast(headers).getAlgorithm()).getShort()); - // Validate unprotected header. - headers = KMArray.cast(coseSign1Arr).get((short) 1); - Assert.assertEquals(0, KMCoseHeaders.cast(headers).length()); - // Get the payload. - short payload = KMArray.cast(coseSign1Arr).get((short) 2); - // Get the signature - short signature = KMArray.cast(coseSign1Arr).get((short) 3); - // Construct COSE_Struct. - short signStructure = - KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), - payload); - byte[] input = new byte[1024]; - short inputLen = encoder.encode(signStructure, input, (short) 0, (short) 1024); - //Get public key from the coseKey. - pubKeyLen = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(pub, pubOff); - byte[] scratchPad = new byte[80]; - short signatureLen = - encodeES256CoseSignSignature( - KMByteBlob.cast(signature).getBuffer(), - KMByteBlob.cast(signature).getStartOff(), - KMByteBlob.cast(signature).length(), - scratchPad, - (short) 0); - // Verify the signature of cose sign1. - Assert.assertTrue( - cryptoProvider.ecVerify256(pub, pubOff, pubKeyLen, input, (short) 0, inputLen, - scratchPad, (short) 0, signatureLen)); - - // Get the public key from the payload. - short certPayload = KMCoseCertPayload.exp(); - short payloadPtr = - decoder.decode(certPayload, KMByteBlob.cast(payload).getBuffer(), - KMByteBlob.cast(payload).getStartOff(), - KMByteBlob.cast(payload).length()); - short coseKeyPtr = KMCoseCertPayload.cast(payloadPtr).getSubjectPublicKey(); - coseKeyPtr = decoder.decode(KMCoseKey.exp(), KMByteBlob.cast(coseKeyPtr).getBuffer(), - KMByteBlob.cast(coseKeyPtr).getStartOff(), KMByteBlob.cast(coseKeyPtr).length()); - prevCoseKey = coseKeyPtr; - } - return pubKeyLen; - } - - public static void validateSignedMac(KMSEProvider cryptoProvider, KMEncoder encoder, - KMDecoder decoder, - byte[] csrChallenge, byte[] encodedCoseKeysArray, short signedMacPtr, byte[] pub, - short pubOff, short pubLen, - short deviceInfoMapPtr, short pubKeysToSignMac) { - //-------------------------------------------- - // Validate Cose_Sign1 - //-------------------------------------------- - short headers = KMArray.cast(signedMacPtr).get((short) 0); - short protectedHeader = headers; - headers = - decoder.decode(KMCoseHeaders.exp(), KMByteBlob.cast(headers).getBuffer(), - KMByteBlob.cast(headers).getStartOff(), KMByteBlob.cast(headers).length()); - Assert.assertEquals(KMCose.COSE_ALG_ES256, - (byte) KMNInteger.cast(KMCoseHeaders.cast(headers).getAlgorithm()).getShort()); - // Validate unprotected header. - headers = KMArray.cast(signedMacPtr).get((short) 1); - Assert.assertEquals(0, KMCoseHeaders.cast(headers).length()); - // Get the payload. - short payload = KMArray.cast(signedMacPtr).get((short) 2); - // Get the signature - short signature = KMArray.cast(signedMacPtr).get((short) 3); - // Prepare Aad [Challenge + deviceInfoMap] - short aad = KMArray.instance((short) 3); - KMArray.cast(aad).add((short) 0, - KMByteBlob.instance(csrChallenge, (short) 0, (short) csrChallenge.length)); - KMArray.cast(aad).add((short) 1, deviceInfoMapPtr); - KMArray.cast(aad).add((short) 2, pubKeysToSignMac); - byte[] aadBuf = new byte[512]; - short aadLen = encoder.encode(aad, aadBuf, (short) 0, (short) 512); - aad = KMByteBlob.instance(aadBuf, (short) 0, aadLen); - // Construct COSE_Struct. - short signStructure = - KMCose.constructCoseSignStructure(protectedHeader, aad, payload); - byte[] input = new byte[1024]; - short inputLen = encoder.encode(signStructure, input, (short) 0, (short) 1024); - byte[] signatureBuf = new byte[80]; - short signatureLen = - encodeES256CoseSignSignature(KMByteBlob.cast(signature).getBuffer(), - KMByteBlob.cast(signature).getStartOff(), - KMByteBlob.cast(signature).length(), signatureBuf, (short) 0); - // Verify the signature of cose sign1. - Assert.assertTrue(cryptoProvider.ecVerify256(pub, pubOff, pubLen, input, (short) 0, inputLen, - signatureBuf, (short) 0, signatureLen)); - //-------------------------------------------- - // Get the ephemeral mac key and verify the signed mac keys. - //-------------------------------------------- - short mac = - constructPubKeysToSignMac(cryptoProvider, encoder, - KMByteBlob.cast(payload).getBuffer(), - KMByteBlob.cast(payload).getStartOff(), - KMByteBlob.cast(payload).length(), - KMByteBlob.instance(encodedCoseKeysArray, (short) 0, - (short) encodedCoseKeysArray.length)); - Assert.assertEquals(0, - Util.arrayCompare( - KMByteBlob.cast(mac).getBuffer(), - KMByteBlob.cast(mac).getStartOff(), - KMByteBlob.cast(pubKeysToSignMac).getBuffer(), - KMByteBlob.cast(pubKeysToSignMac).getStartOff(), - KMByteBlob.cast(pubKeysToSignMac).length() - ) - ); - } - - public static short constructPubKeysToSignMac(KMSEProvider cryptoProvider, KMEncoder encoder, - byte[] ephemeralKey, - short ephemeralKeyOff, short ephemeralKeyLen, short pubKeysToSign) { - short ptr; - short len; - byte[] scratchPad = new byte[2048]; - short[] headerScratchpad = new short[15]; - short headerPtr = KMCose.constructHeaders(headerScratchpad, - KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - len = encoder.encode(headerPtr, scratchPad, (short) 0, (short) 2048); - short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); - // create MAC_Structure - ptr = - KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), - pubKeysToSign); - // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 - len = encoder.encode(ptr, scratchPad, (short) 0, (short) 2048); - ptr = - cryptoProvider.hmacSign( - ephemeralKey, - ephemeralKeyOff, - ephemeralKeyLen, - scratchPad, - (short) 0, - len, - scratchPad, - len // offset - ); - return KMByteBlob.instance(scratchPad, len, ptr); - } - - public static short constructCoseSign1(KMSEProvider cryptoProvider, KMEncoder encoder, - short protectedHeader, short unProtectedHeader, short payload, - short aad, - KMDeviceUniqueKeyPair signingKey) { - byte[] scratchpad = new byte[500]; - short signStructure = KMCose.constructCoseSignStructure(protectedHeader, aad, payload); - signStructure = encoder.encode(signStructure, scratchpad, (short) 0, (short) 500); - short len = cryptoProvider.ecSign256(signingKey, scratchpad, (short) 0, signStructure, - scratchpad, signStructure); - signStructure = KMByteBlob.instance(scratchpad, signStructure, len); - KMAsn1Parser asn1Parser = KMAsn1Parser.instance(); - len = asn1Parser.decodeEcdsa256Signature(signStructure, scratchpad, (short) 0); - KMByteBlob.cast(signStructure).setValue(scratchpad, (short) 0, len); - return KMCose.constructCoseSign1(protectedHeader, unProtectedHeader, payload, signStructure); - } - - public static short constructCoseKey(short keyType, short keyId, short keyAlg, short keyOps, - short curve, - byte[] pubKey, short pubKeyOff, short pubKeyLen, - byte[] priv, short privKeyOff, short privKeyLen, boolean testMode) { - if (pubKey[pubKeyOff] == 0x04) { // uncompressed format - pubKeyOff += 1; - pubKeyLen -= 1; - } - pubKeyLen = (short) (pubKeyLen / 2); - short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen); - short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen); - short privPtr = KMByteBlob.instance(priv, privKeyOff, privKeyLen); - short[] scratchpad = new short[20]; - short coseKey = KMCose.constructCoseKey(scratchpad, keyType, keyId, keyAlg, keyOps, curve, xPtr, - yPtr, privPtr, testMode); - KMCoseKey.cast(coseKey).canonicalize(); - return coseKey; - } - - public static short getSenderPublicKeyAndKeyIdFromRecipientStructure(KMDecoder decoder, - byte[] EEK_KEY_ID, short protectedDataArrPtr, - byte[] pub, short pubOff, - byte[] eekId, short eekIdOff, short eekIdLen) { - //-------------------------------------------- - // Get Recipients and validate recipients - //-------------------------------------------- - short recipientsArr = KMArray.cast(protectedDataArrPtr).get((short) 3); - // recipients array should contain only 1 recipient. - Assert.assertEquals(1, KMArray.cast(recipientsArr).length()); - short recipient = KMArray.cast(recipientsArr).get((short) 0); - // The recipient should be an array of length 3. - Assert.assertEquals(3, KMArray.cast(recipient).length()); - // The 3rd element inside the recipient should be an null value of simple type. - short simplePtr = KMArray.cast(recipient).get((short) 2); - Assert.assertEquals(KMSimpleValue.NULL, KMSimpleValue.cast(simplePtr).getValue()); - //-------------------------------------------- - // Get and validate protected parameters inside the recipient structure. - //-------------------------------------------- - short params = KMArray.cast(recipient).get((short) 0); - //print(KMByteBlob.cast(params).getBuffer(), - //KMByteBlob.cast(params).getStartOff(), KMByteBlob.cast(params).length()); - 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 ECDH_ES_HKDF_256. - Assert.assertEquals(1, KMArray.cast(params).length()); - short param = KMArray.cast(params).get((short) 0); - Assert.assertEquals(KMCose.COSE_ALG_ECDH_ES_HKDF_256, - (byte) KMNInteger.cast(KMCosePairNegIntegerTag.cast(param).getValuePtr()).getShort()); - //-------------------------------------------- - // Get and validate unprotected parameters inside the recipient structure. - //-------------------------------------------- - params = KMArray.cast(recipient).get((short) 1); - short coseKey = KMCoseHeaders.cast(params).getCoseKey(); - //-------------------------------------------- - // Validate the COSE_Key. - //-------------------------------------------- - short[] scratchBuffer = new short[20]; - Assert.assertTrue( - KMCoseKey.cast(coseKey) - .isDataValid(scratchBuffer, KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, - KMCose.COSE_ALG_ES256, - KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)); - //-------------------------------------------- - // Validate the EEK Key id. - //-------------------------------------------- - short receivedEekId = KMCoseHeaders.cast(params).getKeyIdentifier(); - Assert.assertEquals(eekIdLen, KMByteBlob.cast(receivedEekId).length()); - Assert.assertEquals(0, - Util.arrayCompare(EEK_KEY_ID, (short) 0, KMByteBlob.cast(receivedEekId).getBuffer(), - KMByteBlob.cast(receivedEekId).getStartOff(), eekIdLen)); - Util.arrayCopyNonAtomic(KMByteBlob.cast(receivedEekId).getBuffer(), - KMByteBlob.cast(receivedEekId).getStartOff(), eekId, eekIdOff, eekIdLen); - return KMCoseKey.cast(coseKey).getEcdsa256PublicKey(pub, pubOff); - } - - public static short ecdhHkdfDeriveKey(KMSEProvider cryptoProvider, KMEncoder encoder, - byte[] privKeyA, short privKeyAOff, short privKeyALen, - byte[] pubKeyA, - short pubKeyAOff, short pubKeyALen, byte[] pubKeyB, short pubKeyBOff, - short pubKeyBLen, byte[] sessionKey, short sessionKeyOff) { - byte[] scratchPad = new byte[1024]; - short key = - cryptoProvider.ecdhKeyAgreement(privKeyA, privKeyAOff, privKeyALen, pubKeyB, pubKeyBOff, - pubKeyBLen, scratchPad, (short) 0); - key = KMByteBlob.instance(scratchPad, (short) 0, key); - - // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04. - pubKeyAOff += 1; - pubKeyALen -= 1; - pubKeyBOff += 1; - pubKeyBLen -= 1; - short kdfContext = - KMCose.constructKdfContext(pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, - false); - kdfContext = encoder.encode(kdfContext, scratchPad, (short) 0, (short) 1024); - kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext); - - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0); - cryptoProvider.hkdf( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, - (short) 0, - (short) 32, - KMByteBlob.cast(kdfContext).getBuffer(), - KMByteBlob.cast(kdfContext).getStartOff(), - KMByteBlob.cast(kdfContext).length(), - scratchPad, - (short) 32, // offset - (short) 32 // Length of expected output. - ); - Util.arrayCopy(scratchPad, (short) 32, sessionKey, sessionKeyOff, (short) 32); - return (short) 32; - } - public static KeyPair generateEcKeyPair(KMSEProvider cryptoProvider, byte[] pub, byte[] priv, short[] lengths) { cryptoProvider diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java index c45ecfc0..b34a1e79 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMJCardSimulator.java @@ -1297,19 +1297,6 @@ public short getAttestationKeyAlgorithm(){ return KMType.INVALID_VALUE; } - @Override - public KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair( - KMDeviceUniqueKeyPair key, byte[] pubKey, short pubKeyOff, - short pubKeyLen, byte[] privKey, short privKeyOff, short privKeyLen) { - if (key == null) { - KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); - key = new KMECDeviceUniqueKey(ecKeyPair); - } - ((KMECDeviceUniqueKey) key).setS(privKey, privKeyOff, privKeyLen); - ((KMECDeviceUniqueKey) key).setW(pubKey, pubKeyOff, pubKeyLen); - return (KMDeviceUniqueKeyPair) key; - } - @Override public short rsaSign256Pkcs1( byte[] secret, @@ -1350,18 +1337,6 @@ public short ecSign256(KMAttestationKey attestationKey, outputDataBuf, outputDataStart); } - - @Override - public short ecSign256(KMDeviceUniqueKeyPair deviceUniqueKey, byte[] inputDataBuf, short inputDataStart, - short inputDataLength, byte[] outputDataBuf, short outputDataStart) { - ECPrivateKey key = ((KMECDeviceUniqueKey) deviceUniqueKey).getPrivateKey(); - Signature signer = Signature - .getInstance(Signature.ALG_ECDSA_SHA_256, false); - signer.init(key, Signature.MODE_SIGN); - return signer.sign(inputDataBuf, inputDataStart, inputDataLength, - outputDataBuf, outputDataStart); - } - @Override public boolean ecVerify256(byte[] pubKey, short pubKeyOffset, short pubKeyLen, byte[] inputDataBuf, short inputDataStart, short inputDataLength, byte[] signatureDataBuf, @@ -1416,7 +1391,7 @@ public com.android.javacard.seprovider.KMMasterKey createMasterKey( @Override public boolean isAttestationKeyProvisioned(){ - return false; + return true; } public boolean isPowerReset(){ @@ -1428,6 +1403,18 @@ public boolean isPowerReset(){ return flag; } + @Override + public KMAttestationKey createAttestationKey(KMAttestationKey key, + byte[] keyData, short offset, short length) { + if (key == null) { + KeyPair ecKeyPair = new KeyPair(KeyPair.ALG_EC_FP, KeyBuilder.LENGTH_EC_FP_256); + key = new KMECPrivateKey(ecKeyPair); + } + ECPrivateKey ecKeyPair = (ECPrivateKey) ((KMECPrivateKey) key).getPrivateKey(); + ecKeyPair.setS(keyData, offset, length); + return (KMAttestationKey) key; + } + @Override public short messageDigest256(byte[] inBuff, short inOffset, short inLength, byte[] outBuff, short outOffset) { @@ -1462,18 +1449,6 @@ public short getBackupObjectCount(byte interfaceType) { return 0; } - @Override - public KMRkpMacKey createRkpMacKey(KMRkpMacKey rkpMacKey, byte[] keyData, - short offset, short length) { - if (rkpMacKey == null) { - HMACKey key = (HMACKey) KeyBuilder.buildKey(KeyBuilder.TYPE_HMAC, (short) (length * 8), - false); - rkpMacKey = new KMHmacKey(key); - } - ((KMHmacKey) rkpMacKey).setKey(keyData, offset, length); - return rkpMacKey; - } - @Override public com.android.javacard.seprovider.KMPreSharedKey createPreSharedKey( com.android.javacard.seprovider.KMPreSharedKey presharedKey, byte[] keyData, short offset, @@ -1491,31 +1466,6 @@ public com.android.javacard.seprovider.KMPreSharedKey createPreSharedKey( return (KMPreSharedKey) preSharedKey; } - -@Override -public KMOperation getRkpOperation(byte purpose, byte alg, - byte digest, byte padding, byte blockMode, byte[] keyBuf, short keyStart, - short keyLength, byte[] ivBuf, short ivStart, short ivLength, - short macLength) { - KMOperation opr = null; - switch (alg) { - case KMType.AES: - KMCipher aesGcm = createAesGcmCipher(purpose, macLength, keyBuf, keyStart, keyLength, - ivBuf, ivStart, ivLength); - opr = new KMOperationImpl(aesGcm); - break; - case KMType.HMAC: - Signature signerVerifier = createHmacSignerVerifier(purpose, digest, keyBuf, keyStart, - keyLength); - opr = new KMOperationImpl(signerVerifier); - break; - default: - CryptoException.throwIt(CryptoException.NO_SUCH_ALGORITHM); - break; - } - return opr; - } - @Override public boolean isBootSignalEventSupported() { return isBootEventSignalSupported; diff --git a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java index c758889f..bb4cf83c 100644 --- a/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java +++ b/Applet/JCardSimProviderLib/src/com/android/javacard/seprovider/KMSEProvider.java @@ -489,24 +489,6 @@ boolean ecVerify256( short signatureDataStart, short signatureDataLen); - /** - * This is a oneshot operation that signs the data using device unique key. - * - * @param ecPrivKey instance of KMECDeviceUniqueKey to sign the input data. - * @param inputDataBuf is the buffer of the input data. - * @param inputDataStart is the start of the input data buffer. - * @param inputDataLength is the length of the input data buffer in bytes. - * @param outputDataBuf is the output buffer that contains the signature. - * @param outputDataStart is the start of the output data buffer. - * @return length of the decrypted data. - */ - short ecSign256( - KMDeviceUniqueKeyPair ecPrivKey, - byte[] inputDataBuf, - short inputDataStart, - short inputDataLength, - byte[] outputDataBuf, - short outputDataStart); short ecSign256(byte[] secret, short secretStart, short secretLength, byte[] inputDataBuf, short inputDataStart, short inputDataLength, @@ -598,40 +580,7 @@ KMOperation initSymmetricOperation( short ivLength, short macLength, boolean oneShot); - - /** - * This function creates an Operation instance only for RKP module. - * - * @param purpose is KMType.ENCRYPT or KMType.DECRYPT for AES and DES algorithm. It will be - * KMType.SIGN and KMType.VERIFY for HMAC algorithm - * @param alg is KMType.HMAC, KMType.AES or KMType.DES. - * @param digest is KMType.SHA2_256 in case of HMAC else it will be KMType.DIGEST_NONE. - * @param padding is KMType.PADDING_NONE or KMType.PKCS7 (in case of AES and DES). - * @param blockMode is KMType.CTR, KMType.GCM. KMType.CBC or KMType.ECB for AES or DES else it is - * 0. - * @param keyBuf is aes, des or hmac key buffer. - * @param keyStart is the start of the key buffer. - * @param keyLength is the length of the key buffer. - * @param ivBuf is the iv buffer (in case on AES and DES algorithm without ECB mode) - * @param ivStart is the start of the iv buffer. - * @param ivLength is the length of the iv buffer. It will be zero in case of HMAC and AES/DES - * with ECB mode. - * @param macLength is the mac length in case of signing operation for hmac algorithm. - * @return KMOperation instance. - */ - KMOperation getRkpOperation(byte purpose, - byte alg, - byte digest, - byte padding, - byte blockMode, - byte[] keyBuf, - short keyStart, - short keyLength, - byte[] ivBuf, - short ivStart, - short ivLength, - short macLength); - + /** * This creates a persistent operation for signing, verify, encryption and decryption using RSA * and EC algorithms when keymaster hal's beginOperation function is executed. For RSA the public @@ -707,20 +656,18 @@ KMOperation initAsymmetricOperation( short getAttestationKeyAlgorithm(); /** - * Creates an ECKey instance and sets the public and private keys to it. + * This function creates an ECKey and initializes the ECPrivateKey with the provided input key + * data. The initialized Key is maintained by the SEProvider. This function should be called only + * while provisioning the attestation key. * - * @param testMode to indicate if current execution is for test or production. - * @param pubKey buffer containing the public key. - * @param pubKeyOff public key buffer start offset. - * @param pubKeyLen public key buffer length. - * @param privKey buffer containing the private key. - * @param privKeyOff private key buffer start offset. - * @param privKeyLen private key buffer length. - * @return instance of KMDeviceUniqueKey. + * @param key instance of the KMAttestationKey. + * @param keyData buffer containing the ec private key. + * @param offset start of the buffer. + * @param length length of the buffer. + * @return An instance of KMAttestationKey. */ - KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(KMDeviceUniqueKeyPair key, - byte[] pubKey, short pubKeyOff, short pubKeyLen, - byte[] privKey, short privKeyOff, short privKeyLen); + KMAttestationKey createAttestationKey(KMAttestationKey key, + byte[] keyData, short offset, short length); /** * This is a one-shot operation the does digest of the input mesage. @@ -781,16 +728,4 @@ KMPreSharedKey createPreSharedKey(KMPreSharedKey presharedKey, byte[] key, short * @return count of the objects. */ short getBackupObjectCount(byte interfaceType); - - /** - * This function creates an HMACKey and initializes the key with the provided input key data. - * - * @param keyData buffer containing the key data. - * @param offset start of the buffer. - * @param length length of the buffer. - * @return An instance of the KMRkpMacKey. - */ - KMRkpMacKey createRkpMacKey(KMRkpMacKey createComputedHmacKey, byte[] keyData, - short offset, short length); - } diff --git a/Applet/src/com/android/javacard/keymaster/KMCose.java b/Applet/src/com/android/javacard/keymaster/KMCose.java index ca72db64..6d35a24c 100644 --- a/Applet/src/com/android/javacard/keymaster/KMCose.java +++ b/Applet/src/com/android/javacard/keymaster/KMCose.java @@ -16,113 +16,24 @@ package com.android.javacard.keymaster; -import javacard.framework.ISO7816; -import javacard.framework.ISOException; - /** * This class constructs the Cose messages like CoseKey, CoseMac0, MacStructure, * CoseSign1, SignStructure, CoseEncrypt, EncryptStructure and ReceipientStructures. */ public class KMCose { - //COSE SIGN1 - public static final byte COSE_SIGN1_ENTRY_COUNT = 4; - public static final byte COSE_SIGN1_PROTECTED_PARAMS_OFFSET = 0; - public static final short COSE_SIGN1_UNPROTECTED_PARAMS_OFFSET = 1; - public static final short COSE_SIGN1_PAYLOAD_OFFSET = 2; - public static final short COSE_SIGN1_SIGNATURE_OFFSET = 3; //COSE MAC0 public static final short COSE_MAC0_ENTRY_COUNT = 4; public static final short COSE_MAC0_PROTECTED_PARAMS_OFFSET = 0; public static final short COSE_MAC0_UNPROTECTED_PARAMS_OFFSET = 1; public static final short COSE_MAC0_PAYLOAD_OFFSET = 2; public static final short COSE_MAC0_TAG_OFFSET = 3; - //COSE ENCRYPT - public static final short COSE_ENCRYPT_ENTRY_COUNT = 4; - public static final short COSE_ENCRYPT_STRUCTURE_ENTRY_COUNT = 3; - public static final short COSE_ENCRYPT_RECIPIENT_ENTRY_COUNT = 3; - public static final short COSE_ENCRYPT_PROTECTED_PARAMS_OFFSET = 0; - public static final short COSE_ENCRYPT_UNPROTECTED_PARAMS_OFFSET = 1; - public static final short COSE_ENCRYPT_PAYLOAD_OFFSET = 2; - public static final short COSE_ENCRYPT_RECIPIENTS_OFFSET = 3; - //COSE Labels public static final byte COSE_LABEL_ALGORITHM = 1; - public static final byte COSE_LABEL_KEYID = 4; - public static final byte COSE_LABEL_IV = 5; - public static final byte COSE_LABEL_COSE_KEY = (byte) 0xFF; // -1 - //COSE Algorithms - public static final byte COSE_ALG_AES_GCM_256 = 3; //AES-GCM mode w/ 256-bit key, 128-bit tag. public static final byte COSE_ALG_HMAC_256 = 5; //HMAC w/ SHA-256 - public static final byte COSE_ALG_ES256 = (byte) 0xF9; // ECDSA w/ SHA-256; -7 - public static final byte COSE_ALG_ECDH_ES_HKDF_256 = (byte) 0xE7; // ECDH-EC+HKDF-256; -25 - - //COSE P256 EC Curve - public static final byte COSE_ECCURVE_256 = 1; - - //COSE key types - public static final byte COSE_KEY_TYPE_EC2 = 2; - public static final byte COSE_KEY_TYPE_SYMMETRIC_KEY = 4; - - //COSE Key Operations - public static final byte COSE_KEY_OP_SIGN = 1; - public static final byte COSE_KEY_OP_VERIFY = 2; - public static final byte COSE_KEY_OP_ENCRYPT = 3; - public static final byte COSE_KEY_OP_DECRYPT = 4; - - // AES GCM - public static final short AES_GCM_NONCE_LENGTH = 12; - public static final short AES_GCM_TAG_SIZE = 16; - public static final short AES_GCM_KEY_SIZE = 32; - public static final short AES_GCM_KEY_SIZE_BITS = 256; - // Cose key parameters. - public static final byte COSE_KEY_KEY_TYPE = 1; - public static final byte COSE_KEY_KEY_ID = 2; - public static final byte COSE_KEY_ALGORITHM = 3; - public static final byte COSE_KEY_KEY_OPS = 4; - public static final byte COSE_KEY_CURVE = -1; - public static final byte COSE_KEY_PUBKEY_X = -2; - public static final byte COSE_KEY_PUBKEY_Y = -3; - public static final byte COSE_KEY_PRIV_KEY = -4; - public static final byte[] COSE_TEST_KEY = {(byte) 0xFF, (byte) 0xFE, (byte) 0xEE, (byte) 0x90}; // -70000 - public static final short COSE_KEY_MAX_SIZE = 4; - - // kdfcontext strings - public static final byte[] client = {0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74}; - public static final byte[] server = {0x73, 0x65, 0x72, 0x76, 0x65, 0x72}; //Context strings 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" - // Certificate payload supported keys - public static final byte ISSUER = (byte) 0x01; - public static final byte SUBJECT = (byte) 0x02; - public static final byte[] CODE_HASH = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAF}; - public static final byte[] CODE_DESCRIPTOR = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAE}; - public static final byte[] CONFIG_HASH = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAD}; - public static final byte[] CONFIG_DESCRIPTOR = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAC}; - public static final byte[] AUTHORITY_HASH = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAB}; - public static final byte[] AUTHORITY_DESCRIPTOR = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xAA}; - public static final byte[] MODE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA9}; - public static final byte[] SUBJECT_PUBLIC_KEY = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA8}; - public static final byte[] KEY_USAGE = {(byte) 0xFF, (byte) 0xB8, (byte) 0xBB, (byte) 0xA7}; - // text strings - public static final byte[] TEST_ISSUER_NAME = {(byte) 0x49, 0x73, 0x73, 0x75, 0x65, 0x72}; // "Issuer" - public static final byte[] TEST_SUBJECT_NAME = {0x53, 0x75, 0x62, 0x6A, 0x65, 0x63, 0x74}; // "Subject" - public static final byte[] KEY_USAGE_SIGN = {0x20}; // Key usage sign - public static final byte[] MAC_DERIVE_KEY_CTX = - {0x4B, 0x65, 0x79, 0x20, 0x74, 0x6F, 0x20, 0x4D, 0x41, 0x43, 0x20, 0x70, 0x75, 0x62, 0x6C, 0x69, 0x63, - 0x20, 0x6B, 0x65, 0x79, 0x73}; // "Key to MAC public keys" - - public static final short[] coseKeyConst = {KMCose.COSE_KEY_KEY_TYPE, KMCose.COSE_KEY_KEY_ID, KMCose.COSE_KEY_ALGORITHM, KMCose.COSE_KEY_KEY_OPS, - KMCose.COSE_KEY_CURVE, KMCose.COSE_KEY_PUBKEY_X, KMCose.COSE_KEY_PUBKEY_Y, KMCose.COSE_KEY_PRIV_KEY}; - public static final short[] coseHeaderConst = {KMCose.COSE_LABEL_ALGORITHM, KMCose.COSE_LABEL_KEYID, KMCose.COSE_LABEL_IV, KMCose.COSE_LABEL_COSE_KEY}; /** * Constructs the Cose MAC structure. * @@ -182,375 +93,4 @@ public static short constructCoseMac0(short protectedHeader, short unprotectedHe return arrPtr; } - /** - * Constructs the COSE_Signature structure. - * - * @param protectedHeader Bstr pointer which holds the protected header. - * @param extAad Bstr pointer which holds the aad. - * @param payload Bstr pointer which holds the payload. - * @return KMArray instance of COSE_Signature object. - */ - public static short constructCoseSignStructure(short protectedHeader, short extAad, short payload) { - // Sig_structure = [ - // context : "Signature" / "Signature1" / "CounterSignature", - // body_protected : empty_or_serialized_map, - // ? sign_protected : empty_or_serialized_map, - // external_aad : bstr, - // payload : bstr - // ] - short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT); - // 1 - Context - KMArray.cast(arrPtr).add((short) 0, KMTextString.instance(KMCose.SIGNATURE1_CONTEXT, (short) 0, - (short) KMCose.SIGNATURE1_CONTEXT.length)); - // 2 - Protected headers. - KMArray.cast(arrPtr).add((short) 1, protectedHeader); - // 3 - external aad - KMArray.cast(arrPtr).add((short) 2, extAad); - // 4 - payload. - KMArray.cast(arrPtr).add((short) 3, payload); - return arrPtr; - } - - /** - * Constructs the COSE_Sign1 object. - * - * @param protectedHeader Bstr pointer which holds the protected header. - * @param unProtectedHeader Bstr pointer which holds the unprotected header. - * @param payload Bstr pointer which holds the payload. - * @param signature Bstr pointer which holds the signature. - * @return KMArray instance of COSE_Sign1 object. - */ - public static short constructCoseSign1(short protectedHeader, short unProtectedHeader, short payload, - short signature) { - // COSE_Sign = [ - // protectedHeader, - // unprotectedHeader, - // payload : bstr / nil, - // signatures : [+ COSE_Signature] - // ] - short arrPtr = KMArray.instance(KMCose.COSE_SIGN1_ENTRY_COUNT); - // 1 - protected headers - KMArray.cast(arrPtr).add((short) 0, protectedHeader); - // 2 - unprotected headers - KMArray.cast(arrPtr).add((short) 1, unProtectedHeader); - // 2 - payload - KMArray.cast(arrPtr).add((short) 2, payload); - // 3 - tag - KMArray.cast(arrPtr).add((short) 3, signature); - return arrPtr; - } - - /** - * Constructs array based on the tag values provided. - * - * @param tags array of tag values to be constructed. - * @param includeTestMode flag which indicates if TEST_COSE_KEY should be included or not. - * @return instance of KMArray. - */ - private static short handleCosePairTags(short[] tag, short[] keyValues, short valueIndex, boolean includeTestMode) { - short index = 0; - // var is used to calculate the length of the array. - short var = 0; - short tagLen = (short) tag.length; - // var is used to calculate the length of the array. - while (index < tagLen) { - if (keyValues[index] != KMType.INVALID_VALUE) { - keyValues[(short)(index + valueIndex)] = buildCosePairTag((byte) tag[index], keyValues[index]); - var++; - } - index++; - } - var += includeTestMode ? 1 : 0; - short arrPtr = KMArray.instance(var); - index = 0; - // var is used to index the array. - var = 0; - while (index < tagLen) { - if (keyValues[(short)(index + valueIndex)] != KMType.INVALID_VALUE) { - KMArray.cast(arrPtr).add(var++, keyValues[(short)(index + valueIndex)]); - } - index++; - } - return arrPtr; - } - - /** - * Constructs the COSE_sign1 payload for certificate. - * - * @param issuer instance of KMCosePairTextStringTag which contains issuer value. - * @param subject instance of KMCosePairTextStringTag which contains subject value. - * @param subPublicKey instance of KMCosePairByteBlobTag which contains encoded KMCoseKey. - * @param keyUsage instance of KMCosePairByteBlobTag which contains key usage value. - * @return instance of KMArray. - */ - public static short constructCoseCertPayload(short issuer, short subject, short subPublicKey, short keyUsage) { - short certPayload = KMArray.instance((short) 4); - KMArray.cast(certPayload).add((short) 0, issuer); - KMArray.cast(certPayload).add((short) 1, subject); - KMArray.cast(certPayload).add((short) 2, subPublicKey); - KMArray.cast(certPayload).add((short) 3, keyUsage); - certPayload = KMCoseCertPayload.instance(certPayload); - KMCoseCertPayload.cast(certPayload).canonicalize(); - return certPayload; - } - - /** - * Construct headers structure. Headers can be part of COSE_Sign1, COSE_Encrypt, - * COSE_Mac0 and COSE_Key. - * - * @param alg instance of either KMNInteger or KMInteger, based on the sign of algorithm value. - * @param keyId instance of KMByteBlob which contains the key identifier. - * @param iv instance of KMByteblob which contains the iv buffer. - * @param ephemeralKey instance of KMCoseKey. - * @return instance of KMCoseHeaders. - */ - public static short constructHeaders(short []buff, short alg, short keyId, short iv, short ephemeralKey) { - buff[0]= alg; - buff[1]= keyId; - buff[2]= iv; - buff[3]= ephemeralKey; - for(short i = 4; i < 8; i++) { - buff[i] = KMType.INVALID_VALUE; - } - short ptr = handleCosePairTags(coseHeaderConst, buff, (short)4, false); - ptr = KMCoseHeaders.instance(ptr); - KMCoseHeaders.cast(ptr).canonicalize(); - return ptr; - } - - /** - * 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. - * - * @param key value of the key. - * @param valuePtr instance of one of KMType. - * @return instance of KMCosePair*Value object. - */ - public static short buildCosePairTag(byte key, short valuePtr) { - short type = KMType.getType(valuePtr); - short keyPtr; - if (key < 0) { - keyPtr = KMNInteger.uint_8(key); - } else { - keyPtr = KMInteger.uint_8(key); - } - switch (type) { - case KMType.INTEGER_TYPE: - return KMCosePairIntegerTag.instance(keyPtr, valuePtr); - case KMType.NEG_INTEGER_TYPE: - return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr); - case KMType.BYTE_BLOB_TYPE: - return KMCosePairByteBlobTag.instance(keyPtr, valuePtr); - case KMType.TEXT_STRING_TYPE: - return KMCosePairTextStringTag.instance(keyPtr, valuePtr); - case KMType.COSE_KEY_TYPE: - return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr); - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - return 0; - } - } - - /** - * Constructs a CoseKey with the provided input paramters. - * - * @param keyType Instance of the identification of the key type. - * @param keyId Instance of key identification value. - * @param keyAlg Instance of the algorithm that is used with this key. - * @param keyOps Instance of the operation that this key is used for. - * @param curve Instance of the EC curve that is used with this key. - * @param pubKey Buffer containing the public key. - * @param pubKeyOff Start offset of the buffer. - * @param pubKeyLen Length of the public key. - * @param privKeyPtr Instance of the private key. - * @param testMode Represents if key is used in test mode or production mode. - * @return Instance of the CoseKey structure. - */ - public static short constructCoseKey(short []buff, short keyType, short keyId, short keyAlg, short keyOps, - short curve, byte[] pubKey, short pubKeyOff, short pubKeyLen, - short privKeyPtr, boolean testMode) { - if (pubKey[pubKeyOff] == 0x04) { // uncompressed format - pubKeyOff += 1; - pubKeyLen -= 1; - } - pubKeyLen = (short) (pubKeyLen / 2); - short xPtr = KMByteBlob.instance(pubKey, pubKeyOff, pubKeyLen); - short yPtr = KMByteBlob.instance(pubKey, (short) (pubKeyOff + pubKeyLen), pubKeyLen); - short coseKey = constructCoseKey(buff, keyType, keyId, keyAlg, keyOps, curve, xPtr, yPtr, privKeyPtr, testMode); - KMCoseKey.cast(coseKey).canonicalize(); - return coseKey; - } - - /** - * Constructs the cose key based on input parameters supplied. All the parameters must be instantiated from - * either KMInteger or KMNInteger or KMByteblob types. - * - * @param keyType instance of KMInteger/KMNInteger which holds valid COSE key types. - * @param keyId instance of KMByteBlob which holds key identifier value. - * @param keyAlg instance of KMInteger/KMNInteger which holds valid COSE key algorithm. - * @param keyOps instance of KMInteger/KMNInteger which holds valid COSE key operations. - * @param curve instance of KMInteger/KMNInteger which holds valid COSE EC curve. - * @param pubX instance of KMByteBlob which holds EC public key's x value. - * @param pubY instance of KMByteBlob which holds EC public key's y value. - * @param priv instance of KMByteBlob which holds EC private value. - * @param includeTestKey flag which identifies whether to construct test key or production key. - * @return instance of the KMCoseKey object. - */ - public static short constructCoseKey(short []buff, short keyType, short keyId, short keyAlg, short keyOps, short curve, - short pubX, short pubY, short priv, boolean includeTestKey) { - short valueIndex = 8; - buff[0] = keyType; - buff[1] = keyId; - buff[2] = keyAlg; - buff[3] = keyOps; - buff[4] = curve; - buff[5] = pubX; - buff[6] = pubY; - buff[7] = priv; - for (short i = valueIndex; i < 16; i++) { - buff[i] = KMType.INVALID_VALUE; - } - short arrPtr = handleCosePairTags(coseKeyConst, buff, valueIndex, includeTestKey); - if (includeTestKey) { - short testKey = - KMCosePairSimpleValueTag.instance(KMNInteger.uint_32(KMCose.COSE_TEST_KEY, (short) 0), - KMSimpleValue.instance(KMSimpleValue.NULL)); - KMArray.cast(arrPtr).add((short) (KMArray.cast(arrPtr).length() - 1), testKey); - } - arrPtr = KMCoseKey.instance(arrPtr); - KMCoseKey.cast(arrPtr).canonicalize(); - return arrPtr; - } - - /** - * Constructs key derivation context which is required to compute HKDF. - * - * @param publicKeyA public key buffer from the first party. - * @param publicKeyAOff start position of the public key buffer from first party. - * @param publicKeyALen length of the public key buffer from first party. - * @param publicKeyB public key buffer from the second party. - * @param publicKeyBOff start position of the public key buffer from second party. - * @param publicKeyBLen length of the public key buffer from second party. - * @param senderIsA true if caller is first party, false if caller is second party. - * @return instance of KMArray. - */ - public static short constructKdfContext(byte[] publicKeyA, short publicKeyAOff, short publicKeyALen, - byte[] publicKeyB, short publicKeyBOff, short publicKeyBLen, - boolean senderIsA) { - short index = 0; - // Prepare sender info - short senderInfo = KMArray.instance((short) 3); - KMArray.cast(senderInfo).add(index++, KMByteBlob.instance(client, (short) 0, (short) client.length)); - KMArray.cast(senderInfo).add(index++, KMByteBlob.instance((short) 0)); - KMArray.cast(senderInfo).add(index, senderIsA ? - KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen) : - KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen)); - - // Prepare recipient info - index = 0; - short recipientInfo = KMArray.instance((short) 3); - KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance(server, (short) 0, (short) server.length)); - KMArray.cast(recipientInfo).add(index++, KMByteBlob.instance((short) 0)); - KMArray.cast(recipientInfo).add(index, senderIsA ? - KMByteBlob.instance(publicKeyB, publicKeyBOff, publicKeyBLen) : - KMByteBlob.instance(publicKeyA, publicKeyAOff, publicKeyALen)); - - // supply public info - index = 0; - short publicInfo = KMArray.instance((short) 2); - KMArray.cast(publicInfo).add(index++, KMInteger.uint_16(AES_GCM_KEY_SIZE_BITS)); - KMArray.cast(publicInfo).add(index, KMByteBlob.instance((short) 0)); - - // construct kdf context - index = 0; - short arrPtr = KMArray.instance((short) 4); - KMArray.cast(arrPtr).add(index++, KMInteger.uint_8(COSE_ALG_AES_GCM_256)); - KMArray.cast(arrPtr).add(index++, senderInfo); - KMArray.cast(arrPtr).add(index++, recipientInfo); - KMArray.cast(arrPtr).add(index, publicInfo); - - return arrPtr; - } } diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java b/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java deleted file mode 100644 index 76535416..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCoseCertPayload.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCoseCertPayload represents the COSE_Sign1 payload for each certificate in BCC. The supported key types are - * KMInteger, KMNInteger and the supported value types are KMByteBlob and KMTextString. - * It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where - * arrayPtr is a pointer to array with any KMCosePairTagType subtype instances. - */ -public class KMCoseCertPayload extends KMCoseMap { - - private static KMCoseCertPayload prototype; - - private KMCoseCertPayload() { - } - - private static KMCoseCertPayload proto(short ptr) { - if (prototype == null) { - prototype = new KMCoseCertPayload(); - } - instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] = ptr; - return prototype; - } - - public static short exp() { - short arrPtr = KMArray.instance((short) 2); - KMArray arr = KMArray.cast(arrPtr); - arr.add((short) 0, KMCosePairTextStringTag.exp()); - arr.add((short) 1, KMCosePairByteBlobTag.exp()); - return KMCoseCertPayload.instance(arrPtr); - } - - public static short instance(short vals) { - short ptr = KMType.instance(COSE_CERT_PAYLOAD_TYPE, (short) 2); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); - return ptr; - } - - public static KMCoseCertPayload cast(short ptr) { - if (heap[ptr] != COSE_CERT_PAYLOAD_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - if (heap[arrPtr] != ARRAY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - @Override - public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_CERT_PAYLOAD_OFFSET] + TLV_HEADER_SIZE)); - } - - @Override - public short length() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); - } - - @Override - public void canonicalize() { - KMCoseMap.canonicalize(getVals()); - } - - private short getValueType(short key, short significantKey) { - short arr = getVals(); - short length = length(); - short keyPtr; - short valPtr = 0; - short index = 0; - short tagType; - boolean found = false; - while (index < length) { - tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index)); - switch (tagType) { - case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: - keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == KMCosePairTagType.getKeyValueShort(keyPtr) && - significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) { - valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: - keyPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairTextStringTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - default: - break; - - } - if (found) - break; - index++; - } - return valPtr; - } - - public short getSubjectPublicKey() { - return getValueType(Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 2), // LSB - Util.getShort(KMCose.SUBJECT_PUBLIC_KEY, (short) 0) // MSB (Significant) - ); - } - - public short getSubject() { - return getValueType(KMCose.SUBJECT, KMType.INVALID_VALUE); - } - - public short getIssuer() { - return getValueType(KMCose.ISSUER, KMType.INVALID_VALUE); - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java b/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java deleted file mode 100644 index e8939f1e..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCoseHeaders.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCoseHeaders represents headers section from the Cose standard - * https://datatracker.ietf.org/doc/html/rfc8152#section-3. The supported key types are - * KMInteger, KMNInteger and the supported value types are KMInteger, KMNInteger, KMByteBlob, - * KMCoseKey. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } where - * arrayPtr is a pointer to array with any KMTag subtype instances. - */ -public class KMCoseHeaders extends KMCoseMap { - - private static KMCoseHeaders prototype; - - private KMCoseHeaders() { - } - - private static KMCoseHeaders proto(short ptr) { - if (prototype == null) { - prototype = new KMCoseHeaders(); - } - instanceTable[KM_COSE_HEADERS_OFFSET] = ptr; - return prototype; - } - - public static short exp() { - short arrPtr = KMArray.instance((short) 4); - // CoseKey is internally an Array so evaluate it separately. - short coseKeyValueExp = KMCosePairCoseKeyTag.exp(); - KMArray arr = KMArray.cast(arrPtr); - arr.add((short) 0, KMCosePairIntegerTag.exp()); - arr.add((short) 1, KMCosePairNegIntegerTag.exp()); - arr.add((short) 2, KMCosePairByteBlobTag.exp()); - arr.add((short) 3, coseKeyValueExp); - return KMCoseHeaders.instance(arrPtr); - } - - - public static short instance(short vals) { - short ptr = KMType.instance(COSE_HEADERS_TYPE, (short) 2); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); - return ptr; - } - - public static KMCoseHeaders cast(short ptr) { - if (heap[ptr] != COSE_HEADERS_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - if (heap[arrPtr] != ARRAY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - @Override - public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_HEADERS_OFFSET] + TLV_HEADER_SIZE)); - } - - @Override - public short length() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); - } - - @Override - public void canonicalize() { - KMCoseMap.canonicalize(getVals()); - } - - private short getValueType(short key) { - short index = 0; - short len = length(); - short arr = getVals(); - short tagType; - short valPtr = 0; - short keyPtr; - boolean found = false; - while (index < len) { - tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index)); - switch (tagType) { - case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: - keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: - keyPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairCoseKeyTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - case KMType.COSE_PAIR_INT_TAG_TYPE: - keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: - keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - default: - break; - } - if (found) - break; - index++; - } - return valPtr; - } - - public short getKeyIdentifier() { - return getValueType(KMCose.COSE_LABEL_KEYID); - } - - public short getCoseKey() { - return getValueType(KMCose.COSE_LABEL_COSE_KEY); - } - - public short getIV() { - return getValueType(KMCose.COSE_LABEL_IV); - } - - public short getAlgorithm() { - return getValueType(KMCose.COSE_LABEL_ALGORITHM); - } - - public boolean isDataValid(short []buff, short alg, short keyIdPtr) { - short bufLen = 4; - buff[0] = KMCose.COSE_LABEL_ALGORITHM; - buff[1] = alg; - buff[2] = KMCose.COSE_LABEL_KEYID; - buff[3] = keyIdPtr; - boolean valid = false; - short value; - short ptr; - short tagIndex = 0; - while (tagIndex < bufLen) { - value = buff[(short) (tagIndex + 1)]; - if (value != KMType.INVALID_VALUE) { - valid = false; - ptr = getValueType(buff[tagIndex]); - switch (KMType.getType(ptr)) { - case KMType.BYTE_BLOB_TYPE: - if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length()) && - (0 == - Util.arrayCompare(KMByteBlob.cast(value).getBuffer(), - KMByteBlob.cast(value).getStartOff(), - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()))) { - valid = true; - } - break; - case KMType.INTEGER_TYPE: - if (value == KMInteger.cast(ptr).getShort()) { - valid = true; - } - break; - case KMType.NEG_INTEGER_TYPE: - if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) { - valid = true; - } - break; - default: - break; - } - if (!valid) - break; - } - tagIndex += 2; - } - return valid; - } - - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseKey.java b/Applet/src/com/android/javacard/keymaster/KMCoseKey.java deleted file mode 100644 index a39d3225..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCoseKey.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCoseKey represents COSE_Key section from the Cose standard https://datatracker.ietf.org/doc/html/rfc8152#section-7 - * The supported key types are KMNInteger, KMInteger and the supported value types are KMInteger, KMNInteger, - * KMByteBlob, KMSimpleValue. It corresponds to a CBOR Map type. struct{byte TAG_TYPE; short length; short arrayPtr } - * where arrayPtr is a pointer to array with any KMTag subtype instances. - */ -public class KMCoseKey extends KMCoseMap { - - private static KMCoseKey prototype; - - private KMCoseKey() { - } - - private static KMCoseKey proto(short ptr) { - if (prototype == null) { - prototype = new KMCoseKey(); - } - instanceTable[KM_COSE_KEY_OFFSET] = ptr; - return prototype; - } - - public static short exp() { - short arrPtr = KMArray.instance((short) 4); - KMArray arr = KMArray.cast(arrPtr); - arr.add((short) 0, KMCosePairIntegerTag.exp()); - arr.add((short) 1, KMCosePairNegIntegerTag.exp()); - arr.add((short) 2, KMCosePairByteBlobTag.exp()); - arr.add((short) 3, KMCosePairSimpleValueTag.exp()); - return KMCoseKey.instance(arrPtr); - } - - - public static short instance(short vals) { - short ptr = KMType.instance(COSE_KEY_TYPE, (short) 2); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), vals); - return ptr; - } - - public static KMCoseKey cast(short ptr) { - if (heap[ptr] != COSE_KEY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short arrPtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - if (heap[arrPtr] != ARRAY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - @Override - public short getVals() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_OFFSET] + TLV_HEADER_SIZE)); - } - - @Override - public short length() { - short arrPtr = getVals(); - return KMArray.cast(arrPtr).length(); - } - - private short getValueType(short key, short significantKey) { - short arr = getVals(); - short length = length(); - short keyPtr; - short valPtr = 0; - short index = 0; - short tagType; - boolean found = false; - while (index < length) { - tagType = KMCosePairTagType.getTagValueType(KMArray.cast(arr).get(index)); - switch (tagType) { - case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: - keyPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairByteBlobTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - case KMType.COSE_PAIR_INT_TAG_TYPE: - keyPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: - keyPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == (byte) KMCosePairTagType.getKeyValueShort(keyPtr)) { - valPtr = KMCosePairNegIntegerTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: - keyPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getKeyPtr(); - if (key == KMCosePairTagType.getKeyValueShort(keyPtr) && - significantKey == KMCosePairTagType.getKeyValueSignificantShort(keyPtr)) { - valPtr = KMCosePairSimpleValueTag.cast(KMArray.cast(arr).get(index)).getValuePtr(); - found = true; - } - break; - default: - break; - - } - if (found) - break; - index++; - } - return valPtr; - } - - public short getKeyIdentifier() { - return getValueType(KMCose.COSE_KEY_KEY_ID, KMType.INVALID_VALUE); - } - - public short getEcdsa256PublicKey(byte[] pubKey, short pubKeyOff) { - short baseOffset = pubKeyOff; - pubKey[pubKeyOff] = (byte) 0x04; // uncompressed. - pubKeyOff++; - short ptr = getValueType(KMCose.COSE_KEY_PUBKEY_X, KMType.INVALID_VALUE); - Util.arrayCopy(KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), - pubKey, pubKeyOff, KMByteBlob.cast(ptr).length()); - pubKeyOff += KMByteBlob.cast(ptr).length(); - ptr = getValueType(KMCose.COSE_KEY_PUBKEY_Y, KMType.INVALID_VALUE); - Util.arrayCopy(KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), - pubKey, pubKeyOff, KMByteBlob.cast(ptr).length()); - pubKeyOff += KMByteBlob.cast(ptr).length(); - return (short) (pubKeyOff - baseOffset); - } - - public short getPrivateKey(byte[] priv, short privOff) { - short ptr = getValueType(KMCose.COSE_KEY_PRIV_KEY, KMType.INVALID_VALUE); - Util.arrayCopy(KMByteBlob.cast(ptr).getBuffer(), KMByteBlob.cast(ptr).getStartOff(), - priv, privOff, KMByteBlob.cast(ptr).length()); - return KMByteBlob.cast(ptr).length(); - } - - public boolean isTestKey() { - short ptr = - getValueType( - Util.getShort(KMCose.COSE_TEST_KEY, (short) 2), // LSB - Util.getShort(KMCose.COSE_TEST_KEY, (short) 0) // MSB (Significant) - ); - boolean isTestKey = false; - if (ptr != 0) - isTestKey = (KMSimpleValue.cast(ptr).getValue() == KMSimpleValue.NULL); - return isTestKey; - } - - /** - * Verifies the KMCoseKey values against the input values. - * - * @param keyType value of the key type - * @param keyIdPtr instance of KMByteBlob containing the key id. - * @param keyAlg value of the algorithm. - * @param keyOps value of the key operations. - * @param curve value of the curve. - * @return true if valid, otherwise false. - */ - public boolean isDataValid(short []buff, short keyType, short keyIdPtr, short keyAlg, short keyOps, short curve) { - short buffLen = 10; - buff[0] = KMCose.COSE_KEY_KEY_TYPE; - buff[1] = keyType; - buff[2] = KMCose.COSE_KEY_KEY_ID; - buff[3] = keyIdPtr; - buff[4] = KMCose.COSE_KEY_ALGORITHM; - buff[5] = keyAlg; - buff[6] = KMCose.COSE_KEY_KEY_OPS; - buff[7] = keyOps; - buff[8] = KMCose.COSE_KEY_CURVE; - buff[9] = curve; - boolean valid = false; - short ptr; - short tagIndex = 0; - short value; - while (tagIndex < buffLen) { - value = buff[(short) (tagIndex + 1)]; - if (value != KMType.INVALID_VALUE) { - valid = false; - ptr = getValueType(buff[tagIndex], KMType.INVALID_VALUE); - switch (KMType.getType(ptr)) { - case KMType.BYTE_BLOB_TYPE: - if ((KMByteBlob.cast(value).length() == KMByteBlob.cast(ptr).length()) && - (0 == - Util.arrayCompare(KMByteBlob.cast(value).getBuffer(), - KMByteBlob.cast(value).getStartOff(), - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - KMByteBlob.cast(ptr).length()))) { - valid = true; - } - break; - case KMType.INTEGER_TYPE: - if (value == KMInteger.cast(ptr).getShort()) { - valid = true; - } - break; - case KMType.NEG_INTEGER_TYPE: - if ((byte) value == (byte) KMNInteger.cast(ptr).getShort()) { - valid = true; - } - break; - } - if (!valid) - break; - } - tagIndex += 2; - } - return valid; - } - - @Override - public void canonicalize() { - KMCoseMap.canonicalize(getVals()); - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCoseMap.java b/Applet/src/com/android/javacard/keymaster/KMCoseMap.java deleted file mode 100644 index 2f57be40..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCoseMap.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" (short)0IS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; - -/** - * This class represents either a Cose_key or Cose headers as defined in - * https://datatracker.ietf.org/doc/html/rfc8152 This is basically a map containing key value pairs. - * The label for the key can be (uint / int / tstr) and the value can be of any type. But this class - * is confined to support only key and value types which are required for remote key provisioning. - * So keys of type (int / uint) and values of type (int / uint / simple / bstr) only are supported. - * KMCoseHeaders and KMCoseKey implements this class. - */ -public abstract class KMCoseMap extends KMType { - - public static byte[] scratchpad; - - /** - * This function creates an instance of either KMCoseHeaders or KMCoseKey based on the type - * information provided. - * - * @param typePtr type information of the underlying KMType. - * @param arrPtr instance of KMArray. - * @return instance type of either KMCoseHeaders or KMCoseKey. - */ - public static short createInstanceFromType(short typePtr, short arrPtr) { - short mapType = KMType.getType(typePtr); - switch (mapType) { - case KMType.COSE_HEADERS_TYPE: - return KMCoseHeaders.instance(arrPtr); - case KMType.COSE_KEY_TYPE: - return KMCoseKey.instance(arrPtr); - case KMType.COSE_CERT_PAYLOAD_TYPE: - return KMCoseCertPayload.instance(arrPtr); - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - return 0; - } - } - - public static short getVals(short ptr) { - short mapType = KMType.getType(ptr); - switch (mapType) { - case KMType.COSE_HEADERS_TYPE: - return KMCoseHeaders.cast(ptr).getVals(); - case KMType.COSE_KEY_TYPE: - return KMCoseKey.cast(ptr).getVals(); - case KMType.COSE_CERT_PAYLOAD_TYPE: - return KMCoseCertPayload.cast(ptr).getVals(); - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - return 0; - } - } - - abstract public short getVals(); - - abstract public short length(); - - abstract public void canonicalize(); - - private static short getKey(short tagPtr) { - short tagType = KMCosePairTagType.getTagValueType(tagPtr); - switch (tagType) { - case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: - return KMCosePairByteBlobTag.cast(tagPtr).getKeyPtr(); - case KMType.COSE_PAIR_INT_TAG_TYPE: - return KMCosePairIntegerTag.cast(tagPtr).getKeyPtr(); - case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: - return KMCosePairNegIntegerTag.cast(tagPtr).getKeyPtr(); - case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: - return KMCosePairSimpleValueTag.cast(tagPtr).getKeyPtr(); - case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: - return KMCosePairCoseKeyTag.cast(tagPtr).getKeyPtr(); - case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: - return KMCosePairTextStringTag.cast(tagPtr).getKeyPtr(); - default: - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return 0; - } - - private static void createScratchBuffer() { - if (scratchpad == null) { - scratchpad = JCSystem.makeTransientByteArray((short) 120, JCSystem.CLEAR_ON_RESET); - } - } - - protected static void canonicalize(short arr) { - canonicalize(arr, KMArray.cast(arr).length()); - } - - private static void swap(short ptr, short firstIndex, short secondIndex) { - if (KMType.getType(ptr) == KMType.ARRAY_TYPE) { - KMArray.cast(ptr).swap(firstIndex, secondIndex); - } else { - KMMap.cast(ptr).swap(firstIndex, secondIndex); - } - } - - private static boolean compareAndSwap(short ptr, short index) { - short firstKey; - short secondKey; - short firstKeyLen; - short secondKeyLen; - if (KMType.getType(ptr) == KMType.ARRAY_TYPE) { - firstKey = getKey(KMArray.cast(ptr).get(index)); - secondKey = getKey(KMArray.cast(ptr).get((short) (index + 1))); - } else { // Map - firstKey = KMMap.cast(ptr).getKey(index); - secondKey = KMMap.cast(ptr).getKey((short) (index + 1)); - } - 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)))) { - swap(ptr, index, (short) (index + 1)); - return true; - } - return false; - } - - /** - * Canonicalizes using bubble sort. - * - * @param ptr instance pointer of either array or map. - * @param length length of the array or map instance. - */ - public static void canonicalize(short ptr, short length) { - short index = 0; - short innerIndex = 0; - createScratchBuffer(); - boolean swapped; - while (index < length) { - swapped = false; - innerIndex = 0; - while (innerIndex < (short) (length - index - 1)) { - swapped |= compareAndSwap(ptr, innerIndex); - innerIndex++; - } - if (!swapped) { - break; - } - index++; - } - } -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java deleted file mode 100644 index 4a03295e..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCosePairByteBlobTag.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCosePairByteBlobTag represents a key-value type, where key can be KMInteger or KMNInteger and value is - * KMByteBlob type. struct{byte TAG_TYPE; short length; struct{short BYTE_BLOB_TYPE; short key; short value}}. - */ -public class KMCosePairByteBlobTag extends KMCosePairTagType { - - private static KMCosePairByteBlobTag prototype; - - public static Object[] keys; - - private KMCosePairByteBlobTag() { - } - - private static KMCosePairByteBlobTag proto(short ptr) { - if (prototype == null) { - prototype = new KMCosePairByteBlobTag(); - } - instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] = ptr; - return prototype; - } - - // pointer to an empty instance used as expression - public static short exp() { - short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMByteBlob.exp()); - return ptr; - } - - public static short instance(short keyPtr, short valuePtr) { - if (!isKeyValueValid(keyPtr)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); - return ptr; - } - - public static KMCosePairByteBlobTag cast(short ptr) { - byte[] heap = repository.getHeap(); - if (heap[ptr] != COSE_PAIR_TAG_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // Validate the value pointer. - short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); - if (KMType.getType(valuePtr) != BYTE_BLOB_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public short getValueType() { - return BYTE_BLOB_TYPE; - } - - @Override - public short getKeyPtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); - } - - @Override - public short getValuePtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_BYTE_BLOB_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); - } - - private static void createKeys() { - if (keys == null) { - keys = new Object[]{ - (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_X}, - (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PUBKEY_Y}, - (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_PRIV_KEY}, - (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_IV}, - (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_LABEL_KEYID}, - (Object) new byte[]{(byte) 0, (byte) 0, (byte) 0, KMCose.COSE_KEY_KEY_ID}, - (Object) KMCose.SUBJECT_PUBLIC_KEY, - (Object) KMCose.KEY_USAGE - }; - } - } - - public static boolean isKeyValueValid(short keyPtr) { - createKeys(); - short type = KMType.getType(keyPtr); - short offset = 0; - if (type == INTEGER_TYPE) { - offset = KMInteger.cast(keyPtr).getStartOff(); - } else if (type == NEG_INTEGER_TYPE) { - offset = KMNInteger.cast(keyPtr).getStartOff(); - } else { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - short index = 0; - while (index < (short) keys.length) { - if (0 == Util.arrayCompare((byte[]) keys[index], (short) 0, heap, offset, (short) ((byte[]) keys[index]).length)) { - return true; - } - index++; - } - return false; - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java deleted file mode 100644 index 7eb1251e..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCosePairCoseKeyTag.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCosePairCoseKeyTag represents a key-value type, where key can be KMInteger or KMNInteger and value is - * KMCOseKey type. struct{byte TAG_TYPE; short length; struct{short COSE_KEY_VALUE_TYPE; short key; short value}}. - */ -public class KMCosePairCoseKeyTag extends KMCosePairTagType { - - public static final byte[] keys = { - KMCose.COSE_LABEL_COSE_KEY - }; - private static KMCosePairCoseKeyTag prototype; - - private KMCosePairCoseKeyTag() { - } - - private static KMCosePairCoseKeyTag proto(short ptr) { - if (prototype == null) { - prototype = new KMCosePairCoseKeyTag(); - } - instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] = ptr; - return prototype; - } - - // pointer to an empty instance used as expression - public static short exp() { - short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMCoseKey.exp()); - return ptr; - } - - public static short instance(short keyPtr, short valuePtr) { - if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - if (KMType.getType(valuePtr) != COSE_KEY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_COSE_KEY_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); - return ptr; - } - - public static KMCosePairCoseKeyTag cast(short ptr) { - byte[] heap = repository.getHeap(); - if (heap[ptr] != COSE_PAIR_TAG_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // Validate the value pointer. - short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); - if (KMType.getType(valuePtr) != COSE_KEY_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public short getValueType() { - return COSE_KEY_TYPE; - } - - @Override - public short getKeyPtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); - } - - @Override - public short getValuePtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_COSE_KEY_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); - } - - public static boolean isKeyValueValid(short keyVal) { - short index = 0; - while (index < (short) keys.length) { - if ((byte) (keyVal & 0xFF) == keys[index]) - return true; - index++; - } - return false; - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java deleted file mode 100644 index d5fe47b9..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCosePairIntegerTag.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCosePairIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and value is - * KMInteger type. struct{byte TAG_TYPE; short length; struct{short INT_VALUE_TYPE; short key; short value}}. - */ -public class KMCosePairIntegerTag extends KMCosePairTagType { - - private static KMCosePairIntegerTag prototype; - - - private KMCosePairIntegerTag() { - } - - private static KMCosePairIntegerTag proto(short ptr) { - if (prototype == null) { - prototype = new KMCosePairIntegerTag(); - } - instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] = ptr; - return prototype; - } - - // pointer to an empty instance used as expression - public static short exp() { - short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMInteger.exp()); - return ptr; - } - - public static short instance(short keyPtr, short valuePtr) { - short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); - if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, - KMInteger.cast(valuePtr).getShort())) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_INT_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); - return ptr; - } - - public static KMCosePairIntegerTag cast(short ptr) { - byte[] heap = repository.getHeap(); - if (heap[ptr] != COSE_PAIR_TAG_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // Validate the value ptr. - short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); - if (INTEGER_TYPE != getType(valuePtr)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public short getValueType() { - return INTEGER_TYPE; - } - - @Override - public short getKeyPtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); - } - - @Override - public short getValuePtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_INT_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); - } - - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java deleted file mode 100644 index b2abb95d..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCosePairNegIntegerTag.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCosePairNegIntegerTag represents a key-value type, where key can be KMInteger or KMNInteger and value is - * KMNInteger type. struct{byte TAG_TYPE; short length; struct{short NINT_VALUE_TYPE; short key; short value}}. - */ -public class KMCosePairNegIntegerTag extends KMCosePairTagType { - - private static KMCosePairNegIntegerTag prototype; - - - private KMCosePairNegIntegerTag() { - } - - private static KMCosePairNegIntegerTag proto(short ptr) { - if (prototype == null) { - prototype = new KMCosePairNegIntegerTag(); - } - instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] = ptr; - return prototype; - } - - // pointer to an empty instance used as expression - public static short exp() { - short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMNInteger.exp()); - return ptr; - } - - public static KMCosePairNegIntegerTag cast(short ptr) { - byte[] heap = repository.getHeap(); - if (heap[ptr] != COSE_PAIR_TAG_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // Validate the value ptr. - short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); - if (NEG_INTEGER_TYPE != getType(valuePtr)) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public static short instance(short keyPtr, short valuePtr) { - short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); - if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, - KMNInteger.cast(valuePtr).getShort())) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_NEG_INT_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); - return ptr; - } - - public short getValueType() { - return NEG_INTEGER_TYPE; - } - - @Override - public short getKeyPtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); - } - - @Override - public short getValuePtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_NINT_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java deleted file mode 100644 index 6aea5e30..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCosePairSimpleValueTag.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCosePairSimpleValueTag represents a key-value type, where key can be KMInteger or KMNInteger and value is - * KMSimpleValue type. struct{byte TAG_TYPE; short length; struct{short SIMPLE_VALUE_TYPE; short key; short value}}. - */ -public class KMCosePairSimpleValueTag extends KMCosePairTagType { - - private static KMCosePairSimpleValueTag prototype; - - private KMCosePairSimpleValueTag() { - } - - private static KMCosePairSimpleValueTag proto(short ptr) { - if (prototype == null) { - prototype = new KMCosePairSimpleValueTag(); - } - instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] = ptr; - return prototype; - } - - // pointer to an empty instance used as expression - public static short exp() { - short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMSimpleValue.exp()); - return ptr; - } - - public static short instance(short keyPtr, short valuePtr) { - short offset = KMCosePairTagType.getKeyStartOffset(keyPtr); - if (!KMCosePairTagType.isKeyPairValid(heap, offset, KMCose.COSE_KEY_MAX_SIZE, - KMSimpleValue.cast(valuePtr).getValue())) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); - return ptr; - } - - public static KMCosePairSimpleValueTag cast(short ptr) { - byte[] heap = repository.getHeap(); - if (heap[ptr] != COSE_PAIR_TAG_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // Validate the value pointer. - short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); - if (KMType.getType(valuePtr) != SIMPLE_VALUE_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public short getValueType() { - return SIMPLE_VALUE_TYPE; - } - - @Override - public short getKeyPtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); - } - - @Override - public short getValuePtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_SIMPLE_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java b/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java deleted file mode 100644 index 053fc633..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCosePairTagType.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * This class represents the COSE_Key as defined in https://datatracker.ietf.org/doc/html/rfc8152#section-7. - * This is basically a map containing key value pairs. The label for the key can be (uint / int / tstr) and - * the value can be of any type. But this class is confined to support only key and value types which are - * required for remote key provisioning. So keys of type (int / uint) and values of type (int / uint / simple / bstr) - * only are supported. The structure representing all the sub classes of KMCosePairTagType is as follows: - * KM_COSE_PAIR_TAG_TYPE(1byte), Length(2 bytes), COSE_PAIR_*_TAG_TYPE(2 bytes), Key(2 bytes), Value(2 bytes). - * Key can be either KMInteger or KMNInteger and Value can be either KMIntger or KMNinteger or KMSimpleValue - * or KMByteBlob or KMTextString or KMCoseKey. Each subclass of KMCosePairTagType is named after their corresponding - * value type of the Cose pair. - */ -public abstract class KMCosePairTagType extends KMType { - - /** - * Below table represents the allowed values for a key. The maximum length of the key - * can be 4 bytes so each key is represented as 4 bytes. The allowed values are - * placed next to their corresponding key. - */ - public static Object[] allowedKeyPairs; - - private static void createAllowedKeyPairs() { - if (allowedKeyPairs == null) { - allowedKeyPairs = - new Object[]{ - // Key type - (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_KEY_TYPE}, (Object) new byte[]{KMCose.COSE_KEY_TYPE_EC2, - KMCose.COSE_KEY_TYPE_SYMMETRIC_KEY}, - // Key Algorithm - (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_ALGORITHM}, - (Object) new byte[]{KMCose.COSE_ALG_AES_GCM_256, KMCose.COSE_ALG_HMAC_256, - KMCose.COSE_ALG_ECDH_ES_HKDF_256, KMCose.COSE_ALG_ES256}, - // Key operations - (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_KEY_OPS}, (Object) new byte[]{KMCose.COSE_KEY_OP_SIGN, KMCose.COSE_KEY_OP_VERIFY, - KMCose.COSE_KEY_OP_ENCRYPT, KMCose.COSE_KEY_OP_DECRYPT}, - // Key Curve - (Object) new byte[]{0, 0, 0, KMCose.COSE_KEY_CURVE}, (Object) new byte[]{KMCose.COSE_ECCURVE_256}, - // Header Label Algorithm - (Object) new byte[]{0, 0, 0, KMCose.COSE_LABEL_ALGORITHM}, (Object) new byte[]{KMCose.COSE_ALG_AES_GCM_256, - KMCose.COSE_ALG_HMAC_256, KMCose.COSE_ALG_ES256, KMCose.COSE_ALG_ECDH_ES_HKDF_256}, - // Test Key - KMCose.COSE_TEST_KEY, (Object) new byte[]{KMSimpleValue.NULL}, - }; - } - } - - - /** - * Validates the key and the values corresponding to key. - * - * @param key Buffer containing the key. - * @param keyOff Offset in the buffer from where key starts. - * @param keyLen Length of the key buffer. - * @param value Value corresponding to the key. - * @return true if key pair is valid, otherwise false. - */ - public static boolean isKeyPairValid(byte[] key, short keyOff, short keyLen, short value) { - short index = 0; - short valueIdx; - byte[] values; - boolean valid = false; - createAllowedKeyPairs(); - while (index < allowedKeyPairs.length) { - valueIdx = 0; - if (isEqual((byte[]) allowedKeyPairs[index], (short) 0, (short) ((byte[]) allowedKeyPairs[index]).length, - key, keyOff, keyLen)) { - values = (byte[]) allowedKeyPairs[(short) (index + 1)]; - while (valueIdx < values.length) { - if (values[valueIdx] == (byte) value) { - valid = true; - break; - } - valueIdx++; - } - if (valid) - break; - } - index += (short) 2; - } - return valid; - } - - /** - * Compares two key buffers. - * - * @param key1 First buffer containing the key. - * @param offset1 Offset of the first buffer. - * @param length1 Length of the first buffer. - * @param key2 Second buffer containing the key. - * @param offset2 Offset of the second buffer. - * @param length2 Length of the second buffer. - * @return true if both keys are equal, otherwise false. - */ - private static boolean isEqual(byte[] key1, short offset1, short length1, byte[] key2, short offset2, - short length2) { - if (length1 != length2) - return false; - return (0 == KMInteger.unsignedByteArrayCompare(key1, offset1, key2, offset2, length1)); - } - - /** - * Returns the short value of the key. - * - * @param keyPtr Pointer to either KMInteger or KMNInteger - * @return value of the key as short. - */ - public static short getKeyValueShort(short keyPtr) { - short type = KMType.getType(keyPtr); - short value = 0; - if (type == INTEGER_TYPE) { - value = KMInteger.cast(keyPtr).getShort(); - } else if (type == NEG_INTEGER_TYPE) { - value = KMNInteger.cast(keyPtr).getShort(); - } else { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return value; - } - - /** - * Returns the significant short value of the key. - * - * @param keyPtr Pointer to either KMInteger or KMNInteger - * @return value of the key as short. - */ - public static short getKeyValueSignificantShort(short keyPtr) { - short type = KMType.getType(keyPtr); - short value = 0; - if (type == INTEGER_TYPE) { - value = KMInteger.cast(keyPtr).getSignificantShort(); - } else if (type == NEG_INTEGER_TYPE) { - value = KMNInteger.cast(keyPtr).getSignificantShort(); - } else { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return value; - } - - public static void getKeyValue(short keyPtr, byte[] dest, short offset, short len) { - short type = KMType.getType(keyPtr); - if (type == INTEGER_TYPE) { - KMInteger.cast(keyPtr).getValue(dest, offset, len); - } else if (type == NEG_INTEGER_TYPE) { - KMNInteger.cast(keyPtr).getValue(dest, offset, len); - } else { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - } - - /** - * Returns the key offset from the key pointer. - * - * @param keyPtr Pointer to either KMInteger or KMNInteger - * @return offset from where the key starts. - */ - public static short getKeyStartOffset(short keyPtr) { - short type = KMType.getType(keyPtr); - short offset = 0; - if (type == INTEGER_TYPE) { - offset = KMInteger.cast(keyPtr).getStartOff(); - } else if (type == NEG_INTEGER_TYPE) { - offset = KMNInteger.cast(keyPtr).getStartOff(); - } else { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return offset; - } - - /** - * Returns the key length. - * - * @param keyPtr pointer to either KMInteger/KMInteger. - * @return length of the key. - */ - public static short getKeyLength(short keyPtr) { - short type = KMType.getType(keyPtr); - short len = 0; - if (type == INTEGER_TYPE) { - len = KMInteger.cast(keyPtr).length(); - } else if (type == NEG_INTEGER_TYPE) { - len = KMNInteger.cast(keyPtr).length(); - } else { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return len; - } - - /** - * This function returns one of COSE_KEY_TAG_*_VALUE_TYPE tag - * information. - * - * @param ptr Pointer to one of the KMCoseKey*Value class. - * @return Tag value type. - */ - public static short getTagValueType(short ptr) { - return Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE)); - } - - /** - * This function returns the key pointer. - * - * @return key pointer. - */ - public abstract short getKeyPtr(); - - /** - * This function returns the value pointer. - * - * @return value pointer. - */ - public abstract short getValuePtr(); -} diff --git a/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java b/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java deleted file mode 100644 index 165e3d15..00000000 --- a/Applet/src/com/android/javacard/keymaster/KMCosePairTextStringTag.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.android.javacard.keymaster; - -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.Util; - -/** - * KMCosePairTextStringTag represents a key-value type, where key can be KMInteger or KMNInteger and value is - * KMTextString type. struct{byte TAG_TYPE; short length; struct{short TXT_STR_VALUE_TYPE; short key; short value}}. - */ -public class KMCosePairTextStringTag extends KMCosePairTagType { - - private static KMCosePairTextStringTag prototype; - - public static final byte[] keys = { - KMCose.ISSUER, - KMCose.SUBJECT, - }; - - private KMCosePairTextStringTag() { - } - - private static KMCosePairTextStringTag proto(short ptr) { - if (prototype == null) { - prototype = new KMCosePairTextStringTag(); - } - instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] = ptr; - return prototype; - } - - // pointer to an empty instance used as expression - public static short exp() { - short ptr = instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), KMType.INVALID_VALUE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), KMTextString.exp()); - return ptr; - } - - public static short instance(short keyPtr, short valuePtr) { - if (!isKeyValueValid(KMCosePairTagType.getKeyValueShort(keyPtr))) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - short ptr = KMType.instance(COSE_PAIR_TAG_TYPE, (short) 6); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE), KMType.COSE_PAIR_TEXT_STR_TAG_TYPE); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 2), keyPtr); - Util.setShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4), valuePtr); - return ptr; - } - - public static KMCosePairTextStringTag cast(short ptr) { - byte[] heap = repository.getHeap(); - if (heap[ptr] != COSE_PAIR_TAG_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // Validate the value pointer. - short valuePtr = Util.getShort(heap, (short) (ptr + TLV_HEADER_SIZE + 4)); - if (KMType.getType(valuePtr) != TEXT_STRING_TYPE) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - return proto(ptr); - } - - public short getValueType() { - return TEXT_STRING_TYPE; - } - - @Override - public short getKeyPtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 2)); - } - - @Override - public short getValuePtr() { - return Util.getShort(heap, (short) (instanceTable[KM_COSE_KEY_TXT_STR_VAL_OFFSET] + TLV_HEADER_SIZE + 4)); - } - - public static boolean isKeyValueValid(short keyVal) { - short index = 0; - while (index < (short) keys.length) { - if ((byte) (keyVal & 0xFF) == keys[index]) - return true; - index++; - } - return false; - } - -} diff --git a/Applet/src/com/android/javacard/keymaster/KMDecoder.java b/Applet/src/com/android/javacard/keymaster/KMDecoder.java index 04eabe54..baae3c45 100644 --- a/Applet/src/com/android/javacard/keymaster/KMDecoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMDecoder.java @@ -119,13 +119,6 @@ private short decode(short exp) { return decodeHmacSharingParam(exp); case KMType.HW_AUTH_TOKEN_TYPE: return decodeHwAuthToken(exp); - case KMType.COSE_KEY_TYPE: - case KMType.COSE_HEADERS_TYPE: - case KMType.COSE_CERT_PAYLOAD_TYPE: - return decodeCoseMap(exp); - case KMType.COSE_PAIR_TAG_TYPE: - short tagValueType = KMCosePairTagType.getTagValueType(exp); - return decodeCosePairTag(tagValueType, exp); case KMType.TAG_TYPE: short tagType = KMTag.getTagType(exp); return decodeTag(tagType, exp); @@ -180,159 +173,6 @@ private short decodeKeyChar(short exp) { return KMKeyCharacteristics.instance(vals); } - private short decodeCosePairKey(short exp) { - byte[] buffer = (byte[]) bufferRef[0]; - short startOff = scratchBuf[START_OFFSET]; - short keyPtr = (short) 0; - // Cose Key should be always either UINT or Negative int - if ((buffer[startOff] & MAJOR_TYPE_MASK) == UINT_TYPE) { - keyPtr = decodeInteger(exp); - } else if ((buffer[startOff] & MAJOR_TYPE_MASK) == NEG_INT_TYPE) { - keyPtr = decodeNegInteger(exp); - } else { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return keyPtr; - } - - private short decodeCosePairSimpleValueTag(short exp) { - short keyPtr = decodeCosePairKey((KMCosePairSimpleValueTag.cast(exp).getKeyPtr())); - short valuePtr = decode(KMCosePairSimpleValueTag.cast(exp).getValuePtr()); - return KMCosePairSimpleValueTag.instance(keyPtr, valuePtr); - } - - private short decodeCosePairIntegerValueTag(short exp) { - short keyPtr = decodeCosePairKey((KMCosePairIntegerTag.cast(exp).getKeyPtr())); - short valuePtr = decode(KMCosePairIntegerTag.cast(exp).getValuePtr()); - return KMCosePairIntegerTag.instance(keyPtr, valuePtr); - } - - private short decodeCosePairNegIntegerTag(short exp) { - short keyPtr = decodeCosePairKey((KMCosePairNegIntegerTag.cast(exp).getKeyPtr())); - short valuePtr = decode(KMCosePairNegIntegerTag.cast(exp).getValuePtr()); - return KMCosePairNegIntegerTag.instance(keyPtr, valuePtr); - } - - private short decodeCosePairTxtStringTag(short exp) { - short keyPtr = decodeCosePairKey((KMCosePairTextStringTag.cast(exp).getKeyPtr())); - short valuePtr = decode(KMCosePairTextStringTag.cast(exp).getValuePtr()); - return KMCosePairTextStringTag.instance(keyPtr, valuePtr); - } - - private short decodeCosePairCoseKeyTag(short exp) { - short keyPtr = decodeCosePairKey((KMCosePairCoseKeyTag.cast(exp).getKeyPtr())); - short valuePtr = decode(KMCosePairCoseKeyTag.cast(exp).getValuePtr()); - return KMCosePairCoseKeyTag.instance(keyPtr, valuePtr); - } - - private short decodeCosePairByteBlobTag(short exp) { - short keyPtr = decodeCosePairKey((KMCosePairByteBlobTag.cast(exp).getKeyPtr())); - short valuePtr = decode(KMCosePairByteBlobTag.cast(exp).getValuePtr()); - return KMCosePairByteBlobTag.instance(keyPtr, valuePtr); - } - - private short peekCosePairTagType() { - byte[] buffer = (byte[]) bufferRef[0]; - short startOff = scratchBuf[START_OFFSET]; - // Cose Key should be always either UINT or Negative int - if ((buffer[startOff] & MAJOR_TYPE_MASK) != UINT_TYPE && - (buffer[startOff] & MAJOR_TYPE_MASK) != NEG_INT_TYPE) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - - short additionalMask = (short) (buffer[startOff] & ADDITIONAL_MASK); - short increment = 0; - if (additionalMask < UINT8_LENGTH) { - increment++; - } else if (additionalMask == UINT8_LENGTH) { - increment += 2; - } else if (additionalMask == UINT16_LENGTH) { - increment += 3; - } else if (additionalMask == UINT32_LENGTH) { - increment += 5; - } else { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - short majorType = (short) (buffer[(short) (startOff + increment)] & MAJOR_TYPE_MASK); - short tagValueType = 0; - if (majorType == BYTES_TYPE) { - tagValueType = KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE; - } else if (majorType == UINT_TYPE) { - tagValueType = KMType.COSE_PAIR_INT_TAG_TYPE; - } else if (majorType == NEG_INT_TYPE) { - tagValueType = KMType.COSE_PAIR_NEG_INT_TAG_TYPE; - } else if (majorType == MAP_TYPE) { - tagValueType = KMType.COSE_PAIR_COSE_KEY_TAG_TYPE; - } else if (majorType == SIMPLE_VALUE_TYPE) { - tagValueType = KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE; - } else if (majorType == TSTR_TYPE) { - tagValueType = KMType.COSE_PAIR_TEXT_STR_TAG_TYPE; - }else { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return tagValueType; - } - - private short decodeCosePairTag(short tagValueType, short exp) { - switch (tagValueType) { - case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: - return decodeCosePairByteBlobTag(exp); - case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: - return decodeCosePairNegIntegerTag(exp); - case KMType.COSE_PAIR_INT_TAG_TYPE: - return decodeCosePairIntegerValueTag(exp); - case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: - return decodeCosePairSimpleValueTag(exp); - case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: - return decodeCosePairCoseKeyTag(exp); - case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: - return decodeCosePairTxtStringTag(exp); - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - return 0; - } - } - - private short decodeCoseMap(short exp) { - short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); - // get allowed key pairs - short allowedKeyPairs = KMCoseMap.getVals(exp); - short vals = KMArray.instance(payloadLength); - short length = KMArray.cast(allowedKeyPairs).length(); - short index = 0; - boolean tagFound; - short tagInd; - short cosePairTagType; - short tagClass; - short allowedType; - short obj; - - // For each tag in payload ... - while (index < payloadLength) { - tagFound = false; - tagInd = 0; - cosePairTagType = peekCosePairTagType(); - // Check against the allowed tags ... - while (tagInd < length) { - tagClass = KMArray.cast(allowedKeyPairs).get(tagInd); - allowedType = KMCosePairTagType.getTagValueType(tagClass); - if (allowedType == cosePairTagType) { - obj = decode(tagClass); - KMArray.cast(vals).add(index, obj); - tagFound = true; - break; - } - tagInd++; - } - if (!tagFound) { - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } else { - index++; - } - } - return KMCoseMap.createInstanceFromType(exp, vals); - } - private short decodeKeyParam(short exp) { short payloadLength = readMajorTypeWithPayloadLength(MAP_TYPE); // allowed tags @@ -761,4 +601,33 @@ public short readCertificateChainHeaderLen(byte[] buf, short bufOffset, readMajorTypeWithPayloadLength(BYTES_TYPE); return (short) (scratchBuf[START_OFFSET] - bufOffset); } + + // Reads the offset and length values of the ByteBlobs from a CBOR array buffer. + // CertificateData = [ + // X509CertifcateChain: bstr, ; DER encoded X.509 certificate chain ordered from Leaf to Root. + // Issuer: bstr, ; DER encoded subject field + // Expiry: bstr + // ] + public void decodeCertificateData(short expectedArrLen, byte[] buf, short bufOffset, short bufLen, + byte[] out, short outOff) { + bufferRef[0] = buf; + scratchBuf[START_OFFSET] = bufOffset; + scratchBuf[LEN_OFFSET] = (short) (bufOffset + bufLen); + short byteBlobLength = 0; + // Read Array length + short payloadLength = readMajorTypeWithPayloadLength(ARRAY_TYPE); + if (expectedArrLen != payloadLength) { + ISOException.throwIt(ISO7816.SW_DATA_INVALID); + } + short index = 0; + while (index < payloadLength) { + incrementStartOff(byteBlobLength); + byteBlobLength = readMajorTypeWithPayloadLength(BYTES_TYPE); + Util.setShort(out, outOff, scratchBuf[START_OFFSET]); // offset + outOff += 2; + Util.setShort(out, outOff, byteBlobLength); // length + outOff += 2; + index++; + } + } } diff --git a/Applet/src/com/android/javacard/keymaster/KMEncoder.java b/Applet/src/com/android/javacard/keymaster/KMEncoder.java index b27d720f..a180cb77 100644 --- a/Applet/src/com/android/javacard/keymaster/KMEncoder.java +++ b/Applet/src/com/android/javacard/keymaster/KMEncoder.java @@ -107,7 +107,6 @@ 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) { if (bufferStart > certStart) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); @@ -127,6 +126,34 @@ public short encodeCert(byte[] certBuffer, short bufferStart, short certStart, s writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length return bufferStart; } + + // [KMError.OK, CertChain] + public short encodeProvisionedCertChain(byte[] certBuffer, short bufferStart, short certStart, short certLength, short errInt32Ptr) { + bufferRef[0] = certBuffer; + scratchBuf[START_OFFSET] = certStart; + scratchBuf[LEN_OFFSET] = (short) (certStart + 1); + //Array header - 2 elements i.e. 1 byte + scratchBuf[START_OFFSET]--; + // Integer header + value + scratchBuf[START_OFFSET] -= getEncodedIntegerLength(errInt32Ptr); + // Cert Byte blob - typically 2 bytes length i.e. 3 bytes header + scratchBuf[START_OFFSET] -= 2; + if (certLength >= SHORT_PAYLOAD) { + scratchBuf[START_OFFSET]--; + } + if (scratchBuf[START_OFFSET] < bufferStart) { + ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); + } + bufferStart = scratchBuf[START_OFFSET]; + writeMajorTypeWithLength(ARRAY_TYPE, (short) 2); // Array of 2 elements + encodeInteger( + KMInteger.cast(errInt32Ptr).getBuffer(), + KMInteger.cast(errInt32Ptr).length(), + KMInteger.cast(errInt32Ptr).getStartOff(), UINT_TYPE); + writeMajorTypeWithLength(ARRAY_TYPE, (short) 1); // Array of 1 element + writeMajorTypeWithLength(BYTES_TYPE, certLength); // Cert Byte Blob of length + return bufferStart; + } private void encode() { while (scratchBuf[STACK_PTR_OFFSET] > 0) { @@ -163,11 +190,6 @@ private void encode() { case KMType.SEMANTIC_TAG_TYPE: encodeSemanticTag(exp); break; - case KMType.COSE_KEY_TYPE: - case KMType.COSE_HEADERS_TYPE: - case KMType.COSE_CERT_PAYLOAD_TYPE: - encodeCoseMap(exp); - break; case KMType.KEY_CHAR_TYPE: encodeKeyChar(exp); break; @@ -184,82 +206,12 @@ private void encode() { short tagType = KMTag.getTagType(exp); encodeTag(tagType, exp); break; - case KMType.COSE_PAIR_TAG_TYPE: - short cosePairTagType = KMCosePairTagType.getTagValueType(exp); - encodeCosePairTag(cosePairTagType, exp); - break; default: ISOException.throwIt(ISO7816.SW_DATA_INVALID); } } } - private void encodeCosePairIntegerTag(short exp) { - KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); - // push key and value ptr in stack to get encoded. - encode(cosePairIntTag.getValuePtr()); - encode(cosePairIntTag.getKeyPtr()); - } - - private void encodeCosePairByteBlobTag(short exp) { - KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); - // push key and value ptr in stack to get encoded. - encode(cosePairByteBlobTag.getValuePtr()); - encode(cosePairByteBlobTag.getKeyPtr()); - } - - private void encodeCosePairCoseKeyTag(short exp) { - KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); - // push key and value ptr in stack to get encoded. - encode(cosePairCoseKeyTag.getValuePtr()); - encode(cosePairCoseKeyTag.getKeyPtr()); - } - - private void encodeCosePairTextStringTag(short exp) { - KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); - // push key and value ptr in stack to get encoded. - encode(cosePairTextStringTag.getValuePtr()); - encode(cosePairTextStringTag.getKeyPtr()); - } - - private void encodeCosePairSimpleValueTag(short exp) { - KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); - // push key and value ptr in stack to get encoded. - encode(cosePairSimpleValueTag.getValuePtr()); - encode(cosePairSimpleValueTag.getKeyPtr()); - } - - private void encodeCosePairNegIntegerTag(short exp) { - KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); - // push key and value ptr in stack to get encoded. - encode(cosePairNegIntegerTag.getValuePtr()); - encode(cosePairNegIntegerTag.getKeyPtr()); - } - - private void encodeCosePairTag(short tagType, short exp) { - switch (tagType) { - case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: - encodeCosePairByteBlobTag(exp); - return; - case KMType.COSE_PAIR_INT_TAG_TYPE: - encodeCosePairIntegerTag(exp); - return; - case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: - encodeCosePairNegIntegerTag(exp); - return; - case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: - encodeCosePairSimpleValueTag(exp); - return; - case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: - encodeCosePairTextStringTag(exp); - return; - case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: - encodeCosePairCoseKeyTag(exp); - return; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - } private void encodeTag(short tagType, short exp) { switch (tagType) { @@ -292,10 +244,6 @@ private void encodeTag(short tagType, short exp) { } } - private void encodeCoseMap(short obj) { - encodeAsMap(KMCoseMap.getVals(obj)); - } - private void encodeKeyParam(short obj) { encodeAsMap(KMKeyParameters.cast(obj).getVals()); } @@ -556,60 +504,12 @@ public short getEncodedLength(short ptr) { case KMType.MAP_TYPE: len += getEncodedMapLen(ptr); break; - case KMType.COSE_PAIR_TAG_TYPE: - short cosePairTagType = KMCosePairTagType.getTagValueType(ptr); - len += getEncodedCosePairTagLen(cosePairTagType, ptr); - break; - case KMType.COSE_KEY_TYPE: - case KMType.COSE_HEADERS_TYPE: - case KMType.COSE_CERT_PAYLOAD_TYPE: - len += getEncodedArrayLen(KMCoseMap.getVals(ptr)); - break; default: KMException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } return len; } - private short getEncodedCosePairTagLen(short tagType, short exp) { - short length = 0; - switch (tagType) { - case KMType.COSE_PAIR_BYTE_BLOB_TAG_TYPE: - KMCosePairByteBlobTag cosePairByteBlobTag = KMCosePairByteBlobTag.cast(exp); - length = getEncodedLength(cosePairByteBlobTag.getKeyPtr()); - length += getEncodedLength(cosePairByteBlobTag.getValuePtr()); - break; - case KMType.COSE_PAIR_INT_TAG_TYPE: - KMCosePairIntegerTag cosePairIntTag = KMCosePairIntegerTag.cast(exp); - length = getEncodedLength(cosePairIntTag.getValuePtr()); - length += getEncodedLength(cosePairIntTag.getKeyPtr()); - break; - case KMType.COSE_PAIR_NEG_INT_TAG_TYPE: - KMCosePairNegIntegerTag cosePairNegIntegerTag = KMCosePairNegIntegerTag.cast(exp); - length = getEncodedLength(cosePairNegIntegerTag.getValuePtr()); - length += getEncodedLength(cosePairNegIntegerTag.getKeyPtr()); - break; - case KMType.COSE_PAIR_SIMPLE_VALUE_TAG_TYPE: - KMCosePairSimpleValueTag cosePairSimpleValueTag = KMCosePairSimpleValueTag.cast(exp); - length = getEncodedLength(cosePairSimpleValueTag.getValuePtr()); - length += getEncodedLength(cosePairSimpleValueTag.getKeyPtr()); - break; - case KMType.COSE_PAIR_TEXT_STR_TAG_TYPE: - KMCosePairTextStringTag cosePairTextStringTag = KMCosePairTextStringTag.cast(exp); - length = getEncodedLength(cosePairTextStringTag.getValuePtr()); - length += getEncodedLength(cosePairTextStringTag.getKeyPtr()); - break; - case KMType.COSE_PAIR_COSE_KEY_TAG_TYPE: - KMCosePairCoseKeyTag cosePairCoseKeyTag = KMCosePairCoseKeyTag.cast(exp); - length = getEncodedLength(cosePairCoseKeyTag.getValuePtr()); - length += getEncodedLength(cosePairCoseKeyTag.getKeyPtr()); - break; - default: - ISOException.throwIt(ISO7816.SW_DATA_INVALID); - } - return length; - } - private short getEncodedMapLen(short obj) { short mapLen = KMMap.cast(obj).length(); short len = getEncodedBytesLength(mapLen); diff --git a/Applet/src/com/android/javacard/keymaster/KMError.java b/Applet/src/com/android/javacard/keymaster/KMError.java index f8c9ec4d..512354a5 100644 --- a/Applet/src/com/android/javacard/keymaster/KMError.java +++ b/Applet/src/com/android/javacard/keymaster/KMError.java @@ -104,14 +104,6 @@ public class KMError { //Generic Unknown error. public static final short GENERIC_UNKNOWN_ERROR = 10013; - // Remote key provisioning error codes. - public static final short STATUS_FAILED = 32000; - public static final short STATUS_INVALID_MAC = 32001; - public static final short STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 32002; - public static final short STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 32003; - public static final short STATUS_INVALID_EEK = 32004; - public static final short INVALID_STATE = 32005; - public static short translate(short err) { switch(err) { case SW_CONDITIONS_NOT_SATISFIED: diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 5efdc382..a606f022 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -18,7 +18,6 @@ import com.android.javacard.seprovider.KMAttestationCert; import com.android.javacard.seprovider.KMDataStoreConstants; -import com.android.javacard.seprovider.KMDeviceUniqueKeyPair; import com.android.javacard.seprovider.KMException; import com.android.javacard.seprovider.KMOperation; import com.android.javacard.seprovider.KMSEProvider; @@ -30,7 +29,6 @@ import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.CryptoException; -import javacard.security.Signature; import javacardx.apdu.ExtendedLength; /** @@ -134,9 +132,6 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe KMType.ATTESTATION_ID_SERIAL }; - private static final byte OEM_LOCK = 1; - private static final byte OEM_UNLOCK = 0; - public static final short MAX_COSE_BUF_SIZE = (short) 1024; // Maximum allowed buffer size for to encode the key parameters // which is used while creating mac for key paramters. @@ -285,13 +280,12 @@ public class KMKeymasterApplet extends Applet implements AppletEvent, ExtendedLe public static final short PROVISION_STATUS_ATTEST_IDS = 0x0008; public static final short PROVISION_STATUS_PRESHARED_SECRET = 0x0010; public static final short PROVISION_STATUS_PROVISIONING_LOCKED = 0x0020; - public static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040; - public static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080; + //public static final short PROVISION_STATUS_DEVICE_UNIQUE_KEYPAIR = 0x0040; + //public static final short PROVISION_STATUS_ADDITIONAL_CERT_CHAIN = 0x0080; public static final short PROVISION_STATUS_SE_LOCKED = 0x0100; public static final short PROVISION_STATUS_OEM_PUBLIC_KEY = 0x0200; public static final short PROVISION_STATUS_SECURE_BOOT_MODE = 0x0400; - protected static RemotelyProvisionedComponentDevice rkp; protected static KMEncoder encoder; protected static KMDecoder decoder; protected static KMRepository repository; @@ -330,7 +324,6 @@ protected KMKeymasterApplet(KMSEProvider seImpl) { } // initialize default values initHmacNonceAndSeed(); - rkp = new RemotelyProvisionedComponentDevice(encoder, decoder, repository, seProvider, kmDataStore); } protected void initHmacNonceAndSeed(){ @@ -542,15 +535,8 @@ public void process(APDU apdu) { case INS_UPDATE_AAD_OPERATION_CMD: processUpdateAadOperationCmd(apdu); break; - case INS_GENERATE_RKP_KEY_CMD: - case INS_BEGIN_SEND_DATA_CMD: - case INS_UPDATE_CHALLENGE_CMD: - case INS_UPDATE_EEK_CHAIN_CMD: - case INS_UPDATE_KEY_CMD: - case INS_FINISH_SEND_DATA_CMD: - case INS_GET_RESPONSE_CMD: - case INS_GET_RKP_HARDWARE_INFO: - rkp.process(apduIns, apdu); + case INS_GET_CERT_CHAIN_CMD: + processGetCertChainCmd(apdu); break; //KeyMint 2.0 case INS_GET_ROT_CHALLENGE_CMD: @@ -599,11 +585,44 @@ private void processGetRootOfTrustChallenge(APDU apdu) { sendOutgoing(apdu, arr); } + private void validateROTHeaders(short arrPtr) { + // Validate protected Headers. + short intExp = KMInteger.exp(); + short mapExp = KMMap.instance((short) 1); + KMMap.cast(mapExp).add((short) 0, intExp, intExp); + short pH = KMArray.cast(arrPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET); + pH = decoder.decode( + mapExp, + KMByteBlob.cast(pH).getBuffer(), + KMByteBlob.cast(pH).getStartOff(), + KMByteBlob.cast(pH).length()); + //protected: bstr .cbor { + // * 1 : 5, ; Algorithm : HMAC-256 + // * }, + // As per specification the the key should be 1 and + // value should be 5. + short ptr = KMMap.cast(pH).getKey((short) 0); + if (KMInteger.cast(ptr).getByte() != KMCose.COSE_LABEL_ALGORITHM) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + ptr = KMMap.cast(pH).getKeyValue((short) 0); + if (KMInteger.cast(ptr).getByte() != KMCose.COSE_ALG_HMAC_256) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + // Validate unprotected headers. + short unPh = KMArray.cast(arrPtr).get(KMCose.COSE_MAC0_UNPROTECTED_PARAMS_OFFSET); + if (KMMap.cast(unPh).length() != 0) { + KMException.throwIt(KMError.VERIFICATION_FAILED); + } + } + private short sendRootOfTrustCmd(APDU apdu) { short arrInst = KMArray.instance((short) 4); - short headers = KMCoseHeaders.exp(); + short intExp = KMInteger.exp(); + short mapExp = KMMap.instance((short) 1); + KMMap.cast(mapExp).add((short) 0, intExp, intExp); KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); - KMArray.cast(arrInst).add((short) 1, headers); + KMArray.cast(arrInst).add((short) 1, mapExp); KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); short semanticTag = KMSemanticTag.exp(arrInst); @@ -623,27 +642,18 @@ private void processSendRootOfTrust(APDU apdu) { } short semanticTag = KMArray.cast(cmd).get((short) 0); - short coseMacPtr = KMSemanticTag.cast(semanticTag).getValuePtr(); - // Exp for KMCoseHeaders - short coseHeadersExp = KMCoseHeaders.exp(); - // validate protected Headers - short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET); - ptr = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - - if (!KMCoseHeaders.cast(ptr).isDataValid(tmpVariables, KMCose.COSE_ALG_HMAC_256, - KMType.INVALID_VALUE)) { - KMException.throwIt(KMError.VERIFICATION_FAILED); - } + short arrPtr = KMSemanticTag.cast(semanticTag).getValuePtr(); + // Validate headers + validateROTHeaders(arrPtr); // Validate the Mac short len = kmDataStore.getChallenge(scratchPad, (short) 0); short extAad = KMByteBlob.instance(scratchPad, (short) 0, len); // Compute CoseMac Structure and compare the macs. - short rotPayload = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET); + short rotPayload = KMArray.cast(arrPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET); short macStructure = KMCose.constructCoseMacStructure( - KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET), + KMArray.cast(arrPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET), extAad, rotPayload); short encodedLen = KMKeymasterApplet.encodeToApduBuffer(macStructure, scratchPad, (short) 0, @@ -651,9 +661,9 @@ private void processSendRootOfTrust(APDU apdu) { if (!seProvider.hmacVerify(kmDataStore.getComputedHmacKey(), scratchPad, (short) 0, encodedLen, - KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(), - KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getStartOff(), - KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length())) { + KMByteBlob.cast(KMArray.cast(arrPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(), + KMByteBlob.cast(KMArray.cast(arrPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getStartOff(), + KMByteBlob.cast(KMArray.cast(arrPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length())) { KMException.throwIt(KMError.VERIFICATION_FAILED); } // Store the data only once after reboot. @@ -1437,28 +1447,6 @@ private void processBeginImportWrappedKeyCmd(APDU apdu) { setWrappingKey(transportKey); sendResponse(apdu, KMError.OK); } - private short aesGCMEncrypt(short aesSecret, short input, short nonce, short authData, short authTag,byte[] scratchPad){ - Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0); - short len = seProvider.aesGCMEncrypt( - KMByteBlob.cast(aesSecret).getBuffer(), - KMByteBlob.cast(aesSecret).getStartOff(), - KMByteBlob.cast(aesSecret).length(), - KMByteBlob.cast(input).getBuffer(), - KMByteBlob.cast(input).getStartOff(), - KMByteBlob.cast(input).length(), - scratchPad, - (short) 0, - KMByteBlob.cast(nonce).getBuffer(), - KMByteBlob.cast(nonce).getStartOff(), - KMByteBlob.cast(nonce).length(), - KMByteBlob.cast(authData).getBuffer(), - KMByteBlob.cast(authData).getStartOff(), - KMByteBlob.cast(authData).length(), - KMByteBlob.cast(authTag).getBuffer(), - KMByteBlob.cast(authTag).getStartOff(), - KMByteBlob.cast(authTag).length()); - return KMByteBlob.instance(scratchPad, (short) 0, len); - } private short aesGCMDecrypt(short aesSecret, short input, short nonce, short authData, short authTag,byte[] scratchPad){ Util.arrayFillNonAtomic(scratchPad, (short) 0, KMByteBlob.cast(input).length(), (byte) 0); @@ -1583,6 +1571,45 @@ private KMAttestationCert makeCommonCert(byte[] scratchPad) { cert.serialNumber(serialNum); return cert; } + + private KMAttestationCert makeFactoryAttestationCert(short attChallenge, byte[] scratchPad) { + KMAttestationCert cert = makeCommonCert(scratchPad); + // Challenge + cert.attestationChallenge(attChallenge); + // Issuer + byte[] issuer = kmDataStore.getAttestationCertIssuer(); + short issuerBlob = KMByteBlob.instance(issuer, (short) 2, kmDataStore.getAttestationCertIssuerLength()); + cert.issuer(issuerBlob); + // Public key + cert.publicKey(data[PUB_KEY]); + // Signing Key + cert.ecAttestKey(KMType.INVALID_VALUE, KMType.FACTORY_ATTESTATION_CERT); + + // Save attestation application id - must be present. + short attAppId = KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.ATTESTATION_APPLICATION_ID, + data[KEY_PARAMETERS]); + if (attAppId == KMType.INVALID_VALUE) { + KMException.throwIt(KMError.ATTESTATION_APPLICATION_ID_MISSING); + } + cert.extensionTag(attAppId, false); + // unique id byte blob - uses application id and temporal month count of + // creation time. + attAppId = KMByteTag.cast(attAppId).getValue(); + setUniqueId(cert, attAppId, scratchPad); + // Add Attestation Ids if present + addAttestationIds(cert, scratchPad); + + // Add Tags + addTags(data[HW_PARAMETERS], true, cert); + addTags(data[SW_PARAMETERS], false, cert); + // Add Device Boot locked status + cert.deviceLocked(kmDataStore.isDeviceBootLocked()); + // VB data + cert.verifiedBootHash(getVerifiedBootHash(scratchPad)); + cert.verifiedBootKey(getBootKey(scratchPad)); + cert.verifiedBootState((byte) kmDataStore.getBootState()); + return cert; + } private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyParam, short attChallenge, short issuer, byte[] scratchPad) { @@ -1615,7 +1642,6 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara KMException.throwIt(KMError.INCOMPATIBLE_PURPOSE); } KMAsn1Parser asn1Decoder = KMAsn1Parser.instance(); - short length = 0; try { asn1Decoder.validateDerSubject(issuer); } catch (KMException e) { @@ -2513,14 +2539,6 @@ private void processBeginOperationCmd(APDU apdu) { sendOutgoing(apdu, resp); } - private void authorizeAlgorithm(KMOperationState op) { - short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); - if (alg == KMType.INVALID_VALUE) { - KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); - } - op.setAlgorithm((byte) alg); - } - private void authorizePurpose(KMOperationState op) { switch (op.getAlgorithm()) { case KMType.AES: @@ -3304,7 +3322,7 @@ private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) { importHmacKey(scratchPad); break; case KMType.EC: - importECKeys(scratchPad); + importECKeys(scratchPad, (byte) keyFmt); break; default: KMException.throwIt(KMError.UNSUPPORTED_ALGORITHM); @@ -3315,13 +3333,36 @@ private void importKey(APDU apdu, short keyFmt, byte[] scratchPad) { createEncryptedKeyBlob(scratchPad); sendOutgoing(apdu, cert, data[CERTIFICATE], data[KEY_BLOB], data[KEY_CHARACTERISTICS]); } - - private void importECKeys(byte[] scratchPad) { + + private void decodeRawECKey() { + // Decode key material + tmpVariables[0] = KMArray.instance((short) 2); + KMArray.cast(tmpVariables[0]).add((short) 0, KMByteBlob.exp()); // secret + KMArray.cast(tmpVariables[0]).add((short) 1, KMByteBlob.exp()); // public key + tmpVariables[0] = + decoder.decode( + tmpVariables[0], + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getBuffer(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).getStartOff(), + KMByteBlob.cast(data[IMPORTED_KEY_BLOB]).length()); + data[SECRET] = KMArray.cast(tmpVariables[0]).get((short) 0); + data[PUB_KEY] = KMArray.cast(tmpVariables[0]).get((short) 1); + } + + private void decodePKCS8ECKeys() { // Decode key material 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); + } + + protected void importECKeys(byte[] scratchPad, byte keyFormat) { + if (keyFormat == KMType.RAW) { + decodeRawECKey(); + } else { + decodePKCS8ECKeys(); + } // initialize 256 bit p256 key for given private key and public key. short index = 0; // check whether the key size tag is present in key parameters. @@ -3632,7 +3673,6 @@ private short initStrongBoxCmd(APDU apdu){ // is moved to a place where it is called on every boot. private void processInitStrongBoxCmd(APDU apdu) { short cmd = initStrongBoxCmd(apdu); - byte[] scratchPad = apdu.getBuffer(); short osVersion = KMArray.cast(cmd).get((short) 0); short osPatchLevel = KMArray.cast(cmd).get((short) 1); @@ -3799,7 +3839,7 @@ private short getAttestationMode(short attKeyBlob, short attChallenge) { } else { // no attestation key blob // Attestation challenge present then it is an error because no factory provisioned attest key if (attChallenge != KMType.INVALID_VALUE && KMByteBlob.cast(attChallenge).length() > 0) { - KMException.throwIt(KMError.ATTESTATION_KEYS_NOT_PROVISIONED); + mode = KMType.FACTORY_ATTESTATION_CERT; } else if (KMEnumArrayTag.contains(KMType.PURPOSE, KMType.ATTEST_KEY, data[HW_PARAMETERS]) || KMEnumArrayTag.contains(KMType.PURPOSE, KMType.SIGN, data[HW_PARAMETERS])) { // The Purpose value can be read from either data[HW_PARAMETERS] or data[KEY_PARAMETERS] @@ -3844,6 +3884,9 @@ private KMAttestationCert generateAttestation(short attKeyBlob, short attKeyPar case KMType.FAKE_CERT: cert = makeSelfSignedCert(KMType.INVALID_VALUE, data[PUB_KEY], mode, scratchPad); break; + case KMType.FACTORY_ATTESTATION_CERT: + cert = makeFactoryAttestationCert(attChallenge, scratchPad); + break; default: data[CERTIFICATE] = KMType.INVALID_VALUE; return null; @@ -4632,177 +4675,6 @@ public static short encodeToApduBuffer(short object, byte[] apduBuf, short apduO return len; } - public static short validateCertChain(boolean validateEekRoot, byte expCertAlg, - byte expLeafCertAlg, short certChainArr, byte[] scratchPad, Object[] authorizedEekRoots) { - short len = KMArray.cast(certChainArr).length(); - short coseHeadersExp = KMCoseHeaders.exp(); - //prepare exp for coseky - short coseKeyExp = KMCoseKey.exp(); - short ptr1; - short ptr2; - short signStructure; - short encodedLen; - short prevCoseKey = 0; - short keySize; - short alg = expCertAlg; - short index; - for (index = 0; index < len; index++) { - ptr1 = KMArray.cast(certChainArr).get(index); - - // validate protected Headers - ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET); - ptr2 = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr2).getBuffer(), - KMByteBlob.cast(ptr2).getStartOff(), KMByteBlob.cast(ptr2).length()); - if (!KMCoseHeaders.cast(ptr2).isDataValid(rkp.rkpTmpVariables, alg, KMType.INVALID_VALUE)) { - KMException.throwIt(KMError.STATUS_FAILED); - } - - // parse and get the public key from payload. - ptr2 = KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET); - ptr2 = decoder.decode(coseKeyExp, KMByteBlob.cast(ptr2).getBuffer(), - KMByteBlob.cast(ptr2).getStartOff(), KMByteBlob.cast(ptr2).length()); - if ((index == (short) (len - 1)) && len > 1) { - alg = expLeafCertAlg; - } - if (!KMCoseKey.cast(ptr2).isDataValid(rkp.rkpTmpVariables, KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, alg, - KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)) { - KMException.throwIt(KMError.STATUS_FAILED); - } - if (prevCoseKey == 0) { - prevCoseKey = ptr2; - } - // Get the public key. - keySize = KMCoseKey.cast(prevCoseKey).getEcdsa256PublicKey(scratchPad, (short) 0); - if (keySize != 65) { - KMException.throwIt(KMError.STATUS_FAILED); - } - if (validateEekRoot && (index == 0)) { - boolean found = false; - // In prod mode the first pubkey should match a well-known Google public key. - for (short i = 0; i < (short) authorizedEekRoots.length; i++) { - if (0 == Util.arrayCompare(scratchPad, (short) 0, (byte[]) authorizedEekRoots[i], - (short) 0, (short) ((byte[]) authorizedEekRoots[i]).length)) { - found = true; - break; - } - } - if (!found) { - KMException.throwIt(KMError.STATUS_FAILED); - } - } - // Validate signature. - signStructure = - KMCose.constructCoseSignStructure( - KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PROTECTED_PARAMS_OFFSET), - KMByteBlob.instance((short) 0), - KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_PAYLOAD_OFFSET)); - encodedLen = KMKeymasterApplet.encodeToApduBuffer(signStructure, scratchPad, - keySize, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - - short signatureLen = - rkp.encodeES256CoseSignSignature( - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getBuffer(), - KMByteBlob.cast(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)).getStartOff(), - KMByteBlob.length(KMArray.cast(ptr1).get(KMCose.COSE_SIGN1_SIGNATURE_OFFSET)), - scratchPad, - (short) (keySize + encodedLen)); - - if (!seProvider.ecVerify256(scratchPad, (short) 0, keySize, scratchPad, keySize, encodedLen, - scratchPad, (short) (keySize + encodedLen), signatureLen)) { - KMException.throwIt(KMError.STATUS_FAILED); - } - prevCoseKey = ptr2; - } - return prevCoseKey; - } - - - public static short generateBcc(boolean testMode, byte[] scratchPad) { - if (!testMode && kmDataStore.isProvisionLocked()) { - KMException.throwIt(KMError.STATUS_FAILED); - } - KMDeviceUniqueKeyPair deviceUniqueKey = kmDataStore.getRkpDeviceUniqueKeyPair(testMode); - short temp = deviceUniqueKey.getPublicKey(scratchPad, (short) 0); - short coseKey = - KMCose.constructCoseKey(rkp.rkpTmpVariables, - KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), - KMType.INVALID_VALUE, - KMNInteger.uint_8(KMCose.COSE_ALG_ES256), - KMInteger.uint_8(KMCose.COSE_KEY_OP_VERIFY), - KMInteger.uint_8(KMCose.COSE_ECCURVE_256), - scratchPad, - (short) 0, - temp, - KMType.INVALID_VALUE, - false - ); - temp = KMKeymasterApplet.encodeToApduBuffer(coseKey, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - // Construct payload. - short payload = - KMCose.constructCoseCertPayload( - KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.ISSUER), - KMTextString.instance(KMCose.TEST_ISSUER_NAME, (short) 0, - (short) KMCose.TEST_ISSUER_NAME.length)), - KMCosePairTextStringTag.instance(KMInteger.uint_8(KMCose.SUBJECT), - KMTextString.instance(KMCose.TEST_SUBJECT_NAME, (short) 0, - (short) KMCose.TEST_SUBJECT_NAME.length)), - KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.SUBJECT_PUBLIC_KEY, (short) 0), - KMByteBlob.instance(scratchPad, (short) 0, temp)), - KMCosePairByteBlobTag.instance(KMNInteger.uint_32(KMCose.KEY_USAGE, (short) 0), - KMByteBlob.instance(KMCose.KEY_USAGE_SIGN, (short) 0, - (short) KMCose.KEY_USAGE_SIGN.length)) - ); - // temp temporarily holds the length of encoded cert payload. - temp = KMKeymasterApplet.encodeToApduBuffer(payload, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - payload = KMByteBlob.instance(scratchPad, (short) 0, temp); - - // protected header - short protectedHeader = - KMCose.constructHeaders(rkp.rkpTmpVariables, KMNInteger.uint_8(KMCose.COSE_ALG_ES256), KMType.INVALID_VALUE, - KMType.INVALID_VALUE, KMType.INVALID_VALUE); - // temp temporarily holds the length of encoded headers. - temp = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, temp); - - //unprotected headers. - short arr = KMArray.instance((short) 0); - short unprotectedHeader = KMCoseHeaders.instance(arr); - - // construct cose sign structure. - short coseSignStructure = - KMCose.constructCoseSignStructure(protectedHeader, KMByteBlob.instance((short) 0), payload); - // temp temporarily holds the length of encoded sign structure. - // Encode cose Sign_Structure. - temp = KMKeymasterApplet.encodeToApduBuffer(coseSignStructure, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - // do sign - short len = - seProvider.ecSign256( - deviceUniqueKey, - scratchPad, - (short) 0, - temp, - scratchPad, - temp - ); - len = KMAsn1Parser.instance(). - decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, temp, len), scratchPad, temp); - coseSignStructure = KMByteBlob.instance(scratchPad, temp, len); - - // construct cose_sign1 - short coseSign1 = - KMCose.constructCoseSign1(protectedHeader, unprotectedHeader, payload, coseSignStructure); - - // [Cose_Key, Cose_Sign1] - short bcc = KMArray.instance((short) 2); - KMArray.cast(bcc).add((short) 0, coseKey); - KMArray.cast(bcc).add((short) 1, coseSign1); - return bcc; - } - private void updateTrustedConfirmationOperation(KMOperationState op) { if (op.isTrustedConfirmationRequired()) { op.getTrustedConfirmationSigner().update(KMByteBlob.cast(data[INPUT_DATA]).getBuffer(), @@ -4849,4 +4721,28 @@ private boolean isDigestSupported(short alg, short digest) { } return true; } + + private void processGetCertChainCmd(APDU apdu) { + // Make the response + short certChainLen = kmDataStore.getAttestationCertChainLength(); + short offset = repository.allocAvailableMemory(); + byte[] buffer = repository.getHeap(); + short pos = offset; + Util.setShort(buffer, pos, (short) 0x8200); + pos += 2; + pos += + encoder.encodeByteBlobHeader(certChainLen, buffer, pos, (short) 3); + // copy the certificate chain to the end of the buffer. + byte[] certChain = kmDataStore.getAttestationCertChain(); + Util.arrayCopyNonAtomic(certChain, (short) 2, buffer, + pos, certChainLen); + pos += certChainLen; + short bufferLen = (short) (pos - offset); + // TODO Use sendOutgoing() + // Send data + apdu.setOutgoing(); + apdu.setOutgoingLength(bufferLen); + apdu.sendBytesLong(buffer, offset, bufferLen); + } + } diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java index 776f93fa..528d2151 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymintDataStore.java @@ -2,19 +2,13 @@ import org.globalplatform.upgrade.Element; -import com.android.javacard.seprovider.KMAESKey; import com.android.javacard.seprovider.KMAttestationKey; import com.android.javacard.seprovider.KMComputedHmacKey; import com.android.javacard.seprovider.KMDataStoreConstants; -import com.android.javacard.seprovider.KMDeviceUniqueKeyPair; -import com.android.javacard.seprovider.KMECDeviceUniqueKey; -import com.android.javacard.seprovider.KMECPrivateKey; import com.android.javacard.seprovider.KMError; import com.android.javacard.seprovider.KMException; -import com.android.javacard.seprovider.KMHmacKey; import com.android.javacard.seprovider.KMMasterKey; import com.android.javacard.seprovider.KMPreSharedKey; -import com.android.javacard.seprovider.KMRkpMacKey; import com.android.javacard.seprovider.KMSEProvider; import com.android.javacard.seprovider.KMType; import com.android.javacard.seprovider.KMUpgradable; @@ -23,11 +17,6 @@ import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.Util; -import javacard.security.AESKey; -import javacard.security.CryptoException; -import javacard.security.HMACKey; -import javacard.security.KeyBuilder; -import javacard.security.KeyPair; public class KMKeymintDataStore implements KMUpgradable { @@ -70,12 +59,11 @@ public class KMKeymintDataStore implements KMUpgradable { public static final short AUTH_TAG_LENGTH = 16; public static final short AUTH_TAG_COUNTER_SIZE = 4; public static final short AUTH_TAG_ENTRY_SIZE = (AUTH_TAG_LENGTH + AUTH_TAG_COUNTER_SIZE + 1); - private static final short MASTER_KEY_SIZE = 16; private static final short SHARED_SECRET_KEY_SIZE = 32; private static final byte DEVICE_STATUS_FLAG_SIZE = 1; - private static final short ADDITIONAL_CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. - private static final short BCC_MAX_SIZE = 512; + private static final short ATTESTATION_CERT_CHAIN_MAX_SIZE = 2500;//First 2 bytes for length. + private static final short ATTESTATION_CERT_ISSUER_MAX_SIZE = 250;//First 2 bytes for length. //Device boot states. Applet starts executing the // core commands once all the states are set. The commands @@ -115,17 +103,15 @@ public class KMKeymintDataStore implements KMUpgradable { private byte[] dataTable; private KMSEProvider seProvider; private KMRepository repository; - private byte[] additionalCertChain; - private byte[] bcc; private KMMasterKey masterKey; - private KMDeviceUniqueKeyPair testDeviceUniqueKeyPair; - private KMDeviceUniqueKeyPair deviceUniqueKeyPair; + private KMAttestationKey attestationKeyPair; private KMPreSharedKey preSharedKey; private KMComputedHmacKey computedHmacKey; - private KMRkpMacKey rkpMacKey; private byte[] oemRootPublicKey; private short provisionStatus; private static KMKeymintDataStore kmDataStore; + private byte[] attestationCertificateChain; + private byte[] attestationCertIssuer; public static KMKeymintDataStore instance() { return kmDataStore; @@ -138,9 +124,9 @@ public KMKeymintDataStore(KMSEProvider provider, KMRepository repo) { initDataTable(); //Initialize the device locked status if (!isUpgrading) { - additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; - bcc = new byte[BCC_MAX_SIZE]; oemRootPublicKey = new byte[65]; + attestationCertificateChain = new byte[ATTESTATION_CERT_CHAIN_MAX_SIZE]; + attestationCertIssuer = new byte[ATTESTATION_CERT_ISSUER_MAX_SIZE]; } setDeviceLockPasswordOnly(false); setDeviceLock(false); @@ -242,8 +228,6 @@ public short getHmacNonce() { return readData(HMAC_NONCE); } - private static final byte[] zero = {0, 0, 0, 0, 0, 0, 0, 0}; - public short getOsVersion() { short blob = readData(BOOT_OS_VERSION); if (blob == KMType.INVALID_VALUE) { @@ -502,51 +486,45 @@ public void setRateLimitedKeyCount(short authTag, byte[] buf, short off, short l KMByteBlob.cast(dataPtr).length()); } } - - public void persistAdditionalCertChain(byte[] buf, short offset, short len) { - // Input buffer contains encoded additional certificate chain as shown below. - // AdditionalDKSignatures = { - // + SignerName => DKCertChain - // } - // SignerName = tstr - // DKCertChain = [ - // 2* Certificate // Root -> Leaf. Root is the vendo r - // // self-signed cert, leaf contains DK_pu b - // ] - // Certificate = COSE_Sign1 of a public key - if ((short) (len + 2) > ADDITIONAL_CERT_CHAIN_MAX_SIZE) { + + public void persistAttestationCertChain(byte[] buf, short offset, short len) { + if ((short) (len + 2) > ATTESTATION_CERT_CHAIN_MAX_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); - Util.setShort(additionalCertChain, (short) 0, (short) len); - Util.arrayCopyNonAtomic(buf, offset, additionalCertChain, + Util.setShort(attestationCertificateChain, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, attestationCertificateChain, (short) 2, len); JCSystem.commitTransaction(); } - public short getAdditionalCertChainLength() { - return Util.getShort(additionalCertChain, (short) 0); - } - - public byte[] getAdditionalCertChain() { - return additionalCertChain; + public short getAttestationCertChainLength() { + return Util.getShort(attestationCertificateChain, (short) 0); } - public byte[] getBootCertificateChain() { - return bcc; + public byte[] getAttestationCertChain() { + return attestationCertificateChain; } - - public void persistBootCertificateChain(byte[] buf, short offset, short len) { - if ((short) (len + 2) > BCC_MAX_SIZE) { + + public void persistAttestationCertIssuer(byte[] buf, short offset, short len) { + if ((short) (len + 2) > ATTESTATION_CERT_ISSUER_MAX_SIZE) { KMException.throwIt(KMError.INVALID_INPUT_LENGTH); } JCSystem.beginTransaction(); - Util.setShort(bcc, (short) 0, (short) len); - Util.arrayCopyNonAtomic(buf, offset, bcc, + Util.setShort(attestationCertIssuer, (short) 0, (short) len); + Util.arrayCopyNonAtomic(buf, offset, attestationCertIssuer, (short) 2, len); JCSystem.commitTransaction(); } - + + public short getAttestationCertIssuerLength() { + return Util.getShort(attestationCertIssuer, (short) 0); + } + + public byte[] getAttestationCertIssuer() { + return attestationCertIssuer; + } + private void writeAuthTagState(byte[] buf, short offset, byte state) { buf[offset] = state; } @@ -596,53 +574,20 @@ public KMComputedHmacKey getComputedHmacKey() { return computedHmacKey; } - public KMDeviceUniqueKeyPair createRkpTestDeviceUniqueKeyPair(byte[] pubKey, short pubKeyOff, short pubKeyLen, - byte[] privKey, short privKeyOff, short privKeyLen) { - if (testDeviceUniqueKeyPair == null) { - testDeviceUniqueKeyPair = seProvider.createRkpDeviceUniqueKeyPair(testDeviceUniqueKeyPair, pubKey, pubKeyOff, - pubKeyLen, privKey, - privKeyOff, privKeyLen); - } else { - seProvider.createRkpDeviceUniqueKeyPair(testDeviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, - privKeyOff, + public KMAttestationKey createAttestationKeyPair(byte[] privKey, short privKeyOff, short privKeyLen) { + if (attestationKeyPair == null) { + attestationKeyPair = seProvider.createAttestationKey(attestationKeyPair, privKey, privKeyOff, privKeyLen); - } - return testDeviceUniqueKeyPair; - } - - public KMDeviceUniqueKeyPair createRkpDeviceUniqueKeyPair(byte[] pubKey, short pubKeyOff, short pubKeyLen, - byte[] privKey, short privKeyOff, - short privKeyLen) { - if (deviceUniqueKeyPair == null) { - deviceUniqueKeyPair = seProvider.createRkpDeviceUniqueKeyPair(deviceUniqueKeyPair, pubKey, pubKeyOff, - pubKeyLen, privKey, - privKeyOff, privKeyLen); - } else { - seProvider.createRkpDeviceUniqueKeyPair(deviceUniqueKeyPair, pubKey, pubKeyOff, pubKeyLen, privKey, - privKeyOff, privKeyLen); - } - return deviceUniqueKeyPair; - } - - public KMDeviceUniqueKeyPair getRkpDeviceUniqueKeyPair(boolean testMode) { - return ((KMDeviceUniqueKeyPair) (testMode ? testDeviceUniqueKeyPair : deviceUniqueKeyPair)); - } - - public void createRkpMacKey(byte[] keydata, short offset, short length) { - if (rkpMacKey == null) { - rkpMacKey = seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length); } else { - seProvider.createRkpMacKey(rkpMacKey, keydata, offset, length); + seProvider.createAttestationKey(attestationKeyPair, privKey, privKeyOff, privKeyLen); } + return attestationKeyPair; } - - public KMRkpMacKey getRkpMacKey() { - if (rkpMacKey == null) { - KMException.throwIt(KMError.INVALID_DATA); - } - return rkpMacKey; + + public KMAttestationKey getAttestationKeyPair() { + return (KMAttestationKey) attestationKeyPair; } - + public short getAttestationId(short tag, byte[] buffer, short start) { byte[] attestId = null; switch (tag) { @@ -902,82 +847,29 @@ public void onSave(Element element) { element.write(attIdMeId); element.write(attIdManufacturer); element.write(attIdModel); - element.write(additionalCertChain); - element.write(bcc); element.write(oemRootPublicKey); + element.write(attestationCertificateChain); + element.write(attestationCertIssuer); // Key Objects seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY, masterKey); seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY, preSharedKey); - seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR, deviceUniqueKeyPair); - seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY, rkpMacKey); + seProvider.onSave(element, KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY, attestationKeyPair); } @Override public void onRestore(Element element, short oldVersion, short currentVersion) { - if (oldVersion <= KM_APPLET_PACKAGE_VERSION_1) { - // 1.0 to 3.0 Upgrade happens here. - handlePreviousVersionUpgrade(element); - return; - } else if (oldVersion == KM_APPLET_PACKAGE_VERSION_2) { - handleUpgrade(element, oldVersion); - JCSystem.beginTransaction(); - // While upgrading Secure Boot Mode flag from 2.0 to 3.0, implementations - // have to update the secureBootMode with the correct input. - secureBootMode = 0; - provisionStatus |= KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE; - // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain. - // From package version 3, the additionalCertificateChain is in X.509 format. - // So Unreference the old address and allocate new persistent memory. - additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; - JCSystem.commitTransaction(); - // Request for ObjectDeletion for unreferenced address of additionalCertChain. - JCSystem.requestObjectDeletion(); - return; + if (oldVersion <= KM_APPLET_PACKAGE_VERSION_2) { + // Upgrade from older versions 1.0 and 2.0 not supported + ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); } handleUpgrade(element, oldVersion); } - private void handlePreviousVersionUpgrade(Element element) { - // Read Primitives - //restore old data table index - short oldDataIndex = element.readShort(); - element.readBoolean(); // pop deviceBootLocked - element.readShort(); // pop bootState - - // Read Objects - //restore old data table - byte[] oldDataTable = (byte[]) element.readObject(); - - attIdBrand = (byte[]) element.readObject(); - attIdDevice = (byte[]) element.readObject(); - attIdProduct = (byte[]) element.readObject(); - attIdSerial = (byte[]) element.readObject(); - attIdImei = (byte[]) element.readObject(); - attIdMeId = (byte[]) element.readObject(); - attIdManufacturer = (byte[]) element.readObject(); - attIdModel = (byte[]) element.readObject(); - element.readObject(); // pop verifiedHash - element.readObject(); //pop bootKey - element.readObject(); // pop bootPatchLevel - additionalCertChain = (byte[]) element.readObject(); - bcc = (byte[]) element.readObject(); - - // Read Key Objects - masterKey = (KMMasterKey) seProvider.onRestore(element); - seProvider.onRestore(element); // pop computedHmacKey - preSharedKey = (KMPreSharedKey) seProvider.onRestore(element); - deviceUniqueKeyPair = (KMDeviceUniqueKeyPair) seProvider.onRestore(element); - rkpMacKey = (KMRkpMacKey) seProvider.onRestore(element); - handleProvisionStatusUpgrade(oldDataTable, oldDataIndex); - } - private void handleUpgrade(Element element, short oldVersion) { // Read Primitives provisionStatus = element.readShort(); - if (oldVersion >= KM_APPLET_PACKAGE_VERSION_3) { - secureBootMode = element.readByte(); - } + secureBootMode = element.readByte(); // Read Objects attIdBrand = (byte[]) element.readObject(); attIdDevice = (byte[]) element.readObject(); @@ -987,14 +879,13 @@ private void handleUpgrade(Element element, short oldVersion) { attIdMeId = (byte[]) element.readObject(); attIdManufacturer = (byte[]) element.readObject(); attIdModel = (byte[]) element.readObject(); - additionalCertChain = (byte[]) element.readObject(); - bcc = (byte[]) element.readObject(); oemRootPublicKey = (byte[]) element.readObject(); + attestationCertificateChain = (byte[]) element.readObject(); + attestationCertIssuer = (byte[]) element.readObject(); // Read Key Objects masterKey = (KMMasterKey) seProvider.onRestore(element); preSharedKey = (KMPreSharedKey) seProvider.onRestore(element); - deviceUniqueKeyPair = (KMDeviceUniqueKeyPair) seProvider.onRestore(element); - rkpMacKey = (KMRkpMacKey) seProvider.onRestore(element); + attestationKeyPair = (KMAttestationKey) seProvider.onRestore(element); } public void getProvisionStatus(byte[] dataTable, byte[] scratchpad, short offset) { @@ -1002,31 +893,6 @@ public void getProvisionStatus(byte[] dataTable, byte[] scratchpad, short offset readDataEntry(dataTable, OLD_PROVISIONED_STATUS_OFFSET, scratchpad, offset); } - void handleProvisionStatusUpgrade(byte[] dataTable, short dataTableIndex){ - short dInex = repository.allocReclaimableMemory((short)2); - byte data[] = repository.getHeap(); - getProvisionStatus(dataTable, data, dInex); - short pStatus = (short)( data[dInex] & 0x00ff); - if( KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED - == (pStatus & KMKeymasterApplet.PROVISION_STATUS_PROVISIONING_LOCKED)) { - pStatus |= KMKeymasterApplet.PROVISION_STATUS_SE_LOCKED - | KMKeymasterApplet.PROVISION_STATUS_SECURE_BOOT_MODE; - } - JCSystem.beginTransaction(); - // While upgrading Secure Boot Mode flag from 1.0 to 3.0, implementations - // have to update the secureBootMode with the correct input. - secureBootMode = 0; - provisionStatus = pStatus; - // Applet package Versions till 2 had CoseSign1 for additionalCertificateChain. - // From package version 3, the additionalCertificateChain is in X.509 format. - // So Unreference the old address and allocate new persistent memory. - additionalCertChain = new byte[ADDITIONAL_CERT_CHAIN_MAX_SIZE]; - JCSystem.commitTransaction(); - repository.reclaimMemory((short)2); - // Request object deletion for unreferenced address for additionalCertChain - JCSystem.requestObjectDeletion(); - } - @Override public short getBackupPrimitiveByteCount() { // provisionStatus - 2 bytes @@ -1034,21 +900,19 @@ public short getBackupPrimitiveByteCount() { return (short) (3 + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + - seProvider.getBackupPrimitiveByteCount( KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR) + - seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); + seProvider.getBackupPrimitiveByteCount(KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY)); } @Override public short getBackupObjectCount() { // AttestationIds - 8 - // AdditionalCertificateChain - 1 - // BCC - 1 + // AttestationCertificateChain - 1 + // AttestationCertIssuer - 1 // oemRootPublicKey - 1 return (short) (11 + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_MASTER_KEY) + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_PRE_SHARED_KEY) + - seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_DEVICE_UNIQUE_KEY_PAIR) + - seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_RKP_MAC_KEY)); + seProvider.getBackupObjectCount(KMDataStoreConstants.INTERFACE_TYPE_ATTESTATION_KEY)); } } diff --git a/Applet/src/com/android/javacard/keymaster/KMMap.java b/Applet/src/com/android/javacard/keymaster/KMMap.java index 4b559fa3..4acb78fa 100644 --- a/Applet/src/com/android/javacard/keymaster/KMMap.java +++ b/Applet/src/com/android/javacard/keymaster/KMMap.java @@ -131,10 +131,6 @@ public void swap(short index1, short index2) { indexPtr1); } - public void canonicalize() { - KMCoseMap.canonicalize(instanceTable[KM_MAP_OFFSET], length()); - } - public short containedType() { return Util.getShort(heap, (short) (instanceTable[KM_MAP_OFFSET] + TLV_HEADER_SIZE)); } diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 08171c57..2664e989 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -16,12 +16,6 @@ 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; -import org.globalplatform.upgrade.Element; - import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; diff --git a/Applet/src/com/android/javacard/keymaster/KMType.java b/Applet/src/com/android/javacard/keymaster/KMType.java index 357ad00a..0f23be13 100644 --- a/Applet/src/com/android/javacard/keymaster/KMType.java +++ b/Applet/src/com/android/javacard/keymaster/KMType.java @@ -346,6 +346,7 @@ public abstract class KMType { public static final byte ATTESTATION_CERT = 1; public static final byte SELF_SIGNED_CERT = 2; public static final byte FAKE_CERT = 3; + public static final byte FACTORY_ATTESTATION_CERT = 4; // Buffering Mode public static final byte BUF_NONE = 0; public static final byte BUF_RSA_DECRYPT_OR_NO_DIGEST = 1; diff --git a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java b/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java deleted file mode 100644 index e92f7713..00000000 --- a/Applet/src/com/android/javacard/keymaster/RemotelyProvisionedComponentDevice.java +++ /dev/null @@ -1,1486 +0,0 @@ -/* - * Copyright(C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.javacard.keymaster; - -import com.android.javacard.seprovider.KMDeviceUniqueKeyPair; -import com.android.javacard.seprovider.KMException; -import com.android.javacard.seprovider.KMOperation; -import com.android.javacard.seprovider.KMSEProvider; - -import javacard.framework.APDU; -import javacard.framework.ISO7816; -import javacard.framework.ISOException; -import javacard.framework.JCSystem; -import javacard.framework.Util; - -/* - * This class handles the remote key provisioning. Generates an RKP key and generates a certificate signing - * request(CSR). The generation of CSR is divided amoung multiple functions to the save the memory inside - * the Applet. The set of functions to be called sequentially in the order to complete the process of - * generating the CSR are processBeginSendData, processUpdateKey, processUpdateEekChain, - * processUpdateChallenge, processFinishSendData and getResponse. ProcessUpdateKey is called N times, where - * N is the number of keys. Similarly getResponse is called is multiple times till the client receives the - * response completely. - */ -public class RemotelyProvisionedComponentDevice { - - private static final byte TRUE = 0x01; - private static final byte FALSE = 0x00; - // RKP Version - private static final short RKP_VERSION = (short) 0x02; - // Boot params - private static final byte OS_VERSION_ID = 0x00; - private static final byte SYSTEM_PATCH_LEVEL_ID = 0x01; - private static final byte BOOT_PATCH_LEVEL_ID = 0x02; - private static final byte VENDOR_PATCH_LEVEL_ID = 0x03; - // Device Info labels - public static final byte[] BRAND = {0x62, 0x72, 0x61, 0x6E, 0x64}; - public static final byte[] MANUFACTURER = {0x6D, 0x61, 0x6E, 0x75, 0x66, 0x61, 0x63, 0x74, 0x75, - 0x72, 0x65, 0x72}; - public static final byte[] PRODUCT = {0x70, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x74}; - public static final byte[] MODEL = {0x6D, 0x6F, 0x64, 0x65, 0x6C}; - public static final byte[] DEVICE = {0x64, 0x65, 0x76, 0x69, 0x63, 0x65}; - public static final byte[] VB_STATE = {0x76, 0x62, 0x5F, 0x73, 0x74, 0x61, 0x74, 0x65}; - public static final byte[] BOOTLOADER_STATE = - {0x62, 0x6F, 0x6F, 0x74, 0x6C, 0x6F, 0x61, 0x64, 0x65, 0x72, 0x5F, 0x73, 0x74, 0x61, 0x74, - 0x65}; - public static final byte[] VB_META_DIGEST = - {0X76, 0X62, 0X6D, 0X65, 0X74, 0X61, 0X5F, 0X64, 0X69, 0X67, 0X65, 0X73, 0X74}; - public static final byte[] OS_VERSION = {0x6F, 0x73, 0x5F, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, - 0x6E}; - public static final byte[] SYSTEM_PATCH_LEVEL = - {0x73, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, - 0x76, 0x65, 0x6C}; - public static final byte[] BOOT_PATCH_LEVEL = - {0x62, 0x6F, 0x6F, 0x74, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, 0x76, 0x65, - 0x6C}; - public static final byte[] VENDOR_PATCH_LEVEL = - {0x76, 0x65, 0x6E, 0x64, 0x6F, 0x72, 0x5F, 0x70, 0x61, 0x74, 0x63, 0x68, 0x5F, 0x6C, 0x65, - 0x76, 0x65, 0x6C}; - public static final byte[] DEVICE_INFO_VERSION = - {0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E}; - public static final byte[] SECURITY_LEVEL = - {0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x5F, 0x6C, 0x65, 0x76, 0x65, 0x6C}; - public static final byte[] FUSED = - {0x66, 0x75, 0x73, 0x65, 0x64}; - // Verified boot state values - public static final byte[] VB_STATE_GREEN = {0x67, 0x72, 0x65, 0x65, 0x6E}; - public static final byte[] VB_STATE_YELLOW = {0x79, 0x65, 0x6C, 0x6C, 0x6F, 0x77}; - public static final byte[] VB_STATE_ORANGE = {0x6F, 0x72, 0x61, 0x6E, 0x67, 0x65}; - public static final byte[] VB_STATE_RED = {0x72, 0x65, 0x64}; - // Boot loader state values - public static final byte[] UNLOCKED = {0x75, 0x6E, 0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; - public static final byte[] LOCKED = {0x6C, 0x6F, 0x63, 0x6B, 0x65, 0x64}; - // Device info CDDL schema version - public static final byte DI_SCHEMA_VERSION = 2; - public static final byte[] DI_SECURITY_LEVEL = {0x73, 0x74, 0x72, 0x6F, 0x6E, 0x67, 0x62, 0x6F, - 0x78}; - private static final boolean IS_ACC_SUPPORTED_IN_RKP_SERVER = false; - private static final short MAX_SEND_DATA = 512; - - private static final byte[] google = {0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65}; - - private static final byte[] uniqueId = {0x73, 0x74, 0x72, 0x6f, 0x6e, 0x67, 0x62, 0x6f, 0x78, - 0x20, 0x6b, 0x65, 0x79, 0x6d, 0x69, 0x6e, 0x74}; // "strongbox keymint" - // more data or no data - private static final byte MORE_DATA = 0x01; // flag to denote more data to retrieve - private static final byte NO_DATA = 0x00; - // Response processing states - private static final byte START_PROCESSING = 0x00; - private static final byte PROCESSING_BCC_IN_PROGRESS = 0x02; - private static final byte PROCESSING_BCC_COMPLETE = 0x04; - private static final byte PROCESSING_ACC_IN_PROGRESS = 0x08; // Additional certificate chain. - private static final byte PROCESSING_ACC_COMPLETE = 0x0A; - // data table - private static final short DATA_SIZE = 512; - private static final short DATA_INDEX_SIZE = 11; - public static final short DATA_INDEX_ENTRY_SIZE = 4; - public static final short DATA_INDEX_ENTRY_LENGTH = 0; - public static final short DATA_INDEX_ENTRY_OFFSET = 2; - // data offsets - private static final short EPHEMERAL_MAC_KEY = 0; - private static final short TOTAL_KEYS_TO_SIGN = 1; - private static final short KEYS_TO_SIGN_COUNT = 2; - private static final short TEST_MODE = 3; - private static final short EEK_KEY = 4; - private static final short EEK_KEY_ID = 5; - private static final short CHALLENGE = 6; - private static final short GENERATE_CSR_PHASE = 7; - private static final short EPHEMERAL_PUB_KEY = 8; - private static final short RESPONSE_PROCESSING_STATE = 9; - private static final short ACC_PROCESSED_LENGTH = 10; - - // data item sizes - private static final short MAC_KEY_SIZE = 32; - private static final short SHORT_SIZE = 2; - private static final short BYTE_SIZE = 1; - private static final short TEST_MODE_SIZE = 1; - // generate csr states - private static final byte BEGIN = 0x01; - private static final byte UPDATE = 0x02; - private static final byte FINISH = 0x04; - private static final byte GET_RESPONSE = 0x06; - - //RKP mac key size - private static final short RKP_MAC_KEY_SIZE = 32; - - // variables - private byte[] data; - private KMEncoder encoder; - private KMDecoder decoder; - private KMRepository repository; - private KMSEProvider seProvider; - private KMKeymintDataStore storeDataInst; - private Object[] operation; - private short[] dataIndex; - public static Object[] authorizedEekRoots; - public short[] rkpTmpVariables; - - public RemotelyProvisionedComponentDevice(KMEncoder encoder, KMDecoder decoder, - KMRepository repository, KMSEProvider seProvider, KMKeymintDataStore storeDInst) { - this.encoder = encoder; - this.decoder = decoder; - this.repository = repository; - this.seProvider = seProvider; - this.storeDataInst = storeDInst; - rkpTmpVariables = JCSystem.makeTransientShortArray((short) 32, JCSystem.CLEAR_ON_RESET); - data = JCSystem.makeTransientByteArray(DATA_SIZE, JCSystem.CLEAR_ON_RESET); - operation = JCSystem.makeTransientObjectArray((short) 1, JCSystem.CLEAR_ON_RESET); - dataIndex = JCSystem.makeTransientShortArray((short) 1, JCSystem.CLEAR_ON_RESET); - // Initialize RKP mac key - if (!seProvider.isUpgrading()) { - short offset = repository.allocReclaimableMemory((short) RKP_MAC_KEY_SIZE); - byte[] buffer = repository.getHeap(); - seProvider.getTrueRandomNumber(buffer, offset, RKP_MAC_KEY_SIZE); - storeDataInst.createRkpMacKey(buffer, offset, RKP_MAC_KEY_SIZE); - repository.reclaimMemory(RKP_MAC_KEY_SIZE); - } - operation[0] = null; - createAuthorizedEEKRoot(); - } - - private void createAuthorizedEEKRoot() { - if (authorizedEekRoots == null) { - authorizedEekRoots = - new Object[] - { - new byte[]{ - 0x04, - (byte)0xf7, (byte)0x14, (byte)0x8a, (byte)0xdb, (byte)0x97, (byte)0xf4, - (byte)0xcc, (byte)0x53, (byte)0xef, (byte)0xd2, (byte)0x64, (byte)0x11, - (byte)0xc4, (byte)0xe3, (byte)0x75, (byte)0x1f, (byte)0x66, (byte)0x1f, - (byte)0xa4, (byte)0x71, (byte)0x0c, (byte)0x6c, (byte)0xcf, (byte)0xfa, - (byte)0x09, (byte)0x46, (byte)0x80, (byte)0x74, (byte)0x87, (byte)0x54, - (byte)0xf2, (byte)0xad, - (byte)0x5e, (byte)0x7f, (byte)0x5b, (byte)0xf6, (byte)0xec, (byte)0xe4, - (byte)0xf6, (byte)0x19, (byte)0xcc, (byte)0xff, (byte)0x13, (byte)0x37, - (byte)0xfd, (byte)0x0f, (byte)0xa1, (byte)0xc8, (byte)0x93, (byte)0xdb, - (byte)0x18, (byte)0x06, (byte)0x76, (byte)0xc4, (byte)0x5d, (byte)0xe6, - (byte)0xd7, (byte)0x6a, (byte)0x77, (byte)0x86, (byte)0xc3, (byte)0x2d, - (byte)0xaf, (byte)0x8f - }, - }; - } - } - - private void initializeDataTable() { - clearDataTable(); - releaseOperation(); - dataIndex[0] = (short) (DATA_INDEX_SIZE * DATA_INDEX_ENTRY_SIZE); - } - - private short dataAlloc(short length) { - if ((short) (dataIndex[0] + length) > (short) data.length) { - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - dataIndex[0] += length; - return (short) (dataIndex[0] - length); - } - - private void clearDataTable() { - Util.arrayFillNonAtomic(data, (short) 0, (short) data.length, (byte) 0x00); - dataIndex[0] = 0x00; - } - - private void releaseOperation() { - if (operation[0] != null) { - ((KMOperation) operation[0]).abort(); - operation[0] = null; - } - } - - private short createEntry(short index, short length) { - index = (short) (index * DATA_INDEX_ENTRY_SIZE); - short ptr = dataAlloc(length); - Util.setShort(data, index, length); - Util.setShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET), ptr); - return ptr; - } - - private short getEntry(short index) { - index = (short) (index * DATA_INDEX_ENTRY_SIZE); - return Util.getShort(data, (short) (index + DATA_INDEX_ENTRY_OFFSET)); - } - - private short getEntryLength(short index) { - index = (short) (index * DATA_INDEX_ENTRY_SIZE); - return Util.getShort(data, index); - } - - private void processGetRkpHwInfoCmd(APDU apdu) { - // Make the response - // Author name - Google. - short respPtr = KMArray.instance((short) 5); - KMArray resp = KMArray.cast(respPtr); - resp.add((short) 0, KMInteger.uint_16(KMError.OK)); - resp.add((short) 1, KMInteger.uint_16(RKP_VERSION)); - resp.add((short) 2, KMByteBlob.instance(google, (short) 0, (short) google.length)); - resp.add((short) 3, KMInteger.uint_8(KMType.RKP_CURVE_P256)); - resp.add((short) 4, KMByteBlob.instance(uniqueId, (short) 0, (short) uniqueId.length)); - KMKeymasterApplet.sendOutgoing(apdu, respPtr); - } - - /** - * This function generates an EC key pair with attest key as purpose and creates an encrypted key - * blob. It then generates a COSEMac message which includes the ECDSA public key. - */ - public void processGenerateRkpKey(APDU apdu) { - short arr = KMArray.instance((short) 1); - KMArray.cast(arr).add((short) 0, KMSimpleValue.exp()); - arr = KMKeymasterApplet.receiveIncoming(apdu, arr); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - // test mode flag. - boolean testMode = - (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 0)).getValue()); - KMKeymasterApplet.generateRkpKey(scratchPad, getEcAttestKeyParameters()); - short pubKey = KMKeymasterApplet.getPubKey(); - short coseMac0 = constructCoseMacForRkpKey(testMode, scratchPad, pubKey); - // Encode the COSE_MAC0 object - arr = KMArray.instance((short) 3); - KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(arr).add((short) 1, coseMac0); - KMArray.cast(arr).add((short) 2, KMKeymasterApplet.getPivateKey()); - KMKeymasterApplet.sendOutgoing(apdu, arr); - } - - public void processBeginSendData(APDU apdu) throws Exception { - try { - initializeDataTable(); - short arr = KMArray.instance((short) 3); - KMArray.cast(arr).add((short) 0, KMInteger.exp()); // Array length - KMArray.cast(arr).add((short) 1, KMInteger.exp()); // Total length of the encoded CoseKeys. - KMArray.cast(arr).add((short) 2, KMSimpleValue.exp()); - arr = KMKeymasterApplet.receiveIncoming(apdu, arr); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - // Generate ephemeral mac key. - short dataEntryIndex = createEntry(EPHEMERAL_MAC_KEY, MAC_KEY_SIZE); - seProvider.newRandomNumber(data, dataEntryIndex, MAC_KEY_SIZE); - // Initialize hmac operation. - initHmacOperation(); - // Partially encode CoseMac structure with partial payload. - constructPartialPubKeysToSignMac(scratchPad, - KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort(), - KMInteger.cast(KMArray.cast(arr).get((short) 1)).getShort()); - // Store the total keys in data table. - dataEntryIndex = createEntry(TOTAL_KEYS_TO_SIGN, SHORT_SIZE); - Util.setShort(data, dataEntryIndex, - KMInteger.cast(KMArray.cast(arr).get((short) 0)).getShort()); - // Store the test mode value in data table. - dataEntryIndex = createEntry(TEST_MODE, TEST_MODE_SIZE); - data[dataEntryIndex] = - (KMSimpleValue.TRUE == KMSimpleValue.cast(KMArray.cast(arr).get((short) 2)).getValue()) ? - TRUE : FALSE; - // Store the current csr status, which is BEGIN. - createEntry(GENERATE_CSR_PHASE, BYTE_SIZE); - updateState(BEGIN); - // Send response. - KMKeymasterApplet.sendResponse(apdu, KMError.OK); - } catch (Exception e) { - clearDataTable(); - releaseOperation(); - throw e; - } - } - - public void processUpdateKey(APDU apdu) throws Exception { - try { - // The prior state can be BEGIN or UPDATE - validateState((byte) (BEGIN | UPDATE)); - validateKeysToSignCount(); - short headers = KMCoseHeaders.exp(); - short arrInst = KMArray.instance((short) 4); - KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); - KMArray.cast(arrInst).add((short) 1, headers); - KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); - KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); - short arr = KMArray.exp(arrInst); - arr = KMKeymasterApplet.receiveIncoming(apdu, arr); - arrInst = KMArray.cast(arr).get((short) 0); - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - - // Validate and extract the CoseKey from CoseMac0 message. - short coseKey = validateAndExtractPublicKey(arrInst, scratchPad); - // Encode CoseKey - short length = KMKeymasterApplet.encodeToApduBuffer(coseKey, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - // Do Hmac update with input as encoded CoseKey. - ((KMOperation) operation[0]).update(scratchPad, (short) 0, length); - // Increment the count each time this function gets executed. - // Store the count in data table. - short dataEntryIndex = getEntry(KEYS_TO_SIGN_COUNT); - if (dataEntryIndex == 0) { - dataEntryIndex = createEntry(KEYS_TO_SIGN_COUNT, SHORT_SIZE); - } - length = Util.getShort(data, dataEntryIndex); - Util.setShort(data, dataEntryIndex, ++length); - // Update the csr state - updateState(UPDATE); - // Send response. - KMKeymasterApplet.sendResponse(apdu, KMError.OK); - } catch (Exception e) { - clearDataTable(); - releaseOperation(); - throw e; - } - } - - public void processUpdateEekChain(APDU apdu) throws Exception { - try { - // The prior state can be BEGIN or UPDATE - validateState((byte) (BEGIN | UPDATE)); - short headers = KMCoseHeaders.exp(); - short arrInst = KMArray.instance((short) 4); - KMArray.cast(arrInst).add((short) 0, KMByteBlob.exp()); - KMArray.cast(arrInst).add((short) 1, headers); - KMArray.cast(arrInst).add((short) 2, KMByteBlob.exp()); - KMArray.cast(arrInst).add((short) 3, KMByteBlob.exp()); - short arrSignPtr = KMArray.exp(arrInst); - arrInst = KMKeymasterApplet.receiveIncoming(apdu, arrSignPtr); - if (KMArray.cast(arrInst).length() == 0) { - KMException.throwIt(KMError.STATUS_INVALID_EEK); - } - // Re-purpose the apdu buffer as scratch pad. - byte[] scratchPad = apdu.getBuffer(); - // Validate eek chain. - short eekKey = validateAndExtractEekPub(arrInst, scratchPad); - // Store eek public key and eek id in the data table. - short eekKeyId = KMCoseKey.cast(eekKey).getKeyIdentifier(); - short dataEntryIndex = createEntry(EEK_KEY_ID, KMByteBlob.cast(eekKeyId).length()); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(eekKeyId).getBuffer(), - KMByteBlob.cast(eekKeyId).getStartOff(), - data, - dataEntryIndex, - KMByteBlob.cast(eekKeyId).length() - ); - // Convert the coseKey to a public key. - short len = KMCoseKey.cast(eekKey).getEcdsa256PublicKey(scratchPad, (short) 0); - dataEntryIndex = createEntry(EEK_KEY, len); - Util.arrayCopyNonAtomic(scratchPad, (short) 0, data, dataEntryIndex, len); - // Update the state - updateState(UPDATE); - KMKeymasterApplet.sendResponse(apdu, KMError.OK); - } catch (Exception e) { - clearDataTable(); - releaseOperation(); - throw e; - } - } - - public void processUpdateChallenge(APDU apdu) throws Exception { - try { - // The prior state can be BEGIN or UPDATE - validateState((byte) (BEGIN | UPDATE)); - short arr = KMArray.instance((short) 1); - KMArray.cast(arr).add((short) 0, KMByteBlob.exp()); - arr = KMKeymasterApplet.receiveIncoming(apdu, arr); - // Store the challenge in the data table. - short challenge = KMArray.cast(arr).get((short) 0); - short challengeLen = KMByteBlob.cast(challenge).length(); - if (challengeLen > 64) { - KMException.throwIt(KMError.INVALID_INPUT_LENGTH); - } - short dataEntryIndex = createEntry(CHALLENGE, challengeLen); - Util.arrayCopyNonAtomic( - KMByteBlob.cast(challenge).getBuffer(), - KMByteBlob.cast(challenge).getStartOff(), - data, - dataEntryIndex, - challengeLen - ); - // Update the state - updateState(UPDATE); - KMKeymasterApplet.sendResponse(apdu, KMError.OK); - } catch (Exception e) { - clearDataTable(); - releaseOperation(); - throw e; - } - } - - // This function returns pubKeysToSignMac, deviceInfo and partially constructed protected data - // wrapped inside byte blob. The partial protected data contains Headers and encrypted signedMac. - public void processFinishSendData(APDU apdu) throws Exception { - try { - // The prior state should be UPDATE. - validateState(UPDATE); - byte[] scratchPad = apdu.getBuffer(); - if (data[getEntry(TOTAL_KEYS_TO_SIGN)] != data[getEntry(KEYS_TO_SIGN_COUNT)]) { - // Mismatch in the number of keys sent. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - // PubKeysToSignMac - short empty = repository.alloc((short)0); - short len = - ((KMOperation) operation[0]).sign(repository.getHeap(), (short) empty, - (short) 0, scratchPad, (short) 0); - // release operation - releaseOperation(); - short pubKeysToSignMac = KMByteBlob.instance(scratchPad, (short) 0, len); - // Create DeviceInfo - short deviceInfo = createDeviceInfo(scratchPad); - // Generate Nonce for AES-GCM - seProvider.newRandomNumber(scratchPad, (short) 0, - KMKeymasterApplet.AES_GCM_NONCE_LENGTH); - short nonce = KMByteBlob.instance(scratchPad, (short) 0, - KMKeymasterApplet.AES_GCM_NONCE_LENGTH); - // Initializes cipher instance. - initAesGcmOperation(scratchPad, nonce); - // Encode Enc_Structure as additional data for AES-GCM. - processAesGcmUpdateAad(scratchPad); - short partialPayloadLen = processSignedMac(scratchPad, pubKeysToSignMac, deviceInfo); - short partialCipherText = KMByteBlob.instance(scratchPad, (short) 0, partialPayloadLen); - short coseEncryptProtectedHeader = getCoseEncryptProtectedHeader(scratchPad); - short coseEncryptUnProtectedHeader = getCoseEncryptUnprotectedHeader(scratchPad, nonce); - len = KMKeymasterApplet.encodeToApduBuffer(deviceInfo, scratchPad, - (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - short encodedDeviceInfo = KMByteBlob.instance(scratchPad, (short) 0, len); - updateState(FINISH); - short arr = KMArray.instance((short) 7); - KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(arr).add((short) 1, pubKeysToSignMac); - KMArray.cast(arr).add((short) 2, encodedDeviceInfo); - KMArray.cast(arr).add((short) 3, coseEncryptProtectedHeader); - KMArray.cast(arr).add((short) 4, coseEncryptUnProtectedHeader); - KMArray.cast(arr).add((short) 5, partialCipherText); - KMArray.cast(arr).add((short) 6, KMInteger.uint_8(MORE_DATA)); - KMKeymasterApplet.sendOutgoing(apdu, arr); - } catch (Exception e) { - clearDataTable(); - releaseOperation(); - throw e; - } - } - - public void processGetResponse(APDU apdu) throws Exception { - try { - // The prior state should be FINISH. - validateState((byte) (FINISH | GET_RESPONSE)); - byte[] scratchPad = apdu.getBuffer(); - short len = 0; - short recipientStructure = KMArray.instance((short) 0); - byte moreData = MORE_DATA; - byte state = getCurrentOutputProcessingState(); - switch (state) { - case START_PROCESSING: - case PROCESSING_BCC_IN_PROGRESS: - len = processBcc(scratchPad); - updateState(GET_RESPONSE); - break; - case PROCESSING_BCC_COMPLETE: - case PROCESSING_ACC_IN_PROGRESS: - len = processAdditionalCertificateChain(scratchPad); - updateState(GET_RESPONSE); - break; - case PROCESSING_ACC_COMPLETE: - recipientStructure = processRecipientStructure(scratchPad); - len = processFinalData(scratchPad); - moreData = NO_DATA; - releaseOperation(); - clearDataTable(); - break; - default: - KMException.throwIt(KMError.INVALID_STATE); - } - short data = KMByteBlob.instance(scratchPad, (short) 0, len); - short arr = KMArray.instance((short) 4); - KMArray.cast(arr).add((short) 0, KMInteger.uint_16(KMError.OK)); - KMArray.cast(arr).add((short) 1, data); - KMArray.cast(arr).add((short) 2, recipientStructure); - // represents there is more output to retrieve - KMArray.cast(arr).add((short) 3, KMInteger.uint_8(moreData)); - KMKeymasterApplet.sendOutgoing(apdu, arr); - } catch (Exception e) { - clearDataTable(); - releaseOperation(); - throw e; - } - } - - public void process(short ins, APDU apdu) throws Exception { - switch (ins) { - case KMKeymasterApplet.INS_GET_RKP_HARDWARE_INFO: - processGetRkpHwInfoCmd(apdu); - break; - case KMKeymasterApplet.INS_GENERATE_RKP_KEY_CMD: - processGenerateRkpKey(apdu); - break; - case KMKeymasterApplet.INS_BEGIN_SEND_DATA_CMD: - processBeginSendData(apdu); - break; - case KMKeymasterApplet.INS_UPDATE_KEY_CMD: - processUpdateKey(apdu); - break; - case KMKeymasterApplet.INS_UPDATE_EEK_CHAIN_CMD: - processUpdateEekChain(apdu); - break; - case KMKeymasterApplet.INS_UPDATE_CHALLENGE_CMD: - processUpdateChallenge(apdu); - break; - case KMKeymasterApplet.INS_FINISH_SEND_DATA_CMD: - processFinishSendData(apdu); - break; - case KMKeymasterApplet.INS_GET_RESPONSE_CMD: - processGetResponse(apdu); - break; - default: - ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); - } - } - - private boolean isAdditionalCertificateChainPresent() { - if (!IS_ACC_SUPPORTED_IN_RKP_SERVER || (TRUE == data[getEntry(TEST_MODE)])) { - // Don't include AdditionalCertificateChain in ProtectedData if either - // 1. RKP server does not support processing of X.509 Additional Certificate Chain. - // 2. Requested CSR for test mode. - return false; - } - return (storeDataInst.getAdditionalCertChainLength() == 0 ? false : true); - } - - private short processFinalData(byte[] scratchPad) { - // Call finish on AES GCM Cipher - short empty = repository.alloc((short) 0); - short len = - ((KMOperation) operation[0]).finish(repository.getHeap(), (short) empty, (short) 0, - scratchPad, (short) 0); - return len; - } - - private byte getCurrentOutputProcessingState() { - short index = getEntry(RESPONSE_PROCESSING_STATE); - if (index == 0) { - return START_PROCESSING; - } - return data[index]; - } - - private void updateOutputProcessingState(byte state) { - short dataEntryIndex = getEntry(RESPONSE_PROCESSING_STATE); - data[dataEntryIndex] = state; - } - - /** - * Validates the CoseMac message and extracts the CoseKey from it. - * - * @param coseMacPtr CoseMac instance to be validated. - * @param scratchPad Scratch buffer used to store temp results. - * @return CoseKey instance. - */ - private short validateAndExtractPublicKey(short coseMacPtr, byte[] scratchPad) { - boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; - // Exp for KMCoseHeaders - short coseHeadersExp = KMCoseHeaders.exp(); - // Exp for coseky - short coseKeyExp = KMCoseKey.exp(); - - // validate protected Headers - short ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET); - ptr = decoder.decode(coseHeadersExp, KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - - if (!KMCoseHeaders.cast(ptr).isDataValid(rkpTmpVariables, KMCose.COSE_ALG_HMAC_256, KMType.INVALID_VALUE)) { - KMException.throwIt(KMError.STATUS_FAILED); - } - - // Validate payload. - ptr = KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET); - ptr = decoder.decode(coseKeyExp, KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), KMByteBlob.cast(ptr).length()); - - if (!KMCoseKey.cast(ptr).isDataValid(rkpTmpVariables, KMCose.COSE_KEY_TYPE_EC2, KMType.INVALID_VALUE, - KMCose.COSE_ALG_ES256, KMType.INVALID_VALUE, KMCose.COSE_ECCURVE_256)) { - KMException.throwIt(KMError.STATUS_FAILED); - } - - boolean isTestKey = KMCoseKey.cast(ptr).isTestKey(); - if (isTestKey && !testMode) { - KMException.throwIt(KMError.STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); - } else if (!isTestKey && testMode) { - KMException.throwIt(KMError.STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); - } - - // Compute CoseMac Structure and compare the macs. - short macStructure = - KMCose.constructCoseMacStructure(KMArray.cast(coseMacPtr).get( - KMCose.COSE_MAC0_PROTECTED_PARAMS_OFFSET), - KMByteBlob.instance((short) 0), - KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_PAYLOAD_OFFSET)); - short encodedLen = KMKeymasterApplet.encodeToApduBuffer(macStructure, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - - short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, encodedLen, scratchPad, encodedLen); - - if (hmacLen != KMByteBlob.cast( - KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).length()) { - KMException.throwIt(KMError.STATUS_INVALID_MAC); - } - - if (0 != Util.arrayCompare(scratchPad, encodedLen, - KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getBuffer(), - KMByteBlob.cast(KMArray.cast(coseMacPtr).get(KMCose.COSE_MAC0_TAG_OFFSET)).getStartOff(), - hmacLen)) { - KMException.throwIt(KMError.STATUS_INVALID_MAC); - } - return ptr; - } - - - - /** - * This function validates the EEK Chain and extracts the leaf public key, which is used to - * generate shared secret using ECDH. - * - * @param eekArr EEK cert chain array pointer. - * @param scratchPad Scratch buffer used to store temp results. - * @return CoseKey instance. - */ - private short validateAndExtractEekPub(short eekArr, byte[] scratchPad) { - short leafPubKey = 0; - try { - leafPubKey = - KMKeymasterApplet.validateCertChain( - (TRUE == data[getEntry(TEST_MODE)]) ? false : true, // validate EEK root - KMCose.COSE_ALG_ES256, - KMCose.COSE_ALG_ECDH_ES_HKDF_256, - eekArr, - scratchPad, - authorizedEekRoots - ); - } catch (KMException e) { - KMException.throwIt(KMError.STATUS_INVALID_EEK); - } - return leafPubKey; - } - - private void validateKeysToSignCount() { - short index = getEntry(KEYS_TO_SIGN_COUNT); - short keysToSignCount = 0; - if (index != 0) { - keysToSignCount = Util.getShort(data, index); - } - if (Util.getShort(data, getEntry(TOTAL_KEYS_TO_SIGN)) <= keysToSignCount) { - // Mismatch in the number of keys sent. - ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED); - } - } - - private void validateState(byte expectedState) { - short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); - if (0 == (data[dataEntryIndex] & expectedState)) { - KMException.throwIt(KMError.INVALID_STATE); - } - } - - private void updateState(byte state) { - short dataEntryIndex = getEntry(GENERATE_CSR_PHASE); - if (dataEntryIndex == 0) { - KMException.throwIt(KMError.INVALID_STATE); - } - data[dataEntryIndex] = state; - } - - - /** - * This function constructs a Mac Structure, encode it and signs the encoded buffer with the - * ephemeral mac key. - */ - private void constructPartialPubKeysToSignMac(byte[] scratchPad, short arrayLength, - short encodedCoseKeysLen) { - short ptr; - short len; - short headerPtr = KMCose.constructHeaders(rkpTmpVariables, - KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - len = KMKeymasterApplet.encodeToApduBuffer(headerPtr, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); - // create MAC_Structure - ptr = - KMCose.constructCoseMacStructure(protectedHeader, - KMByteBlob.instance((short) 0), KMType.INVALID_VALUE); - // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 - len = KMKeymasterApplet.encodeToApduBuffer(ptr, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - // Construct partial payload - Bstr Header + Array Header - // The maximum combined length of bstr header and array header length is 6 bytes. - // The lengths will never exceed Max SHORT value. - short arrPtr = KMArray.instance(arrayLength); - for (short i = 0; i < arrayLength; i++) { - KMArray.cast(arrPtr).add(i, KMType.INVALID_VALUE); - } - arrayLength = encoder.getEncodedLength(arrPtr); - short bufIndex = repository.alloc((short) 6); - short partialPayloadLen = - encoder.encodeByteBlobHeader((short) (arrayLength + encodedCoseKeysLen), - repository.getHeap(), - bufIndex, (short) 3); - - partialPayloadLen += - encoder.encode(arrPtr, repository.getHeap(), (short) (bufIndex + partialPayloadLen), repository.getHeapReclaimIndex()); - Util.arrayCopyNonAtomic(repository.getHeap(), bufIndex, scratchPad, len, partialPayloadLen); - ((KMOperation) operation[0]).update(scratchPad, (short) 0, (short) (len + partialPayloadLen)); - } - - private short createSignedMac(KMDeviceUniqueKeyPair deviceUniqueKeyPair, byte[] scratchPad, - short deviceMapPtr, short pubKeysToSign) { - // Challenge - short dataEntryIndex = getEntry(CHALLENGE); - short challengePtr = KMByteBlob.instance(data, dataEntryIndex, getEntryLength(CHALLENGE)); - // Ephemeral mac key - dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); - short ephmeralMacKey = - KMByteBlob.instance(data, dataEntryIndex, getEntryLength(EPHEMERAL_MAC_KEY)); - - /* Prepare AAD */ - short aad = KMArray.instance((short) 3); - KMArray.cast(aad).add((short) 0, challengePtr); - KMArray.cast(aad).add((short) 1, deviceMapPtr); - KMArray.cast(aad).add((short) 2, pubKeysToSign); - aad = KMKeymasterApplet.encodeToApduBuffer(aad, scratchPad, - (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - aad = KMByteBlob.instance(scratchPad, (short) 0, aad); - - /* construct protected header */ - short protectedHeaders = KMCose.constructHeaders(rkpTmpVariables, - KMNInteger.uint_8(KMCose.COSE_ALG_ES256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - protectedHeaders = KMKeymasterApplet.encodeToApduBuffer(protectedHeaders, scratchPad, - (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - protectedHeaders = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaders); - - /* construct cose sign structure */ - short signStructure = - KMCose.constructCoseSignStructure(protectedHeaders, aad, ephmeralMacKey); - signStructure = KMKeymasterApplet.encodeToApduBuffer(signStructure, scratchPad, - (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - short len = - seProvider.ecSign256( - deviceUniqueKeyPair, - scratchPad, - (short) 0, - signStructure, - scratchPad, - signStructure - ); - len = KMAsn1Parser.instance(). - decodeEcdsa256Signature(KMByteBlob.instance(scratchPad, signStructure, len), scratchPad, signStructure); - signStructure = KMByteBlob.instance(scratchPad, signStructure, len); - - /* Construct unprotected headers */ - short unprotectedHeader = KMArray.instance((short) 0); - unprotectedHeader = KMCoseHeaders.instance(unprotectedHeader); - - /* construct Cose_Sign1 */ - return KMCose.constructCoseSign1(protectedHeaders, unprotectedHeader, - ephmeralMacKey, signStructure); - } - - - private KMDeviceUniqueKeyPair createDeviceUniqueKeyPair(boolean testMode, byte[] scratchPad) { - KMDeviceUniqueKeyPair deviceUniqueKeyPair; - rkpTmpVariables[0] = 0; - rkpTmpVariables[1] = 0; - if (testMode) { - seProvider.createAsymmetricKey( - KMType.EC, - scratchPad, - (short) 0, - (short) 128, - scratchPad, - (short) 128, - (short) 128, - rkpTmpVariables); - deviceUniqueKeyPair = - storeDataInst.createRkpTestDeviceUniqueKeyPair(scratchPad, (short) 128, rkpTmpVariables[1], - scratchPad, (short) 0, rkpTmpVariables[0]); - } else { - deviceUniqueKeyPair = storeDataInst.getRkpDeviceUniqueKeyPair(false); - } - return deviceUniqueKeyPair; - } - - /** - * DeviceInfo is a CBOR Map structure described by the following CDDL. - * - * DeviceInfo = { - * "brand" : tstr, - * "manufacturer" : tstr, - * "product" : tstr, - * "model" : tstr, - * "device" : tstr, - * "vb_state" : "green" / "yellow" / "orange", // Taken from the AVB values - * "bootloader_state" : "locked" / "unlocked", // Taken from the AVB values - * "vbmeta_digest": bstr, // Taken from the AVB values - * ? "os_version" : tstr, // Same as android.os.Build.VERSION.release - * "system_patch_level" : uint, // YYYYMMDD - * "boot_patch_level" : uint, //YYYYMMDD - * "vendor_patch_level" : uint, // YYYYMMDD - * "version" : 2, // TheCDDL schema version - * "security_level" : "tee" / "strongbox" - * "fused": 1 / 0, - * } - */ - private short createDeviceInfo(byte[] scratchpad) { - // Device Info Key Value pairs. - for (short i = 0; i < 32; i++) { - rkpTmpVariables[i] = KMType.INVALID_VALUE; - } - short dataOffset = 2; - rkpTmpVariables[0] = dataOffset; - rkpTmpVariables[1] = 0; - short metaOffset = 0; - updateItem(rkpTmpVariables, metaOffset, BRAND, getAttestationId(KMType.ATTESTATION_ID_BRAND, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, MANUFACTURER, - getAttestationId(KMType.ATTESTATION_ID_MANUFACTURER, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, PRODUCT, - getAttestationId(KMType.ATTESTATION_ID_PRODUCT, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, MODEL, getAttestationId(KMType.ATTESTATION_ID_MODEL, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, DEVICE, getAttestationId(KMType.ATTESTATION_ID_DEVICE, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, VB_STATE, getVbState()); - updateItem(rkpTmpVariables, metaOffset, BOOTLOADER_STATE, getBootloaderState()); - updateItem(rkpTmpVariables, metaOffset, VB_META_DIGEST, getVerifiedBootHash(scratchpad)); - updateItem(rkpTmpVariables, metaOffset, OS_VERSION, getBootParams(OS_VERSION_ID, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, SYSTEM_PATCH_LEVEL, - getBootParams(SYSTEM_PATCH_LEVEL_ID, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, BOOT_PATCH_LEVEL, getBootParams(BOOT_PATCH_LEVEL_ID, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, VENDOR_PATCH_LEVEL, - getBootParams(VENDOR_PATCH_LEVEL_ID, scratchpad)); - updateItem(rkpTmpVariables, metaOffset, DEVICE_INFO_VERSION, KMInteger.uint_8(DI_SCHEMA_VERSION)); - updateItem(rkpTmpVariables, metaOffset, SECURITY_LEVEL, - KMTextString.instance(DI_SECURITY_LEVEL, (short) 0, (short) DI_SECURITY_LEVEL.length)); - updateItem(rkpTmpVariables, metaOffset, FUSED, KMInteger.uint_8(storeDataInst.secureBootMode)); - // Create device info map. - short map = KMMap.instance(rkpTmpVariables[1]); - short mapIndex = 0; - short index = 2; - while (index < (short) 32) { - if (rkpTmpVariables[index] != KMType.INVALID_VALUE) { - KMMap.cast(map).add(mapIndex++, rkpTmpVariables[index], rkpTmpVariables[(short) (index + 1)]); - } - index += 2; - } - KMMap.cast(map).canonicalize(); - return map; - } - - // Below 6 methods are helper methods to create device info structure. - //---------------------------------------------------------------------------- - - /** - * Update the item inside the device info structure. - * - * @param deviceIds Device Info structure to be updated. - * @param metaOffset Out parameter meta information. Offset 0 is index and Offset 1 is length. - * @param item Key info to be updated. - * @param value value to be updated. - */ - private void updateItem(short[] deviceIds, short metaOffset, byte[] item, short value) { - if (KMType.INVALID_VALUE != value) { - deviceIds[deviceIds[metaOffset]++] = - KMTextString.instance(item, (short) 0, (short) item.length); - deviceIds[deviceIds[metaOffset]++] = value; - deviceIds[(short)(metaOffset+1)]++; - } - } - - private short getAttestationId(short attestId, byte[] scratchpad) { - short attIdTagLen = storeDataInst.getAttestationId(attestId, scratchpad, (short) 0); - if (attIdTagLen == 0) { - KMException.throwIt(KMError.INVALID_STATE); - } - return KMTextString.instance(scratchpad, (short) 0, attIdTagLen); - } - - private short getVerifiedBootHash(byte[] scratchPad) { - short len = storeDataInst.getVerifiedBootHash(scratchPad, (short) 0); - if (len == 0) { - KMException.throwIt(KMError.INVALID_STATE); - } - return KMByteBlob.instance(scratchPad, (short) 0, len); - } - - private short getBootloaderState() { - short bootloaderState; - if (storeDataInst.isDeviceBootLocked()) { - bootloaderState = KMTextString.instance(LOCKED, (short) 0, (short) LOCKED.length); - } else { - bootloaderState = KMTextString.instance(UNLOCKED, (short) 0, (short) UNLOCKED.length); - } - return bootloaderState; - } - - private short getVbState() { - short state = storeDataInst.getBootState(); - short vbState = KMType.INVALID_VALUE; - if (state == KMType.VERIFIED_BOOT) { - vbState = KMTextString.instance(VB_STATE_GREEN, (short) 0, (short) VB_STATE_GREEN.length); - } else if (state == KMType.SELF_SIGNED_BOOT) { - vbState = KMTextString.instance(VB_STATE_YELLOW, (short) 0, (short) VB_STATE_YELLOW.length); - } else if (state == KMType.UNVERIFIED_BOOT) { - vbState = KMTextString.instance(VB_STATE_ORANGE, (short) 0, (short) VB_STATE_ORANGE.length); - } else if (state == KMType.FAILED_BOOT) { - vbState = KMTextString.instance(VB_STATE_RED, (short) 0, (short) VB_STATE_RED.length); - } - return vbState; - } - - private short converIntegerToTextString(short intPtr, byte[] scratchPad) { - // Prepare Hex Values - short index = 1; - scratchPad[0] = 0x30; // Ascii 0 - while(index < 10) { - scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1); - index++; - } - scratchPad[index++] = 0x41; // Ascii 'A' - while(index < 16) { - scratchPad[index] = (byte) (scratchPad[(short) (index - 1)] + 1); - index++; - } - - - short intLen = KMInteger.cast(intPtr).length(); - short intOffset = KMInteger.cast(intPtr).getStartOff(); - byte[] buf = repository.getHeap(); - short tsPtr = KMTextString.instance((short) (intLen * 2)); - short tsStartOff = KMTextString.cast(tsPtr).getStartOff(); - index = 0; - byte nibble; - while (index < intLen) { - nibble = (byte) ((byte) (buf[intOffset] >> 4) & (byte) 0x0F); - buf[tsStartOff] = scratchPad[nibble]; - nibble = (byte) (buf[intOffset] & 0x0F); - buf[(short) (tsStartOff + 1)] = scratchPad[nibble]; - index++; - intOffset++; - tsStartOff += 2; - } - return tsPtr; - } - - private short getBootParams(byte bootParam, byte[] scratchPad) { - short value = KMType.INVALID_VALUE; - switch (bootParam) { - case OS_VERSION_ID: - value = storeDataInst.getOsVersion(); - break; - case SYSTEM_PATCH_LEVEL_ID: - value = storeDataInst.getOsPatch(); - break; - case BOOT_PATCH_LEVEL_ID: - value = storeDataInst.getBootPatchLevel(); - break; - case VENDOR_PATCH_LEVEL_ID: - value = storeDataInst.getVendorPatchLevel(); - break; - default: - KMException.throwIt(KMError.INVALID_ARGUMENT); - } - // Convert Integer to Text String for OS_VERSION. - if (bootParam == OS_VERSION_ID) { - value = converIntegerToTextString(value, scratchPad); - } - return value; - } - //---------------------------------------------------------------------------- - - //---------------------------------------------------------------------------- - // ECDH HKDF - private short ecdhHkdfDeriveKey(byte[] privKeyA, short privKeyAOff, short privKeyALen, - byte[] pubKeyA, - short pubKeyAOff, short pubKeyALen, byte[] pubKeyB, short pubKeyBOff, - short pubKeyBLen, byte[] scratchPad) { - short key = - seProvider.ecdhKeyAgreement(privKeyA, privKeyAOff, privKeyALen, pubKeyB, pubKeyBOff, - pubKeyBLen, scratchPad, (short) 0); - key = KMByteBlob.instance(scratchPad, (short) 0, key); - - // ignore 0x04 for ephemerical public key as kdfContext should not include 0x04. - pubKeyAOff += 1; - pubKeyALen -= 1; - pubKeyBOff += 1; - pubKeyBLen -= 1; - short kdfContext = - KMCose.constructKdfContext(pubKeyA, pubKeyAOff, pubKeyALen, pubKeyB, pubKeyBOff, pubKeyBLen, - true); - kdfContext = KMKeymasterApplet - .encodeToApduBuffer(kdfContext, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - kdfContext = KMByteBlob.instance(scratchPad, (short) 0, kdfContext); - - Util.arrayFillNonAtomic(scratchPad, (short) 0, (short) 32, (byte) 0); - seProvider.hkdf( - KMByteBlob.cast(key).getBuffer(), - KMByteBlob.cast(key).getStartOff(), - KMByteBlob.cast(key).length(), - scratchPad, - (short) 0, - (short) 32, - KMByteBlob.cast(kdfContext).getBuffer(), - KMByteBlob.cast(kdfContext).getStartOff(), - KMByteBlob.cast(kdfContext).length(), - scratchPad, - (short) 32, // offset - (short) 32 // Length of expected output. - ); - Util.arrayCopy(scratchPad, (short) 32, scratchPad, (short) 0, (short) 32); - return (short) 32; - } - - //---------------------------------------------------------------------------- - // This function returns the instance of private key and It stores the public key in the - // data table for later usage. - private short generateEphemeralEcKey(byte[] scratchPad) { - // Generate ephemeral ec key. - rkpTmpVariables[0] = 0; - rkpTmpVariables[1] = 0; - seProvider.createAsymmetricKey( - KMType.EC, - scratchPad, - (short) 0, - (short) 128, - scratchPad, - (short) 128, - (short) 128, - rkpTmpVariables); - // Copy the ephemeral private key from scratch pad - short ptr = KMByteBlob.instance(rkpTmpVariables[0]); - Util.arrayCopyNonAtomic( - scratchPad, - (short) 0, - KMByteBlob.cast(ptr).getBuffer(), - KMByteBlob.cast(ptr).getStartOff(), - rkpTmpVariables[0]); - //Store ephemeral public key in data table for later usage. - short dataEntryIndex = createEntry(EPHEMERAL_PUB_KEY, rkpTmpVariables[1]); - Util.arrayCopyNonAtomic(scratchPad, (short) 128, data, dataEntryIndex, rkpTmpVariables[1]); - return ptr; - } - - private void initHmacOperation() { - short dataEntryIndex = getEntry(EPHEMERAL_MAC_KEY); - operation[0] = - seProvider.getRkpOperation( - KMType.SIGN, - KMType.HMAC, - KMType.SHA2_256, - KMType.PADDING_NONE, - (byte) 0, - data, - dataEntryIndex, - getEntryLength(EPHEMERAL_MAC_KEY), - null, - (short) 0, - (short) 0, - (short) 0 - ); - if (operation[0] == null) { - KMException.throwIt(KMError.STATUS_FAILED); - } - } - - private void initAesGcmOperation(byte[] scratchPad, short nonce) { - // Generate Ephemeral mac key - short privKey = generateEphemeralEcKey(scratchPad); - short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); - // Generate session key - short eekIndex = getEntry(EEK_KEY); - // Generate session key - short sessionKeyLen = - ecdhHkdfDeriveKey( - KMByteBlob.cast(privKey).getBuffer(), /* Ephemeral Private Key */ - KMByteBlob.cast(privKey).getStartOff(), - KMByteBlob.cast(privKey).length(), - data, /* Ephemeral Public key */ - pubKeyIndex, - getEntryLength(EPHEMERAL_PUB_KEY), - data, /* EEK Public key */ - eekIndex, - getEntryLength(EEK_KEY), - scratchPad /* scratchpad */ - ); - // Initialize the Cipher object. - operation[0] = - seProvider.getRkpOperation( - KMType.ENCRYPT, - KMType.AES, - (byte) 0, - KMType.PADDING_NONE, - KMType.GCM, - scratchPad, /* key */ - (short) 0, - sessionKeyLen, - KMByteBlob.cast(nonce).getBuffer(), /* nonce */ - KMByteBlob.cast(nonce).getStartOff(), - KMByteBlob.cast(nonce).length(), - (short) (KMKeymasterApplet.AES_GCM_AUTH_TAG_LENGTH * 8) - ); - if (operation[0] == null) { - KMException.throwIt(KMError.STATUS_FAILED); - } - } - - private short processRecipientStructure(byte[] scratchPad) { - short protectedHeaderRecipient = KMCose.constructHeaders(rkpTmpVariables, - KMNInteger.uint_8(KMCose.COSE_ALG_ECDH_ES_HKDF_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - protectedHeaderRecipient = KMKeymasterApplet - .encodeToApduBuffer(protectedHeaderRecipient, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - protectedHeaderRecipient = KMByteBlob.instance(scratchPad, (short) 0, protectedHeaderRecipient); - - /* Construct unprotected headers */ - short pubKeyIndex = getEntry(EPHEMERAL_PUB_KEY); - // prepare cosekey - short coseKey = - KMCose.constructCoseKey(rkpTmpVariables, - KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), - KMType.INVALID_VALUE, - KMNInteger.uint_8(KMCose.COSE_ALG_ES256), - KMType.INVALID_VALUE, - KMInteger.uint_8(KMCose.COSE_ECCURVE_256), - data, - pubKeyIndex, - getEntryLength(EPHEMERAL_PUB_KEY), - KMType.INVALID_VALUE, - false - ); - short keyIdentifierPtr = KMByteBlob - .instance(data, getEntry(EEK_KEY_ID), getEntryLength(EEK_KEY_ID)); - short unprotectedHeaderRecipient = - KMCose.constructHeaders(rkpTmpVariables, KMType.INVALID_VALUE, keyIdentifierPtr, KMType.INVALID_VALUE, - coseKey); - - // Construct recipients structure. - return KMCose.constructRecipientsStructure(protectedHeaderRecipient, unprotectedHeaderRecipient, - KMSimpleValue.instance(KMSimpleValue.NULL)); - } - - private short getAdditionalCertChainProcessedLength() { - short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); - if (dataEntryIndex == 0) { - dataEntryIndex = createEntry(ACC_PROCESSED_LENGTH, SHORT_SIZE); - Util.setShort(data, dataEntryIndex, (short) 0); - return (short) 0; - } - return Util.getShort(data, dataEntryIndex); - } - - private void updateAdditionalCertChainProcessedLength(short processedLen) { - short dataEntryIndex = getEntry(ACC_PROCESSED_LENGTH); - Util.setShort(data, dataEntryIndex, processedLen); - } - - private short processAdditionalCertificateChain(byte[] scratchPad) { - byte[] persistedData = storeDataInst.getAdditionalCertChain(); - short totalAccLen = Util.getShort(persistedData, (short) 0); - if (totalAccLen == 0) { - // No Additional certificate chain present. - return 0; - } - short processedLen = getAdditionalCertChainProcessedLength(); - short lengthToSend = (short) (totalAccLen - processedLen); - if (lengthToSend > MAX_SEND_DATA) { - lengthToSend = MAX_SEND_DATA; - } - short cipherTextLen = - ((KMOperation) operation[0]).update(persistedData, (short) (2 + processedLen), lengthToSend, - scratchPad, (short) 0); - processedLen += lengthToSend; - updateAdditionalCertChainProcessedLength(processedLen); - // Update the output processing state. - updateOutputProcessingState( - (processedLen == totalAccLen) ? PROCESSING_ACC_COMPLETE : PROCESSING_ACC_IN_PROGRESS); - return cipherTextLen; - } - - // BCC for STRONGBOX has chain length of 2. So it can be returned in a single go. - private short processBcc(byte[] scratchPad) { - // Construct BCC - boolean testMode = (TRUE == data[getEntry(TEST_MODE)]) ? true : false; - short len; - if (testMode) { - short bcc = KMKeymasterApplet.generateBcc(true, scratchPad); - len = KMKeymasterApplet - .encodeToApduBuffer(bcc, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - } else { - byte[] bcc = storeDataInst.getBootCertificateChain(); - len = Util.getShort(bcc, (short) 0); - Util.arrayCopyNonAtomic(bcc, (short) 2, scratchPad, (short) 0, len); - } - short cipherTextLen = ((KMOperation) operation[0]) - .update(scratchPad, (short) 0, len, scratchPad, len); - // move cipher text on scratch pad from starting position. - Util.arrayCopyNonAtomic(scratchPad, len, scratchPad, (short) 0, cipherTextLen); - createEntry(RESPONSE_PROCESSING_STATE, BYTE_SIZE); - // If there is no additional certificate chain present then put the state to - // PROCESSING_ACC_COMPLETE. - updateOutputProcessingState( - isAdditionalCertificateChainPresent() ? PROCESSING_BCC_COMPLETE : PROCESSING_ACC_COMPLETE); - return cipherTextLen; - } - - // AAD is the CoseEncrypt structure - private void processAesGcmUpdateAad(byte[] scratchPad) { - short protectedHeader = KMCose.constructHeaders(rkpTmpVariables, - KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - protectedHeader = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); - short coseEncryptStr = - KMCose.constructCoseEncryptStructure(protectedHeader, KMByteBlob.instance((short) 0)); - coseEncryptStr = KMKeymasterApplet.encodeToApduBuffer(coseEncryptStr, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - ((KMOperation) operation[0]).updateAAD(scratchPad, (short) 0, coseEncryptStr); - } - - private short processSignedMac(byte[] scratchPad, short pubKeysToSignMac, short deviceInfo) { - // Construct SignedMac - KMDeviceUniqueKeyPair deviceUniqueKeyPair = - createDeviceUniqueKeyPair((TRUE == data[getEntry(TEST_MODE)]) ? true : false, scratchPad); - // Create signedMac - short signedMac = createSignedMac(deviceUniqueKeyPair, scratchPad, deviceInfo, pubKeysToSignMac); - //Prepare partial data for encryption. - short arrLength = (short) (isAdditionalCertificateChainPresent() ? 3 : 2); - short arr = KMArray.instance(arrLength); - KMArray.cast(arr).add((short) 0, signedMac); - KMArray.cast(arr).add((short) 1, KMType.INVALID_VALUE); - if (arrLength == 3) { - KMArray.cast(arr).add((short) 2, KMType.INVALID_VALUE); - } - short len = KMKeymasterApplet - .encodeToApduBuffer(arr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - short cipherTextLen = ((KMOperation) operation[0]) - .update(scratchPad, (short) 0, len, scratchPad, len); - Util.arrayCopyNonAtomic( - scratchPad, - len, - scratchPad, - (short) 0, - cipherTextLen - ); - return cipherTextLen; - } - - private short getCoseEncryptProtectedHeader(byte[] scratchPad) { - // CoseEncrypt protected headers. - short protectedHeader = KMCose.constructHeaders(rkpTmpVariables, - KMInteger.uint_8(KMCose.COSE_ALG_AES_GCM_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - protectedHeader = KMKeymasterApplet.encodeToApduBuffer(protectedHeader, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - return KMByteBlob.instance(scratchPad, (short) 0, protectedHeader); - } - - private short getCoseEncryptUnprotectedHeader(byte[] scratchPad, short nonce) { - /* CoseEncrypt unprotected headers */ - return KMCose - .constructHeaders(rkpTmpVariables, KMType.INVALID_VALUE, KMType.INVALID_VALUE, nonce, KMType.INVALID_VALUE); - } - - private short constructCoseMacForRkpKey(boolean testMode, byte[] scratchPad, short pubKey) { - // prepare cosekey - short coseKey = - KMCose.constructCoseKey(rkpTmpVariables, - KMInteger.uint_8(KMCose.COSE_KEY_TYPE_EC2), - KMType.INVALID_VALUE, - KMNInteger.uint_8(KMCose.COSE_ALG_ES256), - KMType.INVALID_VALUE, - KMInteger.uint_8(KMCose.COSE_ECCURVE_256), - KMByteBlob.cast(pubKey).getBuffer(), - KMByteBlob.cast(pubKey).getStartOff(), - KMByteBlob.cast(pubKey).length(), - KMType.INVALID_VALUE, - testMode); - // Encode the cose key and make it as payload. - short len = KMKeymasterApplet - .encodeToApduBuffer(coseKey, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - short payload = KMByteBlob.instance(scratchPad, (short) 0, len); - // Prepare protected header, which is required to construct the COSE_MAC0 - short headerPtr = KMCose.constructHeaders(rkpTmpVariables, - KMInteger.uint_8(KMCose.COSE_ALG_HMAC_256), - KMType.INVALID_VALUE, - KMType.INVALID_VALUE, - KMType.INVALID_VALUE); - // Encode the protected header as byte blob. - len = KMKeymasterApplet - .encodeToApduBuffer(headerPtr, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - short protectedHeader = KMByteBlob.instance(scratchPad, (short) 0, len); - // create MAC_Structure - short macStructure = - KMCose.constructCoseMacStructure(protectedHeader, KMByteBlob.instance((short) 0), payload); - // Encode the Mac_structure and do HMAC_Sign to produce the tag for COSE_MAC0 - len = KMKeymasterApplet.encodeToApduBuffer(macStructure, scratchPad, (short) 0, - KMKeymasterApplet.MAX_COSE_BUF_SIZE); - // HMAC Sign. - short hmacLen = rkpHmacSign(testMode, scratchPad, (short) 0, len, scratchPad, len); - // Create COSE_MAC0 object - short coseMac0 = - KMCose - .constructCoseMac0(protectedHeader, KMCoseHeaders.instance(KMArray.instance((short) 0)), - payload, - KMByteBlob.instance(scratchPad, len, hmacLen)); - len = KMKeymasterApplet - .encodeToApduBuffer(coseMac0, scratchPad, (short) 0, KMKeymasterApplet.MAX_COSE_BUF_SIZE); - return KMByteBlob.instance(scratchPad, (short) 0, len); - } - - private short getEcAttestKeyParameters() { - short tagIndex = 0; - short arrPtr = KMArray.instance((short) 6); - // Key size - 256 - short keySize = KMIntegerTag - .instance(KMType.UINT_TAG, KMType.KEYSIZE, KMInteger.uint_16((short) 256)); - // Digest - SHA256 - short byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.SHA2_256); - short digest = KMEnumArrayTag.instance(KMType.DIGEST, byteBlob); - // Purpose - Attest - byteBlob = KMByteBlob.instance((short) 1); - KMByteBlob.cast(byteBlob).add((short) 0, KMType.ATTEST_KEY); - short purpose = KMEnumArrayTag.instance(KMType.PURPOSE, byteBlob); - - KMArray.cast(arrPtr).add(tagIndex++, purpose); - // Algorithm - EC - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ALGORITHM, KMType.EC)); - KMArray.cast(arrPtr).add(tagIndex++, keySize); - KMArray.cast(arrPtr).add(tagIndex++, digest); - // Curve - P256 - KMArray.cast(arrPtr).add(tagIndex++, KMEnumTag.instance(KMType.ECCURVE, KMType.P_256)); - // No Authentication is required to use this key. - KMArray.cast(arrPtr).add(tagIndex, KMBoolTag.instance(KMType.NO_AUTH_REQUIRED)); - return KMKeyParameters.instance(arrPtr); - } - - private boolean isSignedByte(byte b) { - return ((b & 0x0080) != 0); - } - - private short writeIntegerHeader(short valueLen, byte[] data, short offset) { - // write length - data[offset] = (byte) valueLen; - // write INTEGER tag - offset--; - data[offset] = 0x02; - return offset; - } - - private short writeSequenceHeader(short valueLen, byte[] data, short offset) { - // write length - data[offset] = (byte) valueLen; - // write INTEGER tag - offset--; - data[offset] = 0x30; - return offset; - } - - private short writeSignatureData(byte[] input, short inputOff, short inputlen, byte[] output, short offset) { - Util.arrayCopyNonAtomic(input, inputOff, output, offset, inputlen); - if (isSignedByte(input[inputOff])) { - offset--; - output[offset] = (byte) 0; - } - return offset; - } - - public short encodeES256CoseSignSignature(byte[] input, short offset, short len, byte[] scratchPad, short scratchPadOff) { - // SEQ [ INTEGER(r), INTEGER(s)] - // write from bottom to the top - if (len != 64) { - KMException.throwIt(KMError.INVALID_DATA); - } - short maxTotalLen = 72; - short end = (short) (scratchPadOff + maxTotalLen); - // write s. - short start = (short) (end - 32); - start = writeSignatureData(input, (short) (offset + 32), (short) 32, scratchPad, start); - // write length and header - short length = (short) (end - start); - start--; - start = writeIntegerHeader(length, scratchPad, start); - // write r - short rEnd = start; - start = (short) (start - 32); - start = writeSignatureData(input, offset, (short) 32, scratchPad, start); - // write length and header - length = (short) (rEnd - start); - start--; - start = writeIntegerHeader(length, scratchPad, start); - // write length and sequence header - length = (short) (end - start); - start--; - start = writeSequenceHeader(length, scratchPad, start); - length = (short) (end - start); - if (start > scratchPadOff) { - // re adjust the buffer - Util.arrayCopyNonAtomic(scratchPad, start, scratchPad, scratchPadOff, length); - } - return length; - } - - private short rkpHmacSign(boolean testMode, byte[] data, short dataStart, short dataLength, byte[] signature, - short signatureStart) { - short result; - if(testMode) { - short macKey = KMByteBlob.instance(MAC_KEY_SIZE); - Util.arrayFillNonAtomic(KMByteBlob.cast(macKey).getBuffer(), - KMByteBlob.cast(macKey).getStartOff(), MAC_KEY_SIZE, (byte) 0); - result = seProvider.hmacSign(KMByteBlob.cast(macKey).getBuffer(), KMByteBlob.cast(macKey).getStartOff(), MAC_KEY_SIZE, data, dataStart, dataLength, signature, signatureStart); - } else { - result = seProvider.hmacSign(storeDataInst.getRkpMacKey(), data, dataStart, dataLength, signature, signatureStart); - } - return result; - } - -} diff --git a/HAL/Android.bp b/HAL/Android.bp index c64fdcdd..514f5b05 100644 --- a/HAL/Android.bp +++ b/HAL/Android.bp @@ -17,13 +17,11 @@ cc_library { name: "libjc_keymint", defaults: [ "keymaster_defaults", - "keymint_use_latest_hal_aidl_ndk_shared", ], srcs: [ "CborConverter.cpp", "JavacardKeyMintDevice.cpp", "JavacardKeyMintOperation.cpp", - "JavacardRemotelyProvisionedComponentDevice.cpp", "JavacardSecureElement.cpp", "JavacardSharedSecret.cpp", "JavacardKeyMintUtils.cpp", @@ -33,6 +31,7 @@ cc_library { shared_libs: [ "android.hardware.security.secureclock-V1-ndk", "android.hardware.security.sharedsecret-V1-ndk", + "android.hardware.security.keymint-V2-ndk", "libbase", "libcppbor_external", "libkeymaster_portable", @@ -83,11 +82,9 @@ cc_binary { "-Wall", "-Wextra", ], - defaults: [ - "keymint_use_latest_hal_aidl_ndk_shared", - ], shared_libs: [ "android.hardware.security.sharedsecret-V1-ndk", + "android.hardware.security.keymint-V2-ndk", "libbase", "libbinder_ndk", "libcppbor_external", diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp index 09443735..93da076c 100644 --- a/HAL/JavacardKeyMintDevice.cpp +++ b/HAL/JavacardKeyMintDevice.cpp @@ -101,6 +101,14 @@ ScopedAStatus JavacardKeyMintDevice::generateKey(const vector& key creationResult->keyCharacteristics = std::move(optKeyChars.value()); creationResult->certificateChain = std::move(optCertChain.value()); creationResult->keyBlob = std::move(optKeyBlob.value()); + if (isFactoryAttestationCertMode(keyParams, attestationKey)) { + // Get provisioned attestation certificate chain. + err = getProvisionedAttestationCertChain(creationResult->certificateChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getting Provisioned attestation certificate chain."; + return km_utils::kmError2ScopedAStatus(err); + } + } return ScopedAStatus::ok(); } @@ -146,6 +154,14 @@ ScopedAStatus JavacardKeyMintDevice::importKey(const vector& keyPa creationResult->keyCharacteristics = std::move(optKeyChars.value()); creationResult->certificateChain = std::move(optCertChain.value()); creationResult->keyBlob = std::move(optKeyBlob.value()); + if (isFactoryAttestationCertMode(keyParams, attestationKey)) { + // Get provisioned attestation certificate chain. + err = getProvisionedAttestationCertChain(creationResult->certificateChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getting Provisioned attestation certificate chain."; + return km_utils::kmError2ScopedAStatus(err); + } + } return ScopedAStatus::ok(); } @@ -450,4 +466,39 @@ ScopedAStatus JavacardKeyMintDevice::sendRootOfTrust(const vector& root return ScopedAStatus::ok(); } +keymaster_error_t +JavacardKeyMintDevice::getProvisionedAttestationCertChain(std::vector& certChain) { + auto [item, err] = card_->sendRequest(Instruction::INS_GET_CERT_CHAIN_CMD); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getProvisionedAttestationCertChain."; + return err; + } + auto optChain = cbor_.getByteArrayVec(item, 1); + if (!optChain) { + LOG(ERROR) << "Error in getProvisionedAttestationCertChain() while getting cert chain from parsed cbor item."; + return KM_ERROR_UNKNOWN_ERROR; + } + err = km_utils::getCertificateChain(*optChain, certChain); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Error in getCertificateChain."; + return err; + } + return KM_ERROR_OK; +} + +bool +JavacardKeyMintDevice::isFactoryAttestationCertMode(const vector& keyParams, const optional& attestationKey) { + AuthorizationSet authSet((KmParamSet(keyParams))); + keymaster_algorithm_t algorithm; + authSet.GetTagValue(TAG_ALGORITHM, &algorithm); + if (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC) { + if (!attestationKey || attestationKey->keyBlob.empty()) { + if (authSet.Contains(TAG_ATTESTATION_CHALLENGE)) { + return true; + } + } + } + return false; +} + } // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintDevice.h b/HAL/JavacardKeyMintDevice.h index d056a536..57e81aa9 100644 --- a/HAL/JavacardKeyMintDevice.h +++ b/HAL/JavacardKeyMintDevice.h @@ -115,6 +115,10 @@ class JavacardKeyMintDevice : public BnKeyMintDevice { ScopedAStatus defaultHwInfo(KeyMintHardwareInfo* info); + keymaster_error_t getProvisionedAttestationCertChain(std::vector& certChain); + + bool isFactoryAttestationCertMode(const vector& keyParams, const optional& attestationKey); + const SecurityLevel securitylevel_; const shared_ptr card_; CborConverter cbor_; diff --git a/HAL/JavacardKeyMintUtils.cpp b/HAL/JavacardKeyMintUtils.cpp index b6ec44f6..a6fc07d6 100644 --- a/HAL/JavacardKeyMintUtils.cpp +++ b/HAL/JavacardKeyMintUtils.cpp @@ -22,6 +22,10 @@ namespace aidl::android::hardware::security::keymint::km_utils { +constexpr uint32_t TAG_SEQUENCE = 0x30; +constexpr uint32_t LENGTH_MASK = 0x80; +constexpr uint32_t LENGTH_VALUE_MASK = 0x7F; + keymaster_key_param_t kInvalidTag{.tag = KM_TAG_INVALID, .integer = 0}; KeyParameter kmEnumParam2Aidl(const keymaster_key_param_t& param) { @@ -240,4 +244,56 @@ keymaster_key_param_set_t aidlKeyParams2Km(const vector& keyParams return set; } +keymaster_error_t +getCertificateChain(std::vector& chainBuffer, std::vector& certChain) { + uint8_t *data = chainBuffer.data(); + int index = 0; + uint32_t length = 0; + while (index < chainBuffer.size()) { + std::vector temp; + if(data[index] == TAG_SEQUENCE) { + // Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length. + if (0 == (data[index+1] & LENGTH_MASK)) { + length = (uint32_t)data[index]; + //Add SEQ and Length fields + length += 2; + } else { + // Long form. Two to 127 octets. Bit 8 of first octet has value "1" and + // bits 7-1 give the number of additional length octets. Second and following + // octets give the actual length. + int additionalBytes = data[index+1] & LENGTH_VALUE_MASK; + if (additionalBytes == 0x01) { + length = data[index+2]; + //Add SEQ and Length fields + length += 3; + } else if (additionalBytes == 0x02) { + length = (data[index+2] << 8 | data[index+3]); + //Add SEQ and Length fields + length += 4; + } else if (additionalBytes == 0x04) { + length = data[index+2] << 24; + length |= data[index+3] << 16; + length |= data[index+4] << 8; + length |= data[index+5]; + //Add SEQ and Length fields + length += 6; + } else { + //Length is larger than uint32_t max limit. + return KM_ERROR_UNKNOWN_ERROR; + } + } + temp.insert(temp.end(), (data+index), (data+index+length)); + index += length; + Certificate certificate; + certificate.encodedCertificate = std::move(temp); + certChain.push_back(std::move(certificate)); + } else { + //SEQUENCE TAG MISSING. + return KM_ERROR_UNKNOWN_ERROR; + } + } + return KM_ERROR_OK; +} + + } // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardKeyMintUtils.h b/HAL/JavacardKeyMintUtils.h index 9545df63..48c01f31 100644 --- a/HAL/JavacardKeyMintUtils.h +++ b/HAL/JavacardKeyMintUtils.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,9 @@ keymaster_error_t legacyHardwareAuthToken(const HardwareAuthToken& aidlToken, keymaster_error_t encodeTimestampToken(const TimeStampToken& timestampToken, vector* encodedToken); +keymaster_error_t getCertificateChain(std::vector& chainBuffer, + std::vector& certChain); + inline ScopedAStatus kmError2ScopedAStatus(const keymaster_error_t value) { return (value == KM_ERROR_OK ? ScopedAStatus::ok() diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp b/HAL/JavacardRemotelyProvisionedComponentDevice.cpp deleted file mode 100644 index ac5a7497..00000000 --- a/HAL/JavacardRemotelyProvisionedComponentDevice.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2021, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "javacard.keymint.device.rkp.strongbox-impl" - -#include "JavacardRemotelyProvisionedComponentDevice.h" - -#include - -#include -#include -#include - -#include "JavacardKeyMintUtils.h" - -namespace aidl::android::hardware::security::keymint { -using namespace cppcose; -using namespace keymaster; -using namespace cppbor; -// RKP error codes defined in keymint applet. -constexpr keymaster_error_t kStatusFailed = static_cast(32000); -constexpr keymaster_error_t kStatusInvalidMac = static_cast(32001); -constexpr keymaster_error_t kStatusProductionKeyInTestRequest = static_cast(32002); -constexpr keymaster_error_t kStatusTestKeyInProductionRequest = static_cast(32003); -constexpr keymaster_error_t kStatusInvalidEek = static_cast(32004); -constexpr keymaster_error_t kStatusInvalidState = static_cast(32005); - -namespace { - -keymaster_error_t translateRkpErrorCode(keymaster_error_t error) { - switch(static_cast(-error)) { - case kStatusFailed: - case kStatusInvalidState: - return static_cast(BnRemotelyProvisionedComponent::STATUS_FAILED); - case kStatusInvalidMac: - return static_cast(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC); - case kStatusProductionKeyInTestRequest: - return static_cast(BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST); - case kStatusTestKeyInProductionRequest: - return static_cast(BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST); - case kStatusInvalidEek: - return static_cast(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK); - } - return error; -} - -ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) { - info->versionNumber = 2; - info->rpcAuthorName = "Google"; - info->supportedEekCurve = RpcHardwareInfo::CURVE_P256; - info->uniqueId = "strongbox keymint"; - return ScopedAStatus::ok(); -} - -uint32_t coseKeyEncodedSize(const std::vector& keysToSign) { - uint32_t size = 0; - for(auto& macKey : keysToSign) { - auto [macedKeyItem, _, coseMacErrMsg] = - cppbor::parse(macKey.macedKey); - if (!macedKeyItem || !macedKeyItem->asArray() || - macedKeyItem->asArray()->size() != kCoseMac0EntryCount) { - LOG(ERROR) << "Invalid COSE_Mac0 structure"; - return 0; - } - auto payload = macedKeyItem->asArray()->get(kCoseMac0Payload)->asBstr(); - if (!payload) return 0; - size += payload->value().size(); - } - return size; -} - -} // namespace - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) { - auto [item, err] = card_->sendRequest(Instruction::INS_GET_RKP_HARDWARE_INFO); - std::optional optVersionNumber; - std::optional optSupportedEekCurve; - std::optional optRpcAuthorName; - std::optional optUniqueId; - if (err != KM_ERROR_OK || - !(optVersionNumber = cbor_.getUint64(item, 1)) || - !(optRpcAuthorName = cbor_.getByteArrayStr(item, 2)) || - !(optSupportedEekCurve = cbor_.getUint64(item, 3)) || - !(optUniqueId = cbor_.getByteArrayStr(item, 4))) { - LOG(ERROR) << "Error in response of getHardwareInfo."; - LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; - return defaultHwInfo(info); - } - info->rpcAuthorName = std::move(optRpcAuthorName.value()); - info->versionNumber = static_cast(std::move(optVersionNumber.value())); - info->supportedEekCurve = static_cast(std::move(optSupportedEekCurve.value())); - info->uniqueId = std::move(optUniqueId.value()); - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(bool testMode, - MacedPublicKey* macedPublicKey, - std::vector* privateKeyHandle) { - cppbor::Array array; - array.add(testMode); - auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_RKP_KEY_CMD, array); - if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in sending generateEcdsaP256KeyPair."; - return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); - } - std::optional> optMacedKey; - std::optional> optPKeyHandle; - if (!(optMacedKey = cbor_.getByteArrayVec(item, 1)) || - !(optPKeyHandle = cbor_.getByteArrayVec(item, 2))) { - LOG(ERROR) << "Error in decoding og response in generateEcdsaP256KeyPair."; - return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); - } - *privateKeyHandle = std::move(optPKeyHandle.value()); - macedPublicKey->macedKey = std::move(optMacedKey.value()); - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::beginSendData( - bool testMode, const std::vector& keysToSign) { - uint32_t totalEncodedSize = coseKeyEncodedSize(keysToSign); - cppbor::Array array; - array.add(keysToSign.size()); - array.add(totalEncodedSize); - array.add(testMode); - auto [_, err] = card_->sendRequest(Instruction::INS_BEGIN_SEND_DATA_CMD, array); - if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in beginSendData."; - return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); - } - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::updateMacedKey( - const std::vector& keysToSign) { - for(auto& macedPublicKey : keysToSign) { - cppbor::Array array; - array.add(EncodedItem(macedPublicKey.macedKey)); - auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_KEY_CMD, array); - if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in updateMacedKey."; - return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); - } - } - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::updateChallenge( - const std::vector& challenge) { - Array array; - array.add(challenge); - auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_CHALLENGE_CMD, array); - if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in updateChallenge."; - return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); - } - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::updateEEK( - const std::vector& endpointEncCertChain) { - std::vector eekChain = endpointEncCertChain; - auto [_, err] = card_->sendRequest(Instruction::INS_UPDATE_EEK_CHAIN_CMD, eekChain); - if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in updateEEK."; - return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); - } - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::finishSendData( - std::vector* keysToSignMac, DeviceInfo* deviceInfo, - std::vector& coseEncryptProtectedHeader, cppbor::Map& coseEncryptUnProtectedHeader, - std::vector& partialCipheredData, uint32_t& respFlag) { - - auto [item, err] = card_->sendRequest(Instruction::INS_FINISH_SEND_DATA_CMD); - if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in finishSendData."; - return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); - } - auto optDecodedKeysToSignMac = cbor_.getByteArrayVec(item, 1); - auto optDecodedDeviceInfo = cbor_.getByteArrayVec(item, 2); - auto optCEncryptProtectedHeader = cbor_.getByteArrayVec(item, 3); - auto optCEncryptUnProtectedHeader = cbor_.getMapItem(item, 4); - auto optPCipheredData = cbor_.getByteArrayVec(item, 5); - auto optRespFlag = cbor_.getUint64(item, 6); - if (!optDecodedKeysToSignMac || !optDecodedDeviceInfo || - !optCEncryptProtectedHeader || !optCEncryptUnProtectedHeader || - !optPCipheredData || !optRespFlag) { - LOG(ERROR) << "Error in decoding og response in finishSendData."; - return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); - } - *keysToSignMac = std::move(optDecodedKeysToSignMac.value()); - deviceInfo->deviceInfo = std::move(optDecodedDeviceInfo.value()); - coseEncryptProtectedHeader = std::move(optCEncryptProtectedHeader.value()); - coseEncryptUnProtectedHeader = std::move(optCEncryptUnProtectedHeader.value()); - partialCipheredData.insert(partialCipheredData.end(), optPCipheredData->begin(), optPCipheredData->end()); - respFlag = std::move(optRespFlag.value()); - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::getResponse( - std::vector& partialCipheredData, cppbor::Array& recepientStructure, - uint32_t& respFlag) { - auto [item, err] = card_->sendRequest(Instruction::INS_GET_RESPONSE_CMD); - if (err != KM_ERROR_OK) { - LOG(ERROR) << "Error in getResponse."; - return km_utils::kmError2ScopedAStatus(translateRkpErrorCode(err)); - } - auto optPCipheredData = cbor_.getByteArrayVec(item, 1); - auto optArray = cbor_.getArrayItem(item, 2); - auto optRespFlag = cbor_.getUint64(item, 3); - if (!optPCipheredData || !optArray || !optRespFlag) { - LOG(ERROR) << "Error in decoding og response in getResponse."; - return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); - } - recepientStructure = std::move(optArray.value()); - partialCipheredData.insert(partialCipheredData.end(), optPCipheredData->begin(), optPCipheredData->end()); - respFlag = std::move(optRespFlag.value()); - return ScopedAStatus::ok(); -} - -ScopedAStatus -JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest(bool testMode, - const std::vector& keysToSign, - const std::vector& endpointEncCertChain, - const std::vector& challenge, - DeviceInfo* deviceInfo, ProtectedData* protectedData, - std::vector* keysToSignMac) { - std::vector coseEncryptProtectedHeader; - cppbor::Map coseEncryptUnProtectedHeader; - cppbor::Array recipients; - std::vector cipheredData; - uint32_t respFlag; - auto ret = beginSendData(testMode, keysToSign); - if (!ret.isOk()) return ret; - - ret = updateMacedKey(keysToSign); - if (!ret.isOk()) return ret; - - ret = updateChallenge(challenge); - if (!ret.isOk()) return ret; - - ret = updateEEK(endpointEncCertChain); - if (!ret.isOk()) return ret; - - ret = finishSendData(keysToSignMac, deviceInfo, coseEncryptProtectedHeader, - coseEncryptUnProtectedHeader, cipheredData, - respFlag); - if (!ret.isOk()) return ret; - - while (respFlag != 0) { // more data is pending to receive - ret = getResponse(cipheredData, recipients, respFlag); - if (!ret.isOk()) return ret; - } - // Create ConseEncrypt structure. - protectedData->protectedData = - cppbor::Array() - .add(coseEncryptProtectedHeader) // Protected - .add(std::move(coseEncryptUnProtectedHeader)) // Unprotected - .add(cipheredData) // Payload - .add(std::move(recipients)) - .encode(); - return ScopedAStatus::ok(); -} - -} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/JavacardRemotelyProvisionedComponentDevice.h b/HAL/JavacardRemotelyProvisionedComponentDevice.h deleted file mode 100644 index 99b939a2..00000000 --- a/HAL/JavacardRemotelyProvisionedComponentDevice.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2021, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#include "CborConverter.h" -#include "JavacardSecureElement.h" - -namespace aidl::android::hardware::security::keymint { -using namespace ::keymint::javacard; -using ndk::ScopedAStatus; - -class JavacardRemotelyProvisionedComponentDevice - : public BnRemotelyProvisionedComponent { - public: - explicit JavacardRemotelyProvisionedComponentDevice( - shared_ptr card) - : card_(card) {} - - virtual ~JavacardRemotelyProvisionedComponentDevice() = default; - - ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override; - - ScopedAStatus generateEcdsaP256KeyPair( - bool testMode, MacedPublicKey* macedPublicKey, - std::vector* privateKeyHandle) override; - - ScopedAStatus generateCertificateRequest( - bool testMode, const std::vector& keysToSign, - const std::vector& endpointEncCertChain, - const std::vector& challenge, DeviceInfo* deviceInfo, - ProtectedData* protectedData, - std::vector* keysToSignMac) override; - - private: - ScopedAStatus beginSendData(bool testMode, - const std::vector& keysToSign); - - ScopedAStatus updateMacedKey(const std::vector& keysToSign); - - ScopedAStatus updateChallenge(const std::vector& challenge); - - ScopedAStatus updateEEK(const std::vector& endpointEncCertChain); - - ScopedAStatus finishSendData(std::vector* keysToSignMac, - DeviceInfo* deviceInfo, - std::vector& coseEncryptProtectedHeader, - cppbor::Map& coseEncryptUnProtectedHeader, - std::vector& partialCipheredData, - uint32_t& respFlag); - - ScopedAStatus getResponse(std::vector& partialCipheredData, - cppbor::Array& recepientStructure, - uint32_t& respFlag); - std::shared_ptr card_; - CborConverter cbor_; -}; - -} // namespace aidl::android::hardware::security::keymint diff --git a/HAL/android.hardware.security.keymint-service.strongbox.xml b/HAL/android.hardware.security.keymint-service.strongbox.xml index 0631f129..ba52aadd 100644 --- a/HAL/android.hardware.security.keymint-service.strongbox.xml +++ b/HAL/android.hardware.security.keymint-service.strongbox.xml @@ -3,8 +3,4 @@ android.hardware.security.keymint IKeyMintDevice/strongbox - - android.hardware.security.keymint - IRemotelyProvisionedComponent/strongbox - diff --git a/HAL/service.cpp b/HAL/service.cpp index 508486c6..7b547466 100644 --- a/HAL/service.cpp +++ b/HAL/service.cpp @@ -26,7 +26,6 @@ #include "JavacardKeyMintDevice.h" #include "JavacardSecureElement.h" #include "JavacardSharedSecret.h" -#include "JavacardRemotelyProvisionedComponentDevice.h" #include "keymint_utils.h" #include "OmapiTransport.h" #include "SocketTransport.h" @@ -81,8 +80,6 @@ int main() { addService(card); // Add Shared Secret Service addService(card); - // Add Remotely Provisioned Component Service - addService(card); ABinderProcess_joinThreadPool(); return EXIT_FAILURE; // should not reach diff --git a/ProvisioningTool/include/constants.h b/ProvisioningTool/include/constants.h index b7682872..092c8de3 100644 --- a/ProvisioningTool/include/constants.h +++ b/ProvisioningTool/include/constants.h @@ -80,7 +80,7 @@ constexpr uint64_t kTagAttestationIdModel = 2415919821u; constexpr uint64_t kCurveP256 = 1; constexpr uint64_t kAlgorithmEc = 3; constexpr uint64_t kDigestSha256 = 4; -constexpr uint64_t kPurposeAttest = 0x7F; +constexpr uint64_t kPurposeAttest = 7; constexpr uint64_t kPurposeVerify = 3; constexpr uint64_t kKeyFormatRaw = 3; @@ -102,6 +102,8 @@ constexpr char kUnLockProvision[] = "unlock_provision"; constexpr char kSecureBootMode[] = "secure_boot_mode"; // Instruction constatnts +constexpr int kAttestationKeyCmd = INS_BEGIN_KM_CMD + 1; +constexpr int kAttestCertDataCmd = INS_BEGIN_KM_CMD + 2; constexpr int kAttestationIdsCmd = INS_BEGIN_KM_CMD + 3; constexpr int kPresharedSecretCmd = INS_BEGIN_KM_CMD + 15; constexpr int kBootParamsCmd = INS_BEGIN_KM_CMD + 16; diff --git a/ProvisioningTool/sample_json_keymint_cf.txt b/ProvisioningTool/sample_json_keymint_cf.txt index a046baef..1242b19b 100644 --- a/ProvisioningTool/sample_json_keymint_cf.txt +++ b/ProvisioningTool/sample_json_keymint_cf.txt @@ -17,15 +17,12 @@ "boot_state": 2, "device_locked": 1 }, - "device_unique_key": "test_resources/batch_key.der", - "signer_info": { - "signer_name": "Google", - "signing_keys": [ - "test_resources/ca_cert.der", - "test_resources/intermediate_cert.der", - "test_resources/batch_cert.der" - ] - }, + "attest_key": "test_resources/attest_key.der", + "attest_cert_chain": [ + "test_resources/attest_cert.der", + "test_resources/intermediate_cert.der", + "test_resources/ca_cert.der" + ], "oem_root_key": "test_resources/oem_root_key.der", "secure_boot_mode": 0 } diff --git a/ProvisioningTool/sample_json_keymint_gf.txt b/ProvisioningTool/sample_json_keymint_gf.txt index a624d1a5..3c45642b 100644 --- a/ProvisioningTool/sample_json_keymint_gf.txt +++ b/ProvisioningTool/sample_json_keymint_gf.txt @@ -17,15 +17,12 @@ "boot_state": 0, "device_locked": 1 }, - "device_unique_key": "test_resources/batch_key.der", - "signer_info": { - "signer_name": "Google", - "signing_keys": [ - "test_resources/ca_cert.der", - "test_resources/intermediate_cert.der", - "test_resources/batch_cert.der" - ] - }, + "attest_key": "test_resources/attest_key.der", + "attest_cert_chain": [ + "test_resources/attest_cert.der", + "test_resources/intermediate_cert.der", + "test_resources/ca_cert.der" + ], "oem_root_key": "test_resources/oem_root_key.der", "secure_boot_mode": 0 } diff --git a/ProvisioningTool/src/construct_apdus.cpp b/ProvisioningTool/src/construct_apdus.cpp index 0a8b7437..dbf0d189 100644 --- a/ProvisioningTool/src/construct_apdus.cpp +++ b/ProvisioningTool/src/construct_apdus.cpp @@ -60,6 +60,8 @@ static int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector< std::vector& pub_x, std::vector& pub_y); static int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& publicKey); +static int processAttestationKey(); +static int processAttestationCertificateData(); static int processAttestationIds(); static int processSharedSecret(); static int processSetBootParameters(); @@ -70,6 +72,9 @@ static int getBlobValue(Json::Value& Obj, const char* key, std::vector& static int getStringValue(Json::Value& Obj, const char* key, std::string& str); static int processDeviceUniqueKey(); static int processAdditionalCertificateChain(); +static X509* parseDerCertificate(std::vector& certData); +static int getNotAfter(X509* x509, std::vector& notAfterDate); +static int getDerSubjectName(X509* x509, std::vector& subject); static int getDeviceUniqueKey(bytevec& privKey, bytevec& x, bytevec& y); static int processOEMRootPublicKey(); static int processSEFactoryLock(); @@ -92,6 +97,57 @@ void usage() { printf("-o, --output jsonFile \t Output json file \n"); } +X509* parseDerCertificate(std::vector& certData) { + X509 *x509 = nullptr; + + /* Create BIO instance from certificate data */ + BIO *bio = BIO_new_mem_buf(certData.data(), certData.size()); + if(bio == nullptr) { + printf("\n Failed to create BIO from buffer.\n"); + return nullptr; + } + /* Create X509 instance from BIO */ + x509 = d2i_X509_bio(bio, NULL); + if(x509 == nullptr) { + printf("\n Failed to get X509 instance from BIO.\n"); + return nullptr; + } + BIO_free(bio); + return x509; +} + +int getDerSubjectName(X509* x509, std::vector& subject) { + uint8_t *subjectDer = NULL; + X509_NAME* asn1Subject = X509_get_subject_name(x509); + if(asn1Subject == NULL) { + printf("\n Failed to read the subject.\n"); + return FAILURE; + } + /* Convert X509_NAME to der encoded subject */ + int len = i2d_X509_NAME(asn1Subject, &subjectDer); + if (len < 0) { + printf("\n Failed to get readable name from X509_NAME.\n"); + return FAILURE; + } + subject.insert(subject.begin(), subjectDer, subjectDer+len); + return SUCCESS; +} + +int getNotAfter(X509* x509, std::vector& notAfterDate) { + const ASN1_TIME* notAfter = X509_get0_notAfter(x509); + if(notAfter == NULL) { + printf("\n Failed to read expiry time.\n"); + return FAILURE; + } + int strNotAfterLen = ASN1_STRING_length(notAfter); + const uint8_t *strNotAfter = ASN1_STRING_get0_data(notAfter); + if(strNotAfter == NULL) { + printf("\n Failed to read expiry time from ASN1 string.\n"); + return FAILURE; + } + notAfterDate.insert(notAfterDate.begin(), strNotAfter, strNotAfter + strNotAfterLen); + return SUCCESS; +} int ecRawKeyFromPKCS8(const std::vector& pkcs8Blob, std::vector& secret, std::vector& pub_x, std::vector& pub_y) { @@ -294,8 +350,8 @@ int processInputFile() { if (0 != readJsonFile(root, inputFileName)) { return FAILURE; } - if (0 != processDeviceUniqueKey() || - 0 != processAdditionalCertificateChain() || + if (0 != processAttestationKey() || + 0 != processAttestationCertificateData() || 0 != processAttestationIds() || 0 != processSharedSecret() || 0 != processOEMRootPublicKey() || @@ -565,6 +621,107 @@ int processSecureBootMode() { return SUCCESS; } +int processAttestationKey() { + Json::Value keyFile = root.get(kAttestKey, Json::Value::nullRef); + if (!keyFile.isNull()) { + std::vector data; + std::vector privateKey; + std::vector publicKey; + + std::string keyFileName = keyFile.asString(); + if(SUCCESS != readDataFromFile(keyFileName.data(), data)) { + printf("\n Failed to read the attestation key from the file.\n"); + return FAILURE; + } + if (SUCCESS != ecRawKeyFromPKCS8(data, privateKey, publicKey)) { + return FAILURE; + } + + // Prepare cbor input. + Array input; + Array keys; + Map map; + keys.add(privateKey); + keys.add(publicKey); + map.add(kTagAlgorithm, kAlgorithmEc); + map.add(kTagDigest, std::vector({kDigestSha256})); + map.add(kTagCurve, kCurveP256); + map.add(kTagPurpose, std::vector({kPurposeAttest})); + // Add elements inside cbor array. + input.add(std::move(map)); + input.add(kKeyFormatRaw); + input.add(keys.encode()); + std::vector cborData = input.encode(); + + if(SUCCESS != addApduHeader(kAttestationKeyCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestKey] = getHexString(cborData); + } else { + printf("\n Improper value for attest_key in json file \n"); + return FAILURE; + } + printf("\n Constructed attestation key APDU successfully. \n"); + return SUCCESS; +} + +static int processAttestationCertificateData() { + Json::Value certChainFiles = root.get(kAttestCertChain, Json::Value::nullRef); + if (!certChainFiles.isNull()) { + std::vector certData; + std::vector subject; + + if(certChainFiles.isArray()) { + if (certChainFiles.size() == 0) { + printf("\n empty certificate.\n"); + return FAILURE; + } + for (uint32_t i = 0; i < certChainFiles.size(); i++) { + if(certChainFiles[i].isString()) { + /* Read the certificates. */ + if(SUCCESS != readDataFromFile(certChainFiles[i].asString().data(), certData)) { + printf("\n Failed to read the Root certificate\n"); + return FAILURE; + } + if (i == 0) { // Leaf certificate + /* Subject, AuthorityKeyIdentifier and Expirty time of the root certificate are required by javacard. */ + /* Get X509 certificate instance for the root certificate.*/ + X509_Ptr x509(parseDerCertificate(certData)); + if (!x509) { + return FAILURE; + } + + /* Get subject in DER */ + getDerSubjectName(x509.get(), subject); + } + } else { + printf("\n Fail: Only proper certificate paths as a string is allowed inside the json file. \n"); + return FAILURE; + } + } + } else { + printf("\n Fail: cert chain value should be an array inside the json file. \n"); + return FAILURE; + } + // Prepare cbor input + Array array; + array.add(certData); + array.add(subject); + std::vector cborData = array.encode(); + if (SUCCESS != addApduHeader(kAttestCertDataCmd, cborData)) { + return FAILURE; + } + // Write to json. + writerRoot[kAttestCertChain] = getHexString(cborData); + } else { + printf("\n Fail: Improper value found for attest_cert_chain key inside json file \n"); + return FAILURE; + } + printf("\n Constructed attestation certificate chain APDU successfully. \n"); + return SUCCESS; +} + int processAttestationIds() { //AttestIDParams params; Json::Value attestIds = root.get("attest_ids", Json::Value::nullRef); diff --git a/ProvisioningTool/src/provision.cpp b/ProvisioningTool/src/provision.cpp index 9befbab3..d403d44a 100644 --- a/ProvisioningTool/src/provision.cpp +++ b/ProvisioningTool/src/provision.cpp @@ -212,8 +212,8 @@ int processInputFile() { return false; } if (!isSEFactoryProvisionLocked(provisionStatus) && - ((0 != provisionData(pSocket, kDeviceUniqueKey)) || - (0 != provisionData(pSocket, kAdditionalCertChain)))) { + ((0 != provisionData(pSocket, kAttestKey)) || + (0 != provisionData(pSocket, kAttestCertChain)))) { return FAILURE; } if (!isOEMProvisionLocked(provisionStatus) && diff --git a/ProvisioningTool/test_resources/attest_cert.der b/ProvisioningTool/test_resources/attest_cert.der new file mode 100644 index 00000000..355bc984 Binary files /dev/null and b/ProvisioningTool/test_resources/attest_cert.der differ diff --git a/ProvisioningTool/test_resources/attest_key.der b/ProvisioningTool/test_resources/attest_key.der new file mode 100644 index 00000000..f4902073 Binary files /dev/null and b/ProvisioningTool/test_resources/attest_key.der differ