[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),