Move verifyProtectedData into remote_prov_utils
This way, rkp_factory_extraction_tool can reuse the code to perform a test on the factory line if a partner so chooses. Test: rkp_factory_extraction_tool --self_test Test: atest VtsHalRemotelyProvisionedComponentTargetTest Bug: 239839050 Change-Id: I3989ba606750be77f1945a50fe2307a631d19d11
This commit is contained in:
parent
f1f6215c09
commit
2fc6f83df6
3 changed files with 252 additions and 129 deletions
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
|
||||
|
||||
#include <AndroidRemotelyProvisionedComponentDevice.h>
|
||||
|
@ -368,82 +369,6 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
|
|||
}
|
||||
}
|
||||
|
||||
ErrMsgOr<bytevec> getSessionKey(ErrMsgOr<std::pair<bytevec, bytevec>>& senderPubkey) {
|
||||
if (rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_25519 ||
|
||||
rpcHardwareInfo.supportedEekCurve == RpcHardwareInfo::CURVE_NONE) {
|
||||
return x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
} else {
|
||||
return ECDH_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
}
|
||||
}
|
||||
|
||||
void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const bytevec& keysToSignMac, const ProtectedData& protectedData,
|
||||
std::vector<BccEntryData>* bccOutput = nullptr) {
|
||||
auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
|
||||
ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
|
||||
ASSERT_TRUE(parsedProtectedData->asArray());
|
||||
ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
|
||||
|
||||
auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
|
||||
ASSERT_TRUE(senderPubkey) << senderPubkey.message();
|
||||
EXPECT_EQ(senderPubkey->second, eekId_);
|
||||
|
||||
auto sessionKey = getSessionKey(senderPubkey);
|
||||
ASSERT_TRUE(sessionKey) << sessionKey.message();
|
||||
|
||||
auto protectedDataPayload =
|
||||
decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
|
||||
ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
|
||||
|
||||
auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
|
||||
ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
|
||||
ASSERT_TRUE(parsedPayload->asArray());
|
||||
// Strongbox may contain additional certificate chain.
|
||||
EXPECT_LE(parsedPayload->asArray()->size(), 3U);
|
||||
|
||||
auto& signedMac = parsedPayload->asArray()->get(0);
|
||||
auto& bcc = parsedPayload->asArray()->get(1);
|
||||
ASSERT_TRUE(signedMac && signedMac->asArray());
|
||||
ASSERT_TRUE(bcc && bcc->asArray());
|
||||
|
||||
// BCC is [ pubkey, + BccEntry]
|
||||
auto bccContents = validateBcc(bcc->asArray());
|
||||
ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
|
||||
ASSERT_GT(bccContents->size(), 0U);
|
||||
|
||||
auto deviceInfoResult =
|
||||
parseAndValidateDeviceInfo(deviceInfo.deviceInfo, provisionable_.get());
|
||||
ASSERT_TRUE(deviceInfoResult) << deviceInfoResult.message();
|
||||
std::unique_ptr<cppbor::Map> deviceInfoMap = deviceInfoResult.moveValue();
|
||||
auto& signingKey = bccContents->back().pubKey;
|
||||
auto macKey = verifyAndParseCoseSign1(signedMac->asArray(), signingKey,
|
||||
cppbor::Array() // SignedMacAad
|
||||
.add(challenge_)
|
||||
.add(std::move(deviceInfoMap))
|
||||
.add(keysToSignMac)
|
||||
.encode());
|
||||
ASSERT_TRUE(macKey) << macKey.message();
|
||||
|
||||
auto coseMac0 = cppbor::Array()
|
||||
.add(cppbor::Map() // protected
|
||||
.add(ALGORITHM, HMAC_256)
|
||||
.canonicalize()
|
||||
.encode())
|
||||
.add(cppbor::Map()) // unprotected
|
||||
.add(keysToSign.encode()) // payload (keysToSign)
|
||||
.add(keysToSignMac); // tag
|
||||
|
||||
auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
|
||||
ASSERT_TRUE(macPayload) << macPayload.message();
|
||||
|
||||
if (bccOutput) {
|
||||
*bccOutput = std::move(*bccContents);
|
||||
}
|
||||
}
|
||||
|
||||
bytevec eekId_;
|
||||
size_t testEekLength_;
|
||||
EekChain testEekChain_;
|
||||
|
@ -470,7 +395,10 @@ TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
|
|||
&protectedData, &keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
checkProtectedData(deviceInfo, cppbor::Array(), keysToSignMac, protectedData);
|
||||
auto result = verifyProductionProtectedData(
|
||||
deviceInfo, cppbor::Array(), keysToSignMac, protectedData, testEekChain_, eekId_,
|
||||
rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
|
||||
ASSERT_TRUE(result) << result.message();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -492,22 +420,24 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
|
|||
&protectedData, &keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
std::vector<BccEntryData> firstBcc;
|
||||
checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
|
||||
&firstBcc);
|
||||
auto firstBcc = verifyProductionProtectedData(
|
||||
deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
|
||||
eekId_, rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
|
||||
ASSERT_TRUE(firstBcc) << firstBcc.message();
|
||||
|
||||
status = provisionable_->generateCertificateRequest(
|
||||
testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
|
||||
&protectedData, &keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
std::vector<BccEntryData> secondBcc;
|
||||
checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
|
||||
&secondBcc);
|
||||
auto secondBcc = verifyProductionProtectedData(
|
||||
deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
|
||||
eekId_, rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
|
||||
ASSERT_TRUE(secondBcc) << secondBcc.message();
|
||||
|
||||
// Verify that none of the keys in the first BCC are repeated in the second one.
|
||||
for (const auto& i : firstBcc) {
|
||||
for (auto& j : secondBcc) {
|
||||
for (const auto& i : *firstBcc) {
|
||||
for (auto& j : *secondBcc) {
|
||||
ASSERT_THAT(i.pubKey, testing::Not(testing::ElementsAreArray(j.pubKey)))
|
||||
<< "Found a repeated pubkey in two generateCertificateRequest test mode calls";
|
||||
}
|
||||
|
@ -550,7 +480,10 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
|
|||
&keysToSignMac);
|
||||
ASSERT_TRUE(status.isOk()) << status.getMessage();
|
||||
|
||||
checkProtectedData(deviceInfo, cborKeysToSign_, keysToSignMac, protectedData);
|
||||
auto result = verifyProductionProtectedData(
|
||||
deviceInfo, cborKeysToSign_, keysToSignMac, protectedData, testEekChain_, eekId_,
|
||||
rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
|
||||
ASSERT_TRUE(result) << result.message();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -143,10 +143,38 @@ JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name,
|
|||
|
||||
/**
|
||||
* Parses a DeviceInfo structure from the given CBOR data. The parsed data is then validated to
|
||||
* ensure it is formatted correctly and that it contains the required values for Remote Key
|
||||
* Provisioning.
|
||||
* ensure it contains the minimum required data at the time of manufacturing. This is only a
|
||||
* partial validation, as some fields may not be provisioned yet at the time this information
|
||||
* is parsed in the manufacturing process.
|
||||
*/
|
||||
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
|
||||
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateFactoryDeviceInfo(
|
||||
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable);
|
||||
|
||||
/**
|
||||
* Parses a DeviceInfo structure from the given CBOR data. The parsed data is then validated to
|
||||
* ensure it is formatted correctly and that it contains the required values for Remote Key
|
||||
* Provisioning. This is a full validation, and assumes the device is provisioned as if it is
|
||||
* suitable for the end user.
|
||||
*/
|
||||
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateProductionDeviceInfo(
|
||||
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable);
|
||||
|
||||
/**
|
||||
* Verify the protected data as if the device is still early in the factory process and may not
|
||||
* have all device identifiers provisioned yet.
|
||||
*/
|
||||
ErrMsgOr<std::vector<BccEntryData>> verifyFactoryProtectedData(
|
||||
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
|
||||
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
|
||||
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
|
||||
/**
|
||||
* Verify the protected data as if the device is a final production sample.
|
||||
*/
|
||||
ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
|
||||
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
|
||||
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
|
||||
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
|
||||
|
||||
} // namespace aidl::android::hardware::security::keymint::remote_prov
|
||||
|
|
|
@ -445,7 +445,7 @@ JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name, const cppbor:
|
|||
return JsonOutput::Ok(Json::writeString(factory, json));
|
||||
}
|
||||
|
||||
std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorType,
|
||||
std::string checkMapEntry(bool isFactory, const cppbor::Map& devInfo, cppbor::MajorType majorType,
|
||||
const std::string& entryName) {
|
||||
const std::unique_ptr<cppbor::Item>& val = devInfo.get(entryName);
|
||||
if (!val) {
|
||||
|
@ -454,6 +454,9 @@ std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorTyp
|
|||
if (val->type() != majorType) {
|
||||
return entryName + " has the wrong type.\n";
|
||||
}
|
||||
if (isFactory) {
|
||||
return "";
|
||||
}
|
||||
switch (majorType) {
|
||||
case cppbor::TSTR:
|
||||
if (val->asTstr()->value().size() <= 0) {
|
||||
|
@ -471,13 +474,17 @@ std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorTyp
|
|||
return "";
|
||||
}
|
||||
|
||||
std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorType,
|
||||
std::string checkMapEntry(bool isFactory, const cppbor::Map& devInfo, cppbor::MajorType majorType,
|
||||
const std::string& entryName, const cppbor::Array& allowList) {
|
||||
std::string error = checkMapEntry(devInfo, majorType, entryName);
|
||||
std::string error = checkMapEntry(isFactory, devInfo, majorType, entryName);
|
||||
if (!error.empty()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (isFactory) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const std::unique_ptr<cppbor::Item>& val = devInfo.get(entryName);
|
||||
for (auto i = allowList.begin(); i != allowList.end(); ++i) {
|
||||
if (**i == *val) {
|
||||
|
@ -488,31 +495,39 @@ std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorTyp
|
|||
}
|
||||
|
||||
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
|
||||
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable) {
|
||||
const cppbor::Array kAllowedVbStates = {"green", "yellow", "orange"};
|
||||
const cppbor::Array kAllowedBootloaderStates = {"locked", "unlocked"};
|
||||
const cppbor::Array kAllowedSecurityLevels = {"tee", "strongbox"};
|
||||
const cppbor::Array kAllowedAttIdStates = {"locked", "open"};
|
||||
const cppbor::Array kAllowedFused = {0, 1};
|
||||
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable,
|
||||
bool isFactory) {
|
||||
const cppbor::Array kValidVbStates = {"green", "yellow", "orange"};
|
||||
const cppbor::Array kValidBootloaderStates = {"locked", "unlocked"};
|
||||
const cppbor::Array kValidSecurityLevels = {"tee", "strongbox"};
|
||||
const cppbor::Array kValidAttIdStates = {"locked", "open"};
|
||||
const cppbor::Array kValidFused = {0, 1};
|
||||
|
||||
constexpr std::array kAttestationIdEntrySet = {"brand", "manufacturer", "product", "model",
|
||||
"device"};
|
||||
struct AttestationIdEntry {
|
||||
const char* id;
|
||||
bool alwaysValidate;
|
||||
};
|
||||
constexpr AttestationIdEntry kAttestationIdEntrySet[] = {{"brand", false},
|
||||
{"manufacturer", true},
|
||||
{"product", false},
|
||||
{"model", false},
|
||||
{"device", false}};
|
||||
|
||||
auto [parsedVerifiedDeviceInfo, ignore1, errMsg] = cppbor::parse(deviceInfoBytes);
|
||||
if (!parsedVerifiedDeviceInfo) {
|
||||
return errMsg;
|
||||
}
|
||||
|
||||
std::unique_ptr<cppbor::Map> deviceInfo(parsedVerifiedDeviceInfo->asMap());
|
||||
if (!deviceInfo) {
|
||||
std::unique_ptr<cppbor::Map> parsed(parsedVerifiedDeviceInfo->asMap());
|
||||
if (!parsed) {
|
||||
return "DeviceInfo must be a CBOR map.";
|
||||
}
|
||||
parsedVerifiedDeviceInfo.release();
|
||||
|
||||
if (deviceInfo->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
|
||||
if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
|
||||
return "DeviceInfo ordering is non-canonical.";
|
||||
}
|
||||
const std::unique_ptr<cppbor::Item>& version = deviceInfo->get("version");
|
||||
const std::unique_ptr<cppbor::Item>& version = parsed->get("version");
|
||||
if (!version) {
|
||||
return "Device info is missing version";
|
||||
}
|
||||
|
@ -526,14 +541,15 @@ ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
|
|||
") does not match the remotely provisioned component version (" +
|
||||
std::to_string(info.versionNumber) + ").";
|
||||
}
|
||||
std::string errorString;
|
||||
std::string error;
|
||||
switch (version->asUint()->value()) {
|
||||
case 2:
|
||||
for (const auto& attId : kAttestationIdEntrySet) {
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, attId);
|
||||
for (const auto& entry : kAttestationIdEntrySet) {
|
||||
error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
|
||||
entry.id);
|
||||
}
|
||||
if (!errorString.empty()) {
|
||||
return errorString +
|
||||
if (!error.empty()) {
|
||||
return error +
|
||||
"Attestation IDs are missing or malprovisioned. If this test is being\n"
|
||||
"run against an early proto or EVT build, this error is probably WAI\n"
|
||||
"and indicates that Device IDs were not provisioned in the factory. If\n"
|
||||
|
@ -541,35 +557,181 @@ ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
|
|||
"something is likely wrong with the factory provisioning process.";
|
||||
}
|
||||
// TODO: Refactor the KeyMint code that validates these fields and include it here.
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "vb_state", kAllowedVbStates);
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "bootloader_state",
|
||||
kAllowedBootloaderStates);
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::BSTR, "vbmeta_digest");
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "system_patch_level");
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "boot_patch_level");
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "vendor_patch_level");
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "fused", kAllowedFused);
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "security_level",
|
||||
kAllowedSecurityLevels);
|
||||
if (deviceInfo->get("security_level")->asTstr()->value() == "tee") {
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "os_version");
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "vb_state", kValidVbStates);
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "bootloader_state",
|
||||
kValidBootloaderStates);
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::BSTR, "vbmeta_digest");
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "system_patch_level");
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "boot_patch_level");
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "vendor_patch_level");
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "fused", kValidFused);
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "security_level",
|
||||
kValidSecurityLevels);
|
||||
if (parsed->get("security_level") && parsed->get("security_level")->asTstr() &&
|
||||
parsed->get("security_level")->asTstr()->value() == "tee") {
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "os_version");
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "security_level",
|
||||
kAllowedSecurityLevels);
|
||||
errorString +=
|
||||
checkMapEntry(*deviceInfo, cppbor::TSTR, "att_id_state", kAllowedAttIdStates);
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "security_level",
|
||||
kValidSecurityLevels);
|
||||
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "att_id_state",
|
||||
kValidAttIdStates);
|
||||
break;
|
||||
default:
|
||||
return "Unrecognized version: " + std::to_string(version->asUint()->value());
|
||||
}
|
||||
|
||||
if (!errorString.empty()) {
|
||||
return errorString;
|
||||
if (!error.empty()) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return std::move(deviceInfo);
|
||||
return std::move(parsed);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::security::keymint::remote_prov
|
||||
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateFactoryDeviceInfo(
|
||||
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable) {
|
||||
return parseAndValidateDeviceInfo(deviceInfoBytes, provisionable, /*isFactory=*/true);
|
||||
}
|
||||
|
||||
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateProductionDeviceInfo(
|
||||
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable) {
|
||||
return parseAndValidateDeviceInfo(deviceInfoBytes, provisionable, /*isFactory=*/false);
|
||||
}
|
||||
|
||||
ErrMsgOr<bytevec> getSessionKey(ErrMsgOr<std::pair<bytevec, bytevec>>& senderPubkey,
|
||||
const EekChain& eekChain, int32_t supportedEekCurve) {
|
||||
if (supportedEekCurve == RpcHardwareInfo::CURVE_25519 ||
|
||||
supportedEekCurve == RpcHardwareInfo::CURVE_NONE) {
|
||||
return x25519_HKDF_DeriveKey(eekChain.last_pubkey, eekChain.last_privkey,
|
||||
senderPubkey->first, false /* senderIsA */);
|
||||
} else {
|
||||
return ECDH_HKDF_DeriveKey(eekChain.last_pubkey, eekChain.last_privkey, senderPubkey->first,
|
||||
false /* senderIsA */);
|
||||
}
|
||||
}
|
||||
|
||||
ErrMsgOr<std::vector<BccEntryData>> verifyProtectedData(
|
||||
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
|
||||
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
|
||||
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
|
||||
bool isFactory) {
|
||||
auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
|
||||
if (!parsedProtectedData) {
|
||||
return protDataErrMsg;
|
||||
}
|
||||
if (!parsedProtectedData->asArray()) {
|
||||
return "Protected data is not a CBOR array.";
|
||||
}
|
||||
if (parsedProtectedData->asArray()->size() != kCoseEncryptEntryCount) {
|
||||
return "The protected data COSE_encrypt structure must have " +
|
||||
std::to_string(kCoseEncryptEntryCount) + " entries, but it only has " +
|
||||
std::to_string(parsedProtectedData->asArray()->size());
|
||||
}
|
||||
|
||||
auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
|
||||
if (!senderPubkey) {
|
||||
return senderPubkey.message();
|
||||
}
|
||||
if (senderPubkey->second != eekId) {
|
||||
return "The COSE_encrypt recipient does not match the expected EEK identifier";
|
||||
}
|
||||
|
||||
auto sessionKey = getSessionKey(senderPubkey, eekChain, supportedEekCurve);
|
||||
if (!sessionKey) {
|
||||
return sessionKey.message();
|
||||
}
|
||||
|
||||
auto protectedDataPayload =
|
||||
decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
|
||||
if (!protectedDataPayload) {
|
||||
return protectedDataPayload.message();
|
||||
}
|
||||
|
||||
auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
|
||||
if (!parsedPayload) {
|
||||
return "Failed to parse payload: " + payloadErrMsg;
|
||||
}
|
||||
if (!parsedPayload->asArray()) {
|
||||
return "The protected data payload must be an Array.";
|
||||
}
|
||||
if (parsedPayload->asArray()->size() != 3U && parsedPayload->asArray()->size() != 2U) {
|
||||
return "The protected data payload must contain SignedMAC and BCC. It may optionally "
|
||||
"contain AdditionalDKSignatures. However, the parsed payload has " +
|
||||
std::to_string(parsedPayload->asArray()->size()) + " entries.";
|
||||
}
|
||||
|
||||
auto& signedMac = parsedPayload->asArray()->get(0);
|
||||
auto& bcc = parsedPayload->asArray()->get(1);
|
||||
if (!signedMac->asArray()) {
|
||||
return "The SignedMAC in the protected data payload is not an Array.";
|
||||
}
|
||||
if (!bcc->asArray()) {
|
||||
return "The BCC in the protected data payload is not an Array.";
|
||||
}
|
||||
|
||||
// BCC is [ pubkey, + BccEntry]
|
||||
auto bccContents = validateBcc(bcc->asArray());
|
||||
if (!bccContents) {
|
||||
return bccContents.message() + "\n" + prettyPrint(bcc.get());
|
||||
}
|
||||
if (bccContents->size() == 0U) {
|
||||
return "The BCC is empty. It must contain at least one entry.";
|
||||
}
|
||||
|
||||
auto deviceInfoResult =
|
||||
parseAndValidateDeviceInfo(deviceInfo.deviceInfo, provisionable, isFactory);
|
||||
if (!deviceInfoResult) {
|
||||
return deviceInfoResult.message();
|
||||
}
|
||||
std::unique_ptr<cppbor::Map> deviceInfoMap = deviceInfoResult.moveValue();
|
||||
auto& signingKey = bccContents->back().pubKey;
|
||||
auto macKey = verifyAndParseCoseSign1(signedMac->asArray(), signingKey,
|
||||
cppbor::Array() // SignedMacAad
|
||||
.add(challenge)
|
||||
.add(std::move(deviceInfoMap))
|
||||
.add(keysToSignMac)
|
||||
.encode());
|
||||
if (!macKey) {
|
||||
return macKey.message();
|
||||
}
|
||||
|
||||
auto coseMac0 = cppbor::Array()
|
||||
.add(cppbor::Map() // protected
|
||||
.add(ALGORITHM, HMAC_256)
|
||||
.canonicalize()
|
||||
.encode())
|
||||
.add(cppbor::Map()) // unprotected
|
||||
.add(keysToSign.encode()) // payload (keysToSign)
|
||||
.add(keysToSignMac); // tag
|
||||
|
||||
auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
|
||||
if (!macPayload) {
|
||||
return macPayload.message();
|
||||
}
|
||||
|
||||
return *bccContents;
|
||||
}
|
||||
|
||||
ErrMsgOr<std::vector<BccEntryData>> verifyFactoryProtectedData(
|
||||
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
|
||||
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
|
||||
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
|
||||
return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain,
|
||||
eekId, supportedEekCurve, provisionable, challenge,
|
||||
/*isFactory=*/true);
|
||||
}
|
||||
|
||||
ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
|
||||
const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
|
||||
const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
|
||||
const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
|
||||
IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
|
||||
return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain,
|
||||
eekId, supportedEekCurve, provisionable, challenge,
|
||||
/*isFactory=*/false);
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::security::keymint::remote_prov
|
Loading…
Reference in a new issue