Merge "[attestation] Build the certificate output for attestation" into main
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);
diff --git a/service_vm/comm/Android.bp b/service_vm/comm/Android.bp
index 6e05587..bdfc099 100644
--- a/service_vm/comm/Android.bp
+++ b/service_vm/comm/Android.bp
@@ -24,6 +24,7 @@
         "libbssl_avf_error_nostd",
         "libciborium_nostd",
         "libcoset_nostd",
+        "libder_nostd",
         "liblog_rust_nostd",
         "libserde_nostd",
     ],
diff --git a/service_vm/comm/src/message.rs b/service_vm/comm/src/message.rs
index 6dd0ccd..87c8378 100644
--- a/service_vm/comm/src/message.rs
+++ b/service_vm/comm/src/message.rs
@@ -66,6 +66,13 @@
 
     /// The key blob retrieved from RKPD by virtualizationservice.
     pub remotely_provisioned_key_blob: Vec<u8>,
+
+    /// The leaf certificate of the certificate chain retrieved from RKPD by
+    /// virtualizationservice.
+    ///
+    /// This certificate is a DER-encoded X.509 certificate that includes the remotely
+    /// provisioned public key.
+    pub remotely_provisioned_cert: Vec<u8>,
 }
 
 /// Represents a response to a request sent to the service VM.
@@ -120,6 +127,9 @@
 
     /// The requested operation has not been implemented.
     OperationUnimplemented,
+
+    /// An error happened during the DER encoding/decoding.
+    DerError,
 }
 
 impl fmt::Display for RequestProcessingError {
@@ -142,6 +152,9 @@
             Self::OperationUnimplemented => {
                 write!(f, "The requested operation has not been implemented")
             }
+            Self::DerError => {
+                write!(f, "An error happened during the DER encoding/decoding")
+            }
         }
     }
 }
@@ -166,6 +179,14 @@
     }
 }
 
+#[cfg(not(feature = "std"))]
+impl From<der::Error> for RequestProcessingError {
+    fn from(e: der::Error) -> Self {
+        error!("DER encoding/decoding error: {e}");
+        Self::DerError
+    }
+}
+
 /// Represents the params passed to GenerateCertificateRequest
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct GenerateCertificateRequestParams {
diff --git a/service_vm/requests/Android.bp b/service_vm/requests/Android.bp
index ecede8b..52b54b4 100644
--- a/service_vm/requests/Android.bp
+++ b/service_vm/requests/Android.bp
@@ -21,10 +21,13 @@
         "libcbor_util_nostd",
         "libciborium_nostd",
         "libcoset_nostd",
+        "libder_nostd",
         "libdiced_open_dice_nostd",
         "liblog_rust_nostd",
         "libserde_nostd",
         "libservice_vm_comm_nostd",
+        "libspki_nostd",
+        "libx509_cert_nostd",
         "libzeroize_nostd",
     ],
 }
diff --git a/service_vm/requests/src/cert.rs b/service_vm/requests/src/cert.rs
new file mode 100644
index 0000000..68ca382
--- /dev/null
+++ b/service_vm/requests/src/cert.rs
@@ -0,0 +1,130 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Generation of certificates and attestation extensions.
+
+use alloc::vec;
+use der::{
+    asn1::{BitStringRef, ObjectIdentifier, UIntRef},
+    oid::AssociatedOid,
+    Decode, Sequence,
+};
+use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};
+use x509_cert::{
+    certificate::{Certificate, TbsCertificate, Version},
+    ext::Extension,
+    name::Name,
+    time::Validity,
+};
+
+/// OID value for ECDSA with SHA-256, see RFC 5912 s6.
+const ECDSA_WITH_SHA_256: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
+
+/// OID value for the protected VM remote attestation extension.
+///
+/// This OID value was added at cl/584542390.
+const AVF_ATTESTATION_EXTENSION_V1: ObjectIdentifier =
+    ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.1.29.1");
+
+/// Attestation extension contents
+///
+/// ```asn1
+/// AttestationDescription ::= SEQUENCE {
+///     attestationChallenge       OCTET_STRING,
+/// }
+/// ```
+/// TODO(b/312448064): Add VM root of trust and payload information to the extension.
+#[derive(Debug, Clone, Sequence)]
+pub(crate) struct AttestationExtension<'a> {
+    #[asn1(type = "OCTET STRING")]
+    attestation_challenge: &'a [u8],
+}
+
+impl<'a> AssociatedOid for AttestationExtension<'a> {
+    const OID: ObjectIdentifier = AVF_ATTESTATION_EXTENSION_V1;
+}
+
+impl<'a> AttestationExtension<'a> {
+    pub(crate) fn new(challenge: &'a [u8]) -> Self {
+        Self { attestation_challenge: challenge }
+    }
+}
+
+/// Builds an X.509 `Certificate` as defined in RFC 5280 Section 4.1:
+///
+/// ```asn1
+/// Certificate  ::=  SEQUENCE  {
+///   tbsCertificate       TBSCertificate,
+///   signatureAlgorithm   AlgorithmIdentifier,
+///   signature            BIT STRING
+/// }
+/// ```
+pub(crate) fn build_certificate<'a>(
+    tbs_cert: TbsCertificate<'a>,
+    signature: &'a [u8],
+) -> der::Result<Certificate<'a>> {
+    Ok(Certificate {
+        signature_algorithm: tbs_cert.signature,
+        tbs_certificate: tbs_cert,
+        signature: BitStringRef::new(0, signature)?,
+    })
+}
+
+/// Builds an X.509 `TbsCertificate` as defined in RFC 5280 Section 4.1:
+///
+/// ```asn1
+/// TBSCertificate  ::=  SEQUENCE  {
+///   version         [0]  EXPLICIT Version DEFAULT v1,
+///   serialNumber         CertificateSerialNumber,
+///   signature            AlgorithmIdentifier,
+///   issuer               Name,
+///   validity             Validity,
+///   subject              Name,
+///   subjectPublicKeyInfo SubjectPublicKeyInfo,
+///   issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+///                        -- If present, version MUST be v2 or v3
+///   subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+///                        -- If present, version MUST be v2 or v3
+///   extensions      [3]  Extensions OPTIONAL
+///                        -- If present, version MUST be v3 --
+/// }
+/// ```
+pub(crate) fn build_tbs_certificate<'a>(
+    serial_number: &'a [u8],
+    issuer: Name<'a>,
+    subject: Name<'a>,
+    validity: Validity,
+    subject_public_key_info: &'a [u8],
+    attestation_ext: &'a [u8],
+) -> der::Result<TbsCertificate<'a>> {
+    let signature = AlgorithmIdentifier { oid: ECDSA_WITH_SHA_256, parameters: None };
+    let subject_public_key_info = SubjectPublicKeyInfo::from_der(subject_public_key_info)?;
+    let extensions = vec![Extension {
+        extn_id: AttestationExtension::OID,
+        critical: false,
+        extn_value: attestation_ext,
+    }];
+    Ok(TbsCertificate {
+        version: Version::V3,
+        serial_number: UIntRef::new(serial_number)?,
+        signature,
+        issuer,
+        validity,
+        subject,
+        subject_public_key_info,
+        issuer_unique_id: None,
+        subject_unique_id: None,
+        extensions: Some(extensions),
+    })
+}
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index 612605f..e1f345c 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -15,14 +15,17 @@
 //! This module contains functions related to the attestation of the
 //! client VM.
 
+use crate::cert;
 use crate::keyblob::decrypt_private_key;
 use alloc::vec::Vec;
-use bssl_avf::{sha256, EcKey};
+use bssl_avf::{rand_bytes, sha256, EcKey, EvpPKey};
 use core::result;
 use coset::{CborSerializable, CoseSign};
+use der::{Decode, Encode};
 use diced_open_dice::DiceArtifacts;
 use log::error;
 use service_vm_comm::{ClientVmAttestationParams, Csr, CsrPayload, RequestProcessingError};
+use x509_cert::{certificate::Certificate, name::Name};
 
 type Result<T> = result::Result<T, RequestProcessingError>;
 
@@ -50,21 +53,41 @@
     cose_sign.verify_signature(ATTESTATION_KEY_SIGNATURE_INDEX, aad, |signature, message| {
         ecdsa_verify(&ec_public_key, signature, message)
     })?;
+    let subject_public_key_info = EvpPKey::try_from(ec_public_key)?.subject_public_key_info()?;
 
     // TODO(b/278717513): Compare client VM's DICE chain in the `csr` up to pvmfw
     // cert with RKP VM's DICE chain.
 
+    // Builds the TBSCertificate.
+    // The serial number can be up to 20 bytes according to RFC5280 s4.1.2.2.
+    // In this case, a serial number with a length of 20 bytes is used to ensure that each
+    // certificate signed by RKP VM has a unique serial number.
+    let mut serial_number = [0u8; 20];
+    rand_bytes(&mut serial_number)?;
+    let subject = Name::encode_from_string("CN=Android Protected Virtual Machine Key")?;
+    let rkp_cert = Certificate::from_der(&params.remotely_provisioned_cert)?;
+    let attestation_ext = cert::AttestationExtension::new(&csr_payload.challenge).to_vec()?;
+    let tbs_cert = cert::build_tbs_certificate(
+        &serial_number,
+        rkp_cert.tbs_certificate.subject,
+        Name::from_der(&subject)?,
+        rkp_cert.tbs_certificate.validity,
+        &subject_public_key_info,
+        &attestation_ext,
+    )?;
+
+    // Signs the TBSCertificate and builds the Certificate.
+    // The two private key structs below will be zeroed out on drop.
     let private_key =
         decrypt_private_key(&params.remotely_provisioned_key_blob, dice_artifacts.cdi_seal())
             .map_err(|e| {
                 error!("Failed to decrypt the remotely provisioned key blob: {e}");
                 RequestProcessingError::FailedToDecryptKeyBlob
             })?;
-    let _ec_private_key = EcKey::from_ec_private_key(private_key.as_slice())?;
-
-    // TODO(b/309441500): Build a new certificate signed with the remotely provisioned
-    // `_private_key`.
-    Err(RequestProcessingError::OperationUnimplemented)
+    let ec_private_key = EcKey::from_ec_private_key(private_key.as_slice())?;
+    let signature = ecdsa_sign(&ec_private_key, &tbs_cert.to_vec()?)?;
+    let certificate = cert::build_certificate(tbs_cert, &signature)?;
+    Ok(certificate.to_vec()?)
 }
 
 fn ecdsa_verify(key: &EcKey, signature: &[u8], message: &[u8]) -> bssl_avf::Result<()> {
@@ -72,3 +95,8 @@
     let digest = sha256(message)?;
     key.ecdsa_verify(signature, &digest)
 }
+
+fn ecdsa_sign(key: &EcKey, message: &[u8]) -> bssl_avf::Result<Vec<u8>> {
+    let digest = sha256(message)?;
+    key.ecdsa_sign(&digest)
+}
diff --git a/service_vm/requests/src/lib.rs b/service_vm/requests/src/lib.rs
index b2db298..3f687a4 100644
--- a/service_vm/requests/src/lib.rs
+++ b/service_vm/requests/src/lib.rs
@@ -19,6 +19,7 @@
 extern crate alloc;
 
 mod api;
+mod cert;
 mod client_vm;
 mod keyblob;
 mod pub_key;
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index d1f7291..3ac1e60 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -196,10 +196,14 @@
             ))
             .with_log();
         }
-        let certificate = request_attestation(csr, &attestation_key.keyBlob)
-            .context("Failed to request attestation")
-            .with_log()
-            .or_service_specific_exception(-1)?;
+        let certificate = request_attestation(
+            csr.to_vec(),
+            attestation_key.keyBlob,
+            certificate_chain[0].encodedCertificate.clone(),
+        )
+        .context("Failed to request attestation")
+        .with_log()
+        .or_service_specific_exception(-1)?;
         certificate_chain.insert(0, Certificate { encodedCertificate: certificate });
 
         Ok(certificate_chain)
diff --git a/virtualizationservice/src/rkpvm.rs b/virtualizationservice/src/rkpvm.rs
index 5087120..79e09b0 100644
--- a/virtualizationservice/src/rkpvm.rs
+++ b/virtualizationservice/src/rkpvm.rs
@@ -24,15 +24,14 @@
 use service_vm_manager::ServiceVm;
 
 pub(crate) fn request_attestation(
-    csr: &[u8],
-    remotely_provisioned_keyblob: &[u8],
+    csr: Vec<u8>,
+    remotely_provisioned_key_blob: Vec<u8>,
+    remotely_provisioned_cert: Vec<u8>,
 ) -> Result<Vec<u8>> {
     let mut vm = ServiceVm::start()?;
 
-    let params = ClientVmAttestationParams {
-        csr: csr.to_vec(),
-        remotely_provisioned_key_blob: remotely_provisioned_keyblob.to_vec(),
-    };
+    let params =
+        ClientVmAttestationParams { csr, remotely_provisioned_key_blob, remotely_provisioned_cert };
     let request = Request::RequestClientVmAttestation(params);
     match vm.process_request(request).context("Failed to process request")? {
         Response::RequestClientVmAttestation(cert) => Ok(cert),