[attestation] Validate vendor module loaded by client VM in RKP VM

This cl added the following tasks to the RKP VM:

- Parses a client VM DICE chain containing an additional vendor
module entry.
- Validates the code hash in the vendor module DICE entry against
the code hash read from the device tree.

The cl also adds a CTS test that triggers the VM attestation from
a VM with vendor module.

Bug: 330678211
Test: atest MicrodroidTests
Change-Id: Id56c6edd8baa32bae6a8ad7b5bca7b18ce167022
diff --git a/service_vm/requests/src/dice.rs b/service_vm/requests/src/dice.rs
index 657e482..df29676 100644
--- a/service_vm/requests/src/dice.rs
+++ b/service_vm/requests/src/dice.rs
@@ -31,7 +31,7 @@
     Label,
 };
 use diced_open_dice::{DiceMode, HASH_SIZE};
-use log::error;
+use log::{debug, error, info};
 use service_vm_comm::RequestProcessingError;
 
 type Result<T> = result::Result<T, RequestProcessingError>;
@@ -50,7 +50,8 @@
 const SUB_COMPONENT_CODE_HASH: i64 = 3;
 const SUB_COMPONENT_AUTHORITY_HASH: i64 = 4;
 
-const MICRODROID_KERNEL_COMPONENT_NAME: &str = "vm_entry";
+const KERNEL_COMPONENT_NAME: &str = "vm_entry";
+const VENDOR_PARTITION_COMPONENT_NAME: &str = "Microdroid vendor";
 const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
 
 /// Represents a partially decoded `DiceCertChain` from the client VM.
@@ -63,6 +64,10 @@
 #[derive(Debug, Clone)]
 pub(crate) struct ClientVmDiceChain {
     payloads: Vec<DiceChainEntryPayload>,
+    /// The index of the vendor partition entry in the DICE chain if it exists.
+    vendor_partition_index: Option<usize>,
+    /// The index of the kernel entry in the DICE chain.
+    kernel_index: usize,
 }
 
 impl ClientVmDiceChain {
@@ -75,7 +80,11 @@
     /// Returns a partially decoded client VM's DICE chain if the verification succeeds.
     pub(crate) fn validate_signatures_and_parse_dice_chain(
         mut client_vm_dice_chain: Vec<Value>,
+        service_vm_dice_chain_len: usize,
     ) -> Result<Self> {
+        let has_vendor_partition =
+            vendor_partition_exists(client_vm_dice_chain.len(), service_vm_dice_chain_len)?;
+
         let root_public_key =
             CoseKey::from_cbor_value(client_vm_dice_chain.remove(0))?.try_into()?;
 
@@ -93,45 +102,62 @@
             payloads.push(payload);
             previous_public_key = &payloads.last().unwrap().subject_public_key;
         }
-        // After successfully calling `validate_client_vm_dice_chain_prefix_match`, we can be
-        // certain that the client VM's DICE chain must contain at least three entries that
-        // describe:
-        // - pvmfw
-        // - Microdroid kernel
-        // - Apk/Apexes
-        assert!(
-            payloads.len() >= 3,
-            "The client VM DICE chain must contain at least three DiceChainEntryPayloads"
-        );
-        let chain = Self { payloads };
-        chain.validate_microdroid_components_names()?;
-        Ok(chain)
+
+        Self::build(payloads, has_vendor_partition)
     }
 
-    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;
+    fn build(
+        dice_entry_payloads: Vec<DiceChainEntryPayload>,
+        has_vendor_partition: bool,
+    ) -> Result<Self> {
+        let microdroid_payload_name =
+            &dice_entry_payloads[dice_entry_payloads.len() - 1].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 {}",
+                 payload. Got '{}'",
                 microdroid_payload_name
             );
             return Err(RequestProcessingError::InvalidDiceChain);
         }
-        Ok(())
+
+        let (vendor_partition_index, kernel_index) = if has_vendor_partition {
+            let index = dice_entry_payloads.len() - 2;
+            let vendor_partition_name =
+                &dice_entry_payloads[index].config_descriptor.component_name;
+            if VENDOR_PARTITION_COMPONENT_NAME != vendor_partition_name {
+                error!(
+                    "The vendor partition entry in the client VM DICE chain must describe the \
+                        vendor partition. Got '{}'",
+                    vendor_partition_name,
+                );
+                return Err(RequestProcessingError::InvalidDiceChain);
+            }
+            (Some(index), index - 1)
+        } else {
+            (None, dice_entry_payloads.len() - 2)
+        };
+
+        let kernel_name = &dice_entry_payloads[kernel_index].config_descriptor.component_name;
+        if KERNEL_COMPONENT_NAME != kernel_name {
+            error!(
+                "The microdroid kernel entry in the client VM DICE chain must describe the \
+                 Microdroid kernel. Got '{}'",
+                kernel_name,
+            );
+            return Err(RequestProcessingError::InvalidDiceChain);
+        }
+
+        debug!("All entries in the client VM DICE chain have correct component names");
+        Ok(Self { payloads: dice_entry_payloads, vendor_partition_index, kernel_index })
     }
 
     pub(crate) fn microdroid_kernel(&self) -> &DiceChainEntryPayload {
-        &self.payloads[self.payloads.len() - 2]
+        &self.payloads[self.kernel_index]
+    }
+
+    pub(crate) fn vendor_partition(&self) -> Option<&DiceChainEntryPayload> {
+        self.vendor_partition_index.map(|i| &self.payloads[i])
     }
 
     pub(crate) fn microdroid_payload(&self) -> &DiceChainEntryPayload {
@@ -148,39 +174,33 @@
     }
 }
 
-/// Validates that the `client_vm_dice_chain` matches the `service_vm_dice_chain` up to the pvmfw
-/// entry.
-///
-/// Returns `Ok(())` if the verification succeeds.
-pub(crate) fn validate_client_vm_dice_chain_prefix_match(
-    client_vm_dice_chain: &[Value],
-    service_vm_dice_chain: &[Value],
-) -> Result<()> {
-    if service_vm_dice_chain.len() < 3 {
-        // The service VM's DICE chain must contain the root key and at least two other entries
-        // that describe:
-        //   - pvmfw
-        //   - Service VM kernel
-        error!("The service VM DICE chain must contain at least three entries");
-        return Err(RequestProcessingError::InternalError);
+fn vendor_partition_exists(
+    client_vm_dice_chain_len: usize,
+    service_vm_dice_chain_len: usize,
+) -> Result<bool> {
+    let entries_up_to_pvmfw_len = service_vm_dice_chain_len - 1;
+    // Client VM DICE chain = entries_up_to_pvmfw
+    //    + Vendor module entry (exists only when the vendor partition is present)
+    //    + Microdroid kernel entry (added in pvmfw)
+    //    + Apk/Apexes entry (added in microdroid)
+    match client_vm_dice_chain_len.checked_sub(entries_up_to_pvmfw_len) {
+        Some(2) => {
+            debug!("The vendor partition entry is not present in the client VM's DICE chain");
+            Ok(false)
+        }
+        Some(3) => {
+            info!("The vendor partition entry is present in the client VM's DICE chain");
+            Ok(true)
+        }
+        _ => {
+            error!(
+                "The client VM's DICE chain must contain two or three extra entries. \
+            Service VM DICE chain: {} entries, client VM DICE chain: {} entries",
+                service_vm_dice_chain_len, client_vm_dice_chain_len
+            );
+            Err(RequestProcessingError::InvalidDiceChain)
+        }
     }
-    // Ignores the last entry that describes service VM
-    let entries_up_to_pvmfw = &service_vm_dice_chain[0..(service_vm_dice_chain.len() - 1)];
-    if entries_up_to_pvmfw.len() + 2 != client_vm_dice_chain.len() {
-        // Client VM DICE chain = entries_up_to_pvmfw
-        //    + Microdroid kernel entry (added in pvmfw)
-        //    + Apk/Apexes entry (added in microdroid)
-        error!("The client VM's DICE chain must contain exactly two extra entries");
-        return Err(RequestProcessingError::InvalidDiceChain);
-    }
-    if entries_up_to_pvmfw != &client_vm_dice_chain[0..entries_up_to_pvmfw.len()] {
-        error!(
-            "The client VM's DICE chain does not match service VM's DICE chain up to \
-             the pvmfw entry"
-        );
-        return Err(RequestProcessingError::InvalidDiceChain);
-    }
-    Ok(())
 }
 
 #[derive(Debug, Clone)]