Merge changes I36abf21b,Iede88046

* changes:
  Move APEX handling to a library
  Migrate payload/apex.rs to thiserror
diff --git a/libs/apexutil/Android.bp b/libs/apexutil/Android.bp
new file mode 100644
index 0000000..a1a1ca6
--- /dev/null
+++ b/libs/apexutil/Android.bp
@@ -0,0 +1,40 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_defaults {
+    name: "libapexutil_rust.defaults",
+    crate_name: "apexutil",
+    host_supported: true,
+    srcs: ["src/lib.rs"],
+    prefer_rlib: true,
+    edition: "2018",
+    rustlibs: [
+        "libavb_bindgen",
+        "liblog_rust",
+        "libthiserror",
+        "libzip",
+    ],
+}
+
+rust_library {
+    name: "libapexutil_rust",
+    defaults: ["libapexutil_rust.defaults"],
+}
+
+rust_test {
+    name: "libapexutil_rust.test",
+    defaults: ["libapexutil_rust.defaults"],
+    test_suites: ["general-tests"],
+    data: ["tests/data/*"],
+    target: {
+        host: {
+            // TODO(b/204562227): remove once the build does this automatically
+            data_libs: [
+                "libc++",
+                "libcrypto",
+                "libz",
+            ],
+        },
+    },
+}
diff --git a/libs/apexutil/src/lib.rs b/libs/apexutil/src/lib.rs
new file mode 100644
index 0000000..d53e907
--- /dev/null
+++ b/libs/apexutil/src/lib.rs
@@ -0,0 +1,312 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! 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 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, 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), 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)
+        .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()))
+        .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))
+}
+
+// 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::*;
+    fn to_hex_string(buf: &[u8]) -> String {
+        buf.iter().map(|b| format!("{:02x}", b)).collect()
+    }
+    #[test]
+    fn test_open_apex() {
+        let res = verify("tests/data/test.apex").unwrap();
+        assert_eq!(
+            to_hex_string(&res.root_digest),
+            "fe11ab17da0a3a738b54bdc3a13f6139cbdf91ec32f001f8d4bbbf8938e04e39"
+        );
+    }
+}
diff --git a/microdroid_manager/tests/data/README.md b/libs/apexutil/tests/data/README.md
similarity index 100%
rename from microdroid_manager/tests/data/README.md
rename to libs/apexutil/tests/data/README.md
diff --git a/microdroid_manager/tests/data/test.apex b/libs/apexutil/tests/data/test.apex
similarity index 100%
rename from microdroid_manager/tests/data/test.apex
rename to libs/apexutil/tests/data/test.apex
Binary files differ
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index e4827aa..203e889 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -14,8 +14,8 @@
         "android.system.virtualizationservice-rust",
         "android.system.virtualmachineservice-rust",
         "libanyhow",
+        "libapexutil_rust",
         "libapkverify",
-        "libavb_bindgen",
         "libbinder_rpc_unstable_bindgen",
         "libbinder_rs",
         "libbyteorder",
@@ -40,7 +40,6 @@
         "libuuid",
         "libvsock",
         "librand",
-        "libzip",
     ],
     shared_libs: [
         "libbinder_rpc_unstable",
@@ -71,5 +70,4 @@
             enabled: false,
         },
     },
-    data: ["tests/data/*"],
 }
diff --git a/microdroid_manager/src/payload.rs b/microdroid_manager/src/payload.rs
index 48535f3..a553ce4 100644
--- a/microdroid_manager/src/payload.rs
+++ b/microdroid_manager/src/payload.rs
@@ -14,12 +14,10 @@
 
 //! Routines for handling payload
 
-mod apex;
-
 use crate::instance::ApexData;
 use crate::ioutil::wait_for_file;
 use anyhow::Result;
-use apex::verify;
+use apexutil::verify;
 use log::info;
 use microdroid_metadata::{read_metadata, ApexPayload, Metadata};
 use std::time::Duration;
diff --git a/microdroid_manager/src/payload/apex.rs b/microdroid_manager/src/payload/apex.rs
deleted file mode 100644
index 24c4f05..0000000
--- a/microdroid_manager/src/payload/apex.rs
+++ /dev/null
@@ -1,225 +0,0 @@
-// Copyright 2021, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! 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::mem::{size_of, zeroed};
-use std::ops::Deref;
-use std::ptr::null_mut;
-use std::slice::{from_raw_parts, from_raw_parts_mut};
-use zip::ZipArchive;
-
-const APEX_PUBKEY_ENTRY: &str = "apex_pubkey";
-const APEX_PAYLOAD_ENTRY: &str = "apex_payload.img";
-
-/// Verification result holds public key and root digest of apex_payload.img
-pub struct ApexVerificationResult {
-    pub public_key: Vec<u8>,
-    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)?;
-    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)?;
-
-    let mut public_key = Vec::new();
-    z.by_name(APEX_PUBKEY_ENTRY)?.read_to_end(&mut public_key)?;
-
-    let (image_offset, image_size) =
-        z.by_name(APEX_PAYLOAD_ENTRY).map(|f| (f.data_start(), f.size()))?;
-
-    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>> {
-    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();
-        }
-    }
-    Err(anyhow!("HashtreeDescriptor is not found."))
-}
-
-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> {
-        // 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 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));
-        }
-        // 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<()> {
-        // 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,
-            );
-            ensure!(
-                res == AvbVBMetaVerifyResult_AVB_VBMETA_VERIFY_RESULT_OK,
-                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.");
-        Ok(())
-    }
-    // Return a slice of AvbDescriptor pointers
-    fn descriptors(&self) -> Result<Descriptors> {
-        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.");
-            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> {
-        // 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));
-        }
-        ensure!({ desc.tag } == AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE as u64);
-        // 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(
-                descriptor as *const AvbHashtreeDescriptor,
-                &mut hashtree_descriptor,
-            ));
-        }
-        Ok(Self { ptr: descriptor as *const u8, inner: hashtree_descriptor })
-    }
-    fn root_digest(&self) -> Result<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)
-        };
-        Ok(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::*;
-    fn to_hex_string(buf: &[u8]) -> String {
-        buf.iter().map(|b| format!("{:02x}", b)).collect()
-    }
-    #[test]
-    fn test_open_apex() {
-        let res = verify("tests/data/test.apex").unwrap();
-        assert_eq!(
-            to_hex_string(&res.root_digest),
-            "fe11ab17da0a3a738b54bdc3a13f6139cbdf91ec32f001f8d4bbbf8938e04e39"
-        );
-    }
-}