Actually derive microdroid vendor dice node
The derive_microdroid_vendor_dice_node binary gets the current dice
chain from the /dev/open-dice0 driver, derives the new dice chain with
the microdroid vendor node and writes it to the
/microdroid_resources/dice_chain.raw file.
The microdroid_manager will read the dice chain from
/microdroid_resources/dice_chain.raw and derive the final dice chain
with the payload node. After the derivation is done, microdroid_manager
will delete the /microdroid_resources/dice_chain.raw file.
Additionally, since /microdroid_resources is mounted in first_stage_init
which happens before selinux is configured, we also call the
restorecon_recursive /microdroid_resources before starting
microdroid_manager to make sure that the /microdroid_resources and
/microdroid_resources/dice_chain.raw have correct context.
Bug: 287593065
Test: run microdroid with vendor partition
Test: atest MicrodroidTests
Change-Id: Ibeb05b0ed24610624b11ac2c3e907cc900bd4cab
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 169ecae..33d98dc 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -55,6 +55,7 @@
properties: [
"deps",
"dirs",
+ "multilib",
],
}
@@ -157,9 +158,11 @@
// Below are dependencies that are conditionally enabled depending on value of build flags.
soong_config_variables: {
release_avf_enable_dice_changes: {
- deps: [
- "derive_microdroid_vendor_dice_node",
- ],
+ multilib: {
+ lib64: {
+ deps: ["derive_microdroid_vendor_dice_node"],
+ },
+ },
dirs: [
"microdroid_resources",
],
diff --git a/microdroid/derive_microdroid_vendor_dice_node/Android.bp b/microdroid/derive_microdroid_vendor_dice_node/Android.bp
index de1bef7..8b79aad 100644
--- a/microdroid/derive_microdroid_vendor_dice_node/Android.bp
+++ b/microdroid/derive_microdroid_vendor_dice_node/Android.bp
@@ -10,9 +10,20 @@
rustlibs: [
"libanyhow",
"libclap",
+ "libcstr",
+ "libdice_driver",
+ "libdiced_open_dice",
+ "libdm_rust",
+ "libserde_cbor",
+ "libvbmeta_rust",
],
bootstrap: true,
prefer_rlib: true,
+ multilib: {
+ lib32: {
+ enabled: false,
+ },
+ },
}
rust_binary {
diff --git a/microdroid/derive_microdroid_vendor_dice_node/src/main.rs b/microdroid/derive_microdroid_vendor_dice_node/src/main.rs
index 1d5db0d..c7bc3f5 100644
--- a/microdroid/derive_microdroid_vendor_dice_node/src/main.rs
+++ b/microdroid/derive_microdroid_vendor_dice_node/src/main.rs
@@ -14,9 +14,19 @@
//! Derives microdroid vendor dice node.
-use anyhow::Error;
+use anyhow::{bail, Context, Result};
use clap::Parser;
-use std::path::PathBuf;
+use cstr::cstr;
+use dice_driver::DiceDriver;
+use diced_open_dice::{
+ hash, retry_bcc_format_config_descriptor, DiceConfigValues, OwnedDiceArtifacts, HIDDEN_SIZE,
+};
+use dm::util::blkgetsize64;
+use std::fs::{read_link, File};
+use std::path::{Path, PathBuf};
+use vbmeta::VbMetaImage;
+
+const AVF_STRICT_BOOT: &str = "/proc/device-tree/chosen/avf,strict-boot";
#[derive(Parser)]
struct Args {
@@ -31,8 +41,74 @@
output: PathBuf,
}
-fn main() -> Result<(), Error> {
+// TODO(ioffe): move to a library to reuse same code here, in microdroid_manager and in
+// first_stage_init.
+fn is_strict_boot() -> bool {
+ Path::new(AVF_STRICT_BOOT).exists()
+}
+
+fn build_descriptor(vbmeta: &VbMetaImage) -> Result<Vec<u8>> {
+ let values = DiceConfigValues {
+ component_name: Some(cstr!("Microdroid vendor")),
+ security_version: Some(vbmeta.rollback_index()),
+ ..Default::default()
+ };
+ Ok(retry_bcc_format_config_descriptor(&values)?)
+}
+
+// TODO(ioffe): move to libvbmeta.
+fn find_root_digest(vbmeta: &VbMetaImage) -> Result<Option<Vec<u8>>> {
+ for descriptor in vbmeta.descriptors()?.iter() {
+ if let vbmeta::Descriptor::Hashtree(_) = descriptor {
+ return Ok(Some(descriptor.to_hashtree()?.root_digest().to_vec()));
+ }
+ }
+ Ok(None)
+}
+
+fn dice_derivation(dice: DiceDriver, vbmeta: &VbMetaImage) -> Result<OwnedDiceArtifacts> {
+ let authority_hash = if let Some(pubkey) = vbmeta.public_key() {
+ hash(pubkey).context("hash pubkey")?
+ } else {
+ bail!("no public key")
+ };
+ let code_hash = if let Some(root_digest) = find_root_digest(vbmeta)? {
+ hash(root_digest.as_ref()).context("hash root_digest")?
+ } else {
+ bail!("no hashtree")
+ };
+ let desc = build_descriptor(vbmeta).context("build descriptor")?;
+ let hidden = [0; HIDDEN_SIZE];
+ // The microdroid vendor partition doesn't contribute to the debuggability of the VM, and it is
+ // a bit tricky to propagate the info on whether the VM is debuggable to
+ // derive_microdroid_dice_node binary. Given these, we just always set `is_debuggable: false`
+ // for the "Microdroid vendor" dice node. The adjacent dice nodes (pvmfw & payload) provide the
+ // accurate information on whether VM is debuggable.
+ dice.derive(code_hash, &desc, authority_hash, /* debug= */ false, hidden)
+}
+
+fn extract_vbmeta(block_dev: &Path) -> Result<VbMetaImage> {
+ let size = blkgetsize64(block_dev).context("blkgetsize64 failed")?;
+ let file = File::open(block_dev).context("open failed")?;
+ let vbmeta = VbMetaImage::verify_reader_region(file, 0, size)?;
+ Ok(vbmeta)
+}
+
+fn try_main() -> Result<()> {
let args = Args::parse();
- eprintln!("{:?} {:?} {:?}", args.dice_driver, args.microdroid_vendor_disk_image, args.output);
+ let dice =
+ DiceDriver::new(&args.dice_driver, is_strict_boot()).context("Failed to load DICE")?;
+ let path = read_link(args.microdroid_vendor_disk_image).context("failed to read symlink")?;
+ let vbmeta = extract_vbmeta(&path).context("failed to extract vbmeta")?;
+ let dice_artifacts = dice_derivation(dice, &vbmeta).context("failed to derive dice chain")?;
+ let file = File::create(&args.output).context("failed to create output")?;
+ serde_cbor::to_writer(file, &dice_artifacts).context("failed to write dice artifacts")?;
Ok(())
}
+
+fn main() {
+ if let Err(e) = try_main() {
+ eprintln!("failed with {:?}", e);
+ std::process::exit(1);
+ }
+}