Merge "[x509] Include Apk/Apex info in attestation certificate extension" into main
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")
     }