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