Merge "[x509] Parse certificate chain from RKPD into individual certificate" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 6983fde..4da96c8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -117,6 +117,9 @@
"path": "packages/modules/Virtualization/service_vm/requests"
},
{
+ "path": "packages/modules/Virtualization/virtualizationservice"
+ },
+ {
"path": "packages/modules/Virtualization/vm"
},
{
diff --git a/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl b/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
index 51796f1..4813b35 100644
--- a/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
+++ b/microdroid_manager/aidl/android/system/virtualization/payload/IVmPayloadService.aidl
@@ -59,8 +59,8 @@
* Sequence of DER-encoded X.509 certificates that make up the attestation
* key's certificate chain.
*
- * The certificate chain starts with a root certificate and ends with a leaf
- * certificate covering the attested public key.
+ * The certificate chain starts with a leaf certificate covering the attested
+ * public key and ends with a root certificate.
*/
Certificate[] certificateChain;
}
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
diff --git a/vm_payload/include/vm_payload.h b/vm_payload/include/vm_payload.h
index 78cd80d..3483e1d 100644
--- a/vm_payload/include/vm_payload.h
+++ b/vm_payload/include/vm_payload.h
@@ -224,8 +224,8 @@
* Gets the number of certificates in the certificate chain.
*
* The certificate chain consists of a sequence of DER-encoded X.509 certificates that form
- * the attestation key's certificate chain. It starts with a root certificate and ends with a
- * leaf certificate covering the attested public key.
+ * the attestation key's certificate chain. It starts with a leaf certificate covering the attested
+ * public key and ends with a root certificate.
*
* \param result A pointer to the attestation result obtained from `AVmPayload_requestAttestation`
* when the attestation succeeds.
@@ -240,8 +240,8 @@
* attestation result.
*
* The certificate chain consists of a sequence of DER-encoded X.509 certificates that form
- * the attestation key's certificate chain. It starts with a root certificate and ends with a
- * leaf certificate covering the attested public key.
+ * the attestation key's certificate chain. It starts with a leaf certificate covering the attested
+ * public key and ends with a root certificate.
*
* \param result A pointer to the attestation result obtained from `AVmPayload_requestAttestation`
* when the attestation succeeds.