Merge changes from topic "check-device-info"

* changes:
  Move verifyProtectedData into remote_prov_utils
  Move the device info validation to a helper library
This commit is contained in:
Seth Moore
2022-09-21 12:42:41 +00:00
committed by Gerrit Code Review
4 changed files with 353 additions and 202 deletions

View File

@@ -14,6 +14,8 @@
* limitations under the License.
*/
#include <memory>
#include <string>
#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
#include <AndroidRemotelyProvisionedComponentDevice.h>
@@ -58,26 +60,6 @@ using testing::MatchesRegex;
using namespace remote_prov;
using namespace keymaster;
std::set<std::string> getAllowedVbStates() {
return {"green", "yellow", "orange"};
}
std::set<std::string> getAllowedBootloaderStates() {
return {"locked", "unlocked"};
}
std::set<std::string> getAllowedSecurityLevels() {
return {"tee", "strongbox"};
}
std::set<std::string> getAllowedAttIdStates() {
return {"locked", "open"};
}
std::set<std::string> getAttestationIdEntrySet() {
return {"brand", "manufacturer", "product", "model", "device"};
}
bytevec string_to_bytevec(const char* s) {
const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
return bytevec(p, p + strlen(s));
@@ -387,177 +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 [deviceInfoMap, __2, deviceInfoErrMsg] = cppbor::parse(deviceInfo.deviceInfo);
ASSERT_TRUE(deviceInfoMap) << "Failed to parse deviceInfo: " << deviceInfoErrMsg;
ASSERT_TRUE(deviceInfoMap->asMap());
checkDeviceInfo(*deviceInfoMap->asMap(), deviceInfo.deviceInfo);
auto& signingKey = bccContents->back().pubKey;
deviceInfoMap->asMap()->canonicalize();
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);
}
}
std::optional<std::string> assertAttribute(const cppbor::Map& devInfo,
cppbor::MajorType majorType, std::string entryName) {
const auto& val = devInfo.get(entryName);
if (!val) return entryName + " is missing.\n";
if (val->type() != majorType) return entryName + " has the wrong type.\n";
switch (majorType) {
case cppbor::TSTR:
if (val->asTstr()->value().size() <= 0) {
return entryName + " is present but the value is empty.\n";
}
break;
case cppbor::BSTR:
if (val->asBstr()->value().size() <= 0) {
return entryName + " is present but the value is empty.\n";
}
break;
default:
break;
}
return {};
}
void checkType(const cppbor::Map& devInfo, cppbor::MajorType majorType, std::string entryName) {
if (auto error = assertAttribute(devInfo, majorType, entryName)) {
FAIL() << *error;
}
}
void checkDeviceInfo(const cppbor::Map& deviceInfo, bytevec deviceInfoBytes) {
EXPECT_EQ(deviceInfo.clone()->asMap()->canonicalize().encode(), deviceInfoBytes)
<< "DeviceInfo ordering is non-canonical.";
const auto& version = deviceInfo.get("version");
ASSERT_TRUE(version);
ASSERT_TRUE(version->asUint());
RpcHardwareInfo info;
provisionable_->getHardwareInfo(&info);
ASSERT_EQ(version->asUint()->value(), info.versionNumber);
std::set<std::string> allowList;
std::string problemEntries;
switch (version->asUint()->value()) {
// These fields became mandated in version 2.
case 2:
for (auto attId : getAttestationIdEntrySet()) {
if (auto errMsg = assertAttribute(deviceInfo, cppbor::TSTR, attId)) {
problemEntries += *errMsg;
}
}
EXPECT_EQ("", problemEntries)
<< problemEntries
<< "Attestation IDs are missing or malprovisioned. If this test is being "
"run against an early proto or EVT build, this error is probably WAI "
"and indicates that Device IDs were not provisioned in the factory. If "
"this error is returned on a DVT or later build revision, then "
"something is likely wrong with the factory provisioning process.";
// TODO: Refactor the KeyMint code that validates these fields and include it here.
checkType(deviceInfo, cppbor::TSTR, "vb_state");
allowList = getAllowedVbStates();
EXPECT_NE(allowList.find(deviceInfo.get("vb_state")->asTstr()->value()),
allowList.end());
checkType(deviceInfo, cppbor::TSTR, "bootloader_state");
allowList = getAllowedBootloaderStates();
EXPECT_NE(allowList.find(deviceInfo.get("bootloader_state")->asTstr()->value()),
allowList.end());
checkType(deviceInfo, cppbor::BSTR, "vbmeta_digest");
checkType(deviceInfo, cppbor::UINT, "system_patch_level");
checkType(deviceInfo, cppbor::UINT, "boot_patch_level");
checkType(deviceInfo, cppbor::UINT, "vendor_patch_level");
checkType(deviceInfo, cppbor::UINT, "fused");
EXPECT_LT(deviceInfo.get("fused")->asUint()->value(), 2); // Must be 0 or 1.
checkType(deviceInfo, cppbor::TSTR, "security_level");
allowList = getAllowedSecurityLevels();
EXPECT_NE(allowList.find(deviceInfo.get("security_level")->asTstr()->value()),
allowList.end());
if (deviceInfo.get("security_level")->asTstr()->value() == "tee") {
checkType(deviceInfo, cppbor::TSTR, "os_version");
}
break;
case 1:
checkType(deviceInfo, cppbor::TSTR, "security_level");
allowList = getAllowedSecurityLevels();
EXPECT_NE(allowList.find(deviceInfo.get("security_level")->asTstr()->value()),
allowList.end());
if (version->asUint()->value() == 1) {
checkType(deviceInfo, cppbor::TSTR, "att_id_state");
allowList = getAllowedAttIdStates();
EXPECT_NE(allowList.find(deviceInfo.get("att_id_state")->asTstr()->value()),
allowList.end());
}
break;
default:
FAIL() << "Unrecognized version: " << version->asUint()->value();
}
}
bytevec eekId_;
size_t testEekLength_;
EekChain testEekChain_;
@@ -584,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();
}
}
@@ -606,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";
}
@@ -664,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();
}
}

View File

@@ -65,6 +65,7 @@ cc_library {
],
shared_libs: [
"libbase",
"libbinder_ndk",
"libcppbor_external",
"libcppcose_rkp",
"libcrypto",

View File

@@ -16,7 +16,9 @@
#pragma once
#include <memory>
#include <vector>
#include "aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h"
#include <keymaster/cppcose/cppcose.h>
@@ -139,4 +141,40 @@ struct JsonOutput {
JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name,
const cppbor::Array& csr);
/**
* Parses a DeviceInfo structure from the given CBOR data. The parsed data is then validated to
* 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>> 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

View File

@@ -15,7 +15,11 @@
*/
#include <iterator>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include "aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h"
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
#include <android-base/properties.h>
@@ -441,4 +445,293 @@ JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name, const cppbor:
return JsonOutput::Ok(Json::writeString(factory, json));
}
} // namespace aidl::android::hardware::security::keymint::remote_prov
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) {
return entryName + " is missing.\n";
}
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) {
return entryName + " is present but the value is empty.\n";
}
break;
case cppbor::BSTR:
if (val->asBstr()->value().size() <= 0) {
return entryName + " is present but the value is empty.\n";
}
break;
default:
break;
}
return "";
}
std::string checkMapEntry(bool isFactory, const cppbor::Map& devInfo, cppbor::MajorType majorType,
const std::string& entryName, const cppbor::Array& allowList) {
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) {
return "";
}
}
return entryName + " has an invalid value.\n";
}
ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
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};
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> parsed(parsedVerifiedDeviceInfo->asMap());
if (!parsed) {
return "DeviceInfo must be a CBOR map.";
}
parsedVerifiedDeviceInfo.release();
if (parsed->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
return "DeviceInfo ordering is non-canonical.";
}
const std::unique_ptr<cppbor::Item>& version = parsed->get("version");
if (!version) {
return "Device info is missing version";
}
if (!version->asUint()) {
return "version must be an unsigned integer";
}
RpcHardwareInfo info;
provisionable->getHardwareInfo(&info);
if (version->asUint()->value() != info.versionNumber) {
return "DeviceInfo version (" + std::to_string(version->asUint()->value()) +
") does not match the remotely provisioned component version (" +
std::to_string(info.versionNumber) + ").";
}
std::string error;
switch (version->asUint()->value()) {
case 2:
for (const auto& entry : kAttestationIdEntrySet) {
error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
entry.id);
}
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"
"this error is returned on a DVT or later build revision, then\n"
"something is likely wrong with the factory provisioning process.";
}
// TODO: Refactor the KeyMint code that validates these fields and include it here.
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:
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 (!error.empty()) {
return error;
}
return std::move(parsed);
}
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