[attestation] Build the certificate output for attestation

This cl builds the certificate output for the client VM in
pVM remote attestation. The certificate includes the subject
public key extracted from the CSR of the client VM and is
signed by the remotely provisioned key obtained from RKPD.

However, certain fields such as VM information in the
extension are currently absent and will be incorporated in
future changes.

Bug: 311359366
Bug: 309441500
Test: atest rialto_test
Change-Id: Ie9acf72b6158f371c70fa303932aa2b6bf9cfab4
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 728c1eb..1ab02e9 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -109,17 +109,21 @@
         "android.system.virtualizationservice-rust",
         "libandroid_logger",
         "libanyhow",
+        "libbssl_avf_nostd",
         "libciborium",
         "libclient_vm_csr",
+        "libcoset",
         "libdiced_sample_inputs",
         "liblibc",
         "liblog_rust",
         "libservice_vm_comm",
         "libservice_vm_manager",
         "libvmclient",
+        "libx509_parser",
     ],
     data: [
         ":rialto_unsigned",
+        ":test_rkp_cert_chain",
     ],
     test_suites: ["general-tests"],
     enabled: false,
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 0f59350..85c3efe 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -22,22 +22,32 @@
     binder::{ParcelFileDescriptor, ProcessState},
 };
 use anyhow::{bail, Context, Result};
+use bssl_avf::{sha256, EcKey, EvpPKey};
 use ciborium::value::Value;
 use client_vm_csr::generate_attestation_key_and_csr;
+use coset::{CborSerializable, CoseMac0, CoseSign};
 use log::info;
 use service_vm_comm::{
-    ClientVmAttestationParams, EcdsaP256KeyPair, GenerateCertificateRequestParams, Request,
-    RequestProcessingError, Response, VmType,
+    ClientVmAttestationParams, Csr, CsrPayload, EcdsaP256KeyPair, GenerateCertificateRequestParams,
+    Request, Response, VmType,
 };
 use service_vm_manager::ServiceVm;
+use std::fs;
 use std::fs::File;
 use std::io;
 use std::panic;
 use std::path::PathBuf;
 use vmclient::VmInstance;
+use x509_parser::{
+    certificate::X509Certificate,
+    der_parser::{der::parse_der, oid, oid::Oid},
+    prelude::FromDer,
+    x509::{AlgorithmIdentifier, SubjectPublicKeyInfo, X509Version},
+};
 
 const UNSIGNED_RIALTO_PATH: &str = "/data/local/tmp/rialto_test/arm64/rialto_unsigned.bin";
 const INSTANCE_IMG_PATH: &str = "/data/local/tmp/rialto_test/arm64/instance.img";
+const TEST_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
 
 #[test]
 fn process_requests_in_protected_vm() -> Result<()> {
@@ -55,7 +65,7 @@
     check_processing_reverse_request(&mut vm)?;
     let key_pair = check_processing_generating_key_pair_request(&mut vm)?;
     check_processing_generating_certificate_request(&mut vm, &key_pair.maced_public_key)?;
-    check_attestation_request(&mut vm, &key_pair.key_blob)?;
+    check_attestation_request(&mut vm, &key_pair)?;
     Ok(())
 }
 
@@ -110,7 +120,10 @@
     }
 }
 
-fn check_attestation_request(vm: &mut ServiceVm, key_blob: &[u8]) -> Result<()> {
+fn check_attestation_request(
+    vm: &mut ServiceVm,
+    remotely_provisioned_key_pair: &EcdsaP256KeyPair,
+) -> Result<()> {
     /// The following data was generated randomly with urandom.
     const CHALLENGE: [u8; 16] = [
         0x7d, 0x86, 0x58, 0x79, 0x3a, 0x09, 0xdf, 0x1c, 0xa5, 0x80, 0x80, 0x15, 0x2b, 0x13, 0x17,
@@ -118,10 +131,18 @@
     ];
     let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()?;
     let attestation_data = generate_attestation_key_and_csr(&CHALLENGE, &dice_artifacts)?;
+    let cert_chain = fs::read(TEST_CERT_CHAIN_PATH)?;
+    let (remaining, cert) = X509Certificate::from_der(&cert_chain)?;
 
+    // Builds the mock parameters for the client VM attestation.
+    // The `csr` and `remotely_provisioned_key_blob` parameters are extracted from the same
+    // libraries as in production.
+    // The `remotely_provisioned_cert` parameter is an RKP certificate extracted from a test
+    // certificate chain retrieved from RKPD.
     let params = ClientVmAttestationParams {
-        csr: attestation_data.csr.into_cbor_vec()?,
-        remotely_provisioned_key_blob: key_blob.to_vec(),
+        csr: attestation_data.csr.clone().into_cbor_vec()?,
+        remotely_provisioned_key_blob: remotely_provisioned_key_pair.key_blob.to_vec(),
+        remotely_provisioned_cert: cert_chain[..(cert_chain.len() - remaining.len())].to_vec(),
     };
     let request = Request::RequestClientVmAttestation(params);
 
@@ -129,12 +150,76 @@
     info!("Received response: {response:?}.");
 
     match response {
-        // TODO(b/309441500): Check the certificate once it is implemented.
-        Response::Err(RequestProcessingError::OperationUnimplemented) => Ok(()),
+        Response::RequestClientVmAttestation(certificate) => {
+            check_certificate_for_client_vm(
+                &certificate,
+                &remotely_provisioned_key_pair.maced_public_key,
+                &attestation_data.csr,
+                &cert,
+            )?;
+            Ok(())
+        }
         _ => bail!("Incorrect response type: {response:?}"),
     }
 }
 
+fn check_certificate_for_client_vm(
+    certificate: &[u8],
+    maced_public_key: &[u8],
+    csr: &Csr,
+    parent_certificate: &X509Certificate,
+) -> Result<()> {
+    let cose_mac = CoseMac0::from_slice(maced_public_key)?;
+    let authority_public_key = EcKey::from_cose_public_key(&cose_mac.payload.unwrap()).unwrap();
+    let (remaining, cert) = X509Certificate::from_der(certificate)?;
+    assert!(remaining.is_empty());
+
+    // Checks the certificate signature against the authority public key.
+    const ECDSA_WITH_SHA_256: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .2);
+    let expected_algorithm =
+        AlgorithmIdentifier { algorithm: ECDSA_WITH_SHA_256, parameters: None };
+    assert_eq!(expected_algorithm, cert.signature_algorithm);
+    let digest = sha256(cert.tbs_certificate.as_ref()).unwrap();
+    authority_public_key
+        .ecdsa_verify(cert.signature_value.as_ref(), &digest)
+        .expect("Failed to verify the certificate signature with the authority public key");
+
+    // Checks that the certificate's subject public key is equal to the key in the CSR.
+    let cose_sign = CoseSign::from_slice(&csr.signed_csr_payload)?;
+    let csr_payload =
+        cose_sign.payload.as_ref().and_then(|v| CsrPayload::from_cbor_slice(v).ok()).unwrap();
+    let subject_public_key = EcKey::from_cose_public_key(&csr_payload.public_key).unwrap();
+    let expected_spki_data =
+        EvpPKey::try_from(subject_public_key).unwrap().subject_public_key_info().unwrap();
+    let (remaining, expected_spki) = SubjectPublicKeyInfo::from_der(&expected_spki_data)?;
+    assert!(remaining.is_empty());
+    assert_eq!(&expected_spki, cert.public_key());
+
+    // Checks the certificate extension.
+    const ATTESTATION_EXTENSION_OID: Oid<'static> = oid!(1.3.6 .1 .4 .1 .11129 .2 .1 .29 .1);
+    let extensions = cert.extensions();
+    assert_eq!(1, extensions.len());
+    let extension = &extensions[0];
+    assert_eq!(ATTESTATION_EXTENSION_OID, extension.oid);
+    assert!(!extension.critical);
+    let (remaining, extension) = parse_der(extension.value)?;
+    assert!(remaining.is_empty());
+    let attestation_ext = extension.as_sequence()?;
+    assert_eq!(1, attestation_ext.len());
+    assert_eq!(csr_payload.challenge, attestation_ext[0].as_slice()?);
+
+    // Checks other fields on the certificate
+    assert_eq!(X509Version::V3, cert.version());
+    assert_eq!(parent_certificate.validity(), cert.validity());
+    assert_eq!(
+        String::from("CN=Android Protected Virtual Machine Key"),
+        cert.subject().to_string()
+    );
+    assert_eq!(parent_certificate.subject(), cert.issuer());
+
+    Ok(())
+}
+
 /// TODO(b/300625792): Check the CSR with libhwtrust once the CSR is complete.
 fn check_csr(csr: Vec<u8>) -> Result<()> {
     let mut reader = io::Cursor::new(csr);