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()
     }
 }