diff --git a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java index 49418f90..754cdcdb 100644 --- a/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java +++ b/Applet/src/com/android/javacard/keymaster/KMKeymasterApplet.java @@ -396,6 +396,9 @@ public void process(APDU apdu) { case INS_GENERATE_KEY_CMD: processGenerateKey(apdu); break; + case INS_ATTEST_KEY_CMD: + processAttestKeyCmd(apdu); + break; case INS_IMPORT_KEY_CMD: processImportKeyCmd(apdu); break; @@ -1283,7 +1286,8 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara // Add Tags addTags(hwParameters, true, cert); - addTags(swParameters, false, cert); + short swParams = KMKeyParameters.makeKeystoreEnforced(data[KEY_PARAMETERS], scratchPad); + addTags(swParams, false, cert); // Add Device Boot locked status cert.deviceLocked(kmDataStore.isDeviceBootLocked()); // VB data @@ -1291,8 +1295,8 @@ private KMAttestationCert makeAttestationCert(short attKeyBlob, short attKeyPara cert.verifiedBootKey(getBootKey(scratchPad)); cert.verifiedBootState((byte) kmDataStore.getBootState()); - // TODO remove the following line - makeKeyCharacteristics(scratchPad); + data[SECRET] = privKey; + data[KEY_BLOB] = origBlob; return cert; } @@ -3294,13 +3298,9 @@ protected void setVendorPatchLevel(short patch){ private short generateKeyCmd(APDU apdu){ short params = KMKeyParameters.expAny(); - short blob = KMByteBlob.exp(); // Array of expected arguments - short cmd = KMArray.instance((short) 4); + short cmd = KMArray.instance((short) 1); KMArray.cast(cmd).add((short) 0, params); //key params - KMArray.cast(cmd).add((short) 1, blob); //attest key - KMArray.cast(cmd).add((short) 2, params); //attest key params - KMArray.cast(cmd).add((short) 3, blob); //issuer return receiveIncoming(apdu, cmd); } @@ -3310,10 +3310,6 @@ private void processGenerateKey(APDU apdu) { // Re-purpose the apdu buffer as scratch pad. byte[] scratchPad = apdu.getBuffer(); data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 0); - data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 1); - data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 2); - data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 3); - data[CERTIFICATE] = KMArray.instance((short)0); //by default the cert is empty. // ROLLBACK_RESISTANCE not supported. KMTag.assertAbsence(data[KEY_PARAMETERS], KMType.BOOL_TAG,KMType.ROLLBACK_RESISTANCE, KMError.ROLLBACK_RESISTANCE_UNAVAILABLE); @@ -3359,7 +3355,6 @@ private void processGenerateKey(APDU apdu) { // create key blob and associated attestation. data[ORIGIN] = KMType.GENERATED; makeKeyCharacteristics(scratchPad); - generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS],scratchPad); createEncryptedKeyBlob(scratchPad); // Remove custom tags from key characteristics short teeParams = KMKeyCharacteristics.cast(data[KEY_CHARACTERISTICS]).getTeeEnforced(); @@ -3367,14 +3362,77 @@ private void processGenerateKey(APDU apdu) { KMKeyParameters.cast(teeParams).deleteCustomTags(); } // prepare the response - short resp = KMArray.instance((short) 4); + short resp = KMArray.instance((short) 3); KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); KMArray.cast(resp).add((short) 1, data[KEY_BLOB]); KMArray.cast(resp).add((short) 2, data[KEY_CHARACTERISTICS]); - KMArray.cast(resp).add((short) 3, data[CERTIFICATE]); sendOutgoing(apdu, resp); } + private short generateAttestKeyCmd(APDU apdu) { + short params = KMKeyParameters.expAny(); + short blob = KMByteBlob.exp(); + // Array of expected arguments + short cmd = KMArray.instance((short) 5); + KMArray.cast(cmd).add((short) 0, blob); // key blob + KMArray.cast(cmd).add((short) 1, params); // keyparamters to be attested. + KMArray.cast(cmd).add((short) 2, blob); // attest key blob + KMArray.cast(cmd).add((short) 3, params); // attest key params + KMArray.cast(cmd).add((short) 4, blob); // attest issuer + + return receiveIncoming(apdu, cmd); + } + + public void getAttestKeyInputParameters(short arrPtr, short[] data, byte keyBlobOff, + byte keyParametersOff, + byte attestKeyBlobOff, byte attestKeyParamsOff, byte attestKeyIssuerOff) { + data[keyBlobOff] = KMArray.cast(arrPtr).get((short) 0); + data[keyParametersOff] = KMArray.cast(arrPtr).get((short) 1); + data[attestKeyBlobOff] = KMType.INVALID_VALUE; + data[attestKeyParamsOff] = KMType.INVALID_VALUE; + data[attestKeyIssuerOff] = KMType.INVALID_VALUE; + } + + private void processAttestKeyCmd(APDU apdu) { + // Receive the incoming request fully from the master into buffer. + short cmd = generateAttestKeyCmd(apdu); + // Re-purpose the apdu buffer as scratch pad. + byte[] scratchPad = apdu.getBuffer(); + data[KEY_BLOB] = KMArray.cast(cmd).get((short) 0); + data[KEY_PARAMETERS] = KMArray.cast(cmd).get((short) 1); + data[ATTEST_KEY_BLOB] = KMArray.cast(cmd).get((short) 2); + data[ATTEST_KEY_PARAMS] = KMArray.cast(cmd).get((short) 3); + data[ATTEST_KEY_ISSUER] = KMArray.cast(cmd).get((short) 4); + + data[CERTIFICATE] = KMArray.instance((short) 0); //by default the cert is empty. + + // Check for app id and app data. + data[APP_ID] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_ID, data[KEY_PARAMETERS]); + data[APP_DATA] = + KMKeyParameters.findTag(KMType.BYTES_TAG, KMType.APPLICATION_DATA, data[KEY_PARAMETERS]); + if (data[APP_ID] != KMTag.INVALID_VALUE) { + data[APP_ID] = KMByteTag.cast(data[APP_ID]).getValue(); + } + if (data[APP_DATA] != KMTag.INVALID_VALUE) { + data[APP_DATA] = KMByteTag.cast(data[APP_DATA]).getValue(); + } + // parse key blob + parseEncryptedKeyBlob(data[KEY_BLOB], data[APP_ID], data[APP_DATA], scratchPad); + // The key which is being attested should be asymmetric i.e. RSA or EC + short alg = KMEnumTag.getValue(KMType.ALGORITHM, data[HW_PARAMETERS]); + if (alg != KMType.RSA && alg != KMType.EC) { + KMException.throwIt(KMError.INCOMPATIBLE_ALGORITHM); + } + // Build certificate + generateAttestation(data[ATTEST_KEY_BLOB], data[ATTEST_KEY_PARAMS], scratchPad); + + short resp = KMArray.instance((short) 2); + KMArray.cast(resp).add((short) 0, KMInteger.uint_16(KMError.OK)); + KMArray.cast(resp).add((short) 1, data[CERTIFICATE]); + sendOutgoing(apdu, resp); + } + private short getAttestationMode(short attKeyBlob, short attChallenge){ short alg = KMKeyParameters.findTag(KMType.ENUM_TAG, KMType.ALGORITHM, data[KEY_PARAMETERS]); short mode = KMType.NO_CERT; @@ -3419,7 +3477,7 @@ private void generateAttestation(short attKeyBlob, short attKeyParam, byte[] sc switch (mode){ case KMType.ATTESTATION_CERT: - cert = makeAttestationCert(attKeyBlob,attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER],data[HW_PARAMETERS], + cert = makeAttestationCert(attKeyBlob, attKeyParam, attChallenge, data[ATTEST_KEY_ISSUER],data[HW_PARAMETERS], data[SW_PARAMETERS], scratchPad); break; case KMType.SELF_SIGNED_CERT: @@ -3838,7 +3896,7 @@ private static void makeAuthData(byte[] scratchPad) { KMArray.cast(params).add((short) 2, data[PUB_KEY]); } - short authIndex = repository.alloc(MAX_AUTH_DATA_SIZE); + short authIndex = repository.allocReclaimableMemory(MAX_AUTH_DATA_SIZE); short index = 0; short len = 0; short paramsLen = KMArray.cast(params).length(); @@ -3855,7 +3913,10 @@ private static void makeAuthData(byte[] scratchPad) { } index++; } - data[AUTH_DATA] = authIndex; + short authDataIndex = repository.alloc(len); + Util.arrayCopyNonAtomic(repository.getHeap(), authIndex, repository.getHeap(), authDataIndex, len); + repository.reclaimMemory(MAX_AUTH_DATA_SIZE); + data[AUTH_DATA] = authDataIndex; data[AUTH_DATA_LENGTH] = len; } diff --git a/Applet/src/com/android/javacard/keymaster/KMRepository.java b/Applet/src/com/android/javacard/keymaster/KMRepository.java index 0a158878..1e6c8371 100644 --- a/Applet/src/com/android/javacard/keymaster/KMRepository.java +++ b/Applet/src/com/android/javacard/keymaster/KMRepository.java @@ -32,7 +32,7 @@ */ public class KMRepository { - public static final short HEAP_SIZE = 15000; + public static final short HEAP_SIZE = 10000; // Class Attributes private byte[] heap; diff --git a/HAL/JavacardKeyMintDevice.cpp b/HAL/JavacardKeyMintDevice.cpp index 584c75ed..faa1cd43 100644 --- a/HAL/JavacardKeyMintDevice.cpp +++ b/HAL/JavacardKeyMintDevice.cpp @@ -33,6 +33,7 @@ #include namespace aidl::android::hardware::security::keymint { +using km_utils::KmParamSet; using namespace ::keymaster; using namespace ::keymint::javacard; @@ -81,19 +82,37 @@ ScopedAStatus JavacardKeyMintDevice::generateKey(const vector& key cppbor::Array array; // add key params cbor_.addKeyparameters(array, keyParams); - // add attestation key if any - cbor_.addAttestationKey(array, attestationKey); auto [item, err] = card_->sendRequest(Instruction::INS_GENERATE_KEY_CMD, array); if (err != KM_ERROR_OK) { LOG(ERROR) << "Error in sending generateKey."; return km_utils::kmError2ScopedAStatus(err); } if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || - !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || - !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { + !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics)) { LOG(ERROR) << "Error in decoding og response in generateKey."; return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); } + + AuthorizationSet paramSet; + paramSet.Reinitialize(KmParamSet(keyParams)); + // Call attestKey only Asymmetric algorithms. + keymaster_algorithm_t algorithm; + paramSet.GetTagValue(TAG_ALGORITHM, &algorithm); + if (algorithm == KM_ALGORITHM_RSA || algorithm == KM_ALGORITHM_EC) { + cppbor::Array attestKeyArray; + attestKeyArray.add(creationResult->keyBlob); + cbor_.addKeyparameters(attestKeyArray, keyParams); + cbor_.addAttestationKey(attestKeyArray, attestationKey); + auto [item, err] = card_->sendRequest(Instruction::INS_ATTEST_KEY_CMD, attestKeyArray); + if (err != KM_ERROR_OK) { + LOG(ERROR) << "Failed in attestKey err: "; + return km_utils::kmError2ScopedAStatus(err); + } + if (!cbor_.getCertificateChain(item, 1, creationResult->certificateChain)) { + LOG(ERROR) << "Error in decoding og response in generateKey."; + return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); + } + } return ScopedAStatus::ok(); }