Migrate payload/apex.rs to thiserror
In preparation for extracting the APEX handling code to a reusable
library, migrate errors from anyhow to thiserror.
Test: atest microdroid_manager_test
Change-Id: Iede88046f997f54ba7c4b7aebbb6a15dbba8f424
diff --git a/microdroid_manager/src/payload/apex.rs b/microdroid_manager/src/payload/apex.rs
index 24c4f05..d53e907 100644
--- a/microdroid_manager/src/payload/apex.rs
+++ b/microdroid_manager/src/payload/apex.rs
@@ -14,42 +14,113 @@
//! Routines for handling APEX payload
-use anyhow::{anyhow, ensure, Result};
use avb_bindgen::*;
use std::ffi::{c_void, CStr};
use std::fs::File;
-use std::io::{Read, Seek, SeekFrom};
+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 thiserror::Error;
+use zip::result::ZipError;
use zip::ZipArchive;
const APEX_PUBKEY_ENTRY: &str = "apex_pubkey";
const APEX_PAYLOAD_ENTRY: &str = "apex_payload.img";
+/// Errors from parsing an APEX.
+#[derive(Debug, Error)]
+pub enum ApexParseError {
+ /// There was an IO error.
+ #[error("IO error")]
+ Io(#[from] io::Error),
+ /// The Zip archive was invalid.
+ #[error("Cannot read zip archive")]
+ InvalidZip(&'static str),
+ /// The apex_pubkey file was missing from the APEX.
+ #[error("APEX doesn't contain apex_pubkey")]
+ PubkeyMissing,
+ /// 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")]
+ DescriptorNotHashtree,
+ /// There was an invalid hashtree descriptor in the APEX payload's AVB footer.
+ #[error("Invalid hashtree descriptor found in payload AVB footer")]
+ InvalidHashtreeDescriptor,
+}
+
+/// Errors from verifying an APEX.
+#[derive(Debug, Error)]
+pub enum ApexVerificationError {
+ /// 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,
+}
+
/// Verification result holds public key and root digest of apex_payload.img
pub struct ApexVerificationResult {
+ /// The public key that verifies the payload signature.
pub public_key: Vec<u8>,
+ /// The root digest of the payload hashtree.
pub root_digest: Vec<u8>,
}
/// Verify APEX payload by AVB verification and return public key and root digest
-pub fn verify(path: &str) -> Result<ApexVerificationResult> {
- let apex_file = File::open(path)?;
+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 })
}
-fn get_public_key_and_image_info(apex_file: &File) -> Result<(Vec<u8>, u64, u64)> {
- let mut z = ZipArchive::new(apex_file)?;
+fn get_public_key_and_image_info(apex_file: &File) -> Result<(Vec<u8>, u64, u64), ApexParseError> {
+ let mut z = ZipArchive::new(apex_file).map_err(|err| match err {
+ ZipError::Io(err) => ApexParseError::Io(err),
+ ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
+ ApexParseError::InvalidZip(s)
+ }
+ ZipError::FileNotFound => unreachable!(),
+ })?;
let mut public_key = Vec::new();
- z.by_name(APEX_PUBKEY_ENTRY)?.read_to_end(&mut public_key)?;
+ z.by_name(APEX_PUBKEY_ENTRY)
+ .map_err(|err| match err {
+ ZipError::Io(err) => ApexParseError::Io(err),
+ ZipError::FileNotFound => ApexParseError::PubkeyMissing,
+ ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
+ ApexParseError::InvalidZip(s)
+ }
+ })?
+ .read_to_end(&mut public_key)?;
- let (image_offset, image_size) =
- z.by_name(APEX_PAYLOAD_ENTRY).map(|f| (f.data_start(), f.size()))?;
+ let (image_offset, image_size) = z
+ .by_name(APEX_PAYLOAD_ENTRY)
+ .map(|f| (f.data_start(), f.size()))
+ .map_err(|err| match err {
+ ZipError::Io(err) => ApexParseError::Io(err),
+ ZipError::FileNotFound => ApexParseError::PayloadMissing,
+ ZipError::InvalidArchive(s) | ZipError::UnsupportedArchive(s) => {
+ ApexParseError::InvalidZip(s)
+ }
+ })?;
Ok((public_key, image_offset, image_size))
}
@@ -74,15 +145,15 @@
offset: u64,
size: u64,
public_key: &[u8],
-) -> Result<Vec<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 hashtree_descriptor.root_digest();
+ return Ok(hashtree_descriptor.root_digest());
}
}
- Err(anyhow!("HashtreeDescriptor is not found."))
+ Err(ApexParseError::DescriptorNotHashtree.into())
}
struct VbMeta {
@@ -91,16 +162,23 @@
impl VbMeta {
// Read a VbMeta data from a given image
- fn from<R: Read + Seek>(mut image: R, offset: u64, size: u64) -> Result<VbMeta> {
+ 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
- unsafe {
+ 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)?;
- ensure!(avb_footer_validate_and_byteswap(&footer, &mut footer));
+ 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))?;
@@ -110,7 +188,7 @@
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<()> {
+ 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 {
@@ -122,25 +200,30 @@
&mut pk_ptr,
&mut pk_len,
);
- ensure!(
- res == AvbVBMetaVerifyResult_AVB_VBMETA_VERIFY_RESULT_OK,
- CStr::from_ptr(avb_vbmeta_verify_result_to_string(res))
- .to_string_lossy()
- .into_owned()
- );
+ 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)
};
- ensure!(public_key == outer_public_key, "Public key mismatch with a given one.");
+ if public_key != outer_public_key {
+ return Err(ApexVerificationError::BadPayloadKey);
+ }
Ok(())
}
// Return a slice of AvbDescriptor pointers
- fn descriptors(&self) -> Result<Descriptors> {
+ 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);
- ensure!(!ptr.is_null(), "VbMeta has no descriptors.");
+ if ptr.is_null() {
+ return Err(ApexParseError::NoDescriptors);
+ }
let all = from_raw_parts(ptr, num);
Descriptors { ptr, all }
})
@@ -153,27 +236,31 @@
}
impl HashtreeDescriptor {
- fn from(descriptor: *const AvbDescriptor) -> Result<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
- unsafe {
- ensure!(avb_descriptor_validate_and_byteswap(descriptor, &mut desc));
+ if !unsafe { avb_descriptor_validate_and_byteswap(descriptor, &mut desc) } {
+ return Err(ApexParseError::InvalidDescriptor);
}
- ensure!({ desc.tag } == AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE as u64);
+ 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.
- unsafe {
- ensure!(avb_hashtree_descriptor_validate_and_byteswap(
+ 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) -> Result<Vec<u8>> {
+ 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(
@@ -183,7 +270,7 @@
);
from_raw_parts(digest_ptr, self.inner.root_digest_len as usize)
};
- Ok(root_digest.to_owned())
+ root_digest.to_owned()
}
}