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

Commit f112ec92 authored by Alice Wang's avatar Alice Wang
Browse files

[vts] Verify RKP VM DICE chain in IRPC VTS

This cl adds verifications to the IRPC VTS to check that:

- RKP VM DICE chains have a continuous presence of RKP VM markers
till the last DICE certificate.
- Non-RKP VM DICE chains do not have such continuous presence of
RKP VM markers.

Test: atest VtsHalRemotelyProvisionedComponentTargetTest
Test: atest libkeymint_remote_prov_support_test
Bug: 314128697
Change-Id: Ib966b4bd584f1f931b7f19b4b58a1a37b5266f5e
parent 0de4aa3c
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -79,9 +79,13 @@ void KeyMintRemoteProv::process() {

    while (mFdp.remaining_bytes()) {
        auto invokeProvAPI = mFdp.PickValueInArray<const std::function<void()>>({
                [&]() { verifyFactoryCsr(cborKeysToSign, csr, gRPC.get(), challenge); },
                [&]() { verifyProductionCsr(cborKeysToSign, csr, gRPC.get(), challenge); },
                [&]() { isCsrWithProperDiceChain(csr); },
                [&]() {
                    verifyFactoryCsr(cborKeysToSign, csr, gRPC.get(), kServiceName, challenge);
                },
                [&]() {
                    verifyProductionCsr(cborKeysToSign, csr, gRPC.get(), kServiceName, challenge);
                },
                [&]() { isCsrWithProperDiceChain(csr, kServiceName); },
        });
        invokeProvAPI();
    }
+17 −9
Original line number Diff line number Diff line
@@ -89,6 +89,11 @@ inline constexpr uint8_t kCoseEncodedEcdsa256GeekCert[] = {
 */
bytevec randomBytes(size_t numBytes);

const std::string DEFAULT_INSTANCE_NAME =
        "android.hardware.security.keymint.IRemotelyProvisionedComponent/default";
const std::string RKPVM_INSTANCE_NAME =
        "android.hardware.security.keymint.IRemotelyProvisionedComponent/avf";

struct EekChain {
    bytevec chain;
    bytevec last_pubkey;
@@ -160,7 +165,8 @@ ErrMsgOr<std::vector<BccEntryData>> verifyFactoryProtectedData(
        const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
        const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
        const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge);
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge);
/**
 * Verify the protected data as if the device is a final production sample.
 */
@@ -168,8 +174,8 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
        const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
        const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
        const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
        bool allowAnyMode = false);
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge, bool allowAnyMode = false);

/**
 * Verify the CSR as if the device is still early in the factory process and may not
@@ -177,22 +183,24 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
 */
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
        bool allowDegenerate = true);
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge, bool allowDegenerate = true);
/**
 * Verify the CSR as if the device is a final production sample.
 */
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
        bool allowAnyMode = false);
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge, bool allowAnyMode = false);

/** Checks whether the CSR has a proper DICE chain. */
ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr);
ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr,
                                        const std::string& instanceName);

/** Verify the DICE chain. */
ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc,
                                                hwtrust::DiceChain::Kind kind, bool allowAnyMode,
                                                bool allowDegenerate);
                                                bool allowDegenerate,
                                                const std::string& instanceName);

}  // namespace aidl::android::hardware::security::keymint::remote_prov
+38 −22
Original line number Diff line number Diff line
@@ -52,6 +52,14 @@ using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>;
using X509_Ptr = bssl::UniquePtr<X509>;
using CRYPTO_BUFFER_Ptr = bssl::UniquePtr<CRYPTO_BUFFER>;

std::string device_suffix(const std::string& name) {
    size_t pos = name.find('/');
    if (pos == std::string::npos) {
        return name;
    }
    return name.substr(pos + 1);
}

ErrMsgOr<bytevec> ecKeyGetPrivateKey(const EC_KEY* ecKey) {
    // Extract private key.
    const BIGNUM* bignum = EC_KEY_get0_private_key(ecKey);
@@ -325,7 +333,8 @@ bytevec getProdEekChain(int32_t supportedEekCurve) {

ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc,
                                                hwtrust::DiceChain::Kind kind, bool allowAnyMode,
                                                bool allowDegenerate) {
                                                bool allowDegenerate,
                                                const std::string& instanceName) {
    auto encodedBcc = bcc->encode();

    // Use ro.build.type instead of ro.debuggable because ro.debuggable=1 for VTS testing
@@ -334,7 +343,8 @@ ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc,
        allowAnyMode = true;
    }

    auto chain = hwtrust::DiceChain::Verify(encodedBcc, kind, allowAnyMode);
    auto chain =
            hwtrust::DiceChain::Verify(encodedBcc, kind, allowAnyMode, device_suffix(instanceName));
    if (!chain.ok()) return chain.error().message();

    if (!allowDegenerate && !chain->IsProper()) {
@@ -649,8 +659,8 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProtectedData(
        const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
        const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
        const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
        bool isFactory, bool allowAnyMode = false) {
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge, bool isFactory, bool allowAnyMode = false) {
    auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
    if (!parsedProtectedData) {
        return protDataErrMsg;
@@ -707,7 +717,7 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProtectedData(

    // BCC is [ pubkey, + BccEntry]
    auto bccContents = validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr13, allowAnyMode,
                                   /*allowDegenerate=*/true);
                                   /*allowDegenerate=*/true, instanceName);
    if (!bccContents) {
        return bccContents.message() + "\n" + prettyPrint(bcc.get());
    }
@@ -750,9 +760,10 @@ ErrMsgOr<std::vector<BccEntryData>> verifyFactoryProtectedData(
        const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
        const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
        const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge) {
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge) {
    return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain,
                               eekId, supportedEekCurve, provisionable, challenge,
                               eekId, supportedEekCurve, provisionable, instanceName, challenge,
                               /*isFactory=*/true);
}

@@ -760,10 +771,10 @@ ErrMsgOr<std::vector<BccEntryData>> verifyProductionProtectedData(
        const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
        const std::vector<uint8_t>& keysToSignMac, const ProtectedData& protectedData,
        const EekChain& eekChain, const std::vector<uint8_t>& eekId, int32_t supportedEekCurve,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
        bool allowAnyMode) {
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge, bool allowAnyMode) {
    return verifyProtectedData(deviceInfo, keysToSign, keysToSignMac, protectedData, eekChain,
                               eekId, supportedEekCurve, provisionable, challenge,
                               eekId, supportedEekCurve, provisionable, instanceName, challenge,
                               /*isFactory=*/false, allowAnyMode);
}

@@ -1003,6 +1014,7 @@ ErrMsgOr<hwtrust::DiceChain::Kind> getDiceChainKind() {

ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t>& request,
                                                       const std::vector<uint8_t>& challenge,
                                                       const std::string& instanceName,
                                                       bool allowAnyMode = false,
                                                       bool allowDegenerate = true) {
    auto [parsedRequest, _, csrErrMsg] = cppbor::parse(request);
@@ -1042,7 +1054,8 @@ ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t
        return diceChainKind.message();
    }

    auto diceContents = validateBcc(diceCertChain, *diceChainKind, allowAnyMode, allowDegenerate);
    auto diceContents =
            validateBcc(diceCertChain, *diceChainKind, allowAnyMode, allowDegenerate, instanceName);
    if (!diceContents) {
        return diceContents.message() + "\n" + prettyPrint(diceCertChain);
    }
@@ -1071,6 +1084,7 @@ ErrMsgOr<bytevec> parseAndValidateAuthenticatedRequest(const std::vector<uint8_t
ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSign,
                                                   const std::vector<uint8_t>& csr,
                                                   IRemotelyProvisionedComponent* provisionable,
                                                   const std::string& instanceName,
                                                   const std::vector<uint8_t>& challenge,
                                                   bool isFactory, bool allowAnyMode = false,
                                                   bool allowDegenerate = true) {
@@ -1081,8 +1095,8 @@ ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSi
               ") does not match expected version (3).";
    }

    auto csrPayload =
            parseAndValidateAuthenticatedRequest(csr, challenge, allowAnyMode, allowDegenerate);
    auto csrPayload = parseAndValidateAuthenticatedRequest(csr, challenge, instanceName,
                                                           allowAnyMode, allowDegenerate);
    if (!csrPayload) {
        return csrPayload.message();
    }
@@ -1092,20 +1106,22 @@ ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyCsr(const cppbor::Array& keysToSi

ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyFactoryCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
        bool allowDegenerate) {
    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/true,
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge, bool allowDegenerate) {
    return verifyCsr(keysToSign, csr, provisionable, instanceName, challenge, /*isFactory=*/true,
                     /*allowAnyMode=*/false, allowDegenerate);
}

ErrMsgOr<std::unique_ptr<cppbor::Array>> verifyProductionCsr(
        const cppbor::Array& keysToSign, const std::vector<uint8_t>& csr,
        IRemotelyProvisionedComponent* provisionable, const std::vector<uint8_t>& challenge,
        bool allowAnyMode) {
    return verifyCsr(keysToSign, csr, provisionable, challenge, /*isFactory=*/false, allowAnyMode);
        IRemotelyProvisionedComponent* provisionable, const std::string& instanceName,
        const std::vector<uint8_t>& challenge, bool allowAnyMode) {
    return verifyCsr(keysToSign, csr, provisionable, instanceName, challenge, /*isFactory=*/false,
                     allowAnyMode);
}

ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr) {
ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr,
                                        const std::string& instanceName) {
    auto [parsedRequest, _, csrErrMsg] = cppbor::parse(csr);
    if (!parsedRequest) {
        return csrErrMsg;
@@ -1136,8 +1152,8 @@ ErrMsgOr<bool> isCsrWithProperDiceChain(const std::vector<uint8_t>& csr) {
    }

    auto encodedDiceChain = diceCertChain->encode();
    auto chain =
            hwtrust::DiceChain::Verify(encodedDiceChain, *diceChainKind, /*allowAnyMode=*/false);
    auto chain = hwtrust::DiceChain::Verify(encodedDiceChain, *diceChainKind,
                                            /*allowAnyMode=*/false, device_suffix(instanceName));
    if (!chain.ok()) return chain.error().message();
    return chain->IsProper();
}
+4 −2
Original line number Diff line number Diff line
@@ -298,9 +298,11 @@ TEST(RemoteProvUtilsTest, validateBccDegenerate) {
    ASSERT_TRUE(bcc) << "Error: " << errMsg;

    EXPECT_TRUE(validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr16,
                            /*allowAnyMode=*/false, /*allowDegenerate=*/true));
                            /*allowAnyMode=*/false, /*allowDegenerate=*/true,
                            DEFAULT_INSTANCE_NAME));
    EXPECT_FALSE(validateBcc(bcc->asArray(), hwtrust::DiceChain::Kind::kVsr16,
                             /*allowAnyMode=*/false, /*allowDegenerate=*/false));
                             /*allowAnyMode=*/false, /*allowDegenerate=*/false,
                             DEFAULT_INSTANCE_NAME));
}
}  // namespace
}  // namespace aidl::android::hardware::security::keymint::remote_prov
+26 −26
Original line number Diff line number Diff line
@@ -55,10 +55,7 @@ constexpr int32_t VERSION_WITH_SUPPORTED_NUM_KEYS_IN_CSR = 3;

constexpr uint8_t MIN_CHALLENGE_SIZE = 0;
constexpr uint8_t MAX_CHALLENGE_SIZE = 64;
const string DEFAULT_INSTANCE_NAME =
        "android.hardware.security.keymint.IRemotelyProvisionedComponent/default";
const string RKP_VM_INSTANCE_NAME =
        "android.hardware.security.keymint.IRemotelyProvisionedComponent/avf";

const string KEYMINT_STRONGBOX_INSTANCE_NAME =
        "android.hardware.security.keymint.IKeyMintDevice/strongbox";

@@ -188,7 +185,7 @@ class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::
        }
        ASSERT_NE(provisionable_, nullptr);
        auto status = provisionable_->getHardwareInfo(&rpcHardwareInfo);
        isRkpVmInstance_ = GetParam() == RKP_VM_INSTANCE_NAME;
        isRkpVmInstance_ = GetParam() == RKPVM_INSTANCE_NAME;
        if (isRkpVmInstance_) {
            if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
                GTEST_SKIP() << "The RKP VM is not supported on this system.";
@@ -227,7 +224,7 @@ TEST(NonParameterizedTests, eachRpcHasAUniqueId) {

        RpcHardwareInfo hwInfo;
        auto status = rpc->getHardwareInfo(&hwInfo);
        if (hal == RKP_VM_INSTANCE_NAME && status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
        if (hal == RKPVM_INSTANCE_NAME && status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
            GTEST_SKIP() << "The RKP VM is not supported on this system.";
        }
        ASSERT_TRUE(status.isOk());
@@ -268,7 +265,7 @@ TEST(NonParameterizedTests, requireDiceOnDefaultInstanceIfStrongboxPresent) {
    auto status = rpc->generateCertificateRequestV2({} /* keysToSign */, challenge, &csr);
    EXPECT_TRUE(status.isOk()) << status.getDescription();

    auto result = isCsrWithProperDiceChain(csr);
    auto result = isCsrWithProperDiceChain(csr, DEFAULT_INSTANCE_NAME);
    ASSERT_TRUE(result) << result.message();
    ASSERT_TRUE(*result);
}
@@ -494,7 +491,7 @@ TEST_P(CertificateRequestTest, EmptyRequest_testMode) {

        auto result = verifyProductionProtectedData(
                deviceInfo, cppbor::Array(), keysToSignMac, protectedData, testEekChain_, eekId_,
                rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
                rpcHardwareInfo.supportedEekCurve, provisionable_.get(), GetParam(), challenge_);
        ASSERT_TRUE(result) << result.message();
    }
}
@@ -517,9 +514,10 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
            &protectedData, &keysToSignMac);
    ASSERT_TRUE(status.isOk()) << status.getDescription();

    auto firstBcc = verifyProductionProtectedData(
            deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
            eekId_, rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
    auto firstBcc = verifyProductionProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(),
                                                  keysToSignMac, protectedData, testEekChain_,
                                                  eekId_, rpcHardwareInfo.supportedEekCurve,
                                                  provisionable_.get(), GetParam(), challenge_);
    ASSERT_TRUE(firstBcc) << firstBcc.message();

    status = provisionable_->generateCertificateRequest(
@@ -527,9 +525,10 @@ TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
            &protectedData, &keysToSignMac);
    ASSERT_TRUE(status.isOk()) << status.getDescription();

    auto secondBcc = verifyProductionProtectedData(
            deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData, testEekChain_,
            eekId_, rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
    auto secondBcc = verifyProductionProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(),
                                                   keysToSignMac, protectedData, testEekChain_,
                                                   eekId_, rpcHardwareInfo.supportedEekCurve,
                                                   provisionable_.get(), GetParam(), challenge_);
    ASSERT_TRUE(secondBcc) << secondBcc.message();

    // Verify that none of the keys in the first BCC are repeated in the second one.
@@ -579,7 +578,7 @@ TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {

        auto result = verifyProductionProtectedData(
                deviceInfo, cborKeysToSign_, keysToSignMac, protectedData, testEekChain_, eekId_,
                rpcHardwareInfo.supportedEekCurve, provisionable_.get(), challenge_);
                rpcHardwareInfo.supportedEekCurve, provisionable_.get(), GetParam(), challenge_);
        ASSERT_TRUE(result) << result.message();
    }
}
@@ -767,8 +766,8 @@ TEST_P(CertificateRequestV2Test, EmptyRequest) {
                provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge, &csr);
        ASSERT_TRUE(status.isOk()) << status.getDescription();

        auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge,
                                          isRkpVmInstance_);
        auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), GetParam(),
                                          challenge, isRkpVmInstance_);
        ASSERT_TRUE(result) << result.message();
    }
}
@@ -789,8 +788,8 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequest) {
        auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge, &csr);
        ASSERT_TRUE(status.isOk()) << status.getDescription();

        auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge,
                                          isRkpVmInstance_);
        auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
                                          challenge, isRkpVmInstance_);
        ASSERT_TRUE(result) << result.message();
    }
}
@@ -820,15 +819,15 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequestReproducible) {
    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
    ASSERT_TRUE(status.isOk()) << status.getDescription();

    auto firstCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_,
                                        isRkpVmInstance_);
    auto firstCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
                                        challenge_, isRkpVmInstance_);
    ASSERT_TRUE(firstCsr) << firstCsr.message();

    status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
    ASSERT_TRUE(status.isOk()) << status.getDescription();

    auto secondCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_,
                                         isRkpVmInstance_);
    auto secondCsr = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
                                         challenge_, isRkpVmInstance_);
    ASSERT_TRUE(secondCsr) << secondCsr.message();

    ASSERT_EQ(**firstCsr, **secondCsr);
@@ -846,8 +845,8 @@ TEST_P(CertificateRequestV2Test, NonEmptyRequestMultipleKeys) {
    auto status = provisionable_->generateCertificateRequestV2(keysToSign_, challenge_, &csr);
    ASSERT_TRUE(status.isOk()) << status.getDescription();

    auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), challenge_,
                                      isRkpVmInstance_);
    auto result = verifyProductionCsr(cborKeysToSign_, csr, provisionable_.get(), GetParam(),
                                      challenge_, isRkpVmInstance_);
    ASSERT_TRUE(result) << result.message();
}

@@ -977,7 +976,8 @@ TEST_P(CertificateRequestV2Test, DeviceInfo) {
            provisionable_->generateCertificateRequestV2({} /* keysToSign */, challenge_, &csr);
    ASSERT_TRUE(irpcStatus.isOk()) << irpcStatus.getDescription();

    auto result = verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), challenge_);
    auto result =
            verifyProductionCsr(cppbor::Array(), csr, provisionable_.get(), GetParam(), challenge_);
    ASSERT_TRUE(result) << result.message();

    std::unique_ptr<cppbor::Array> csrPayload = std::move(*result);