diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp index 2e282e0925..e1f65fec78 100644 --- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp +++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include +#include #define LOG_TAG "VtsRemotelyProvisionableComponentTests" #include @@ -58,26 +60,6 @@ using testing::MatchesRegex; using namespace remote_prov; using namespace keymaster; -std::set getAllowedVbStates() { - return {"green", "yellow", "orange"}; -} - -std::set getAllowedBootloaderStates() { - return {"locked", "unlocked"}; -} - -std::set getAllowedSecurityLevels() { - return {"tee", "strongbox"}; -} - -std::set getAllowedAttIdStates() { - return {"locked", "open"}; -} - -std::set getAttestationIdEntrySet() { - return {"brand", "manufacturer", "product", "model", "device"}; -} - bytevec string_to_bytevec(const char* s) { const uint8_t* p = reinterpret_cast(s); return bytevec(p, p + strlen(s)); @@ -387,177 +369,6 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests { } } - ErrMsgOr getSessionKey(ErrMsgOr>& 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* 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 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 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 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 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(); } } diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp index bf2ab02cb2..3f4832085c 100644 --- a/security/keymint/support/Android.bp +++ b/security/keymint/support/Android.bp @@ -65,6 +65,7 @@ cc_library { ], shared_libs: [ "libbase", + "libbinder_ndk", "libcppbor_external", "libcppcose_rkp", "libcrypto", diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h index f3b8608b36..9ea20ace0e 100644 --- a/security/keymint/support/include/remote_prov/remote_prov_utils.h +++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h @@ -16,7 +16,9 @@ #pragma once +#include #include +#include "aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h" #include @@ -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> parseAndValidateFactoryDeviceInfo( + const std::vector& 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> parseAndValidateProductionDeviceInfo( + const std::vector& 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> verifyFactoryProtectedData( + const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const std::vector& keysToSignMac, const ProtectedData& protectedData, + const EekChain& eekChain, const std::vector& eekId, int32_t supportedEekCurve, + IRemotelyProvisionedComponent* provisionable, const std::vector& challenge); +/** + * Verify the protected data as if the device is a final production sample. + */ +ErrMsgOr> verifyProductionProtectedData( + const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const std::vector& keysToSignMac, const ProtectedData& protectedData, + const EekChain& eekChain, const std::vector& eekId, int32_t supportedEekCurve, + IRemotelyProvisionedComponent* provisionable, const std::vector& challenge); + } // namespace aidl::android::hardware::security::keymint::remote_prov diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp index 0dbea5b68c..06514961da 100644 --- a/security/keymint/support/remote_prov_utils.cpp +++ b/security/keymint/support/remote_prov_utils.cpp @@ -15,7 +15,11 @@ */ #include +#include +#include +#include #include +#include "aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h" #include #include @@ -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& 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& 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> parseAndValidateDeviceInfo( + const std::vector& 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 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& 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> parseAndValidateFactoryDeviceInfo( + const std::vector& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable) { + return parseAndValidateDeviceInfo(deviceInfoBytes, provisionable, /*isFactory=*/true); +} + +ErrMsgOr> parseAndValidateProductionDeviceInfo( + const std::vector& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable) { + return parseAndValidateDeviceInfo(deviceInfoBytes, provisionable, /*isFactory=*/false); +} + +ErrMsgOr getSessionKey(ErrMsgOr>& 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> verifyProtectedData( + const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const std::vector& keysToSignMac, const ProtectedData& protectedData, + const EekChain& eekChain, const std::vector& eekId, int32_t supportedEekCurve, + IRemotelyProvisionedComponent* provisionable, const std::vector& 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 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> verifyFactoryProtectedData( + const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const std::vector& keysToSignMac, const ProtectedData& protectedData, + const EekChain& eekChain, const std::vector& eekId, int32_t supportedEekCurve, + IRemotelyProvisionedComponent* provisionable, const std::vector& challenge) { + return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain, + eekId, supportedEekCurve, provisionable, challenge, + /*isFactory=*/true); +} + +ErrMsgOr> verifyProductionProtectedData( + const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign, + const std::vector& keysToSignMac, const ProtectedData& protectedData, + const EekChain& eekChain, const std::vector& eekId, int32_t supportedEekCurve, + IRemotelyProvisionedComponent* provisionable, const std::vector& challenge) { + return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain, + eekId, supportedEekCurve, provisionable, challenge, + /*isFactory=*/false); +} + +} // namespace aidl::android::hardware::security::keymint::remote_prov \ No newline at end of file