Extract library for VBMeta image handling
The APEX handling library included VBMeta image verification and parsing
but split this out to its own library so that it can be used by other
components too. The new library hides the libavb FFI to provide a safe
interface.
The library is not complete, it only knows about hashtree descriptors
and its root digest, but other aspects can be exposed as there is need
for them.
Test: atest libvbmeta_rust.test
Test: atest libapexutil_rust.test
Bug: 234564414
Change-Id: Ie176b816f63d2ff7f75deab6c07e1f9bb2e54594
diff --git a/libs/apexutil/Android.bp b/libs/apexutil/Android.bp
index a1a1ca6..5b55e1c 100644
--- a/libs/apexutil/Android.bp
+++ b/libs/apexutil/Android.bp
@@ -7,12 +7,11 @@
crate_name: "apexutil",
host_supported: true,
srcs: ["src/lib.rs"],
- prefer_rlib: true,
edition: "2018",
rustlibs: [
- "libavb_bindgen",
"liblog_rust",
"libthiserror",
+ "libvbmeta_rust",
"libzip",
],
}
@@ -25,6 +24,7 @@
rust_test {
name: "libapexutil_rust.test",
defaults: ["libapexutil_rust.defaults"],
+ prefer_rlib: true,
test_suites: ["general-tests"],
data: ["tests/data/*"],
target: {
diff --git a/libs/apexutil/src/lib.rs b/libs/apexutil/src/lib.rs
index d53e907..63b09de 100644
--- a/libs/apexutil/src/lib.rs
+++ b/libs/apexutil/src/lib.rs
@@ -14,15 +14,10 @@
//! Routines for handling APEX payload
-use avb_bindgen::*;
-use std::ffi::{c_void, CStr};
use std::fs::File;
-use std::io::{self, Read, Seek, SeekFrom};
-use std::mem::{size_of, zeroed};
-use std::ops::Deref;
-use std::ptr::null_mut;
-use std::slice::{from_raw_parts, from_raw_parts_mut};
+use std::io::{self, Read};
use thiserror::Error;
+use vbmeta::VbMetaImage;
use zip::result::ZipError;
use zip::ZipArchive;
@@ -44,21 +39,12 @@
/// The apex_payload.img file was missing from the APEX.
#[error("APEX doesn't contain apex_payload.img")]
PayloadMissing,
- /// The AVB footer in the APEX payload was invalid.
- #[error("Cannot validate APEX payload AVB footer")]
- InvalidPayloadAvbFooter,
- /// There were no descriptors in the APEX payload's AVB footer.
- #[error("No descriptors found in payload AVB footer")]
- NoDescriptors,
- /// There was an invalid descriptor in the APEX payload's AVB footer.
- #[error("Invalid descriptor found in payload AVB footer")]
- InvalidDescriptor,
- /// There was no hashtree descriptor in the APEX payload's AVB footer.
- #[error("Non-hashtree descriptor found in payload AVB footer")]
+ /// There was no hashtree descriptor in the APEX payload's VBMeta image.
+ #[error("Non-hashtree descriptor found in payload's VBMeta image")]
DescriptorNotHashtree,
- /// There was an invalid hashtree descriptor in the APEX payload's AVB footer.
- #[error("Invalid hashtree descriptor found in payload AVB footer")]
- InvalidHashtreeDescriptor,
+ /// There was an error parsing the APEX payload's VBMeta image.
+ #[error("Could not parse payload's VBMeta image")]
+ PayloadVbmetaError(#[from] vbmeta::VbMetaImageParseError),
}
/// Errors from verifying an APEX.
@@ -67,12 +53,12 @@
/// There was an error parsing the APEX.
#[error("Cannot parse APEX file")]
ParseError(#[from] ApexParseError),
- /// The APEX payload signature did not validate.
- #[error("Cannot verify payload signature")]
- BadPayloadSignature(String),
- /// The APEX payload was signed with a different key.
- #[error("Payload is signed with the wrong key")]
- BadPayloadKey,
+ /// There was an error validating the APEX payload's VBMeta image.
+ #[error("Could not parse payload's VBMeta image")]
+ PayloadVbmetaError(#[from] vbmeta::VbMetaImageVerificationError),
+ /// The APEX payload was not verified with the apex_pubkey.
+ #[error("APEX pubkey mismatch")]
+ ApexPubkeyMistmatch,
}
/// Verification result holds public key and root digest of apex_payload.img
@@ -87,8 +73,24 @@
pub fn verify(path: &str) -> Result<ApexVerificationResult, ApexVerificationError> {
let apex_file = File::open(path).map_err(ApexParseError::Io)?;
let (public_key, image_offset, image_size) = get_public_key_and_image_info(&apex_file)?;
- let root_digest = verify_vbmeta(apex_file, image_offset, image_size, &public_key)?;
- Ok(ApexVerificationResult { public_key, root_digest })
+ let vbmeta = VbMetaImage::verify_reader_region(apex_file, image_offset, image_size)?;
+ let root_digest = find_root_digest(&vbmeta)?;
+ match vbmeta.public_key() {
+ Some(payload_public_key) if public_key == payload_public_key => {
+ Ok(ApexVerificationResult { public_key, root_digest })
+ }
+ _ => Err(ApexVerificationError::ApexPubkeyMistmatch),
+ }
+}
+
+fn find_root_digest(vbmeta: &VbMetaImage) -> Result<Vec<u8>, ApexParseError> {
+ // APEXs use the root digest from the first hashtree descriptor to describe the payload.
+ for descriptor in vbmeta.descriptors()?.iter() {
+ if let vbmeta::Descriptor::Hashtree(_) = descriptor {
+ return Ok(descriptor.to_hashtree()?.root_digest().to_vec());
+ }
+ }
+ Err(ApexParseError::DescriptorNotHashtree)
}
fn get_public_key_and_image_info(apex_file: &File) -> Result<(Vec<u8>, u64, u64), ApexParseError> {
@@ -125,176 +127,6 @@
Ok((public_key, image_offset, image_size))
}
-// Manual addition of a missing enum
-#[allow(non_camel_case_types, dead_code)]
-#[repr(u8)]
-enum AvbDescriptorTag {
- AVB_DESCRIPTOR_TAG_PROPERTY = 0,
- AVB_DESCRIPTOR_TAG_HASHTREE,
- AVB_DESCRIPTOR_TAG_HASH,
- AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE,
- AVB_DESCRIPTOR_TAG_CHAIN_PARTITION,
-}
-
-const FOOTER_SIZE: usize = size_of::<AvbFooter>();
-const HASHTREE_DESCRIPTOR_SIZE: usize = size_of::<AvbHashtreeDescriptor>();
-
-/// Verify VBmeta image and return root digest
-fn verify_vbmeta<R: Read + Seek>(
- image: R,
- offset: u64,
- size: u64,
- public_key: &[u8],
-) -> Result<Vec<u8>, ApexVerificationError> {
- let vbmeta = VbMeta::from(image, offset, size)?;
- vbmeta.verify(public_key)?;
- for &descriptor in vbmeta.descriptors()?.iter() {
- if let Ok(hashtree_descriptor) = HashtreeDescriptor::from(descriptor) {
- return Ok(hashtree_descriptor.root_digest());
- }
- }
- Err(ApexParseError::DescriptorNotHashtree.into())
-}
-
-struct VbMeta {
- data: Vec<u8>,
-}
-
-impl VbMeta {
- // Read a VbMeta data from a given image
- fn from<R: Read + Seek>(
- mut image: R,
- offset: u64,
- size: u64,
- ) -> Result<VbMeta, ApexParseError> {
- // Get AvbFooter first
- image.seek(SeekFrom::Start(offset + size - FOOTER_SIZE as u64))?;
- // SAFETY: AvbDescriptor is a "repr(C,packed)" struct from bindgen
- let mut footer: AvbFooter = unsafe { zeroed() };
- // SAFETY: safe to read because of seek(-FOOTER_SIZE) above
- let avb_footer_valid = unsafe {
- let footer_slice = from_raw_parts_mut(&mut footer as *mut _ as *mut u8, FOOTER_SIZE);
- image.read_exact(footer_slice)?;
- avb_footer_validate_and_byteswap(&footer, &mut footer)
- };
- if !avb_footer_valid {
- return Err(ApexParseError::InvalidPayloadAvbFooter);
- }
- // Get VbMeta block
- image.seek(SeekFrom::Start(offset + footer.vbmeta_offset))?;
- let vbmeta_size = footer.vbmeta_size as usize;
- let mut data = vec![0u8; vbmeta_size];
- image.read_exact(&mut data)?;
- Ok(VbMeta { data })
- }
- // Verify VbMeta image. Its enclosed public key should match with a given public key.
- fn verify(&self, outer_public_key: &[u8]) -> Result<(), ApexVerificationError> {
- // SAFETY: self.data points to a valid VBMeta data and avb_vbmeta_image_verify should work fine
- // with it
- let public_key = unsafe {
- let mut pk_ptr: *const u8 = null_mut();
- let mut pk_len: usize = 0;
- let res = avb_vbmeta_image_verify(
- self.data.as_ptr(),
- self.data.len(),
- &mut pk_ptr,
- &mut pk_len,
- );
- if res != AvbVBMetaVerifyResult_AVB_VBMETA_VERIFY_RESULT_OK {
- return Err(ApexVerificationError::BadPayloadSignature(
- CStr::from_ptr(avb_vbmeta_verify_result_to_string(res))
- .to_string_lossy()
- .into_owned(),
- ));
- }
- from_raw_parts(pk_ptr, pk_len)
- };
-
- if public_key != outer_public_key {
- return Err(ApexVerificationError::BadPayloadKey);
- }
- Ok(())
- }
- // Return a slice of AvbDescriptor pointers
- fn descriptors(&self) -> Result<Descriptors, ApexParseError> {
- let mut num: usize = 0;
- // SAFETY: ptr will be freed by Descriptor.
- Ok(unsafe {
- let ptr = avb_descriptor_get_all(self.data.as_ptr(), self.data.len(), &mut num);
- if ptr.is_null() {
- return Err(ApexParseError::NoDescriptors);
- }
- let all = from_raw_parts(ptr, num);
- Descriptors { ptr, all }
- })
- }
-}
-
-struct HashtreeDescriptor {
- ptr: *const u8,
- inner: AvbHashtreeDescriptor,
-}
-
-impl HashtreeDescriptor {
- fn from(descriptor: *const AvbDescriptor) -> Result<HashtreeDescriptor, ApexParseError> {
- // SAFETY: AvbDescriptor is a "repr(C,packed)" struct from bindgen
- let mut desc: AvbDescriptor = unsafe { zeroed() };
- // SAFETY: both points to valid AvbDescriptor pointers
- if !unsafe { avb_descriptor_validate_and_byteswap(descriptor, &mut desc) } {
- return Err(ApexParseError::InvalidDescriptor);
- }
- if desc.tag != AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE as u64 {
- return Err(ApexParseError::DescriptorNotHashtree);
- }
- // SAFETY: AvbHashtreeDescriptor is a "repr(C, packed)" struct from bindgen
- let mut hashtree_descriptor: AvbHashtreeDescriptor = unsafe { zeroed() };
- // SAFETY: With tag == AVB_DESCRIPTOR_TAG_HASHTREE, descriptor should point to
- // a AvbHashtreeDescriptor.
- if !unsafe {
- avb_hashtree_descriptor_validate_and_byteswap(
- descriptor as *const AvbHashtreeDescriptor,
- &mut hashtree_descriptor,
- )
- } {
- return Err(ApexParseError::InvalidHashtreeDescriptor);
- }
- Ok(Self { ptr: descriptor as *const u8, inner: hashtree_descriptor })
- }
- fn root_digest(&self) -> Vec<u8> {
- // SAFETY: digest_ptr should point to a valid buffer of root_digest_len
- let root_digest = unsafe {
- let digest_ptr = self.ptr.offset(
- HASHTREE_DESCRIPTOR_SIZE as isize
- + self.inner.partition_name_len as isize
- + self.inner.salt_len as isize,
- );
- from_raw_parts(digest_ptr, self.inner.root_digest_len as usize)
- };
- root_digest.to_owned()
- }
-}
-
-// Wraps pointer to a heap-allocated array of AvbDescriptor pointers
-struct Descriptors<'a> {
- ptr: *mut *const AvbDescriptor,
- all: &'a [*const AvbDescriptor],
-}
-
-// Wrapped pointer should be freed with avb_free.
-impl Drop for Descriptors<'_> {
- fn drop(&mut self) {
- // SAFETY: ptr is allocated by avb_descriptor_get_all
- unsafe { avb_free(self.ptr as *mut c_void) }
- }
-}
-
-impl<'a> Deref for Descriptors<'a> {
- type Target = &'a [*const AvbDescriptor];
- fn deref(&self) -> &Self::Target {
- &self.all
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;