Update VtsHalRemotelyProvisionedComponentTargetTest to v3

Bug: 235265072
Test: atest VtsHalRemotelyProvisionedComponentTargetTest
Change-Id: I01e387a0784c3548a4661a73d7bd3d5bec9fb42e
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
index 9b21e4e..4f361bb 100644
--- a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -46,6 +46,7 @@
 namespace {
 
 constexpr int32_t VERSION_WITH_UNIQUE_ID_SUPPORT = 2;
+constexpr int32_t VERSION_WITHOUT_TEST_MODE = 3;
 
 #define INSTANTIATE_REM_PROV_AIDL_TEST(name)                                         \
     GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);                             \
@@ -180,6 +181,15 @@
         return params;
     }
 
+    void checkMacedPubkeyVersioned(const MacedPublicKey& macedPubKey, bool testMode,
+                                   vector<uint8_t>* payload_value) {
+        if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
+            check_maced_pubkey(macedPubKey, false, payload_value);
+        } else {
+            check_maced_pubkey(macedPubKey, testMode, payload_value);
+        }
+    }
+
   protected:
     std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
     RpcHardwareInfo rpcHardwareInfo;
@@ -256,7 +266,7 @@
     auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
     ASSERT_TRUE(status.isOk());
     vector<uint8_t> coseKeyData;
-    check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+    checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
 }
 
 /**
@@ -279,7 +289,7 @@
     auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
     ASSERT_TRUE(status.isOk());
     vector<uint8_t> coseKeyData;
-    check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+    checkMacedPubkeyVersioned(macedPubKey, testMode, &coseKeyData);
 
     AttestationKey attestKey;
     attestKey.keyBlob = std::move(privateKeyBlob);
@@ -334,13 +344,13 @@
     bool testMode = true;
     auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
     ASSERT_TRUE(status.isOk());
-
-    check_maced_pubkey(macedPubKey, testMode, nullptr);
+    checkMacedPubkeyVersioned(macedPubKey, testMode, nullptr);
 }
 
-class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
+class CertificateRequestTestBase : public VtsRemotelyProvisionedComponentTests {
   protected:
-    CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {}
+    CertificateRequestTestBase()
+        : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(64)) {}
 
     void generateTestEekChain(size_t eekLength) {
         auto chain = generateEekChain(rpcHardwareInfo.supportedEekCurve, eekLength, eekId_);
@@ -359,7 +369,7 @@
             ASSERT_TRUE(status.isOk()) << status.getMessage();
 
             vector<uint8_t> payload_value;
-            check_maced_pubkey(key, testMode, &payload_value);
+            checkMacedPubkeyVersioned(key, testMode, &payload_value);
             cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
         }
     }
@@ -372,6 +382,18 @@
     cppbor::Array cborKeysToSign_;
 };
 
+class CertificateRequestTest : public CertificateRequestTestBase {
+  protected:
+    void SetUp() override {
+        CertificateRequestTestBase::SetUp();
+
+        if (rpcHardwareInfo.versionNumber >= VERSION_WITHOUT_TEST_MODE) {
+            GTEST_SKIP() << "This test case only applies to RKP v1 and v2. "
+                         << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
+        }
+    }
+};
+
 /**
  * Generate an empty certificate request in test mode, and decrypt and verify the structure and
  * content.
@@ -638,4 +660,131 @@
 
 INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
 
+class CertificateRequestV2Test : public CertificateRequestTestBase {
+    void SetUp() override {
+        CertificateRequestTestBase::SetUp();
+
+        if (rpcHardwareInfo.versionNumber < VERSION_WITHOUT_TEST_MODE) {
+            GTEST_SKIP() << "This test case only applies to RKP v3 and above. "
+                         << "RKP version discovered: " << rpcHardwareInfo.versionNumber;
+        }
+    }
+};
+
+/**
+ * Generate an empty certificate request, and decrypt and verify the structure and content.
+ */
+TEST_P(CertificateRequestV2Test, EmptyRequest) {
+    bytevec csr;
+
+    auto status =
+            provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request.  Decrypt, parse and validate the contents.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequest) {
+    generateKeys(false /* testMode */, 1 /* numKeys */);
+
+    bytevec csr;
+
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request.  Make sure contents are reproducible.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
+    generateKeys(false /* testMode */, 1 /* numKeys */);
+
+    bytevec csr;
+
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto firstBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(firstBcc) << firstBcc.message();
+
+    status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto secondBcc = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(secondBcc) << secondBcc.message();
+
+    ASSERT_EQ(firstBcc->size(), secondBcc->size());
+    for (auto i = 0; i < firstBcc->size(); i++) {
+        ASSERT_EQ(firstBcc->at(i).pubKey, secondBcc->at(i).pubKey);
+    }
+}
+
+/**
+ * Generate a non-empty certificate request with multiple keys.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestMultipleKeys) {
+    // TODO(b/254137722): define a minimum number of keys that must be supported.
+    generateKeys(false /* testMode */, 5 /* numKeys */);
+
+    bytevec csr;
+
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_);
+    ASSERT_TRUE(result) << result.message();
+}
+
+/**
+ * Generate a non-empty certificate request, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequestCorruptMac) {
+    generateKeys(false /* testMode */, 1 /* numKeys */);
+    auto result = corrupt_maced_key(keysToSign_[0]);
+    ASSERT_TRUE(result) << result.moveMessage();
+    MacedPublicKey keyWithCorruptMac = result.moveValue();
+
+    bytevec csr;
+    auto status =
+            provisionable_->generateCertificateRequestV2({keyWithCorruptMac}, challenge_, &csr);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, with test keys.  Test mode must be
+ * ignored, i.e. test must pass.
+ */
+TEST_P(CertificateRequestV2Test, NonEmptyRequest_testKeyInProdCert) {
+    generateKeys(true /* testMode */, 1 /* numKeys */);
+
+    bytevec csr;
+    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+}
+
+/**
+ * Call generateCertificateRequest(). Make sure it's removed.
+ */
+TEST_P(CertificateRequestV2Test, CertificateRequestV1Removed) {
+    generateTestEekChain(2);
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            true /* testMode */, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_REMOVED);
+}
+
+INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestV2Test);
+
 }  // namespace aidl::android::hardware::security::keymint::test
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 9ea20ac..6871e1b 100644
--- a/security/keymint/support/include/remote_prov/remote_prov_utils.h
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -177,4 +177,19 @@
         const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
         IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
 
+/**
+ * Verify the CSR as if the device is still early in the factory process and may not
+ * have all device identifiers provisioned yet.
+ */
+ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
+                                                     const std::vector<uint8_t>& csr,
+                                                     IRemotelyProvisionedComponent* provisionable,
+                                                     const std::vector<uint8_t>& challenge);
+/**
+ * Verify the CSR as if the device is a final production sample.
+ */
+ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
+        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
+        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& 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 0651496..f7ab3ac 100644
--- a/security/keymint/support/remote_prov_utils.cpp
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -32,6 +32,7 @@
 #include <openssl/base64.h>
 #include <openssl/evp.h>
 #include <openssl/rand.h>
+#include <openssl/x509.h>
 #include <remote_prov/remote_prov_utils.h>
 
 namespace aidl::android::hardware::security::keymint::remote_prov {
@@ -45,6 +46,8 @@
 using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
 using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
 using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>;
+using X509_Ptr = bssl::UniquePtr<X509>;
+using CRYPTO_BUFFER_Ptr = bssl::UniquePtr<CRYPTO_BUFFER>;
 
 ErrMsgOr<bytevec> ecKeyGetPrivateKey(const EC_KEY* ecKey) {
     // Extract private key.
@@ -527,22 +530,27 @@
     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) + ").";
+    if (info.versionNumber < 3) {
+        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";
+        }
+        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()) {
+    switch (info.versionNumber) {
+        case 3:
         case 2:
             for (const auto& entry : kAttestationIdEntrySet) {
                 error += checkMapEntry(isFactory && !entry.alwaysValidate, *parsed, cppbor::TSTR,
@@ -579,7 +587,7 @@
                                    kValidAttIdStates);
             break;
         default:
-            return "Unrecognized version: " + std::to_string(version->asUint()->value());
+            return "Unrecognized version: " + std::to_string(info.versionNumber);
     }
 
     if (!error.empty()) {
@@ -734,4 +742,264 @@
                                /*isFactory=*/false);
 }
 
-}  // namespace aidl::android::hardware::security::keymint::remote_prov
\ No newline at end of file
+ErrMsgOr<X509_Ptr> parseX509Cert(const std::vector<uint8_t>& cert) {
+    CRYPTO_BUFFER_Ptr certBuf(CRYPTO_BUFFER_new(cert.data(), cert.size(), nullptr));
+    if (!certBuf.get()) {
+        return "Failed to create crypto buffer.";
+    }
+    X509_Ptr result(X509_parse_from_buffer(certBuf.get()));
+    if (!result.get()) {
+        return "Failed to parse certificate.";
+    }
+    return result;
+}
+
+std::string getX509IssuerName(const X509_Ptr& cert) {
+    char* name = X509_NAME_oneline(X509_get_issuer_name(cert.get()), nullptr, 0);
+    std::string result(name);
+    OPENSSL_free(name);
+    return result;
+}
+
+std::string getX509SubjectName(const X509_Ptr& cert) {
+    char* name = X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0);
+    std::string result(name);
+    OPENSSL_free(name);
+    return result;
+}
+
+// Validates the certificate chain and returns the leaf public key.
+ErrMsgOr<bytevec> validateCertChain(const cppbor::Array& chain) {
+    uint8_t rawPubKey[64];
+    size_t rawPubKeySize = sizeof(rawPubKey);
+    for (size_t i = 0; i < chain.size(); ++i) {
+        // Root must be self-signed.
+        size_t signingCertIndex = (i > 1) ? i - 1 : i;
+        auto& keyCertItem = chain[i];
+        auto& signingCertItem = chain[signingCertIndex];
+        if (!keyCertItem || !keyCertItem->asBstr()) {
+            return "Key certificate must be a Bstr.";
+        }
+        if (!signingCertItem || !signingCertItem->asBstr()) {
+            return "Signing certificate must be a Bstr.";
+        }
+
+        auto keyCert = parseX509Cert(keyCertItem->asBstr()->value());
+        if (!keyCert) {
+            return keyCert.message();
+        }
+        auto signingCert = parseX509Cert(keyCertItem->asBstr()->value());
+        if (!signingCert) {
+            return signingCert.message();
+        }
+
+        EVP_PKEY_Ptr pubKey(X509_get_pubkey(keyCert->get()));
+        if (!pubKey.get()) {
+            return "Failed to get public key.";
+        }
+        EVP_PKEY_Ptr signingPubKey(X509_get_pubkey(signingCert->get()));
+        if (!signingPubKey.get()) {
+            return "Failed to get signing public key.";
+        }
+
+        if (!X509_verify(keyCert->get(), signingPubKey.get())) {
+            return "Verification of certificate " + std::to_string(i) +
+                   " faile. OpenSSL error string: " + ERR_error_string(ERR_get_error(), NULL);
+        }
+
+        auto certIssuer = getX509IssuerName(*keyCert);
+        auto signerSubj = getX509SubjectName(*signingCert);
+        if (certIssuer != signerSubj) {
+            return "Certificate " + std::to_string(i) + " has wrong issuer. Signer subject is " +
+                   signerSubj + " Issuer subject is " + certIssuer;
+        }
+
+        rawPubKeySize = sizeof(rawPubKey);
+        if (!EVP_PKEY_get_raw_public_key(pubKey.get(), rawPubKey, &rawPubKeySize)) {
+            return "Failed to get raw public key.";
+        }
+    }
+
+    return bytevec(rawPubKey, rawPubKey + rawPubKeySize);
+}
+
+std::string validateUdsCerts(const cppbor::Map& udsCerts, const bytevec& udsPub) {
+    for (const auto& [signerName, udsCertChain] : udsCerts) {
+        if (!signerName || !signerName->asTstr()) {
+            return "Signer Name must be a Tstr.";
+        }
+        if (!udsCertChain || !udsCertChain->asArray()) {
+            return "UDS certificate chain must be an Array.";
+        }
+        if (udsCertChain->asArray()->size() < 2) {
+            return "UDS certificate chain must have at least two entries: root and leaf.";
+        }
+
+        auto leafPubKey = validateCertChain(*udsCertChain->asArray());
+        if (!leafPubKey) {
+            return leafPubKey.message();
+        }
+        if (*leafPubKey != udsPub) {
+            return "Leaf public key in UDS certificat chain doesn't match UDS public key.";
+        }
+    }
+    return "";
+}
+
+ErrMsgOr<cppbor::Array> parseAndValidateCsrPayload(const cppbor::Array& keysToSign,
+                                                   const std::vector<uint8_t>& csrPayload,
+                                                   IRemotelyProvisionedComponent* provisionable,
+                                                   const std::vector<uint8_t>& challenge,
+                                                   bool isFactory) {
+    auto [parsedCsrPayload, _, errMsg] = cppbor::parse(csrPayload);
+    if (!parsedCsrPayload) {
+        return errMsg;
+    }
+    if (!parsedCsrPayload->asArray()) {
+        return "CSR payload is not a CBOR array.";
+    }
+    if (parsedCsrPayload->asArray()->size() != 5U) {
+        return "CSR payload must contain version, certificate type, device info, challenge, keys. "
+               "However, the parsed CSR payload has " +
+               std::to_string(parsedCsrPayload->asArray()->size()) + " entries.";
+    }
+
+    auto& signedVersion = parsedCsrPayload->asArray()->get(0);
+    auto& signedCertificateType = parsedCsrPayload->asArray()->get(1);
+    auto& signedDeviceInfo = parsedCsrPayload->asArray()->get(2);
+    auto& signedChallenge = parsedCsrPayload->asArray()->get(3);
+    auto& signedKeys = parsedCsrPayload->asArray()->get(4);
+
+    if (!signedVersion || !signedVersion->asUint() || signedVersion->asUint()->value() != 1U) {
+        return "CSR payload version must be an unsigned integer and must be equal to 1.";
+    }
+    if (!signedCertificateType || !signedCertificateType->asTstr()) {
+        // Certificate type is allowed to be extendend by vendor, i.e. we can't
+        // enforce its value.
+        return "Certificate type must be a Tstr.";
+    }
+    if (!signedDeviceInfo || !signedDeviceInfo->asMap()) {
+        return "Device info must be an Map.";
+    }
+    if (!signedChallenge || !signedChallenge->asBstr()) {
+        return "Challenge must be a Bstr.";
+    }
+    if (!signedKeys || !signedKeys->asArray()) {
+        return "Keys must be an Array.";
+    }
+
+    auto result = parseAndValidateDeviceInfo(signedDeviceInfo->asMap()->encode(), provisionable,
+                                             isFactory);
+    if (!result) {
+        return result.message();
+    }
+
+    if (challenge.size() < 32 || challenge.size() > 64) {
+        return "Challenge size must be between 32 and 64 bytes inclusive. "
+               "However, challenge is " +
+               std::to_string(challenge.size()) + " bytes long.";
+    }
+
+    auto challengeBstr = cppbor::Bstr(challenge);
+    if (*signedChallenge->asBstr() != challengeBstr) {
+        return "Signed challenge does not match."
+               "\n  Actual: " +
+               cppbor::prettyPrint(signedChallenge->asBstr(), 64 /* maxBStrSize */) +
+               "\nExpected: " + cppbor::prettyPrint(&challengeBstr, 64 /* maxBStrSize */);
+    }
+
+    if (signedKeys->asArray()->encode() != keysToSign.encode()) {
+        return "Signed keys do not match.";
+    }
+
+    return std::move(*parsedCsrPayload->asArray());
+}
+
+ErrMsgOr<std::vector<BccEntryData>> verifyCsr(const cppbor::Array& keysToSign,
+                                              const std::vector<uint8_t>& csr,
+                                              IRemotelyProvisionedComponent* provisionable,
+                                              const std::vector<uint8_t>& challenge,
+                                              bool isFactory) {
+    auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
+    if (!parsedCsr) {
+        return csrErrMsg;
+    }
+    if (!parsedCsr->asArray()) {
+        return "CSR is not a CBOR array.";
+    }
+    if (parsedCsr->asArray()->size() != 4U) {
+        return "CSR must contain version, UDS certificates, DICE chain, and signed data. "
+               "However, the parsed CSR has " +
+               std::to_string(parsedCsr->asArray()->size()) + " entries.";
+    }
+
+    auto& version = parsedCsr->asArray()->get(0);
+    auto& udsCerts = parsedCsr->asArray()->get(1);
+    auto& diceCertChain = parsedCsr->asArray()->get(2);
+    auto& signedData = parsedCsr->asArray()->get(3);
+
+    if (!version || !version->asUint() || version->asUint()->value() != 3U) {
+        return "Version must be an unsigned integer and must be equal to 3.";
+    }
+    if (!udsCerts || !udsCerts->asMap()) {
+        return "UdsCerts must be an Map.";
+    }
+    if (!diceCertChain || !diceCertChain->asArray()) {
+        return "DiceCertChain must be an Array.";
+    }
+    if (!signedData || !signedData->asArray()) {
+        return "SignedData must be an Array.";
+    }
+
+    RpcHardwareInfo info;
+    provisionable->getHardwareInfo(&info);
+    if (version->asUint()->value() != info.versionNumber) {
+        return "CSR version (" + std::to_string(version->asUint()->value()) +
+               ") does not match the remotely provisioned component version (" +
+               std::to_string(info.versionNumber) + ").";
+    }
+
+    // DICE chain is [ pubkey, + DiceChainEntry ]. Its format is the same as BCC from RKP v1-2.
+    auto diceContents = validateBcc(diceCertChain->asArray());
+    if (!diceContents) {
+        return diceContents.message() + "\n" + prettyPrint(diceCertChain.get());
+    }
+    if (diceContents->size() == 0U) {
+        return "The DICE chain is empty. It must contain at least one entry.";
+    }
+
+    auto& udsPub = diceContents->back().pubKey;
+
+    auto error = validateUdsCerts(*udsCerts->asMap(), udsPub);
+    if (!error.empty()) {
+        return error;
+    }
+
+    auto csrPayload = verifyAndParseCoseSign1(signedData->asArray(), udsPub, {} /* aad */);
+    if (!csrPayload) {
+        return csrPayload.message();
+    }
+
+    auto parsedCsrPayload = parseAndValidateCsrPayload(keysToSign, *csrPayload, provisionable,
+                                                       challenge, isFactory);
+    if (!parsedCsrPayload) {
+        return parsedCsrPayload.message();
+    }
+
+    return *diceContents;
+}
+
+ErrMsgOr<std::vector<BccEntryData>> verifyFactoryCsr(const cppbor::Array& keysToSign,
+                                                     const std::vector<uint8_t>& csr,
+                                                     IRemotelyProvisionedComponent* provisionable,
+                                                     const std::vector<uint8_t>& challenge) {
+    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/true);
+}
+
+ErrMsgOr<std::vector<BccEntryData>> verifyProductionCsr(
+        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
+        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
+    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/false);
+}
+
+}  // namespace aidl::android::hardware::security::keymint::remote_prov