Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1f6215c authored by Seth Moore's avatar Seth Moore
Browse files

Move the device info validation to a helper library

rkp_factory_extraction_tool now reuses the VTS logic for validating the
DeviceInfo. This way, partners doing RKP testing can see locally if they
are getting bad DeviceInfo before they try to upload the data to the
google service.

Test: atest VtsHalRemotelyProvisionedComponentTargetTest
Test: rkp_factory_extraction_tool
Bug: 239838563
Change-Id: I80fba3e624e1f5ab6da7aac889a0168f7cb8dbe4
parent 0068fccb
Loading
Loading
Loading
Loading
+5 −119
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include <memory>
#define LOG_TAG "VtsRemotelyProvisionableComponentTests"

#include <AndroidRemotelyProvisionedComponentDevice.h>
@@ -58,26 +59,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));
@@ -433,12 +414,11 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
        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 @@ class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
        }
    }

    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_;
+1 −0
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@ cc_library {
    ],
    shared_libs: [
        "libbase",
        "libbinder_ndk",
        "libcppbor_external",
        "libcppcose_rkp",
        "libcrypto",
+10 −0
Original line number Diff line number Diff line
@@ -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 @@ 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 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
+131 −0
Original line number Diff line number Diff line
@@ -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 @@ JsonOutput jsonEncodeCsrWithBuild(const std::string instance_name, const cppbor:
    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