Merge "Tests to verify importKey with Asymmetric and symmetric keys."
diff --git a/fsverity/fsverity_manifest_generator.py b/fsverity/fsverity_manifest_generator.py
index 79be591..181758a 100644
--- a/fsverity/fsverity_manifest_generator.py
+++ b/fsverity/fsverity_manifest_generator.py
@@ -50,7 +50,7 @@
       required=True)
   p.add_argument(
       'inputs',
-      nargs='+',
+      nargs='*',
       help='input file for the build manifest')
   args = p.parse_args(sys.argv[1:])
 
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index cf76bb2..00fb572 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -153,13 +153,11 @@
         } else {
             match get_rem_prov_attest_key(key.domain, caller_uid, db, &self.km_uuid) {
                 Err(e) => {
-                    log::error!(
-                        "In get_remote_provisioning_key_and_certs: Error occurred: {:?}",
-                        e
-                    );
                     if self.is_rkp_only() {
+                        log::error!("Error occurred: {:?}", e);
                         return Err(e);
                     }
+                    log::warn!("Error occurred: {:?}", e);
                     log_rkp_error_stats(
                         MetricsRkpError::FALL_BACK_DURING_HYBRID,
                         &self.security_level,
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index d73f8fe..f56cfab 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -101,6 +101,15 @@
   recovery_available: true,
 }
 
+genrule {
+  name: "statslog_odsign.h",
+  tools: ["stats-log-api-gen"],
+  cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_odsign.h --module art --namespace art,metrics,statsd",
+  out: [
+    "statslog_odsign.h",
+  ],
+}
+
 cc_binary {
   name: "odsign",
   defaults: [
@@ -114,6 +123,7 @@
     "odsign_main.cpp",
     "StatsReporter.cpp",
   ],
+  generated_headers: ["statslog_odsign.h"],
 
   header_libs: ["odrefresh_headers"],
 
diff --git a/ondevice-signing/StatsReporter.cpp b/ondevice-signing/StatsReporter.cpp
index 65e645a..e4e4a03 100644
--- a/ondevice-signing/StatsReporter.cpp
+++ b/ondevice-signing/StatsReporter.cpp
@@ -20,12 +20,13 @@
 #include <string>
 #include <sys/stat.h>
 
-// Keep these constant in sync with COMPOS_METRIC_NAME & METRICS_FILE in OdsignStatsLogger.java.
+// Keep these constants in sync with those in OdsignStatsLogger.java.
 constexpr const char* kOdsignMetricsFile = "/data/misc/odsign/metrics/odsign-metrics.txt";
 constexpr const char* kComposMetricName = "comp_os_artifacts_check_record";
+constexpr const char* kOdsignMetricName = "odsign_record";
 
 StatsReporter::~StatsReporter() {
-    if (comp_os_artifacts_check_record_ == nullptr) {
+    if (comp_os_artifacts_check_record_ == nullptr && !odsign_record_enabled_) {
         LOG(INFO) << "Metrics report is empty";
 
         // Remove the metrics file if any old version of the file already exists
@@ -42,24 +43,31 @@
         PLOG(ERROR) << "Could not open file: " << kOdsignMetricsFile;
         return;
     }
-
-    odsign_metrics_file_ << kComposMetricName << ' ';
-    odsign_metrics_file_ << comp_os_artifacts_check_record_->current_artifacts_ok << ' ';
-    odsign_metrics_file_ << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
-                         << ' ';
-    odsign_metrics_file_ << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
-                         << '\n';
     if (chmod(kOdsignMetricsFile, 0644) != 0) {
         PLOG(ERROR) << "Could not set correct file permissions for " << kOdsignMetricsFile;
         return;
     }
+
+    if (comp_os_artifacts_check_record_ != nullptr) {
+        odsign_metrics_file_ << kComposMetricName << ' '
+                             << comp_os_artifacts_check_record_->current_artifacts_ok << ' '
+                             << comp_os_artifacts_check_record_->comp_os_pending_artifacts_exists
+                             << ' '
+                             << comp_os_artifacts_check_record_->use_comp_os_generated_artifacts
+                             << '\n';
+    }
+
+    if (odsign_record_enabled_) {
+        odsign_metrics_file_ << kOdsignMetricName << ' ' << odsign_record_.status << '\n';
+    }
+
     odsign_metrics_file_.close();
     if (!odsign_metrics_file_) {
         PLOG(ERROR) << "Failed to close the file";
     }
 }
 
-StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetComposArtifactsCheckRecord() {
+StatsReporter::CompOsArtifactsCheckRecord* StatsReporter::GetOrCreateComposArtifactsCheckRecord() {
     if (comp_os_artifacts_check_record_ == nullptr) {
         comp_os_artifacts_check_record_ = std::make_unique<CompOsArtifactsCheckRecord>();
     }
diff --git a/ondevice-signing/StatsReporter.h b/ondevice-signing/StatsReporter.h
index 2682b96..add7a11 100644
--- a/ondevice-signing/StatsReporter.h
+++ b/ondevice-signing/StatsReporter.h
@@ -18,27 +18,44 @@
 
 #include <fstream>
 
+#include "statslog_odsign.h"
+
 // Class to store CompOsArtifactsCheck related metrics.
 // These are flushed to a file kOdsignMetricsFile and consumed by
 // System Server (in class OdsignStatsLogger) & sent to statsd.
 class StatsReporter {
   public:
-    // Keep sync with EarlyBootCompOsArtifactsCheckReported
-    // definition in proto_logging/stats/atoms.proto.
+    // Keep in sync with the EarlyBootCompOsArtifactsCheckReported definition in
+    // proto_logging/stats/atoms.proto.
     struct CompOsArtifactsCheckRecord {
         bool current_artifacts_ok = false;
         bool comp_os_pending_artifacts_exists = false;
         bool use_comp_os_generated_artifacts = false;
     };
 
+    // Keep in sync with the OdsignReported definition in proto_logging/stats/atoms.proto.
+    struct OdsignRecord {
+        int32_t status = art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_UNSPECIFIED;
+    };
+
     // The report is flushed (from buffer) into a file by the destructor.
     ~StatsReporter();
 
-    // Get pointer to comp_os_artifacts_check_record, caller can then modify it.
-    // Note: pointer remains valid for the lifetime of this StatsReporter.
-    CompOsArtifactsCheckRecord* GetComposArtifactsCheckRecord();
+    // Returns a mutable CompOS record. The pointer remains valid for the lifetime of this
+    // StatsReporter. If this function is not called, no CompOS record will be logged.
+    CompOsArtifactsCheckRecord* GetOrCreateComposArtifactsCheckRecord();
+
+    // Returns a mutable odsign record. The pointer remains valid for the lifetime of this
+    // StatsReporter.
+    OdsignRecord* GetOdsignRecord() { return &odsign_record_; }
+
+    // Enables/disables odsign metrics.
+    void SetOdsignRecordEnabled(bool value) { odsign_record_enabled_ = value; }
 
   private:
     // Temporary buffer which stores the metrics.
     std::unique_ptr<CompOsArtifactsCheckRecord> comp_os_artifacts_check_record_;
+
+    OdsignRecord odsign_record_;
+    bool odsign_record_enabled_ = true;
 };
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index c45e308..93ec3e4 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -35,6 +35,7 @@
 #include "KeystoreKey.h"
 #include "StatsReporter.h"
 #include "VerityUtils.h"
+#include "statslog_odsign.h"
 
 #include "odsign_info.pb.h"
 
@@ -370,7 +371,7 @@
                                                      bool* digests_verified,
                                                      StatsReporter* stats_reporter) {
     StatsReporter::CompOsArtifactsCheckRecord* compos_check_record =
-        stats_reporter->GetComposArtifactsCheckRecord();
+        stats_reporter->GetOrCreateComposArtifactsCheckRecord();
 
     if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
         // No pending CompOS artifacts, all that matters is the current ones.
@@ -468,12 +469,9 @@
 }  // namespace
 
 int main(int /* argc */, char** argv) {
-    // stats_reporter is a pointer so that we can explicitly delete it
-    // instead of waiting for the program to die & its destrcutor be called
-    auto stats_reporter = std::make_unique<StatsReporter>();
     android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
 
-    auto errorScopeGuard = []() {
+    auto scope_guard = android::base::make_scope_guard([]() {
         // In case we hit any error, remove the artifacts and tell Zygote not to use
         // anything
         removeDirectory(kArtArtifactsDir);
@@ -485,17 +483,24 @@
         SetProperty(kOdsignVerificationDoneProp, "1");
         // Tell init it shouldn't try to restart us - see odsign.rc
         SetProperty(kStopServiceProp, "odsign");
-    };
-    auto scope_guard = android::base::make_scope_guard(errorScopeGuard);
+    });
+
+    // `stats_reporter` must come after `scope_guard` so that its destructor is called before
+    // `scope_guard`.
+    auto stats_reporter = std::make_unique<StatsReporter>();
+    StatsReporter::OdsignRecord* odsign_record = stats_reporter->GetOdsignRecord();
 
     if (!android::base::GetBoolProperty("ro.apex.updatable", false)) {
         LOG(INFO) << "Device doesn't support updatable APEX, exiting.";
+        stats_reporter->SetOdsignRecordEnabled(false);
         return 0;
     }
     auto keystoreResult =
         KeystoreKey::getInstance(kPublicKeySignature, kKeyAlias, kKeyNspace, kKeyBootLevel);
     if (!keystoreResult.ok()) {
         LOG(ERROR) << "Could not create keystore key: " << keystoreResult.error();
+        odsign_record->status =
+            art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_KEYSTORE_FAILED;
         return -1;
     }
     SigningKey* key = keystoreResult.value();
@@ -517,6 +522,8 @@
             if (!new_cert.ok()) {
                 LOG(ERROR) << "Failed to create X509 certificate: " << new_cert.error();
                 // TODO apparently the key become invalid - delete the blob / cert
+                odsign_record->status =
+                    art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CERT_FAILED;
                 return -1;
             }
         } else {
@@ -526,6 +533,8 @@
         if (!cert_add_result.ok()) {
             LOG(ERROR) << "Failed to add certificate to fs-verity keyring: "
                        << cert_add_result.error();
+            odsign_record->status =
+                art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CERT_FAILED;
             return -1;
         }
     }
@@ -535,12 +544,6 @@
         useCompOs ? CheckCompOsPendingArtifacts(*key, &digests_verified, stats_reporter.get())
                   : checkArtifacts();
 
-    // Explicitly reset the pointer - We rely on stats_reporter's
-    // destructor for actually writing the buffered metrics. This will otherwise not be called
-    // if the program doesn't exit normally (for ex, killed by init, which actually happens
-    // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
-    stats_reporter.reset();
-
     // The artifacts dir doesn't necessarily need to exist; if the existing
     // artifacts on the system partition are valid, those can be used.
     int err = access(kArtArtifactsDir.c_str(), F_OK);
@@ -578,6 +581,8 @@
                 // instead prevent Zygote from using them (which is taken care of
                 // in the exit handler).
                 LOG(ERROR) << "Failed to remove unknown artifacts.";
+                odsign_record->status =
+                    art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_CLEANUP_FAILED;
                 return -1;
             }
         }
@@ -591,11 +596,16 @@
     if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
         // No new artifacts generated, and we verified existing ones above, nothing left to do.
         LOG(INFO) << "odrefresh said artifacts are VALID";
+        stats_reporter->SetOdsignRecordEnabled(false);
     } else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
                odrefresh_status == art::odrefresh::ExitCode::kCompilationFailed) {
         const bool compiled_all = odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess;
         LOG(INFO) << "odrefresh compiled " << (compiled_all ? "all" : "partial")
                   << " artifacts, returned " << odrefresh_status;
+        // This value may be overwritten later.
+        odsign_record->status =
+            compiled_all ? art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ALL_OK
+                         : art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_PARTIAL_OK;
         Result<std::map<std::string, std::string>> digests;
         if (supportsFsVerity) {
             digests = addFilesToVerityRecursive(kArtArtifactsDir, *key);
@@ -606,24 +616,39 @@
         }
         if (!digests.ok()) {
             LOG(ERROR) << digests.error();
+            odsign_record->status =
+                art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_SIGNING_FAILED;
             return -1;
         }
         auto persistStatus = persistDigests(*digests, *key);
         if (!persistStatus.ok()) {
             LOG(ERROR) << persistStatus.error();
+            odsign_record->status =
+                art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_SIGNING_FAILED;
             return -1;
         }
     } else if (odrefresh_status == art::odrefresh::ExitCode::kCleanupFailed) {
         LOG(ERROR) << "odrefresh failed cleaning up existing artifacts";
+        odsign_record->status =
+            art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ODREFRESH_FAILED;
         return -1;
     } else {
         LOG(ERROR) << "odrefresh exited unexpectedly, returned " << odrefresh_status;
+        odsign_record->status =
+            art::metrics::statsd::ODSIGN_REPORTED__STATUS__STATUS_ODREFRESH_FAILED;
         return -1;
     }
 
     LOG(INFO) << "On-device signing done.";
 
     scope_guard.Disable();
+
+    // Explicitly reset the pointer - We rely on stats_reporter's
+    // destructor for actually writing the buffered metrics. This will otherwise not be called
+    // if the program doesn't exit normally (for ex, killed by init, which actually happens
+    // because odsign (after it finishes) sets kStopServiceProp instructing init to kill it).
+    stats_reporter.reset();
+
     // At this point, we're done with the key for sure
     SetProperty(kOdsignKeyDoneProp, "1");
     // And we did a successful verification
diff --git a/provisioner/rkp_factory_extraction_lib.cpp b/provisioner/rkp_factory_extraction_lib.cpp
index 77d032b..d85e85f 100644
--- a/provisioner/rkp_factory_extraction_lib.cpp
+++ b/provisioner/rkp_factory_extraction_lib.cpp
@@ -46,11 +46,14 @@
 using aidl::android::hardware::security::keymint::remote_prov::getProdEekChain;
 using aidl::android::hardware::security::keymint::remote_prov::jsonEncodeCsrWithBuild;
 using aidl::android::hardware::security::keymint::remote_prov::parseAndValidateFactoryDeviceInfo;
+using aidl::android::hardware::security::keymint::remote_prov::verifyFactoryCsr;
 using aidl::android::hardware::security::keymint::remote_prov::verifyFactoryProtectedData;
 
 using namespace cppbor;
 using namespace cppcose;
 
+constexpr size_t kVersionWithoutSuperencryption = 3;
+
 std::string toBase64(const std::vector<uint8_t>& buffer) {
     size_t base64Length;
     int rc = EVP_EncodedLength(&base64Length, buffer.size());
@@ -97,11 +100,11 @@
     return challenge;
 }
 
-CborResult<Array> composeCertificateRequest(const ProtectedData& protectedData,
-                                            const DeviceInfo& verifiedDeviceInfo,
-                                            const std::vector<uint8_t>& challenge,
-                                            const std::vector<uint8_t>& keysToSignMac,
-                                            IRemotelyProvisionedComponent* provisionable) {
+CborResult<Array> composeCertificateRequestV1(const ProtectedData& protectedData,
+                                              const DeviceInfo& verifiedDeviceInfo,
+                                              const std::vector<uint8_t>& challenge,
+                                              const std::vector<uint8_t>& keysToSignMac,
+                                              IRemotelyProvisionedComponent* provisionable) {
     Array macedKeysToSign = Array()
                                 .add(Map().add(1, 5).encode())  // alg: hmac-sha256
                                 .add(Map())                     // empty unprotected headers
@@ -131,7 +134,7 @@
     return {std::move(certificateRequest), ""};
 }
 
-CborResult<Array> getCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+CborResult<Array> getCsrV1(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
     std::vector<uint8_t> keysToSignMac;
     std::vector<MacedPublicKey> emptyKeys;
     DeviceInfo verifiedDeviceInfo;
@@ -154,11 +157,11 @@
                   << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
         exit(-1);
     }
-    return composeCertificateRequest(protectedData, verifiedDeviceInfo, challenge, keysToSignMac,
-                                     irpc);
+    return composeCertificateRequestV1(protectedData, verifiedDeviceInfo, challenge, keysToSignMac,
+                                       irpc);
 }
 
-void selfTestGetCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+void selfTestGetCsrV1(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
     std::vector<uint8_t> keysToSignMac;
     std::vector<MacedPublicKey> emptyKeys;
     DeviceInfo verifiedDeviceInfo;
@@ -192,4 +195,86 @@
                                              hwInfo.supportedEekCurve, irpc, challenge);
 
     std::cout << "Self test successful." << std::endl;
-}
\ No newline at end of file
+}
+
+CborResult<Array> composeCertificateRequestV3(const std::vector<uint8_t>& csr) {
+    auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
+    if (!parsedCsr) {
+        return {nullptr, csrErrMsg};
+    }
+    if (!parsedCsr->asArray()) {
+        return {nullptr, "CSR is not a CBOR array."};
+    }
+
+    return {std::unique_ptr<Array>(parsedCsr.release()->asArray()), ""};
+}
+
+CborResult<cppbor::Array> getCsrV3(std::string_view componentName,
+                                   IRemotelyProvisionedComponent* irpc) {
+    std::vector<uint8_t> csr;
+    std::vector<MacedPublicKey> emptyKeys;
+    const std::vector<uint8_t> challenge = generateChallenge();
+
+    auto status = irpc->generateCertificateRequestV2(emptyKeys, challenge, &csr);
+    if (!status.isOk()) {
+        std::cerr << "Bundle extraction failed for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    return composeCertificateRequestV3(csr);
+}
+
+void selfTestGetCsrV3(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+    std::vector<uint8_t> csr;
+    std::vector<MacedPublicKey> emptyKeys;
+    const std::vector<uint8_t> challenge = generateChallenge();
+
+    auto status = irpc->generateCertificateRequestV2(emptyKeys, challenge, &csr);
+    if (!status.isOk()) {
+        std::cerr << "Bundle extraction failed for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    auto result = verifyFactoryCsr(/*keysToSign=*/cppbor::Array(), csr, irpc, challenge);
+    if (!result) {
+        std::cerr << "Self test failed for '" << componentName
+                  << "'. Error message: " << result.message() << "." << std::endl;
+        exit(-1);
+    }
+
+    std::cout << "Self test successful." << std::endl;
+}
+
+CborResult<Array> getCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+    RpcHardwareInfo hwInfo;
+    auto status = irpc->getHardwareInfo(&hwInfo);
+    if (!status.isOk()) {
+        std::cerr << "Failed to get hardware info for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    if (hwInfo.versionNumber < kVersionWithoutSuperencryption) {
+        return getCsrV1(componentName, irpc);
+    } else {
+        return getCsrV3(componentName, irpc);
+    }
+}
+
+void selfTestGetCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+    RpcHardwareInfo hwInfo;
+    auto status = irpc->getHardwareInfo(&hwInfo);
+    if (!status.isOk()) {
+        std::cerr << "Failed to get hardware info for '" << componentName
+                  << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+        exit(-1);
+    }
+
+    if (hwInfo.versionNumber < kVersionWithoutSuperencryption) {
+        selfTestGetCsrV1(componentName, irpc);
+    } else {
+        selfTestGetCsrV3(componentName, irpc);
+    }
+}
diff --git a/provisioner/rkp_factory_extraction_lib.h b/provisioner/rkp_factory_extraction_lib.h
index a803582..a218338 100644
--- a/provisioner/rkp_factory_extraction_lib.h
+++ b/provisioner/rkp_factory_extraction_lib.h
@@ -25,7 +25,8 @@
 #include <string_view>
 #include <vector>
 
-constexpr size_t kChallengeSize = 16;
+// Challenge size must be between 32 and 64 bytes inclusive.
+constexpr size_t kChallengeSize = 64;
 
 // Contains a the result of an operation that should return cborData on success.
 // Returns an an error message and null cborData on error.
@@ -50,4 +51,4 @@
 // Generates a test certificate chain and validates it, exiting the process on error.
 void selfTestGetCsr(
     std::string_view componentName,
-    aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent* irpc);
\ No newline at end of file
+    aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent* irpc);
diff --git a/provisioner/rkp_factory_extraction_lib_test.cpp b/provisioner/rkp_factory_extraction_lib_test.cpp
index b27b717..05509b3 100644
--- a/provisioner/rkp_factory_extraction_lib_test.cpp
+++ b/provisioner/rkp_factory_extraction_lib_test.cpp
@@ -72,6 +72,10 @@
                  const std::vector<uint8_t>& in_challenge, DeviceInfo* out_deviceInfo,
                  ProtectedData* out_protectedData, std::vector<uint8_t>* _aidl_return),
                 (override));
+    MOCK_METHOD(ScopedAStatus, generateCertificateRequestV2,
+                (const std::vector<MacedPublicKey>& in_keysToSign,
+                 const std::vector<uint8_t>& in_challenge, std::vector<uint8_t>* _aidl_return),
+                (override));
     MOCK_METHOD(ScopedAStatus, getInterfaceVersion, (int32_t * _aidl_return), (override));
     MOCK_METHOD(ScopedAStatus, getInterfaceHash, (std::string * _aidl_return), (override));
 };
@@ -221,3 +225,35 @@
     EXPECT_THAT(actualMacedKeys->get(2)->asNull(), NotNull());
     EXPECT_THAT(actualMacedKeys->get(3)->asBstr(), Pointee(Eq(Bstr(kFakeMac))));
 }
+
+TEST(LibRkpFactoryExtractionTests, GetCsrWithV3Hal) {
+    const std::vector<uint8_t> kCsr = Array()
+                                          .add(3 /* version */)
+                                          .add(Map() /* UdsCerts */)
+                                          .add(Array() /* DiceCertChain */)
+                                          .add(Array() /* SignedData */)
+                                          .encode();
+    std::vector<uint8_t> challenge;
+
+    // Set up mock, then call getCsr
+    auto mockRpc = SharedRefBase::make<MockIRemotelyProvisionedComponent>();
+    EXPECT_CALL(*mockRpc, getHardwareInfo(NotNull())).WillRepeatedly([](RpcHardwareInfo* hwInfo) {
+        hwInfo->versionNumber = 3;
+        return ScopedAStatus::ok();
+    });
+    EXPECT_CALL(*mockRpc,
+                generateCertificateRequestV2(IsEmpty(),   // keysToSign
+                                             _,           // challenge
+                                             NotNull()))  // _aidl_return
+        .WillOnce(DoAll(SaveArg<1>(&challenge), SetArgPointee<2>(kCsr),
+                        Return(ByMove(ScopedAStatus::ok()))));
+
+    auto [csr, csrErrMsg] = getCsr("mock component name", mockRpc.get());
+    ASSERT_THAT(csr, NotNull()) << csrErrMsg;
+    ASSERT_THAT(csr, Pointee(Property(&Array::size, Eq(4))));
+
+    EXPECT_THAT(csr->get(0 /* version */), Pointee(Eq(Uint(3))));
+    EXPECT_THAT(csr->get(1)->asMap(), NotNull());
+    EXPECT_THAT(csr->get(2)->asArray(), NotNull());
+    EXPECT_THAT(csr->get(3)->asArray(), NotNull());
+}
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 0fe7d74..2aeabe0 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -47,8 +47,6 @@
 constexpr std::string_view kBuildPlusCsr = "build+csr";  // Text-encoded (JSON) build
                                                          // fingerprint plus CSR.
 
-constexpr size_t kChallengeSize = 16;
-
 void writeOutput(const std::string instance_name, const Array& csr) {
     if (FLAGS_output_format == kBinaryCsrOutput) {
         auto bytes = csr.encode();