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:
Seth Moore 2022-09-13 16:10:11 -07:00
parent f1f6215c09
commit 2fc6f83df6
3 changed files with 252 additions and 129 deletions

View file

@ -15,6 +15,7 @@
*/ */
#include <memory> #include <memory>
#include <string>
#define LOG_TAG "VtsRemotelyProvisionableComponentTests" #define LOG_TAG "VtsRemotelyProvisionableComponentTests"
#include <AndroidRemotelyProvisionedComponentDevice.h> #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_; bytevec eekId_;
size_t testEekLength_; size_t testEekLength_;
EekChain testEekChain_; EekChain testEekChain_;
@ -470,7 +395,10 @@ TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
&protectedData, &keysToSignMac); &protectedData, &keysToSignMac);
ASSERT_TRUE(status.isOk()) << status.getMessage(); 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); &protectedData, &keysToSignMac);
ASSERT_TRUE(status.isOk()) << status.getMessage(); ASSERT_TRUE(status.isOk()) << status.getMessage();
std::vector<BccEntryData> firstBcc; auto firstBcc = verifyProductionProtectedData(
checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
&firstBcc); eekId_, rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
ASSERT_TRUE(firstBcc) << firstBcc.message();
status = provisionable_->generateCertificateRequest( status = provisionable_->generateCertificateRequest(
testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo, testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
&protectedData, &keysToSignMac); &protectedData, &keysToSignMac);
ASSERT_TRUE(status.isOk()) << status.getMessage(); ASSERT_TRUE(status.isOk()) << status.getMessage();
std::vector<BccEntryData> secondBcc; auto secondBcc = verifyProductionProtectedData(
checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
&secondBcc); 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. // Verify that none of the keys in the first BCC are repeated in the second one.
for (const auto& i : firstBcc) { for (const auto& i : *firstBcc) {
for (auto& j : secondBcc) { for (auto& j : *secondBcc) {
ASSERT_THAT(i.pubKey, testing::Not(testing::ElementsAreArray(j.pubKey))) ASSERT_THAT(i.pubKey, testing::Not(testing::ElementsAreArray(j.pubKey)))
<< "Found a repeated pubkey in two generateCertificateRequest test mode calls"; << "Found a repeated pubkey in two generateCertificateRequest test mode calls";
} }
@ -550,7 +480,10 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
&keysToSignMac); &keysToSignMac);
ASSERT_TRUE(status.isOk()) << status.getMessage(); 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();
} }
} }

View file

@ -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 * 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 * ensure it contains the minimum required data at the time of manufacturing. This is only a
* Provisioning. * 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); 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 } // namespace aidl::android::hardware::security::keymint::remote_prov

View file

@ -445,7 +445,7 @@ JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name, const cppbor:
return JsonOutput::Ok(Json::writeString(factory, json)); 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::string& entryName) {
const std::unique_ptr<cppbor::Item>& val = devInfo.get(entryName); const std::unique_ptr<cppbor::Item>& val = devInfo.get(entryName);
if (!val) { if (!val) {
@ -454,6 +454,9 @@ std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorTyp
if (val->type() != majorType) { if (val->type() != majorType) {
return entryName + " has the wrong type.\n"; return entryName + " has the wrong type.\n";
} }
if (isFactory) {
return "";
}
switch (majorType) { switch (majorType) {
case cppbor::TSTR: case cppbor::TSTR:
if (val->asTstr()->value().size() <= 0) { if (val->asTstr()->value().size() <= 0) {
@ -471,13 +474,17 @@ std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorTyp
return ""; 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) { 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()) { if (!error.empty()) {
return error; return error;
} }
if (isFactory) {
return "";
}
const std::unique_ptr<cppbor::Item>& val = devInfo.get(entryName); const std::unique_ptr<cppbor::Item>& val = devInfo.get(entryName);
for (auto i = allowList.begin(); i != allowList.end(); ++i) { for (auto i = allowList.begin(); i != allowList.end(); ++i) {
if (**i == *val) { if (**i == *val) {
@ -488,31 +495,39 @@ std::string checkMapEntry(const cppbor::Map& devInfo, cppbor::MajorType majorTyp
} }
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo( ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable) { const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable,
const cppbor::Array kAllowedVbStates = {"green", "yellow", "orange"}; bool isFactory) {
const cppbor::Array kAllowedBootloaderStates = {"locked", "unlocked"}; const cppbor::Array kValidVbStates = {"green", "yellow", "orange"};
const cppbor::Array kAllowedSecurityLevels = {"tee", "strongbox"}; const cppbor::Array kValidBootloaderStates = {"locked", "unlocked"};
const cppbor::Array kAllowedAttIdStates = {"locked", "open"}; const cppbor::Array kValidSecurityLevels = {"tee", "strongbox"};
const cppbor::Array kAllowedFused = {0, 1}; const cppbor::Array kValidAttIdStates = {"locked", "open"};
const cppbor::Array kValidFused = {0, 1};
constexpr std::array kAttestationIdEntrySet = {"brand", "manufacturer", "product", "model", struct AttestationIdEntry {
"device"}; 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); auto [parsedVerifiedDeviceInfo, ignore1, errMsg] = cppbor::parse(deviceInfoBytes);
if (!parsedVerifiedDeviceInfo) { if (!parsedVerifiedDeviceInfo) {
return errMsg; return errMsg;
} }
std::unique_ptr<cppbor::Map> deviceInfo(parsedVerifiedDeviceInfo->asMap()); std::unique_ptr<cppbor::Map> parsed(parsedVerifiedDeviceInfo->asMap());
if (!deviceInfo) { if (!parsed) {
return "DeviceInfo must be a CBOR map."; return "DeviceInfo must be a CBOR map.";
} }
parsedVerifiedDeviceInfo.release(); parsedVerifiedDeviceInfo.release();
if (deviceInfo->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) { if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
return "DeviceInfo ordering is non-canonical."; 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) { if (!version) {
return "Device info is missing 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 (" + ") does not match the remotely provisioned component version (" +
std::to_string(info.versionNumber) + ")."; std::to_string(info.versionNumber) + ").";
} }
std::string errorString; std::string error;
switch (version->asUint()->value()) { switch (version->asUint()->value()) {
case 2: case 2:
for (const auto& attId : kAttestationIdEntrySet) { for (const auto& entry : kAttestationIdEntrySet) {
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, attId); error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
entry.id);
} }
if (!errorString.empty()) { if (!error.empty()) {
return errorString + return error +
"Attestation IDs are missing or malprovisioned. If this test is being\n" "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" "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" "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."; "something is likely wrong with the factory provisioning process.";
} }
// TODO: Refactor the KeyMint code that validates these fields and include it here. // TODO: Refactor the KeyMint code that validates these fields and include it here.
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "vb_state", kAllowedVbStates); error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "vb_state", kValidVbStates);
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "bootloader_state", error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "bootloader_state",
kAllowedBootloaderStates); kValidBootloaderStates);
errorString += checkMapEntry(*deviceInfo, cppbor::BSTR, "vbmeta_digest"); error += checkMapEntry(isFactory, *parsed, cppbor::BSTR, "vbmeta_digest");
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "system_patch_level"); error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "system_patch_level");
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "boot_patch_level"); error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "boot_patch_level");
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "vendor_patch_level"); error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "vendor_patch_level");
errorString += checkMapEntry(*deviceInfo, cppbor::UINT, "fused", kAllowedFused); error += checkMapEntry(isFactory, *parsed, cppbor::UINT, "fused", kValidFused);
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "security_level", error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "security_level",
kAllowedSecurityLevels); kValidSecurityLevels);
if (deviceInfo->get("security_level")->asTstr()->value() == "tee") { if (parsed->get("security_level") && parsed->get("security_level")->asTstr() &&
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "os_version"); parsed->get("security_level")->asTstr()->value() == "tee") {
error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "os_version");
} }
break; break;
case 1: case 1:
errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "security_level", error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "security_level",
kAllowedSecurityLevels); kValidSecurityLevels);
errorString += error += checkMapEntry(isFactory, *parsed, cppbor::TSTR, "att_id_state",
checkMapEntry(*deviceInfo, cppbor::TSTR, "att_id_state", kAllowedAttIdStates); kValidAttIdStates);
break; break;
default: default:
return "Unrecognized version: " + std::to_string(version->asUint()->value()); return "Unrecognized version: " + std::to_string(version->asUint()->value());
} }
if (!errorString.empty()) { if (!error.empty()) {
return errorString; 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