[rkp] Implement main steps to generate certificate request

This cl implements the main steps required to generate a
certificate request within the RKP HAL. The DICE chain will be
included once retrieved in the service VM.

Bug: 299256925
Test: atest rialto_test
Change-Id: Ia2055e13d3f5e31617bde01851059764d814f25e
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 96b9b10..8a56ebe 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -111,6 +111,7 @@
         "android.system.virtualizationservice-rust",
         "libandroid_logger",
         "libanyhow",
+        "libciborium",
         "liblibc",
         "liblog_rust",
         "libservice_vm_comm",
diff --git a/rialto/src/requests/pub_key.rs b/rialto/src/requests/pub_key.rs
index b45c117..3a69a2e 100644
--- a/rialto/src/requests/pub_key.rs
+++ b/rialto/src/requests/pub_key.rs
@@ -25,10 +25,11 @@
 type Result<T> = result::Result<T, RequestProcessingError>;
 
 /// Verifies the MAC of the given public key.
-/// TODO(b/299256925): Return the validated public key.
-pub fn validate_public_key(maced_public_key: &[u8], hmac_key: &[u8]) -> Result<()> {
+pub fn validate_public_key(maced_public_key: &[u8], hmac_key: &[u8]) -> Result<CoseKey> {
     let cose_mac = CoseMac0::from_slice(maced_public_key)?;
-    cose_mac.verify_tag(&[], |tag, data| verify_tag(tag, data, hmac_key))
+    cose_mac.verify_tag(&[], |tag, data| verify_tag(tag, data, hmac_key))?;
+    let payload = cose_mac.payload.ok_or(RequestProcessingError::KeyToSignHasEmptyPayload)?;
+    Ok(CoseKey::from_slice(&payload)?)
 }
 
 fn verify_tag(tag: &[u8], data: &[u8], hmac_key: &[u8]) -> Result<()> {
diff --git a/rialto/src/requests/rkp.rs b/rialto/src/requests/rkp.rs
index 9b3e569..bcddf67 100644
--- a/rialto/src/requests/rkp.rs
+++ b/rialto/src/requests/rkp.rs
@@ -17,8 +17,12 @@
 
 use super::ec_key::EcKey;
 use super::pub_key::{build_maced_public_key, validate_public_key};
+use alloc::string::String;
+use alloc::vec;
 use alloc::vec::Vec;
+use ciborium::{cbor, value::Value};
 use core::result;
+use coset::{iana, AsCborValue, CoseSign1, CoseSign1Builder, HeaderBuilder};
 use diced_open_dice::DiceArtifacts;
 use service_vm_comm::{EcdsaP256KeyPair, GenerateCertificateRequestParams, RequestProcessingError};
 
@@ -39,15 +43,73 @@
     Ok(key_pair)
 }
 
+const CSR_PAYLOAD_SCHEMA_V3: u8 = 3;
+const AUTH_REQ_SCHEMA_V1: u8 = 1;
+// TODO(b/300624493): Add a new certificate type for AVF CSR.
+const CERTIFICATE_TYPE: &str = "keymint";
+
+/// Builds the CSR described in:
+///
+/// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
+/// generateCertificateRequestV2.cddl
 pub(super) fn generate_certificate_request(
     params: GenerateCertificateRequestParams,
     _dice_artifacts: &dyn DiceArtifacts,
 ) -> Result<Vec<u8>> {
     // TODO(b/300590857): Derive the HMAC key from the DICE sealing CDI.
     let hmac_key = [];
+    let mut public_keys: Vec<Value> = Vec::new();
     for key_to_sign in params.keys_to_sign {
-        validate_public_key(&key_to_sign, &hmac_key)?;
+        let public_key = validate_public_key(&key_to_sign, &hmac_key)?;
+        public_keys.push(public_key.to_cbor_value()?);
     }
-    // TODO(b/299256925): Generate the certificate request
+    // Builds `CsrPayload`.
+    let csr_payload = cbor!([
+        Value::Integer(CSR_PAYLOAD_SCHEMA_V3.into()),
+        Value::Text(String::from(CERTIFICATE_TYPE)),
+        // TODO(b/299256925): Add device info in CBOR format here.
+        Value::Array(public_keys),
+    ])?;
+    let csr_payload = cbor_to_vec(&csr_payload)?;
+
+    // Builds `SignedData`.
+    let signed_data_payload =
+        cbor!([Value::Bytes(params.challenge.to_vec()), Value::Bytes(csr_payload)])?;
+    let signed_data = build_signed_data(&signed_data_payload)?.to_cbor_value()?;
+
+    // Builds `AuthenticatedRequest<CsrPayload>`.
+    // TODO(b/287233786): Add UdsCerts and DiceCertChain here.
+    let uds_certs = Value::Map(Vec::new());
+    let dice_cert_chain = Value::Array(Vec::new());
+    let auth_req = cbor!([
+        Value::Integer(AUTH_REQ_SCHEMA_V1.into()),
+        uds_certs,
+        dice_cert_chain,
+        signed_data,
+    ])?;
+    cbor_to_vec(&auth_req)
+}
+
+/// Builds the `SignedData` for the given payload.
+fn build_signed_data(payload: &Value) -> Result<CoseSign1> {
+    // TODO(b/299256925): Adjust the signing algorithm if needed.
+    let signing_algorithm = iana::Algorithm::ES256;
+    let protected = HeaderBuilder::new().algorithm(signing_algorithm).build();
+    let signed_data = CoseSign1Builder::new()
+        .protected(protected)
+        .payload(cbor_to_vec(payload)?)
+        .try_create_signature(&[], sign_data)?
+        .build();
+    Ok(signed_data)
+}
+
+fn sign_data(_data: &[u8]) -> Result<Vec<u8>> {
+    // TODO(b/287233786): Sign the data with the CDI leaf private key.
     Ok(Vec::new())
 }
+
+fn cbor_to_vec(v: &Value) -> Result<Vec<u8>> {
+    let mut data = Vec::new();
+    ciborium::into_writer(v, &mut data).map_err(coset::CoseError::from)?;
+    Ok(data)
+}
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index c9d68ed..6a6dcf4 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -22,12 +22,14 @@
     binder::{ParcelFileDescriptor, ProcessState},
 };
 use anyhow::{bail, Context, Result};
+use ciborium::value::Value;
 use log::info;
 use service_vm_comm::{
     EcdsaP256KeyPair, GenerateCertificateRequestParams, Request, Response, VmType,
 };
 use service_vm_manager::ServiceVm;
 use std::fs::File;
+use std::io;
 use std::panic;
 use std::path::PathBuf;
 use vmclient::VmInstance;
@@ -101,11 +103,24 @@
     info!("Received response: {response:?}.");
 
     match response {
-        Response::GenerateCertificateRequest(_) => Ok(()),
+        Response::GenerateCertificateRequest(csr) => check_csr(csr),
         _ => bail!("Incorrect response type: {response:?}"),
     }
 }
 
+/// 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);
+    let csr: Value = ciborium::from_reader(&mut reader)?;
+    match csr {
+        Value::Array(arr) => {
+            assert_eq!(4, arr.len());
+        }
+        _ => bail!("Incorrect CSR format: {csr:?}"),
+    }
+    Ok(())
+}
+
 fn start_service_vm(vm_type: VmType) -> Result<ServiceVm> {
     android_logger::init_once(
         android_logger::Config::default().with_tag("rialto").with_min_level(log::Level::Debug),