Merge "[dice] Move open-dice Rust wrapper libraries to virt" into main
diff --git a/compos/Android.bp b/compos/Android.bp
index 19123dd..b840506 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -14,6 +14,7 @@
         "libanyhow",
         "libbinder_rs",
         "libcompos_common",
+        "libhex",
         "liblibc",
         "liblog_rust",
         "libminijail_rust",
diff --git a/compos/src/artifact_signer.rs b/compos/src/artifact_signer.rs
index 76da00a..bfd886e 100644
--- a/compos/src/artifact_signer.rs
+++ b/compos/src/artifact_signer.rs
@@ -53,7 +53,7 @@
 
         let file = File::open(path).with_context(|| format!("Opening {}", path.display()))?;
         let digest = fsverity::measure(file.as_fd())?;
-        let digest = to_hex_string(&digest);
+        let digest = hex::encode(digest);
 
         self.file_digests.push((target_path.to_owned(), digest));
         Ok(())
@@ -82,7 +82,3 @@
         Ok(())
     }
 }
-
-fn to_hex_string(buf: &[u8]) -> String {
-    buf.iter().map(|b| format!("{:02x}", b)).collect()
-}
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index 27905c9..a576416 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -1,10 +1,10 @@
-// Copyright 2022, The Android Open Source Project
+// Copyright 2023 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
+//      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,
@@ -12,142 +12,44 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-//! Logic for handling the DICE values and boot operations.
-
-use anyhow::{anyhow, bail, Context, Error, Result};
-use byteorder::{NativeEndian, ReadBytesExt};
+use crate::dice_driver::DiceDriver;
+use crate::{is_debuggable, MicrodroidData};
+use anyhow::{bail, Context, Result};
 use ciborium::{cbor, ser};
-use diced_open_dice::{
-    bcc_handover_parse, retry_bcc_main_flow, BccHandover, Config, DiceArtifacts, DiceMode, Hash,
-    Hidden, InputValues, OwnedDiceArtifacts,
-};
-use keystore2_crypto::ZVec;
-use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
+use diced_open_dice::OwnedDiceArtifacts;
 use microdroid_metadata::PayloadMetadata;
-use openssl::hkdf::hkdf;
-use openssl::md::Md;
-use std::fs;
-use std::os::unix::io::AsRawFd;
-use std::path::{Path, PathBuf};
-use std::ptr::null_mut;
-use std::slice;
+use openssl::sha::Sha512;
 
-/// Artifacts that are mapped into the process address space from the driver.
-pub enum DiceDriver<'a> {
-    Real {
-        driver_path: PathBuf,
-        mmap_addr: *mut c_void,
-        mmap_size: usize,
-        bcc_handover: BccHandover<'a>,
-    },
-    Fake(OwnedDiceArtifacts),
-}
-
-impl DiceDriver<'_> {
-    fn dice_artifacts(&self) -> &dyn DiceArtifacts {
-        match self {
-            Self::Real { bcc_handover, .. } => bcc_handover,
-            Self::Fake(owned_dice_artifacts) => owned_dice_artifacts,
-        }
+/// Perform an open DICE derivation for the payload.
+pub fn dice_derivation(
+    dice: DiceDriver,
+    verified_data: &MicrodroidData,
+    payload_metadata: &PayloadMetadata,
+) -> Result<OwnedDiceArtifacts> {
+    // Calculate compound digests of code and authorities
+    let mut code_hash_ctx = Sha512::new();
+    let mut authority_hash_ctx = Sha512::new();
+    code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
+    authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
+    for extra_apk in &verified_data.extra_apks_data {
+        code_hash_ctx.update(extra_apk.root_hash.as_ref());
+        authority_hash_ctx.update(extra_apk.pubkey.as_ref());
     }
-
-    pub fn new(driver_path: &Path) -> Result<Self> {
-        if driver_path.exists() {
-            log::info!("Using DICE values from driver");
-        } else if super::is_strict_boot() {
-            bail!("Strict boot requires DICE value from driver but none were found");
-        } else {
-            log::warn!("Using sample DICE values");
-            let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()
-                .expect("Failed to create sample dice artifacts.");
-            return Ok(Self::Fake(dice_artifacts));
-        };
-
-        let mut file = fs::File::open(driver_path)
-            .map_err(|error| Error::new(error).context("Opening driver"))?;
-        let mmap_size =
-            file.read_u64::<NativeEndian>()
-                .map_err(|error| Error::new(error).context("Reading driver"))? as usize;
-        // SAFETY: It's safe to map the driver as the service will only create a single
-        // mapping per process.
-        let mmap_addr = unsafe {
-            let fd = file.as_raw_fd();
-            mmap(null_mut(), mmap_size, PROT_READ, MAP_PRIVATE, fd, 0)
-        };
-        if mmap_addr == MAP_FAILED {
-            bail!("Failed to mmap {:?}", driver_path);
-        }
-        let mmap_buf =
-        // SAFETY: The slice is created for the region of memory that was just
-        // successfully mapped into the process address space so it will be
-        // accessible and not referenced from anywhere else.
-            unsafe { slice::from_raw_parts((mmap_addr as *const u8).as_ref().unwrap(), mmap_size) };
-        let bcc_handover =
-            bcc_handover_parse(mmap_buf).map_err(|_| anyhow!("Failed to parse Bcc Handover"))?;
-        Ok(Self::Real {
-            driver_path: driver_path.to_path_buf(),
-            mmap_addr,
-            mmap_size,
-            bcc_handover,
-        })
+    for apex in &verified_data.apex_data {
+        code_hash_ctx.update(apex.root_digest.as_ref());
+        authority_hash_ctx.update(apex.public_key.as_ref());
     }
+    let code_hash = code_hash_ctx.finish();
+    let authority_hash = authority_hash_ctx.finish();
 
-    /// Derives a sealing key of `key_length` bytes from the DICE sealing CDI.
-    pub fn get_sealing_key(&self, identifier: &[u8], key_length: usize) -> Result<ZVec> {
-        // Deterministically derive a key to use for sealing data, rather than using the CDI
-        // directly, so we have the chance to rotate the key if needed. A salt isn't needed as the
-        // input key material is already cryptographically strong.
-        let mut key = ZVec::new(key_length)?;
-        let salt = &[];
-        hkdf(&mut key, Md::sha256(), self.dice_artifacts().cdi_seal(), salt, identifier)?;
-        Ok(key)
-    }
+    let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
 
-    pub fn derive(
-        self,
-        code_hash: Hash,
-        config_desc: &[u8],
-        authority_hash: Hash,
-        debug: bool,
-        hidden: Hidden,
-    ) -> Result<OwnedDiceArtifacts> {
-        let input_values = InputValues::new(
-            code_hash,
-            Config::Descriptor(config_desc),
-            authority_hash,
-            if debug { DiceMode::kDiceModeDebug } else { DiceMode::kDiceModeNormal },
-            hidden,
-        );
-        let current_dice_artifacts = self.dice_artifacts();
-        let next_dice_artifacts = retry_bcc_main_flow(
-            current_dice_artifacts.cdi_attest(),
-            current_dice_artifacts.cdi_seal(),
-            current_dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
-            &input_values,
-        )
-        .context("DICE derive from driver")?;
-        if let Self::Real { driver_path, .. } = &self {
-            // Writing to the device wipes the artifacts. The string is ignored by the driver but
-            // included for documentation.
-            fs::write(driver_path, "wipe")
-                .map_err(|err| Error::new(err).context("Wiping driver"))?;
-        }
-        Ok(next_dice_artifacts)
-    }
-}
+    // Check debuggability, conservatively assuming it is debuggable
+    let debuggable = is_debuggable()?;
 
-impl Drop for DiceDriver<'_> {
-    fn drop(&mut self) {
-        if let &mut Self::Real { mmap_addr, mmap_size, .. } = self {
-            // SAFETY: All references to the mapped region have the same lifetime as self. Since
-            // self is being dropped, so are all the references to the mapped region meaning it's
-            // safe to unmap.
-            let ret = unsafe { munmap(mmap_addr, mmap_size) };
-            if ret != 0 {
-                log::warn!("Failed to munmap ({})", ret);
-            }
-        }
-    }
+    // Send the details to diced
+    let hidden = verified_data.salt.clone().try_into().unwrap();
+    dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
 }
 
 /// Returns a configuration descriptor of the given payload following the BCC's specification:
@@ -160,7 +62,7 @@
 /// PayloadConfig = {
 ///   1: tstr ; payload_binary_name
 /// }
-pub fn format_payload_config_descriptor(payload: &PayloadMetadata) -> Result<Vec<u8>> {
+fn format_payload_config_descriptor(payload: &PayloadMetadata) -> Result<Vec<u8>> {
     const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
 
     let config_descriptor_cbor_value = match payload {
diff --git a/microdroid_manager/src/dice_driver.rs b/microdroid_manager/src/dice_driver.rs
new file mode 100644
index 0000000..229f3e0
--- /dev/null
+++ b/microdroid_manager/src/dice_driver.rs
@@ -0,0 +1,149 @@
+// Copyright 2022, 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.
+
+//! Logic for handling the DICE values and boot operations.
+
+use anyhow::{anyhow, bail, Context, Error, Result};
+use byteorder::{NativeEndian, ReadBytesExt};
+use diced_open_dice::{
+    bcc_handover_parse, retry_bcc_main_flow, BccHandover, Config, DiceArtifacts, DiceMode, Hash,
+    Hidden, InputValues, OwnedDiceArtifacts,
+};
+use keystore2_crypto::ZVec;
+use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
+use openssl::hkdf::hkdf;
+use openssl::md::Md;
+use std::fs;
+use std::os::unix::io::AsRawFd;
+use std::path::{Path, PathBuf};
+use std::ptr::null_mut;
+use std::slice;
+
+/// Artifacts that are mapped into the process address space from the driver.
+pub enum DiceDriver<'a> {
+    Real {
+        driver_path: PathBuf,
+        mmap_addr: *mut c_void,
+        mmap_size: usize,
+        bcc_handover: BccHandover<'a>,
+    },
+    Fake(OwnedDiceArtifacts),
+}
+
+impl DiceDriver<'_> {
+    fn dice_artifacts(&self) -> &dyn DiceArtifacts {
+        match self {
+            Self::Real { bcc_handover, .. } => bcc_handover,
+            Self::Fake(owned_dice_artifacts) => owned_dice_artifacts,
+        }
+    }
+
+    pub fn new(driver_path: &Path) -> Result<Self> {
+        if driver_path.exists() {
+            log::info!("Using DICE values from driver");
+        } else if super::is_strict_boot() {
+            bail!("Strict boot requires DICE value from driver but none were found");
+        } else {
+            log::warn!("Using sample DICE values");
+            let dice_artifacts = diced_sample_inputs::make_sample_bcc_and_cdis()
+                .expect("Failed to create sample dice artifacts.");
+            return Ok(Self::Fake(dice_artifacts));
+        };
+
+        let mut file = fs::File::open(driver_path)
+            .map_err(|error| Error::new(error).context("Opening driver"))?;
+        let mmap_size =
+            file.read_u64::<NativeEndian>()
+                .map_err(|error| Error::new(error).context("Reading driver"))? as usize;
+        // SAFETY: It's safe to map the driver as the service will only create a single
+        // mapping per process.
+        let mmap_addr = unsafe {
+            let fd = file.as_raw_fd();
+            mmap(null_mut(), mmap_size, PROT_READ, MAP_PRIVATE, fd, 0)
+        };
+        if mmap_addr == MAP_FAILED {
+            bail!("Failed to mmap {:?}", driver_path);
+        }
+        let mmap_buf =
+        // SAFETY: The slice is created for the region of memory that was just
+        // successfully mapped into the process address space so it will be
+        // accessible and not referenced from anywhere else.
+            unsafe { slice::from_raw_parts((mmap_addr as *const u8).as_ref().unwrap(), mmap_size) };
+        let bcc_handover =
+            bcc_handover_parse(mmap_buf).map_err(|_| anyhow!("Failed to parse Bcc Handover"))?;
+        Ok(Self::Real {
+            driver_path: driver_path.to_path_buf(),
+            mmap_addr,
+            mmap_size,
+            bcc_handover,
+        })
+    }
+
+    /// Derives a sealing key of `key_length` bytes from the DICE sealing CDI.
+    pub fn get_sealing_key(&self, identifier: &[u8], key_length: usize) -> Result<ZVec> {
+        // Deterministically derive a key to use for sealing data, rather than using the CDI
+        // directly, so we have the chance to rotate the key if needed. A salt isn't needed as the
+        // input key material is already cryptographically strong.
+        let mut key = ZVec::new(key_length)?;
+        let salt = &[];
+        hkdf(&mut key, Md::sha256(), self.dice_artifacts().cdi_seal(), salt, identifier)?;
+        Ok(key)
+    }
+
+    pub fn derive(
+        self,
+        code_hash: Hash,
+        config_desc: &[u8],
+        authority_hash: Hash,
+        debug: bool,
+        hidden: Hidden,
+    ) -> Result<OwnedDiceArtifacts> {
+        let input_values = InputValues::new(
+            code_hash,
+            Config::Descriptor(config_desc),
+            authority_hash,
+            if debug { DiceMode::kDiceModeDebug } else { DiceMode::kDiceModeNormal },
+            hidden,
+        );
+        let current_dice_artifacts = self.dice_artifacts();
+        let next_dice_artifacts = retry_bcc_main_flow(
+            current_dice_artifacts.cdi_attest(),
+            current_dice_artifacts.cdi_seal(),
+            current_dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
+            &input_values,
+        )
+        .context("DICE derive from driver")?;
+        if let Self::Real { driver_path, .. } = &self {
+            // Writing to the device wipes the artifacts. The string is ignored by the driver but
+            // included for documentation.
+            fs::write(driver_path, "wipe")
+                .map_err(|err| Error::new(err).context("Wiping driver"))?;
+        }
+        Ok(next_dice_artifacts)
+    }
+}
+
+impl Drop for DiceDriver<'_> {
+    fn drop(&mut self) {
+        if let &mut Self::Real { mmap_addr, mmap_size, .. } = self {
+            // SAFETY: All references to the mapped region have the same lifetime as self. Since
+            // self is being dropped, so are all the references to the mapped region meaning it's
+            // safe to unmap.
+            let ret = unsafe { munmap(mmap_addr, mmap_size) };
+            if ret != 0 {
+                log::warn!("Failed to munmap ({})", ret);
+            }
+        }
+    }
+}
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index b16a1e1..2ff04f1 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -33,7 +33,7 @@
 //! The payload of a partition is encrypted/signed by a key that is unique to the loader and to the
 //! VM as well. Failing to decrypt/authenticate a partition by a loader stops the boot process.
 
-use crate::dice::DiceDriver;
+use crate::dice_driver::DiceDriver;
 use crate::ioutil;
 
 use anyhow::{anyhow, bail, Context, Result};
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 491d4de..8175753 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -15,16 +15,15 @@
 //! Microdroid Manager
 
 mod dice;
+mod dice_driver;
 mod instance;
 mod ioutil;
 mod payload;
 mod swap;
+mod verify;
 mod vm_payload_service;
 mod vm_secret;
 
-use crate::dice::{DiceDriver, format_payload_config_descriptor};
-use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
-use crate::vm_payload_service::register_vm_payload_service;
 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
 use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
@@ -32,55 +31,46 @@
     VM_PAYLOAD_SERVICE_SOCKET_NAME,
     ENCRYPTEDSTORE_MOUNTPOINT,
 };
+
+use crate::dice::dice_derivation;
+use crate::dice_driver::DiceDriver;
+use crate::instance::{ApexData, InstanceDisk, MicrodroidData};
+use crate::verify::verify_payload;
+use crate::vm_payload_service::register_vm_payload_service;
 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
-use apkverify::{get_public_key_der, verify, V4Signature};
-use apkmanifest::get_manifest_info;
 use binder::Strong;
-use diced_open_dice::OwnedDiceArtifacts;
-use glob::glob;
-use itertools::sorted;
-use libc::VMADDR_CID_HOST;
-use log::{error, info, warn};
 use keystore2_crypto::ZVec;
-use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
+use libc::VMADDR_CID_HOST;
+use log::{error, info};
+use microdroid_metadata::{write_metadata, PayloadMetadata};
 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
 use nix::sys::signal::Signal;
-use openssl::sha::Sha512;
-use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
-use rand::Fill;
+use payload::{load_metadata, to_metadata};
 use rpcbinder::RpcSession;
 use rustutils::sockets::android_get_control_socket;
 use rustutils::system_properties;
 use rustutils::system_properties::PropertyWatcher;
 use std::borrow::Cow::{Borrowed, Owned};
-use std::convert::TryInto;
 use std::env;
 use std::ffi::CString;
-use std::fs::{self, create_dir, OpenOptions, File};
+use std::fs::{self, create_dir, File, OpenOptions};
 use std::io::{Read, Write};
+use std::os::unix::io::{FromRawFd, OwnedFd};
 use std::os::unix::process::CommandExt;
 use std::os::unix::process::ExitStatusExt;
-use std::os::unix::io::{FromRawFd, OwnedFd};
 use std::path::Path;
 use std::process::{Child, Command, Stdio};
 use std::str;
-use std::time::{Duration, SystemTime};
+use std::time::Duration;
 use vm_secret::VmSecret;
 
 const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
-const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
-const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
-const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
-const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
-const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
-const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
 const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
 const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
 const AVF_DEBUG_POLICY_RAMDUMP: &str = "/sys/firmware/devicetree/base/avf/guest/common/ramdump";
 const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
     "/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
 
-const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
 const ENCRYPTEDSTORE_BIN: &str = "/system/bin/encryptedstore";
 const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
 
@@ -164,7 +154,7 @@
 
 fn main() -> Result<()> {
     // If debuggable, print full backtrace to console log with stdio_to_kmsg
-    if system_properties::read_bool(DEBUGGABLE_PROP, true)? {
+    if is_debuggable()? {
         env::set_var("RUST_BACKTRACE", "full");
     }
 
@@ -279,36 +269,6 @@
     }
     Ok(())
 }
-fn dice_derivation(
-    dice: DiceDriver,
-    verified_data: &MicrodroidData,
-    payload_metadata: &PayloadMetadata,
-) -> Result<OwnedDiceArtifacts> {
-    // Calculate compound digests of code and authorities
-    let mut code_hash_ctx = Sha512::new();
-    let mut authority_hash_ctx = Sha512::new();
-    code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
-    authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
-    for extra_apk in &verified_data.extra_apks_data {
-        code_hash_ctx.update(extra_apk.root_hash.as_ref());
-        authority_hash_ctx.update(extra_apk.pubkey.as_ref());
-    }
-    for apex in &verified_data.apex_data {
-        code_hash_ctx.update(apex.root_digest.as_ref());
-        authority_hash_ctx.update(apex.public_key.as_ref());
-    }
-    let code_hash = code_hash_ctx.finish();
-    let authority_hash = authority_hash_ctx.finish();
-
-    let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
-
-    // Check debuggability, conservatively assuming it is debuggable
-    let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
-
-    // Send the details to diced
-    let hidden = verified_data.salt.clone().try_into().unwrap();
-    dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
-}
 
 fn is_strict_boot() -> bool {
     Path::new(AVF_STRICT_BOOT).exists()
@@ -322,10 +282,14 @@
     !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
 }
 
+fn is_debuggable() -> Result<bool> {
+    Ok(system_properties::read_bool(DEBUGGABLE_PROP, true)?)
+}
+
 fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
     match config.export_tombstones {
         Some(b) => b,
-        None => system_properties::read_bool(DEBUGGABLE_PROP, true).unwrap_or(false),
+        None => is_debuggable().unwrap_or(false),
     }
 }
 
@@ -425,7 +389,7 @@
     zipfuse.mount(
         MountForExec::Allowed,
         "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
-        Path::new(DM_MOUNTED_APK_PATH),
+        Path::new(verify::DM_MOUNTED_APK_PATH),
         Path::new(VM_APK_CONTENTS_PATH),
         "microdroid_manager.apk.mounted".to_owned(),
     )?;
@@ -491,28 +455,6 @@
     exec_task(task, service).context("Failed to run payload")
 }
 
-struct ApkDmverityArgument<'a> {
-    apk: &'a str,
-    idsig: &'a str,
-    name: &'a str,
-    saved_root_hash: Option<&'a RootHash>,
-}
-
-fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
-    let mut cmd = Command::new(APKDMVERITY_BIN);
-
-    for argument in args {
-        cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
-        if let Some(root_hash) = argument.saved_root_hash {
-            cmd.arg(&to_hex_string(root_hash));
-        } else {
-            cmd.arg("none");
-        }
-    }
-
-    cmd.spawn().context("Spawn apkdmverity")
-}
-
 enum MountForExec {
     Allowed,
     Disallowed,
@@ -583,147 +525,6 @@
     Ok(())
 }
 
-// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
-// when the root_hash values from the idsig file and the instance disk are different. This function
-// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
-// saved to the instance disk.
-fn verify_payload(
-    metadata: &Metadata,
-    saved_data: Option<&MicrodroidData>,
-) -> Result<MicrodroidData> {
-    let start_time = SystemTime::now();
-
-    // Verify main APK
-    let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
-    let root_hash_trustful =
-        saved_data.map(|d| d.apk_data.root_hash_eq(root_hash_from_idsig.as_ref())).unwrap_or(false);
-
-    // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
-    // instead of the value read from the idsig file.
-    let main_apk_argument = {
-        ApkDmverityArgument {
-            apk: MAIN_APK_PATH,
-            idsig: MAIN_APK_IDSIG_PATH,
-            name: MAIN_APK_DEVICE_NAME,
-            saved_root_hash: if root_hash_trustful {
-                Some(root_hash_from_idsig.as_ref())
-            } else {
-                None
-            },
-        }
-    };
-    let mut apkdmverity_arguments = vec![main_apk_argument];
-
-    // Verify extra APKs
-    // For now, we can't read the payload config, so glob APKs and idsigs.
-    // Later, we'll see if it matches with the payload config.
-
-    // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
-    // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
-    let extra_apks =
-        sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
-    let extra_idsigs =
-        sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
-    ensure!(
-        extra_apks.len() == extra_idsigs.len(),
-        "Extra apks/idsigs mismatch: {} apks but {} idsigs",
-        extra_apks.len(),
-        extra_idsigs.len()
-    );
-
-    let extra_root_hashes_from_idsig: Vec<_> = extra_idsigs
-        .iter()
-        .map(|idsig| {
-            get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from extra idsig")
-        })
-        .collect();
-
-    let extra_root_hashes_trustful: Vec<_> = if let Some(data) = saved_data {
-        extra_root_hashes_from_idsig
-            .iter()
-            .enumerate()
-            .map(|(i, root_hash)| data.extra_apk_root_hash_eq(i, root_hash))
-            .collect()
-    } else {
-        vec![false; extra_root_hashes_from_idsig.len()]
-    };
-    let extra_apk_names: Vec<_> =
-        (0..extra_apks.len()).map(|i| format!("extra-apk-{}", i)).collect();
-
-    for (i, extra_apk) in extra_apks.iter().enumerate() {
-        apkdmverity_arguments.push({
-            ApkDmverityArgument {
-                apk: extra_apk.to_str().unwrap(),
-                idsig: extra_idsigs[i].to_str().unwrap(),
-                name: &extra_apk_names[i],
-                saved_root_hash: if extra_root_hashes_trustful[i] {
-                    Some(&extra_root_hashes_from_idsig[i])
-                } else {
-                    None
-                },
-            }
-        });
-    }
-
-    // Start apkdmverity and wait for the dm-verify block
-    let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
-
-    // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
-    // APEX payload.
-    let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
-
-    // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
-    // Skip writing it if the debug policy ignoring identity is on
-    if is_verified_boot() {
-        write_apex_payload_data(saved_data, &apex_data_from_payload)?;
-    }
-
-    // Start apexd to activate APEXes
-    system_properties::write("ctl.start", "apexd-vm")?;
-
-    // TODO(inseob): add timeout
-    apkdmverity_child.wait()?;
-
-    // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
-    // the APK file and therefore can be very slow if the APK is large. Note that this step is
-    // taken only when the root_hash is un-trustful which can be either when this is the first boot
-    // of the VM or APK was updated in the host.
-    // TODO(jooyung): consider multithreading to make this faster
-    let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
-    let extra_apks_data = extra_root_hashes_from_idsig
-        .into_iter()
-        .enumerate()
-        .map(|(i, extra_root_hash)| {
-            let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
-            let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
-            Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
-        })
-        .collect::<Result<Vec<_>>>()?;
-
-    info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
-
-    // Use the salt from a verified instance, or generate a salt for a new instance.
-    let salt = if let Some(saved_data) = saved_data {
-        saved_data.salt.clone()
-    } else if is_strict_boot() {
-        // No need to add more entropy as a previous stage must have used a new, random salt.
-        vec![0u8; 64]
-    } else {
-        let mut salt = vec![0u8; 64];
-        salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
-        salt
-    };
-
-    // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
-    // fully verifying the APK or by comparing it with the saved root_hash.
-    Ok(MicrodroidData {
-        salt,
-        apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
-        extra_apks_data,
-        apex_data: apex_data_from_payload,
-    })
-}
-
 fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
     // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
     for i in 0..config.extra_apks.len() {
@@ -771,39 +572,6 @@
     Ok(())
 }
 
-fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
-    Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
-}
-
-fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
-    let current_sdk = get_current_sdk()?;
-
-    let public_key_der = if !root_hash_trustful {
-        verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
-            "failed to verify {}",
-            apk
-        )))?
-    } else {
-        get_public_key_der(apk, current_sdk)?
-    };
-
-    match get_manifest_info(apk) {
-        Ok(manifest_info) => {
-            // TODO (b/299591171): Do something with this info
-            info!("Manifest info is {manifest_info:?}")
-        }
-        Err(e) => warn!("Failed to read manifest info from APK: {e:?}"),
-    };
-
-    Ok(public_key_der)
-}
-
-fn get_current_sdk() -> Result<u32> {
-    let current_sdk = system_properties::read("ro.build.version.sdk")?;
-    let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
-    current_sdk.parse().context("Malformed SDK version")
-}
-
 fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
     match payload_metadata {
         PayloadMetadata::ConfigPath(path) => {
@@ -843,7 +611,7 @@
         return Ok(());
     }
 
-    let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
+    let debuggable = is_debuggable()?;
     let ramdump = get_debug_policy_bool(AVF_DEBUG_POLICY_RAMDUMP)?.unwrap_or_default();
     let requested = debuggable | ramdump;
 
@@ -920,10 +688,6 @@
     Ok(path)
 }
 
-fn to_hex_string(buf: &[u8]) -> String {
-    buf.iter().map(|b| format!("{:02X}", b)).collect()
-}
-
 fn prepare_encryptedstore(vm_secret: &VmSecret) -> Result<Child> {
     let mut key = ZVec::new(ENCRYPTEDSTORE_KEYSIZE)?;
     vm_secret.derive_encryptedstore_key(&mut key)?;
diff --git a/microdroid_manager/src/verify.rs b/microdroid_manager/src/verify.rs
new file mode 100644
index 0000000..06b15f7
--- /dev/null
+++ b/microdroid_manager/src/verify.rs
@@ -0,0 +1,236 @@
+// Copyright 2023 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.
+
+use crate::instance::{ApkData, MicrodroidData, RootHash};
+use crate::payload::get_apex_data_from_payload;
+use crate::{is_strict_boot, is_verified_boot, write_apex_payload_data, MicrodroidError};
+use anyhow::{anyhow, ensure, Context, Result};
+use apkmanifest::get_manifest_info;
+use apkverify::{get_public_key_der, verify, V4Signature};
+use glob::glob;
+use itertools::sorted;
+use log::{info, warn};
+use microdroid_metadata::Metadata;
+use rand::Fill;
+use rustutils::system_properties;
+use std::path::Path;
+use std::process::{Child, Command};
+use std::str;
+use std::time::SystemTime;
+
+pub const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
+
+const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
+const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
+const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
+const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
+const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
+
+const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
+
+/// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
+/// when the root_hash values from the idsig file and the instance disk are different. This function
+/// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
+/// saved to the instance disk.
+pub fn verify_payload(
+    metadata: &Metadata,
+    saved_data: Option<&MicrodroidData>,
+) -> Result<MicrodroidData> {
+    let start_time = SystemTime::now();
+
+    // Verify main APK
+    let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
+    let root_hash_trustful =
+        saved_data.map(|d| d.apk_data.root_hash_eq(root_hash_from_idsig.as_ref())).unwrap_or(false);
+
+    // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
+    // instead of the value read from the idsig file.
+    let main_apk_argument = {
+        ApkDmverityArgument {
+            apk: MAIN_APK_PATH,
+            idsig: MAIN_APK_IDSIG_PATH,
+            name: MAIN_APK_DEVICE_NAME,
+            saved_root_hash: if root_hash_trustful {
+                Some(root_hash_from_idsig.as_ref())
+            } else {
+                None
+            },
+        }
+    };
+    let mut apkdmverity_arguments = vec![main_apk_argument];
+
+    // Verify extra APKs
+    // For now, we can't read the payload config, so glob APKs and idsigs.
+    // Later, we'll see if it matches with the payload config.
+
+    // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
+    // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
+    let extra_apks =
+        sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
+    let extra_idsigs =
+        sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
+    ensure!(
+        extra_apks.len() == extra_idsigs.len(),
+        "Extra apks/idsigs mismatch: {} apks but {} idsigs",
+        extra_apks.len(),
+        extra_idsigs.len()
+    );
+
+    let extra_root_hashes_from_idsig: Vec<_> = extra_idsigs
+        .iter()
+        .map(|idsig| {
+            get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from extra idsig")
+        })
+        .collect();
+
+    let extra_root_hashes_trustful: Vec<_> = if let Some(data) = saved_data {
+        extra_root_hashes_from_idsig
+            .iter()
+            .enumerate()
+            .map(|(i, root_hash)| data.extra_apk_root_hash_eq(i, root_hash))
+            .collect()
+    } else {
+        vec![false; extra_root_hashes_from_idsig.len()]
+    };
+    let extra_apk_names: Vec<_> =
+        (0..extra_apks.len()).map(|i| format!("extra-apk-{}", i)).collect();
+
+    for (i, extra_apk) in extra_apks.iter().enumerate() {
+        apkdmverity_arguments.push({
+            ApkDmverityArgument {
+                apk: extra_apk.to_str().unwrap(),
+                idsig: extra_idsigs[i].to_str().unwrap(),
+                name: &extra_apk_names[i],
+                saved_root_hash: if extra_root_hashes_trustful[i] {
+                    Some(&extra_root_hashes_from_idsig[i])
+                } else {
+                    None
+                },
+            }
+        });
+    }
+
+    // Start apkdmverity and wait for the dm-verify block
+    let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
+
+    // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
+    // APEX payload.
+    let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
+
+    // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
+    // Skip writing it if the debug policy ignoring identity is on
+    if is_verified_boot() {
+        write_apex_payload_data(saved_data, &apex_data_from_payload)?;
+    }
+
+    // Start apexd to activate APEXes
+    system_properties::write("ctl.start", "apexd-vm")?;
+
+    // TODO(inseob): add timeout
+    apkdmverity_child.wait()?;
+
+    // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
+    // the APK file and therefore can be very slow if the APK is large. Note that this step is
+    // taken only when the root_hash is un-trustful which can be either when this is the first boot
+    // of the VM or APK was updated in the host.
+    // TODO(jooyung): consider multithreading to make this faster
+    let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
+    let extra_apks_data = extra_root_hashes_from_idsig
+        .into_iter()
+        .enumerate()
+        .map(|(i, extra_root_hash)| {
+            let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
+            let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
+            Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
+        })
+        .collect::<Result<Vec<_>>>()?;
+
+    info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
+
+    // Use the salt from a verified instance, or generate a salt for a new instance.
+    let salt = if let Some(saved_data) = saved_data {
+        saved_data.salt.clone()
+    } else if is_strict_boot() {
+        // No need to add more entropy as a previous stage must have used a new, random salt.
+        vec![0u8; 64]
+    } else {
+        let mut salt = vec![0u8; 64];
+        salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
+        salt
+    };
+
+    // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
+    // fully verifying the APK or by comparing it with the saved root_hash.
+    Ok(MicrodroidData {
+        salt,
+        apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
+        extra_apks_data,
+        apex_data: apex_data_from_payload,
+    })
+}
+
+fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
+    Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
+}
+
+fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
+    let current_sdk = get_current_sdk()?;
+
+    let public_key_der = if !root_hash_trustful {
+        verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
+            "failed to verify {}",
+            apk
+        )))?
+    } else {
+        get_public_key_der(apk, current_sdk)?
+    };
+
+    match get_manifest_info(apk) {
+        Ok(manifest_info) => {
+            // TODO (b/299591171): Do something with this info
+            info!("Manifest info is {manifest_info:?}")
+        }
+        Err(e) => warn!("Failed to read manifest info from APK: {e:?}"),
+    };
+
+    Ok(public_key_der)
+}
+
+fn get_current_sdk() -> Result<u32> {
+    let current_sdk = system_properties::read("ro.build.version.sdk")?;
+    let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
+    current_sdk.parse().context("Malformed SDK version")
+}
+
+struct ApkDmverityArgument<'a> {
+    apk: &'a str,
+    idsig: &'a str,
+    name: &'a str,
+    saved_root_hash: Option<&'a RootHash>,
+}
+
+fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
+    let mut cmd = Command::new(APKDMVERITY_BIN);
+
+    for argument in args {
+        cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
+        if let Some(root_hash) = argument.saved_root_hash {
+            cmd.arg(&hex::encode(root_hash));
+        } else {
+            cmd.arg("none");
+        }
+    }
+
+    cmd.spawn().context("Spawn apkdmverity")
+}