[x509] Parse certificate chain from RKPD into individual certificate

This cl also adjusts the order of the certificates to place the
leaf certificate at the beginning, ensuring consistency with the
certificate order from RKP.

The new test is added to busytown config at cl/584885373.

Bug: 309780089
Test: virtualizationservice_test
Change-Id: Ic3cfdd174483d0905d741c40fef730c652c078c4
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 5cf2a39..3f8d193 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -2,8 +2,8 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-rust_binary {
-    name: "virtualizationservice",
+rust_defaults {
+    name: "virtualizationservice_defaults",
     crate_name: "virtualizationservice",
     defaults: ["avf_build_flags_rust"],
     edition: "2021",
@@ -45,13 +45,39 @@
         "libserde_xml_rs",
         "libservice_vm_comm",
         "libservice_vm_manager",
+        "libx509_parser",
     ],
     apex_available: ["com.android.virt"],
 }
 
+rust_binary {
+    name: "virtualizationservice",
+    defaults: ["virtualizationservice_defaults"],
+}
+
 xsd_config {
     name: "assignable_devices",
     srcs: ["assignable_devices.xsd"],
     api_dir: "schema",
     package_name: "android.system.virtualizationservice",
 }
+
+rust_test {
+    name: "virtualizationservice_test",
+    defaults: ["virtualizationservice_defaults"],
+    test_suites: ["general-tests"],
+    data: [
+        ":test_rkp_cert_chain",
+    ],
+}
+
+// The chain originates from a CTS test for Keymint, with the Keymint certificate
+// (leaf certificate) truncated.
+//
+// The certificate chain begins with a leaf certificate obtained from RKP and ends
+// with a root certificate. Each certificate in the chain possesses a signature that
+// is signed by the private key of the subsequent certificate in the chain.
+filegroup {
+    name: "test_rkp_cert_chain",
+    srcs: ["testdata/rkp_cert_chain.der"],
+}
diff --git a/virtualizationservice/TEST_MAPPING b/virtualizationservice/TEST_MAPPING
new file mode 100644
index 0000000..4fef83c
--- /dev/null
+++ b/virtualizationservice/TEST_MAPPING
@@ -0,0 +1,9 @@
+// When adding or removing tests here, don't forget to amend _all_modules list in
+// wireless/android/busytown/ath_config/configs/prod/avf/tests.gcl
+{
+  "avf-presubmit" : [
+    {
+      "name" : "virtualizationservice_test"
+    }
+  ]
+}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 7cdfdc6..d1f7291 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -52,6 +52,7 @@
 use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
 use vsock::{VsockListener, VsockStream};
 use nix::unistd::{chown, Uid};
+use x509_parser::{traits::FromDer, certificate::X509Certificate};
 
 /// The unique ID of a VM used (together with a port number) for vsock communication.
 pub type Cid = u32;
@@ -166,35 +167,42 @@
         requester_uid: i32,
     ) -> binder::Result<Vec<Certificate>> {
         check_manage_access()?;
-        info!("Received csr. Requestting attestation...");
-        if cfg!(remote_attestation) {
-            let attestation_key = get_rkpd_attestation_key(
-                REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
-                requester_uid as u32,
-            )
-            .context("Failed to retrieve the remotely provisioned keys")
-            .with_log()
-            .or_service_specific_exception(-1)?;
-            let certificate = request_attestation(csr, &attestation_key.keyBlob)
-                .context("Failed to request attestation")
-                .with_log()
-                .or_service_specific_exception(-1)?;
-            // TODO(b/309780089): Parse the remotely provisioned certificate chain into
-            // individual certificates.
-            let mut certificate_chain =
-                vec![Certificate { encodedCertificate: attestation_key.encodedCertChain }];
-            certificate_chain.push(Certificate { encodedCertificate: certificate });
-            Ok(certificate_chain)
-        } else {
-            Err(Status::new_exception_str(
+        if !cfg!(remote_attestation) {
+            return Err(Status::new_exception_str(
                 ExceptionCode::UNSUPPORTED_OPERATION,
                 Some(
                     "requestAttestation is not supported with the remote_attestation feature \
                      disabled",
                 ),
             ))
-            .with_log()
+            .with_log();
         }
+        info!("Received csr. Requestting attestation...");
+        let attestation_key = get_rkpd_attestation_key(
+            REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
+            requester_uid as u32,
+        )
+        .context("Failed to retrieve the remotely provisioned keys")
+        .with_log()
+        .or_service_specific_exception(-1)?;
+        let mut certificate_chain = split_x509_certificate_chain(&attestation_key.encodedCertChain)
+            .context("Failed to split the remotely provisioned certificate chain")
+            .with_log()
+            .or_service_specific_exception(-1)?;
+        if certificate_chain.is_empty() {
+            return Err(Status::new_service_specific_error_str(
+                -1,
+                Some("The certificate chain should contain at least 1 certificate"),
+            ))
+            .with_log();
+        }
+        let certificate = request_attestation(csr, &attestation_key.keyBlob)
+            .context("Failed to request attestation")
+            .with_log()
+            .or_service_specific_exception(-1)?;
+        certificate_chain.insert(0, Certificate { encodedCertificate: certificate });
+
+        Ok(certificate_chain)
     }
 
     fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
@@ -288,6 +296,17 @@
     Ok(devices)
 }
 
+fn split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>> {
+    let mut out = Vec::new();
+    while !cert_chain.is_empty() {
+        let (remaining, _) = X509Certificate::from_der(cert_chain)?;
+        let end = cert_chain.len() - remaining.len();
+        out.push(Certificate { encodedCertificate: cert_chain[..end].to_vec() });
+        cert_chain = remaining;
+    }
+    Ok(out)
+}
+
 #[derive(Debug, Default)]
 struct GlobalVmInstance {
     /// The unique CID assigned to the VM for vsock communication.
@@ -561,3 +580,24 @@
 fn check_use_custom_virtual_machine() -> binder::Result<()> {
     check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::fs;
+
+    const TEST_RKP_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
+
+    #[test]
+    fn splitting_x509_certificate_chain_succeeds() -> Result<()> {
+        let bytes = fs::read(TEST_RKP_CERT_CHAIN_PATH)?;
+        let cert_chain = split_x509_certificate_chain(&bytes)?;
+
+        assert_eq!(4, cert_chain.len());
+        for cert in cert_chain {
+            let (remaining, _) = X509Certificate::from_der(&cert.encodedCertificate)?;
+            assert!(remaining.is_empty());
+        }
+        Ok(())
+    }
+}
diff --git a/virtualizationservice/testdata/rkp_cert_chain.der b/virtualizationservice/testdata/rkp_cert_chain.der
new file mode 100644
index 0000000..f32065d
--- /dev/null
+++ b/virtualizationservice/testdata/rkp_cert_chain.der
Binary files differ