[avb] Collect hash descriptors when verification succeeds
This cl uses `avb_descriptor_foreach()` to collect all the
hash descriptors once the verification succeeds and checks
that there's no unknown, duplicated or non-hash descriptor
in the vbmeta.
We will extract the partition digest from the collected
hash descriptors later.
Bug: 256148034
Bug: 265897559
Test: m pvmfw_img && atest libpvmfw_avb.integration_test
Change-Id: Ifa0a91f1e4384007e58d99585d72cdee81bd8dbc
diff --git a/pvmfw/avb/src/verify.rs b/pvmfw/avb/src/verify.rs
index a062061..14b0e7e 100644
--- a/pvmfw/avb/src/verify.rs
+++ b/pvmfw/avb/src/verify.rs
@@ -14,19 +14,12 @@
//! This module handles the pvmfw payload verification.
-use crate::error::{AvbIOError, AvbSlotVerifyError};
+use crate::descriptor::HashDescriptors;
+use crate::error::AvbSlotVerifyError;
use crate::ops::{Ops, Payload};
use crate::partition::PartitionName;
-use crate::utils::{is_not_null, to_usize, usize_checked_add, write};
-use avb_bindgen::{
- avb_descriptor_foreach, avb_hash_descriptor_validate_and_byteswap, AvbDescriptor,
- AvbHashDescriptor, AvbPartitionData, AvbVBMetaData,
-};
-use core::{
- ffi::{c_char, c_void},
- mem::{size_of, MaybeUninit},
- slice,
-};
+use avb_bindgen::{AvbPartitionData, AvbVBMetaData};
+use core::ffi::c_char;
/// This enum corresponds to the `DebugLevel` in `VirtualMachineConfig`.
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -37,104 +30,6 @@
Full,
}
-extern "C" fn search_initrd_hash_descriptor(
- descriptor: *const AvbDescriptor,
- user_data: *mut c_void,
-) -> bool {
- try_search_initrd_hash_descriptor(descriptor, user_data).is_ok()
-}
-
-fn try_search_initrd_hash_descriptor(
- descriptor: *const AvbDescriptor,
- user_data: *mut c_void,
-) -> Result<(), AvbIOError> {
- let hash_desc = AvbHashDescriptorRef::try_from(descriptor)?;
- if matches!(
- hash_desc.partition_name()?.try_into(),
- Ok(PartitionName::InitrdDebug) | Ok(PartitionName::InitrdNormal),
- ) {
- write(user_data as *mut bool, true)?;
- }
- Ok(())
-}
-
-/// `hash_desc` only contains the metadata like fields length and flags of the descriptor.
-/// The data itself is contained in `ptr`.
-struct AvbHashDescriptorRef {
- hash_desc: AvbHashDescriptor,
- ptr: *const AvbDescriptor,
-}
-
-impl TryFrom<*const AvbDescriptor> for AvbHashDescriptorRef {
- type Error = AvbIOError;
-
- fn try_from(descriptor: *const AvbDescriptor) -> Result<Self, Self::Error> {
- is_not_null(descriptor)?;
- // SAFETY: It is safe as the raw pointer `descriptor` is a nonnull pointer and
- // we have validated that it is of hash descriptor type.
- let hash_desc = unsafe {
- let mut desc = MaybeUninit::uninit();
- if !avb_hash_descriptor_validate_and_byteswap(
- descriptor as *const AvbHashDescriptor,
- desc.as_mut_ptr(),
- ) {
- return Err(AvbIOError::Io);
- }
- desc.assume_init()
- };
- Ok(Self { hash_desc, ptr: descriptor })
- }
-}
-
-impl AvbHashDescriptorRef {
- fn check_is_in_range(&self, index: usize) -> Result<(), AvbIOError> {
- let parent_desc = self.hash_desc.parent_descriptor;
- let total_len = usize_checked_add(
- size_of::<AvbDescriptor>(),
- to_usize(parent_desc.num_bytes_following)?,
- )?;
- if index <= total_len {
- Ok(())
- } else {
- Err(AvbIOError::Io)
- }
- }
-
- /// Returns the non null-terminated partition name.
- fn partition_name(&self) -> Result<&[u8], AvbIOError> {
- let partition_name_offset = size_of::<AvbHashDescriptor>();
- let partition_name_len = to_usize(self.hash_desc.partition_name_len)?;
- self.check_is_in_range(usize_checked_add(partition_name_offset, partition_name_len)?)?;
- let desc = self.ptr as *const u8;
- // SAFETY: The descriptor has been validated as nonnull and the partition name is
- // contained within the image.
- unsafe { Ok(slice::from_raw_parts(desc.add(partition_name_offset), partition_name_len)) }
- }
-}
-
-fn verify_vbmeta_has_no_initrd_descriptor(
- vbmeta_image: &AvbVBMetaData,
-) -> Result<(), AvbSlotVerifyError> {
- is_not_null(vbmeta_image.vbmeta_data).map_err(|_| AvbSlotVerifyError::Io)?;
- let mut has_unexpected_descriptor = false;
- // SAFETY: It is safe as the raw pointer `vbmeta_image.vbmeta_data` is a nonnull pointer.
- if !unsafe {
- avb_descriptor_foreach(
- vbmeta_image.vbmeta_data,
- vbmeta_image.vbmeta_size,
- Some(search_initrd_hash_descriptor),
- &mut has_unexpected_descriptor as *mut _ as *mut c_void,
- )
- } {
- return Err(AvbSlotVerifyError::InvalidMetadata);
- }
- if has_unexpected_descriptor {
- Err(AvbSlotVerifyError::InvalidMetadata)
- } else {
- Ok(())
- }
-}
-
fn verify_only_one_vbmeta_exists(
vbmeta_images: &[AvbVBMetaData],
) -> Result<(), AvbSlotVerifyError> {
@@ -154,6 +49,16 @@
}
}
+fn verify_vbmeta_has_only_one_hash_descriptor(
+ hash_descriptors: &HashDescriptors,
+) -> Result<(), AvbSlotVerifyError> {
+ if hash_descriptors.len() == 1 {
+ Ok(())
+ } else {
+ Err(AvbSlotVerifyError::InvalidMetadata)
+ }
+}
+
fn verify_loaded_partition_has_expected_length(
loaded_partitions: &[AvbPartitionData],
partition_name: PartitionName,
@@ -191,13 +96,17 @@
verify_only_one_vbmeta_exists(vbmeta_images)?;
let vbmeta_image = vbmeta_images[0];
verify_vbmeta_is_from_kernel_partition(&vbmeta_image)?;
+ // SAFETY: It is safe because the `vbmeta_image` is collected from `AvbSlotVerifyData`,
+ // which is returned by `avb_slot_verify()` when the verification succeeds. It is
+ // guaranteed by libavb to be non-null and to point to a valid VBMeta structure.
+ let hash_descriptors = unsafe { HashDescriptors::from_vbmeta(vbmeta_image)? };
+ // TODO(b/265897559): Pass the digest in kernel descriptor to DICE.
+ let _kernel_descriptor = hash_descriptors.find(PartitionName::Kernel)?;
if initrd.is_none() {
- verify_vbmeta_has_no_initrd_descriptor(&vbmeta_image)?;
+ verify_vbmeta_has_only_one_hash_descriptor(&hash_descriptors)?;
return Ok(DebugLevel::None);
}
- // TODO(b/256148034): Check the vbmeta doesn't have hash descriptors other than
- // boot, initrd_normal, initrd_debug.
let initrd = initrd.unwrap();
let (debug_level, initrd_verify_result, initrd_partition_name) =