Add command line support for parsing CSRs am: b6b1b3f550

Original change: https://android-review.googlesource.com/c/platform/tools/security/+/2716099

Change-Id: I804b4d893ba1ff2255ed316105e116fe9cbbcbc2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/remote_provisioning/hwtrust/src/cbor/rkp/csr.rs b/remote_provisioning/hwtrust/src/cbor/rkp/csr.rs
index 6439a17..c432244 100644
--- a/remote_provisioning/hwtrust/src/cbor/rkp/csr.rs
+++ b/remote_provisioning/hwtrust/src/cbor/rkp/csr.rs
@@ -82,7 +82,11 @@
             bail!("Invalid CSR version. Only '1' is supported, found '{}", version);
         }
 
-        let _unverified_info = FieldValue::from_optional_value("UnverifiedDeviceInfo", csr.pop());
+        // CSRs that are uploaded to the backend have an additional unverified info field tacked
+        // onto them. We just ignore that, so if it's there pop it and move on.
+        if csr.len() == 5 {
+            FieldValue::from_optional_value("UnverifiedDeviceInfo", csr.pop());
+        }
 
         let signed_data =
             FieldValue::from_optional_value("SignedData", csr.pop()).into_cose_sign1()?;
diff --git a/remote_provisioning/hwtrust/src/main.rs b/remote_provisioning/hwtrust/src/main.rs
index 12df265..d5d8a61 100644
--- a/remote_provisioning/hwtrust/src/main.rs
+++ b/remote_provisioning/hwtrust/src/main.rs
@@ -32,6 +32,7 @@
     VerifyDiceChain(DiceChainArgs),
     DiceChain(DiceChainArgs),
     FactoryCsr(FactoryCsrArgs),
+    Csr(CsrArgs),
 }
 
 #[derive(Parser)]
@@ -58,6 +59,16 @@
     csr_file: String,
 }
 
+#[derive(Parser)]
+/// Parse and verify a request payload that is suitable for the RKP server's SignCertificates API.
+/// In HALv3, this is the output of generateCertificateRequestV2. For previous HAL versions,
+/// the CSR is constructed by the remote provisioning service client, but is constructed from the
+/// outputs of generateCertificateRequest.
+struct CsrArgs {
+    /// Path to a file containing a single CSR, encoded as CBOR.
+    csr_file: String,
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
 enum VsrVersion {
     /// VSR 13 / Android T / 2022
@@ -106,6 +117,7 @@
         }
         Action::DiceChain(sub_args) => verify_dice_chain(&args, sub_args)?,
         Action::FactoryCsr(sub_args) => parse_factory_csr(&args, sub_args)?,
+        Action::Csr(sub_args) => parse_csr(&args, sub_args)?,
     }
     println!("Success!");
     Ok(())
@@ -141,6 +153,16 @@
     Ok(())
 }
 
+fn parse_csr(args: &Args, sub_args: &CsrArgs) -> Result<()> {
+    let session = session_from_vsr(args.vsr);
+    let input = &fs::File::open(&sub_args.csr_file)?;
+    let csr = rkp::Csr::from_cbor(&session, input)?;
+    if args.verbose {
+        print!("{csr:#?}");
+    }
+    Ok(())
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/remote_provisioning/hwtrust/testdata/csr/bad_csr.cbor b/remote_provisioning/hwtrust/testdata/csr/bad_csr.cbor
new file mode 100644
index 0000000..7a58924
--- /dev/null
+++ b/remote_provisioning/hwtrust/testdata/csr/bad_csr.cbor
Binary files differ
diff --git a/remote_provisioning/hwtrust/testdata/csr/valid_csr.cbor b/remote_provisioning/hwtrust/testdata/csr/valid_csr.cbor
new file mode 100644
index 0000000..bc3bbb9
--- /dev/null
+++ b/remote_provisioning/hwtrust/testdata/csr/valid_csr.cbor
Binary files differ
diff --git a/remote_provisioning/hwtrust/tests/hwtrust_cli.rs b/remote_provisioning/hwtrust/tests/hwtrust_cli.rs
index d487557..8136a7d 100644
--- a/remote_provisioning/hwtrust/tests/hwtrust_cli.rs
+++ b/remote_provisioning/hwtrust/tests/hwtrust_cli.rs
@@ -50,3 +50,17 @@
         .unwrap();
     assert!(!output.status.success());
 }
+
+#[test]
+fn exit_code_for_good_csr() {
+    let output =
+        Command::new(hwtrust_bin()).args(["csr", "testdata/csr/valid_csr.cbor"]).output().unwrap();
+    assert!(output.status.success());
+}
+
+#[test]
+fn exit_code_for_bad_csr() {
+    let output =
+        Command::new(hwtrust_bin()).args(["csr", "testdata/csr/bad_csr.cbor"]).output().unwrap();
+    assert!(!output.status.success());
+}