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