| /* |
| * Copyright 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. |
| */ |
| /****************************************************************************** |
| * |
| * The original Work has been changed by NXP. |
| * |
| * 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. |
| * |
| * Copyright 2022 NXP |
| * |
| ******************************************************************************/ |
| #define LOG_TAG "javacard.keymint.device.strongbox-impl" |
| #include "JavacardKeyMintDevice.h" |
| #include "JavacardKeyMintOperation.h" |
| #include "JavacardSharedSecret.h" |
| #include <JavacardKeyMintUtils.h> |
| #include <algorithm> |
| #include <android-base/logging.h> |
| #include <android-base/properties.h> |
| #include <hardware/hw_auth_token.h> |
| #include <iostream> |
| #include <iterator> |
| #include <keymaster/android_keymaster_messages.h> |
| #include <keymaster/wrapped_key.h> |
| #include <memory> |
| #include <regex.h> |
| #include <string> |
| #include <vector> |
| |
| namespace aidl::android::hardware::security::keymint { |
| using km_utils::KmParamSet; |
| using namespace ::keymaster; |
| using namespace ::keymint::javacard; |
| |
| ScopedAStatus JavacardKeyMintDevice::defaultHwInfo(KeyMintHardwareInfo* info) { |
| info->versionNumber = 1; |
| info->keyMintAuthorName = "Google"; |
| info->keyMintName = "JavacardKeymintDevice"; |
| info->securityLevel = securitylevel_; |
| info->timestampTokenRequired = true; |
| return ScopedAStatus::ok(); |
| } |
| |
| |
| ScopedAStatus JavacardKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) { |
| uint64_t tsRequired = 1; |
| auto [item, err] = card_->sendRequest(Instruction::INS_GET_HW_INFO_CMD); |
| uint32_t secLevel; |
| uint32_t version; |
| if (err != KM_ERROR_OK || !cbor_.getUint64<uint32_t>(item, 1, version) || |
| !cbor_.getUint64<uint32_t>(item, 2, secLevel) || |
| !cbor_.getBinaryArray(item, 3, info->keyMintName) || |
| !cbor_.getBinaryArray(item, 4, info->keyMintAuthorName) || |
| !cbor_.getUint64<uint64_t>(item, 5, tsRequired)) { |
| // TODO should we return HARDWARE_NOT_YET_AVAILABLE instead of default Hardware Info. |
| LOG(ERROR) << "Error in response of getHardwareInfo."; |
| LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo."; |
| return defaultHwInfo(info); |
| } |
| card_->initializeJavacard(); |
| info->timestampTokenRequired = (tsRequired == 1); |
| info->securityLevel = static_cast<SecurityLevel>(secLevel); |
| info->versionNumber = static_cast<int32_t>(version); |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams, |
| const optional<AttestationKey>& attestationKey, |
| KeyCreationResult* creationResult) { |
| 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)) { |
| LOG(ERROR) << "Error in decoding og response in generateKey."; |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) { |
| cppbor::Array request; |
| // add key data |
| request.add(Bstr(data)); |
| auto [item, err] = card_->sendRequest(Instruction::INS_ADD_RNG_ENTROPY_CMD, request); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending addRngEntropy."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::importKey(const vector<KeyParameter>& keyParams, |
| KeyFormat keyFormat, const vector<uint8_t>& keyData, |
| const optional<AttestationKey>& attestationKey, |
| KeyCreationResult* creationResult) { |
| |
| cppbor::Array request; |
| // add key params |
| cbor_.addKeyparameters(request, keyParams); |
| // add key format |
| request.add(Uint(static_cast<uint8_t>(keyFormat))); |
| // add key data |
| request.add(Bstr(keyData)); |
| // add attestation key if any |
| cbor_.addAttestationKey(request, attestationKey); |
| |
| auto [item, err] = card_->sendRequest(Instruction::INS_IMPORT_KEY_CMD, request); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending data in importKey."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || |
| !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || |
| !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { |
| LOG(ERROR) << "Error in decoding response in importKey."; |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| // import wrapped key is divided into 2 stage operation. |
| ScopedAStatus JavacardKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData, |
| const vector<uint8_t>& wrappingKeyBlob, |
| const vector<uint8_t>& maskingKey, |
| const vector<KeyParameter>& unwrappingParams, |
| int64_t passwordSid, int64_t biometricSid, |
| KeyCreationResult* creationResult) { |
| cppbor::Array request; |
| std::unique_ptr<Item> item; |
| vector<uint8_t> keyBlob; |
| std::vector<uint8_t> response; |
| vector<KeyCharacteristics> keyCharacteristics; |
| std::vector<uint8_t> iv; |
| std::vector<uint8_t> transitKey; |
| std::vector<uint8_t> secureKey; |
| std::vector<uint8_t> tag; |
| vector<KeyParameter> authList; |
| KeyFormat keyFormat; |
| std::vector<uint8_t> wrappedKeyDescription; |
| keymaster_error_t errorCode = parseWrappedKey(wrappedKeyData, iv, transitKey, secureKey, tag, |
| authList, keyFormat, wrappedKeyDescription); |
| if (errorCode != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in parse wrapped key in importWrappedKey."; |
| return km_utils::kmError2ScopedAStatus(errorCode); |
| } |
| |
| // begin import |
| std::tie(item, errorCode) = |
| sendBeginImportWrappedKeyCmd(transitKey, wrappingKeyBlob, maskingKey, unwrappingParams); |
| if (errorCode != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in send begin import wrapped key in importWrappedKey."; |
| return km_utils::kmError2ScopedAStatus(errorCode); |
| } |
| // Finish the import |
| std::tie(item, errorCode) = sendFinishImportWrappedKeyCmd( |
| authList, keyFormat, secureKey, tag, iv, wrappedKeyDescription, passwordSid, biometricSid); |
| if (errorCode != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in send finish import wrapped key in importWrappedKey."; |
| return km_utils::kmError2ScopedAStatus(errorCode); |
| } |
| if (!cbor_.getBinaryArray(item, 1, creationResult->keyBlob) || |
| !cbor_.getKeyCharacteristics(item, 2, creationResult->keyCharacteristics) || |
| !cbor_.getCertificateChain(item, 3, creationResult->certificateChain)) { |
| LOG(ERROR) << "Error in decoding the response in importWrappedKey."; |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| std::tuple<std::unique_ptr<Item>, keymaster_error_t> |
| JavacardKeyMintDevice::sendBeginImportWrappedKeyCmd(const std::vector<uint8_t>& transitKey, |
| const std::vector<uint8_t>& wrappingKeyBlob, |
| const std::vector<uint8_t>& maskingKey, |
| const vector<KeyParameter>& unwrappingParams) { |
| Array request; |
| request.add(std::vector<uint8_t>(transitKey)); |
| request.add(std::vector<uint8_t>(wrappingKeyBlob)); |
| request.add(std::vector<uint8_t>(maskingKey)); |
| cbor_.addKeyparameters(request, unwrappingParams); |
| return card_->sendRequest(Instruction::INS_BEGIN_IMPORT_WRAPPED_KEY_CMD, request); |
| } |
| |
| std::tuple<std::unique_ptr<Item>, keymaster_error_t> |
| JavacardKeyMintDevice::sendFinishImportWrappedKeyCmd( |
| const vector<KeyParameter>& keyParams, KeyFormat keyFormat, |
| const std::vector<uint8_t>& secureKey, const std::vector<uint8_t>& tag, |
| const std::vector<uint8_t>& iv, const std::vector<uint8_t>& wrappedKeyDescription, |
| int64_t passwordSid, int64_t biometricSid) { |
| Array request; |
| cbor_.addKeyparameters(request, keyParams); |
| request.add(static_cast<uint64_t>(keyFormat)); |
| request.add(std::vector<uint8_t>(secureKey)); |
| request.add(std::vector<uint8_t>(tag)); |
| request.add(std::vector<uint8_t>(iv)); |
| request.add(std::vector<uint8_t>(wrappedKeyDescription)); |
| request.add(Uint(passwordSid)); |
| request.add(Uint(biometricSid)); |
| return card_->sendRequest(Instruction::INS_FINISH_IMPORT_WRAPPED_KEY_CMD, request); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade, |
| const vector<KeyParameter>& upgradeParams, |
| vector<uint8_t>* keyBlob) { |
| cppbor::Array request; |
| // add key blob |
| request.add(Bstr(keyBlobToUpgrade)); |
| // add key params |
| cbor_.addKeyparameters(request, upgradeParams); |
| auto [item, err] = card_->sendRequest(Instruction::INS_UPGRADE_KEY_CMD, request); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending in upgradeKey."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| if (!cbor_.getBinaryArray(item, 1, *keyBlob)) { |
| LOG(ERROR) << "Error in decoding the response in upgradeKey."; |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) { |
| Array request; |
| request.add(Bstr(keyBlob)); |
| auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_KEY_CMD, request); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending in deleteKey."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::deleteAllKeys() { |
| auto [item, err] = card_->sendRequest(Instruction::INS_DELETE_ALL_KEYS_CMD); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending in deleteAllKeys."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::destroyAttestationIds() { |
| auto [item, err] = card_->sendRequest(Instruction::INS_DESTROY_ATT_IDS_CMD); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending in destroyAttestationIds."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::begin(KeyPurpose purpose, const std::vector<uint8_t>& keyBlob, |
| const std::vector<KeyParameter>& params, |
| const std::optional<HardwareAuthToken>& authToken, |
| BeginResult* result) { |
| |
| cppbor::Array array; |
| std::vector<uint8_t> response; |
| // make request |
| array.add(Uint(static_cast<uint64_t>(purpose))); |
| array.add(Bstr(keyBlob)); |
| cbor_.addKeyparameters(array, params); |
| HardwareAuthToken token = authToken.value_or(HardwareAuthToken()); |
| cbor_.addHardwareAuthToken(array, token); |
| |
| // Send earlyBootEnded if there is any pending earlybootEnded event. |
| handleSendEarlyBootEndedEvent(); |
| |
| auto [item, err] = card_->sendRequest(Instruction::INS_BEGIN_OPERATION_CMD, array); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending in begin."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| // return the result |
| uint64_t opHandle; |
| uint8_t bufMode; |
| uint16_t macLength; |
| if (!cbor_.getKeyParameters(item, 1, result->params) || |
| !cbor_.getUint64<uint64_t>(item, 2, opHandle) || |
| !cbor_.getUint64<uint8_t>(item, 3, bufMode) || |
| !cbor_.getUint64<uint16_t>(item, 4, macLength)) { |
| LOG(ERROR) << "Error in decoding the response in begin."; |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); |
| } |
| result->challenge = opHandle; |
| result->operation = ndk::SharedRefBase::make<JavacardKeyMintOperation>( |
| static_cast<keymaster_operation_handle_t>(opHandle), static_cast<BufferingMode>(bufMode), |
| macLength, card_); |
| return ScopedAStatus::ok(); |
| } |
| |
| // TODO |
| ScopedAStatus |
| JavacardKeyMintDevice::deviceLocked(bool passwordOnly, |
| const std::optional<TimeStampToken>& timestampToken) { |
| Array request; |
| int8_t password = 1; |
| if (!passwordOnly) { |
| password = 0; |
| } |
| request.add(Uint(password)); |
| cbor_.addTimeStampToken(request, timestampToken.value_or(TimeStampToken())); |
| auto [item, err] = card_->sendRequest(Instruction::INS_DEVICE_LOCKED_CMD, request); |
| if (err != KM_ERROR_OK) { |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| void JavacardKeyMintDevice::handleSendEarlyBootEndedEvent() { |
| if (isEarlyBootEventPending) { |
| LOG(INFO) << "JavacardKeyMintDevice::handleSendEarlyBootEndedEvent send earlyBootEnded Event."; |
| if (earlyBootEnded().isOk()) { |
| isEarlyBootEventPending = false; |
| } |
| } |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::earlyBootEnded() { |
| auto [item, err] = card_->sendRequest(Instruction::INS_EARLY_BOOT_ENDED_CMD); |
| if (err != KM_ERROR_OK) { |
| // In case of failure cache the event and send in the next immediate request to Applet. |
| isEarlyBootEventPending = true; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| isEarlyBootEventPending = false; |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::getKeyCharacteristics( |
| const std::vector<uint8_t>& keyBlob, const std::vector<uint8_t>& appId, |
| const std::vector<uint8_t>& appData, std::vector<KeyCharacteristics>* result) { |
| cppbor::Array request; |
| request.add(vector<uint8_t>(keyBlob)); |
| request.add(vector<uint8_t>(appId)); |
| request.add(vector<uint8_t>(appData)); |
| auto [item, err] = card_->sendRequest(Instruction::INS_GET_KEY_CHARACTERISTICS_CMD, request); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sending in getKeyCharacteristics."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| if (!cbor_.getKeyCharacteristics(item, 1, *result)) { |
| LOG(ERROR) << "Error in sending in upgradeKey."; |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::getRootOfTrustChallenge(std::array<uint8_t, 16>* challenge) { |
| auto [item, err] = card_->sendRequest(Instruction::INS_GET_ROT_CHALLENGE_CMD); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in getRootOfTrustChallenge."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| std::vector<uint8_t> rotChallenge; |
| if (!cbor_.getBinaryArray(item, 1, rotChallenge) || |
| (rotChallenge.size() != 16)) { |
| LOG(ERROR) << "Error in RotChallenge Data"; |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR); |
| } |
| std::copy_n(rotChallenge.begin(), 16, challenge->begin()); |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::getRootOfTrust(__attribute__((unused)) const std::array<uint8_t, 16>& in_challenge, |
| __attribute__((unused)) std::vector<uint8_t>* rootOfTrust) { |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::sendRootOfTrust(const std::vector<uint8_t>& in_rootOfTrust) { |
| std::vector<uint8_t> rootOfTrust(in_rootOfTrust); |
| auto [item, err] = card_->sendRequest(Instruction::INS_SEND_ROT_DATA_CMD, rootOfTrust); |
| if (err != KM_ERROR_OK) { |
| LOG(ERROR) << "Error in sendRootOfTrust."; |
| return km_utils::kmError2ScopedAStatus(err); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| keymaster_error_t |
| JavacardKeyMintDevice::parseWrappedKey(const vector<uint8_t>& wrappedKeyData, |
| std::vector<uint8_t>& iv, std::vector<uint8_t>& transitKey, |
| std::vector<uint8_t>& secureKey, std::vector<uint8_t>& tag, |
| vector<KeyParameter>& authList, KeyFormat& keyFormat, |
| std::vector<uint8_t>& wrappedKeyDescription) { |
| KeymasterBlob kmIv; |
| KeymasterKeyBlob kmTransitKey; |
| KeymasterKeyBlob kmSecureKey; |
| KeymasterBlob kmTag; |
| AuthorizationSet authSet; |
| keymaster_key_format_t kmKeyFormat; |
| KeymasterBlob kmWrappedKeyDescription; |
| |
| size_t keyDataLen = wrappedKeyData.size(); |
| uint8_t* keyData = dup_buffer(wrappedKeyData.data(), keyDataLen); |
| keymaster_key_blob_t keyMaterial = {keyData, keyDataLen}; |
| keymaster_error_t error = |
| parse_wrapped_key(KeymasterKeyBlob(keyMaterial), &kmIv, &kmTransitKey, &kmSecureKey, &kmTag, |
| &authSet, &kmKeyFormat, &kmWrappedKeyDescription); |
| if (error != KM_ERROR_OK) { |
| LOG(ERROR) << "Error parsing wrapped key."; |
| return error; |
| } |
| iv = km_utils::kmBlob2vector(kmIv); |
| transitKey = km_utils::kmBlob2vector(kmTransitKey); |
| secureKey = km_utils::kmBlob2vector(kmSecureKey); |
| tag = km_utils::kmBlob2vector(kmTag); |
| authList = km_utils::kmParamSet2Aidl(authSet); |
| keyFormat = static_cast<KeyFormat>(kmKeyFormat); |
| wrappedKeyDescription = km_utils::kmBlob2vector(kmWrappedKeyDescription); |
| return KM_ERROR_OK; |
| } |
| |
| ScopedAStatus JavacardKeyMintDevice::convertStorageKeyToEphemeral( |
| const std::vector<uint8_t>& /* storageKeyBlob */, |
| std::vector<uint8_t>* /* ephemeralKeyBlob */) { |
| return km_utils::kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED); |
| } |
| } // namespace aidl::android::hardware::security::keymint |