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