[x509] Include Apk/Apex info in attestation certificate extension

This cl parses the config descriptor in the Microdroid payload
dice entry of the Client VM DICE chain. It then reads the
subcomponent info from the config descriptor and adds this info
to the attestation certificate extension.

Test: atest rialto_test
Bug: 313815907
Change-Id: I79031b0d5ea69201fd24c76ad3b1695176d6332e
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 2755436..02a5a28 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -31,7 +31,9 @@
     ClientVmAttestationParams, Csr, CsrPayload, EcdsaP256KeyPair, GenerateCertificateRequestParams,
     Request, RequestProcessingError, Response, VmType,
 };
-use service_vm_fake_chain::client_vm::fake_client_vm_dice_artifacts;
+use service_vm_fake_chain::client_vm::{
+    fake_client_vm_dice_artifacts, fake_sub_components, SubComponent,
+};
 use service_vm_manager::ServiceVm;
 use std::fs;
 use std::fs::File;
@@ -41,7 +43,7 @@
 use vmclient::VmInstance;
 use x509_parser::{
     certificate::X509Certificate,
-    der_parser::{der::parse_der, oid, oid::Oid},
+    der_parser::{ber::BerObject, der::parse_der, oid, oid::Oid},
     prelude::FromDer,
     x509::{AlgorithmIdentifier, SubjectPublicKeyInfo, X509Version},
 };
@@ -175,6 +177,25 @@
     }
 }
 
+fn check_vm_components(vm_components: &[BerObject]) -> Result<()> {
+    let expected_components = fake_sub_components();
+    assert_eq!(expected_components.len(), vm_components.len());
+    for i in 0..expected_components.len() {
+        check_vm_component(&vm_components[i], &expected_components[i])?;
+    }
+    Ok(())
+}
+
+fn check_vm_component(vm_component: &BerObject, expected_component: &SubComponent) -> Result<()> {
+    let vm_component = vm_component.as_sequence()?;
+    assert_eq!(4, vm_component.len());
+    assert_eq!(expected_component.name, vm_component[0].as_str()?);
+    assert_eq!(expected_component.version, vm_component[1].as_u64()?);
+    assert_eq!(expected_component.code_hash, vm_component[2].as_slice()?);
+    assert_eq!(expected_component.authority_hash, vm_component[3].as_slice()?);
+    Ok(())
+}
+
 fn check_certificate_for_client_vm(
     certificate: &[u8],
     maced_public_key: &[u8],
@@ -218,13 +239,15 @@
     let (remaining, extension) = parse_der(extension.value)?;
     assert!(remaining.is_empty());
     let attestation_ext = extension.as_sequence()?;
-    assert_eq!(2, attestation_ext.len());
+    assert_eq!(3, attestation_ext.len());
     assert_eq!(csr_payload.challenge, attestation_ext[0].as_slice()?);
     let is_vm_secure = attestation_ext[1].as_bool()?;
     assert!(
         !is_vm_secure,
         "The VM shouldn't be secure as the last payload added in the test is in Debug mode"
     );
+    let vm_components = attestation_ext[2].as_sequence()?;
+    check_vm_components(vm_components)?;
 
     // Checks other fields on the certificate
     assert_eq!(X509Version::V3, cert.version());
diff --git a/service_vm/requests/src/cert.rs b/service_vm/requests/src/cert.rs
index 2baca2a..73828a7 100644
--- a/service_vm/requests/src/cert.rs
+++ b/service_vm/requests/src/cert.rs
@@ -14,9 +14,11 @@
 
 //! Generation of certificates and attestation extensions.
 
+use crate::dice::SubComponent;
 use alloc::vec;
+use alloc::vec::Vec;
 use der::{
-    asn1::{BitStringRef, ObjectIdentifier, UIntRef},
+    asn1::{BitStringRef, ObjectIdentifier, UIntRef, Utf8StringRef},
     oid::AssociatedOid,
     Decode, Sequence,
 };
@@ -42,15 +44,17 @@
 /// ```asn1
 /// AttestationDescription ::= SEQUENCE {
 ///     attestationChallenge       OCTET_STRING,
+///     isVmSecure                 BOOLEAN,
+///     vmComponents               SEQUENCE OF VmComponent,
 /// }
 /// ```
-/// 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],
     /// Indicates whether the VM is operating under a secure configuration.
     is_vm_secure: bool,
+    vm_components: Vec<VmComponent<'a>>,
 }
 
 impl<'a> AssociatedOid for AttestationExtension<'a> {
@@ -58,8 +62,43 @@
 }
 
 impl<'a> AttestationExtension<'a> {
-    pub(crate) fn new(attestation_challenge: &'a [u8], is_vm_secure: bool) -> Self {
-        Self { attestation_challenge, is_vm_secure }
+    pub(crate) fn new(
+        attestation_challenge: &'a [u8],
+        is_vm_secure: bool,
+        vm_components: Vec<VmComponent<'a>>,
+    ) -> Self {
+        Self { attestation_challenge, is_vm_secure, vm_components }
+    }
+}
+
+/// VM component information
+///
+/// ```asn1
+/// VmComponent ::= SEQUENCE {
+///    name               UTF8String,
+///    securityVersion    INTEGER,
+///    codeHash           OCTET STRING,
+///    authorityHash      OCTET STRING,
+/// }
+/// ```
+#[derive(Debug, Clone, Sequence)]
+pub(crate) struct VmComponent<'a> {
+    name: Utf8StringRef<'a>,
+    version: u64,
+    #[asn1(type = "OCTET STRING")]
+    code_hash: &'a [u8],
+    #[asn1(type = "OCTET STRING")]
+    authority_hash: &'a [u8],
+}
+
+impl<'a> VmComponent<'a> {
+    pub(crate) fn new(sub_component: &'a SubComponent) -> der::Result<Self> {
+        Ok(Self {
+            name: Utf8StringRef::new(&sub_component.name)?,
+            version: sub_component.version,
+            code_hash: &sub_component.code_hash,
+            authority_hash: &sub_component.authority_hash,
+        })
     }
 }
 
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index 4e87136..cfdac2d 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -76,9 +76,16 @@
     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 vm_components =
+        if let Some(components) = client_vm_dice_chain.microdroid_payload_components() {
+            components.iter().map(cert::VmComponent::new).collect::<der::Result<Vec<_>>>()?
+        } else {
+            Vec::new()
+        };
     let attestation_ext = cert::AttestationExtension::new(
         &csr_payload.challenge,
         client_vm_dice_chain.all_entries_are_secure(),
+        vm_components,
     )
     .to_vec()?;
     let tbs_cert = cert::build_tbs_certificate(
diff --git a/service_vm/requests/src/dice.rs b/service_vm/requests/src/dice.rs
index 0a5eac1..15cfbc9 100644
--- a/service_vm/requests/src/dice.rs
+++ b/service_vm/requests/src/dice.rs
@@ -14,6 +14,7 @@
 
 //! This module contains functions related to DICE.
 
+use alloc::string::String;
 use alloc::vec::Vec;
 use ciborium::value::{Integer, Value};
 use core::cell::OnceCell;
@@ -35,6 +36,17 @@
 const MODE: i64 = -4670551;
 const SUBJECT_PUBLIC_KEY: i64 = -4670552;
 
+const CONFIG_DESC_COMPONENT_NAME: i64 = -70002;
+const CONFIG_DESC_SUB_COMPONENTS: i64 = -71002;
+
+const SUB_COMPONENT_NAME: i64 = 1;
+const SUB_COMPONENT_VERSION: i64 = 2;
+const SUB_COMPONENT_CODE_HASH: i64 = 3;
+const SUB_COMPONENT_AUTHORITY_HASH: i64 = 4;
+
+const MICRODROID_KERNEL_COMPONENT_NAME: &str = "vm_entry";
+const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
+
 /// Represents a partially decoded `DiceCertChain` from the client VM.
 /// The whole chain is defined as following:
 ///
@@ -85,7 +97,43 @@
             payloads.len() >= 3,
             "The client VM DICE chain must contain at least three DiceChainEntryPayloads"
         );
-        Ok(Self { payloads })
+        let chain = Self { payloads };
+        chain.validate_microdroid_components_names()?;
+        Ok(chain)
+    }
+
+    fn validate_microdroid_components_names(&self) -> Result<()> {
+        let microdroid_kernel_name = &self.microdroid_kernel().config_descriptor.component_name;
+        if MICRODROID_KERNEL_COMPONENT_NAME != microdroid_kernel_name {
+            error!(
+                "The second to last entry in the client VM DICE chain must describe the \
+                    Microdroid kernel. Got {}",
+                microdroid_kernel_name
+            );
+            return Err(RequestProcessingError::InvalidDiceChain);
+        }
+        let microdroid_payload_name = &self.microdroid_payload().config_descriptor.component_name;
+        if MICRODROID_PAYLOAD_COMPONENT_NAME != microdroid_payload_name {
+            error!(
+                "The last entry in the client VM DICE chain must describe the Microdroid \
+                    payload. Got {}",
+                microdroid_payload_name
+            );
+            return Err(RequestProcessingError::InvalidDiceChain);
+        }
+        Ok(())
+    }
+
+    fn microdroid_kernel(&self) -> &DiceChainEntryPayload {
+        &self.payloads[self.payloads.len() - 2]
+    }
+
+    fn microdroid_payload(&self) -> &DiceChainEntryPayload {
+        &self.payloads[self.payloads.len() - 1]
+    }
+
+    pub(crate) fn microdroid_payload_components(&self) -> Option<&Vec<SubComponent>> {
+        self.microdroid_payload().config_descriptor.sub_components.as_ref()
     }
 
     /// Returns true if all payloads in the DICE chain are in normal mode.
@@ -163,9 +211,7 @@
     code_hash: [u8; HASH_SIZE],
     #[allow(dead_code)]
     authority_hash: [u8; HASH_SIZE],
-    /// TODO(b/313815907): Parse the config descriptor and read Apk/Apexes info in it.
-    #[allow(dead_code)]
-    config_descriptor: Vec<u8>,
+    config_descriptor: ConfigDescriptor,
 }
 
 impl DiceChainEntryPayload {
@@ -209,6 +255,7 @@
             }
             CONFIG_DESC => {
                 let config_descriptor = value_to_bytes(value, "config_descriptor")?;
+                let config_descriptor = ConfigDescriptor::from_slice(&config_descriptor)?;
                 builder.config_descriptor(config_descriptor)?;
             }
             _ => {}
@@ -217,10 +264,149 @@
     builder.build()
 }
 
+/// Represents a partially decoded `ConfigurationDescriptor`.
+///
+/// The whole `ConfigurationDescriptor` is defined in:
+///
+/// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
+/// generateCertificateRequestV2.cddl
+#[derive(Debug, Clone)]
+pub(crate) struct ConfigDescriptor {
+    component_name: String,
+    sub_components: Option<Vec<SubComponent>>,
+}
+
+impl ConfigDescriptor {
+    fn from_slice(data: &[u8]) -> Result<Self> {
+        let value = Value::from_slice(data)?;
+        let entries = value_to_map(value, "ConfigDescriptor")?;
+        let mut builder = ConfigDescriptorBuilder::default();
+        for (key, value) in entries.into_iter() {
+            let key: i64 = value_to_num(key, "ConfigDescriptor key")?;
+            match key {
+                CONFIG_DESC_COMPONENT_NAME => {
+                    let name = value_to_text(value, "ConfigDescriptor component_name")?;
+                    builder.component_name(name)?;
+                }
+                CONFIG_DESC_SUB_COMPONENTS => {
+                    let sub_components = value_to_array(value, "ConfigDescriptor sub_components")?;
+                    let sub_components = sub_components
+                        .into_iter()
+                        .map(SubComponent::try_from)
+                        .collect::<Result<Vec<_>>>()?;
+                    builder.sub_components(sub_components)?
+                }
+                _ => {}
+            }
+        }
+        builder.build()
+    }
+}
+
+#[derive(Debug, Clone, Default)]
+struct ConfigDescriptorBuilder {
+    component_name: OnceCell<String>,
+    sub_components: OnceCell<Vec<SubComponent>>,
+}
+
+impl ConfigDescriptorBuilder {
+    fn component_name(&mut self, component_name: String) -> Result<()> {
+        set_once(&self.component_name, component_name, "ConfigDescriptor component_name")
+    }
+
+    fn sub_components(&mut self, sub_components: Vec<SubComponent>) -> Result<()> {
+        set_once(&self.sub_components, sub_components, "ConfigDescriptor sub_components")
+    }
+
+    fn build(mut self) -> Result<ConfigDescriptor> {
+        let component_name =
+            take_value(&mut self.component_name, "ConfigDescriptor component_name")?;
+        let sub_components = self.sub_components.take();
+        Ok(ConfigDescriptor { component_name, sub_components })
+    }
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct SubComponent {
+    pub(crate) name: String,
+    pub(crate) version: u64,
+    pub(crate) code_hash: Vec<u8>,
+    pub(crate) authority_hash: Vec<u8>,
+}
+
+impl TryFrom<Value> for SubComponent {
+    type Error = RequestProcessingError;
+
+    fn try_from(value: Value) -> Result<Self> {
+        let entries = value_to_map(value, "SubComponent")?;
+        let mut builder = SubComponentBuilder::default();
+        for (key, value) in entries.into_iter() {
+            let key: i64 = value_to_num(key, "SubComponent key")?;
+            match key {
+                SUB_COMPONENT_NAME => {
+                    builder.name(value_to_text(value, "SubComponent component_name")?)?
+                }
+                SUB_COMPONENT_VERSION => {
+                    builder.version(value_to_num(value, "SubComponent version")?)?
+                }
+                SUB_COMPONENT_CODE_HASH => {
+                    builder.code_hash(value_to_bytes(value, "SubComponent code_hash")?)?
+                }
+                SUB_COMPONENT_AUTHORITY_HASH => {
+                    builder.authority_hash(value_to_bytes(value, "SubComponent authority_hash")?)?
+                }
+                k => {
+                    error!("Unknown key in SubComponent: {}", k);
+                    return Err(RequestProcessingError::InvalidDiceChain);
+                }
+            }
+        }
+        builder.build()
+    }
+}
+
+#[derive(Debug, Clone, Default)]
+struct SubComponentBuilder {
+    name: OnceCell<String>,
+    version: OnceCell<u64>,
+    code_hash: OnceCell<Vec<u8>>,
+    authority_hash: OnceCell<Vec<u8>>,
+}
+
+impl SubComponentBuilder {
+    fn name(&mut self, name: String) -> Result<()> {
+        set_once(&self.name, name, "SubComponent name")
+    }
+
+    fn version(&mut self, version: u64) -> Result<()> {
+        set_once(&self.version, version, "SubComponent version")
+    }
+
+    fn code_hash(&mut self, code_hash: Vec<u8>) -> Result<()> {
+        set_once(&self.code_hash, code_hash, "SubComponent code_hash")
+    }
+
+    fn authority_hash(&mut self, authority_hash: Vec<u8>) -> Result<()> {
+        set_once(&self.authority_hash, authority_hash, "SubComponent authority_hash")
+    }
+
+    fn build(mut self) -> Result<SubComponent> {
+        let name = take_value(&mut self.name, "SubComponent name")?;
+        let version = take_value(&mut self.version, "SubComponent version")?;
+        let code_hash = take_value(&mut self.code_hash, "SubComponent code_hash")?;
+        let authority_hash = take_value(&mut self.authority_hash, "SubComponent authority_hash")?;
+        Ok(SubComponent { name, version, code_hash, authority_hash })
+    }
+}
+
 fn value_to_array(v: Value, context: &'static str) -> coset::Result<Vec<Value>> {
     v.into_array().map_err(|e| to_unexpected_item_error(&e, "array", context))
 }
 
+fn value_to_text(v: Value, context: &'static str) -> coset::Result<String> {
+    v.into_text().map_err(|e| to_unexpected_item_error(&e, "tstr", context))
+}
+
 fn value_to_map(v: Value, context: &'static str) -> coset::Result<Vec<(Value, Value)>> {
     v.into_map().map_err(|e| to_unexpected_item_error(&e, "map", context))
 }
@@ -273,7 +459,7 @@
     mode: OnceCell<DiceMode>,
     code_hash: OnceCell<[u8; HASH_SIZE]>,
     authority_hash: OnceCell<[u8; HASH_SIZE]>,
-    config_descriptor: OnceCell<Vec<u8>>,
+    config_descriptor: OnceCell<ConfigDescriptor>,
 }
 
 fn set_once<T>(field: &OnceCell<T>, value: T, field_name: &str) -> Result<()> {
@@ -307,7 +493,7 @@
         set_once(&self.authority_hash, authority_hash, "authority_hash")
     }
 
-    fn config_descriptor(&mut self, config_descriptor: Vec<u8>) -> Result<()> {
+    fn config_descriptor(&mut self, config_descriptor: ConfigDescriptor) -> Result<()> {
         set_once(&self.config_descriptor, config_descriptor, "config_descriptor")
     }