blob: d0b45c286503b9ba3171718cde6b6a8d110f8ae6 [file] [log] [blame]
/*
* 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.
*/
/******************************************************************************
**
** 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.rkp.strongbox-impl"
#include <JavacardRemotelyProvisionedComponentDevice.h>
#include <android-base/logging.h>
#include <JavacardKeyMintUtils.h>
#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
#include <keymaster/cppcose/cppcose.h>
#include <keymaster/remote_provisioning_utils.h>
#ifdef NXP_EXTNS
#define KM_RKP_VERSION_1 0x01
#endif
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<keymaster_error_t>(32000);
constexpr keymaster_error_t kStatusInvalidMac = static_cast<keymaster_error_t>(32001);
constexpr keymaster_error_t kStatusProductionKeyInTestRequest = static_cast<keymaster_error_t>(32002);
constexpr keymaster_error_t kStatusTestKeyInProductionRequest = static_cast<keymaster_error_t>(32003);
constexpr keymaster_error_t kStatusInvalidEek = static_cast<keymaster_error_t>(32004);
constexpr keymaster_error_t kStatusInvalidState = static_cast<keymaster_error_t>(32005);
namespace {
keymaster_error_t translateRkpErrorCode(keymaster_error_t error) {
switch(static_cast<int32_t>(-error)) {
case kStatusFailed:
case kStatusInvalidState:
return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_FAILED);
case kStatusInvalidMac:
return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
case kStatusProductionKeyInTestRequest:
return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
case kStatusTestKeyInProductionRequest:
return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
case kStatusInvalidEek:
return static_cast<keymaster_error_t>(BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
}
return error;
}
ScopedAStatus defaultHwInfo(RpcHardwareInfo* info) {
info->versionNumber = 1;
info->rpcAuthorName = "Google";
info->supportedEekCurve = RpcHardwareInfo::CURVE_P256;
return ScopedAStatus::ok();
}
uint32_t coseKeyEncodedSize(const std::vector<MacedPublicKey>& 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);
uint32_t versionNumber;
uint32_t supportedEekCurve;
if (err != KM_ERROR_OK ||
!cbor_.getUint64<uint32_t>(item, 1, versionNumber) ||
!cbor_.getBinaryArray(item, 2, info->rpcAuthorName ) ||
!cbor_.getUint64<uint32_t>(item, 3, supportedEekCurve)) {
LOG(ERROR) << "Error in response of getHardwareInfo.";
LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo.";
return defaultHwInfo(info);
}
info->versionNumber = static_cast<int32_t>(versionNumber);
#ifdef NXP_EXTNS
if (info->versionNumber > KM_RKP_VERSION_1) {
std::string uniqueId;
if (!cbor_.getBinaryArray(item, 4, uniqueId)) {
LOG(ERROR) << "Error in uniqueId response of getHardwareInfo.";
LOG(INFO) << "Returning defaultHwInfo in getHardwareInfo.";
return defaultHwInfo(info);
}
info->uniqueId = static_cast<::std::optional<::std::string>>(uniqueId);
}
#endif
info->supportedEekCurve = static_cast<int32_t>(supportedEekCurve);
return ScopedAStatus::ok();
}
ScopedAStatus
JavacardRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(bool testMode,
MacedPublicKey* macedPublicKey,
std::vector<uint8_t>* 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));
}
if (!cbor_.getBinaryArray(item, 1, macedPublicKey->macedKey) ||
!cbor_.getBinaryArray(item, 2, *privateKeyHandle)) {
LOG(ERROR) << "Error in decoding og response in generateEcdsaP256KeyPair.";
return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
}
return ScopedAStatus::ok();
}
ScopedAStatus
JavacardRemotelyProvisionedComponentDevice::beginSendData(
bool testMode, const std::vector<MacedPublicKey>& 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<MacedPublicKey>& 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<uint8_t>& 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<uint8_t>& endpointEncCertChain) {
std::vector<uint8_t> 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<uint8_t>* keysToSignMac, DeviceInfo* deviceInfo,
std::vector<uint8_t>& coseEncryptProtectedHeader, cppbor::Map& coseEncryptUnProtectedHeader,
std::vector<uint8_t>& partialCipheredData, uint32_t& respFlag) {
std::vector<uint8_t> decodedKeysToSignMac;
std::vector<uint8_t> decodedDeviceInfo;
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));
}
if (!cbor_.getBinaryArray(item, 1, decodedKeysToSignMac) ||
!cbor_.getBinaryArray(item, 2, decodedDeviceInfo) ||
!cbor_.getBinaryArray(item, 3, coseEncryptProtectedHeader) ||
!cbor_.getMapItem(item, 4, coseEncryptUnProtectedHeader) ||
!cbor_.getBinaryArray(item, 5, partialCipheredData) ||
!cbor_.getUint64(item, 6, respFlag)) {
LOG(ERROR) << "Error in decoding og response in finishSendData.";
return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
}
*keysToSignMac = decodedKeysToSignMac;
deviceInfo->deviceInfo = decodedDeviceInfo;
return ScopedAStatus::ok();
}
ScopedAStatus
JavacardRemotelyProvisionedComponentDevice::getResponse(
std::vector<uint8_t>& partialCipheredData, cppbor::Array& recipientStructure,
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));
}
if (!cbor_.getBinaryArray(item, 1, partialCipheredData) ||
!cbor_.getArrayItem(item, 2, recipientStructure) ||
!cbor_.getUint64(item, 3, respFlag)) {
LOG(ERROR) << "Error in decoding og response in getResponse.";
return km_utils::kmError2ScopedAStatus(KM_ERROR_UNKNOWN_ERROR);
}
return ScopedAStatus::ok();
}
ScopedAStatus
JavacardRemotelyProvisionedComponentDevice::generateCertificateRequest(bool testMode,
const std::vector<MacedPublicKey>& keysToSign,
const std::vector<uint8_t>& endpointEncCertChain,
const std::vector<uint8_t>& challenge,
DeviceInfo* deviceInfo, ProtectedData* protectedData,
std::vector<uint8_t>* keysToSignMac) {
std::vector<uint8_t> coseEncryptProtectedHeader;
cppbor::Map coseEncryptUnProtectedHeader;
cppbor::Array recipients;
std::vector<uint8_t> 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