pvmfw: use libavb_rs descriptor APIs
Now that libavb_rs provides the necessary descriptor APIs, stop parsing
them manually and use the library.
Bug: b/320542357
Test: atest libpvmfw_avb.integration_test
Change-Id: If272650150538e935844cc1cd3d091f85bdbb4cb
diff --git a/pvmfw/avb/src/verify.rs b/pvmfw/avb/src/verify.rs
index a85dbbb..2ebe9a1 100644
--- a/pvmfw/avb/src/verify.rs
+++ b/pvmfw/avb/src/verify.rs
@@ -14,17 +14,22 @@
//! This module handles the pvmfw payload verification.
-use crate::descriptor::{Descriptors, Digest};
use crate::ops::{Ops, Payload};
use crate::partition::PartitionName;
use crate::PvmfwVerifyError;
use alloc::vec;
use alloc::vec::Vec;
-use avb::{PartitionData, SlotVerifyError, SlotVerifyNoDataResult, VbmetaData};
+use avb::{
+ Descriptor, DescriptorError, DescriptorResult, HashDescriptor, PartitionData,
+ PropertyDescriptor, SlotVerifyError, SlotVerifyNoDataResult, VbmetaData,
+};
// We use this for the rollback_index field if SlotVerifyData has empty rollback_indexes
const DEFAULT_ROLLBACK_INDEX: u64 = 0;
+/// SHA256 digest type for kernel and initrd.
+pub type Digest = [u8; 32];
+
/// Verified data returned when the payload verification succeeds.
#[derive(Debug, PartialEq, Eq)]
pub struct VerifiedBootData<'a> {
@@ -68,15 +73,21 @@
}
impl Capability {
- const KEY: &'static [u8] = b"com.android.virt.cap";
+ const KEY: &'static str = "com.android.virt.cap";
const REMOTE_ATTEST: &'static [u8] = b"remote_attest";
const SECRETKEEPER_PROTECTION: &'static [u8] = b"secretkeeper_protection";
const SEPARATOR: u8 = b'|';
- fn get_capabilities(property_value: &[u8]) -> Result<Vec<Self>, PvmfwVerifyError> {
+ /// Returns the capabilities indicated in `descriptor`, or error if the descriptor has
+ /// unexpected contents.
+ fn get_capabilities(descriptor: &PropertyDescriptor) -> Result<Vec<Self>, PvmfwVerifyError> {
+ if descriptor.key != Self::KEY {
+ return Err(PvmfwVerifyError::UnknownVbmetaProperty);
+ }
+
let mut res = Vec::new();
- for v in property_value.split(|b| *b == Self::SEPARATOR) {
+ for v in descriptor.value.split(|b| *b == Self::SEPARATOR) {
let cap = match v {
Self::REMOTE_ATTEST => Self::RemoteAttest,
Self::SECRETKEEPER_PROTECTION => Self::SecretkeeperProtection,
@@ -106,16 +117,6 @@
}
}
-fn verify_vbmeta_has_only_one_hash_descriptor(
- descriptors: &Descriptors,
-) -> SlotVerifyNoDataResult<()> {
- if descriptors.num_hash_descriptor() == 1 {
- Ok(())
- } else {
- Err(SlotVerifyError::InvalidMetadata)
- }
-}
-
fn verify_loaded_partition_has_expected_length(
loaded_partitions: &[PartitionData],
partition_name: PartitionName,
@@ -142,15 +143,91 @@
/// Verifies that the vbmeta contains at most one property descriptor and it indicates the
/// vm type is service VM.
fn verify_property_and_get_capabilities(
- descriptors: &Descriptors,
+ descriptors: &[Descriptor],
) -> Result<Vec<Capability>, PvmfwVerifyError> {
- if !descriptors.has_property_descriptor() {
- return Ok(vec![]);
+ let mut iter = descriptors.iter().filter_map(|d| match d {
+ Descriptor::Property(p) => Some(p),
+ _ => None,
+ });
+
+ let descriptor = match iter.next() {
+ // No property descriptors -> no capabilities.
+ None => return Ok(vec![]),
+ Some(d) => d,
+ };
+
+ // Multiple property descriptors -> error.
+ if iter.next().is_some() {
+ return Err(DescriptorError::InvalidContents.into());
}
- descriptors
- .find_property_value(Capability::KEY)
- .ok_or(PvmfwVerifyError::UnknownVbmetaProperty)
- .and_then(Capability::get_capabilities)
+
+ Capability::get_capabilities(descriptor)
+}
+
+/// Hash descriptors extracted from a vbmeta image.
+///
+/// We always have a kernel hash descriptor and may have initrd normal or debug descriptors.
+struct HashDescriptors<'a> {
+ kernel: &'a HashDescriptor<'a>,
+ initrd_normal: Option<&'a HashDescriptor<'a>>,
+ initrd_debug: Option<&'a HashDescriptor<'a>>,
+}
+
+impl<'a> HashDescriptors<'a> {
+ /// Extracts the hash descriptors from all vbmeta descriptors. Any unexpected hash descriptor
+ /// is an error.
+ fn get(descriptors: &'a [Descriptor<'a>]) -> DescriptorResult<Self> {
+ let mut kernel = None;
+ let mut initrd_normal = None;
+ let mut initrd_debug = None;
+
+ for descriptor in descriptors.iter().filter_map(|d| match d {
+ Descriptor::Hash(h) => Some(h),
+ _ => None,
+ }) {
+ let target = match descriptor
+ .partition_name
+ .as_bytes()
+ .try_into()
+ .map_err(|_| DescriptorError::InvalidContents)?
+ {
+ PartitionName::Kernel => &mut kernel,
+ PartitionName::InitrdNormal => &mut initrd_normal,
+ PartitionName::InitrdDebug => &mut initrd_debug,
+ };
+
+ if target.is_some() {
+ // Duplicates of the same partition name is an error.
+ return Err(DescriptorError::InvalidContents);
+ }
+ target.replace(descriptor);
+ }
+
+ // Kernel is required, the others are optional.
+ Ok(Self {
+ kernel: kernel.ok_or(DescriptorError::InvalidContents)?,
+ initrd_normal,
+ initrd_debug,
+ })
+ }
+
+ /// Returns an error if either initrd descriptor exists.
+ fn verify_no_initrd(&self) -> Result<(), PvmfwVerifyError> {
+ match self.initrd_normal.or(self.initrd_debug) {
+ Some(_) => Err(SlotVerifyError::InvalidMetadata.into()),
+ None => Ok(()),
+ }
+ }
+}
+
+/// Returns a copy of the SHA256 digest in `descriptor`, or error if the sizes don't match.
+fn copy_digest(descriptor: &HashDescriptor) -> SlotVerifyNoDataResult<Digest> {
+ let mut digest = Digest::default();
+ if descriptor.digest.len() != digest.len() {
+ return Err(SlotVerifyError::InvalidMetadata);
+ }
+ digest.clone_from_slice(descriptor.digest);
+ Ok(digest)
}
/// Verifies the given initrd partition, and checks that the resulting contents looks like expected.
@@ -186,15 +263,15 @@
verify_only_one_vbmeta_exists(vbmeta_images)?;
let vbmeta_image = &vbmeta_images[0];
verify_vbmeta_is_from_kernel_partition(vbmeta_image)?;
- let descriptors = Descriptors::from_vbmeta(vbmeta_image)?;
+ let descriptors = vbmeta_image.descriptors()?;
+ let hash_descriptors = HashDescriptors::get(&descriptors)?;
let capabilities = verify_property_and_get_capabilities(&descriptors)?;
- let kernel_descriptor = descriptors.find_hash_descriptor(PartitionName::Kernel)?;
if initrd.is_none() {
- verify_vbmeta_has_only_one_hash_descriptor(&descriptors)?;
+ hash_descriptors.verify_no_initrd()?;
return Ok(VerifiedBootData {
debug_level: DebugLevel::None,
- kernel_digest: *kernel_descriptor.digest,
+ kernel_digest: copy_digest(hash_descriptors.kernel)?,
initrd_digest: None,
public_key: trusted_public_key,
capabilities,
@@ -204,19 +281,19 @@
let initrd = initrd.unwrap();
let mut initrd_ops = Ops::new(&payload);
- let (debug_level, initrd_partition_name) =
+ let (debug_level, initrd_descriptor) =
if verify_initrd(&mut initrd_ops, PartitionName::InitrdNormal, initrd).is_ok() {
- (DebugLevel::None, PartitionName::InitrdNormal)
+ (DebugLevel::None, hash_descriptors.initrd_normal)
} else if verify_initrd(&mut initrd_ops, PartitionName::InitrdDebug, initrd).is_ok() {
- (DebugLevel::Full, PartitionName::InitrdDebug)
+ (DebugLevel::Full, hash_descriptors.initrd_debug)
} else {
return Err(SlotVerifyError::Verification(None).into());
};
- let initrd_descriptor = descriptors.find_hash_descriptor(initrd_partition_name)?;
+ let initrd_descriptor = initrd_descriptor.ok_or(DescriptorError::InvalidContents)?;
Ok(VerifiedBootData {
debug_level,
- kernel_digest: *kernel_descriptor.digest,
- initrd_digest: Some(*initrd_descriptor.digest),
+ kernel_digest: copy_digest(hash_descriptors.kernel)?,
+ initrd_digest: Some(copy_digest(initrd_descriptor)?),
public_key: trusted_public_key,
capabilities,
rollback_index,