Move the device info validation to a helper library am: f1f6215c09 am: 7641914291 am: 648d6e245c am: 4dd84d0005 am: 84001d0e59
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/2215305
Change-Id: I7c0512b284d8e343454d23be61137b04e924c182
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 2e282e0..bafe161 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <memory>
#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
#include <AndroidRemotelyProvisionedComponentDevice.h>
@@ -58,26 +59,6 @@
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));
@@ -433,12 +414,11 @@
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 deviceInfoResult =
+ parseAndValidateDeviceInfo(deviceInfo.deviceInfo, provisionable_.get());
+ ASSERT_TRUE(deviceInfoResult) << deviceInfoResult.message();
+ std::unique_ptr<cppbor::Map> deviceInfoMap = deviceInfoResult.moveValue();
auto& signingKey = bccContents->back().pubKey;
- deviceInfoMap->asMap()->canonicalize();
auto macKey = verifyAndParseCoseSign1(signedMac->asArray(), signingKey,
cppbor::Array() // SignedMacAad
.add(challenge_)
@@ -464,100 +444,6 @@
}
}
- 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_;
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
index bf2ab02..3f48320 100644
--- a/security/keymint/support/Android.bp
+++ b/security/keymint/support/Android.bp
@@ -65,6 +65,7 @@
],
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 f3b8608..b1a6880 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 <memory>
#include <vector>
+#include "aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h"
#include <keymaster/cppcose/cppcose.h>
@@ -139,4 +141,12 @@
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 is formatted correctly and that it contains the required values for Remote Key
+ * Provisioning.
+ */
+ErrMsgOr<std::unique_ptr<cppbor::Map>> parseAndValidateDeviceInfo(
+ const std::vector<uint8_t>& deviceInfoBytes, IRemotelyProvisionedComponent* provisionable);
+
} // 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 0dbea5b..a553f16 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -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,131 @@
return JsonOutput::Ok(Json::writeString(factory, json));
}
+std::string checkMapEntry(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";
+ }
+ 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(const cppbor::Map& devInfo, cppbor::MajorType majorType,
+ const std::string& entryName, const cppbor::Array& allowList) {
+ std::string error = checkMapEntry(devInfo, majorType, entryName);
+ if (!error.empty()) {
+ return error;
+ }
+
+ 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) {
+ 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};
+
+ constexpr std::array kAttestationIdEntrySet = {"brand", "manufacturer", "product", "model",
+ "device"};
+
+ auto [parsedVerifiedDeviceInfo, ignore1, errMsg] = cppbor::parse(deviceInfoBytes);
+ if (!parsedVerifiedDeviceInfo) {
+ return errMsg;
+ }
+
+ std::unique_ptr<cppbor::Map> deviceInfo(parsedVerifiedDeviceInfo->asMap());
+ if (!deviceInfo) {
+ return "DeviceInfo must be a CBOR map.";
+ }
+ parsedVerifiedDeviceInfo.release();
+
+ if (deviceInfo->clone()->asMap()->canonicalize().encode() != deviceInfoBytes) {
+ return "DeviceInfo ordering is non-canonical.";
+ }
+ const std::unique_ptr<cppbor::Item>& version = deviceInfo->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 errorString;
+ switch (version->asUint()->value()) {
+ case 2:
+ for (const auto& attId : kAttestationIdEntrySet) {
+ errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, attId);
+ }
+ if (!errorString.empty()) {
+ return errorString +
+ "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.
+ 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");
+ }
+ break;
+ case 1:
+ errorString += checkMapEntry(*deviceInfo, cppbor::TSTR, "security_level",
+ kAllowedSecurityLevels);
+ errorString +=
+ checkMapEntry(*deviceInfo, cppbor::TSTR, "att_id_state", kAllowedAttIdStates);
+ break;
+ default:
+ return "Unrecognized version: " + std::to_string(version->asUint()->value());
+ }
+
+ if (!errorString.empty()) {
+ return errorString;
+ }
+
+ return std::move(deviceInfo);
+}
+
} // namespace aidl::android::hardware::security::keymint::remote_prov