1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Derives microdroid vendor dice node.
16
17 use anyhow::{bail, Context, Result};
18 use clap::Parser;
19 use cstr::cstr;
20 use dice_driver::DiceDriver;
21 use diced_open_dice::{
22 hash, retry_bcc_format_config_descriptor, DiceConfigValues, OwnedDiceArtifacts, HIDDEN_SIZE,
23 };
24 use dm::util::blkgetsize64;
25 use std::fs::{read_link, File};
26 use std::path::{Path, PathBuf};
27 use vbmeta::VbMetaImage;
28
29 const AVF_STRICT_BOOT: &str = "/proc/device-tree/chosen/avf,strict-boot";
30
31 #[derive(Parser)]
32 struct Args {
33 /// Path to the dice driver (e.g. /dev/open-dice0)
34 #[arg(long)]
35 dice_driver: PathBuf,
36 /// Path to the microdroid-vendor.img disk image.
37 #[arg(long)]
38 microdroid_vendor_disk_image: PathBuf,
39 /// File to save resulting dice chain to.
40 #[arg(long)]
41 output: PathBuf,
42 }
43
44 // TODO(ioffe): move to a library to reuse same code here, in microdroid_manager and in
45 // first_stage_init.
is_strict_boot() -> bool46 fn is_strict_boot() -> bool {
47 Path::new(AVF_STRICT_BOOT).exists()
48 }
49
50 // See dice_for_avf_guest.cddl for CDDL of Configuration Descriptor of VM components.
build_descriptor(vbmeta: &VbMetaImage) -> Result<Vec<u8>>51 fn build_descriptor(vbmeta: &VbMetaImage) -> Result<Vec<u8>> {
52 let values = DiceConfigValues {
53 component_name: Some(cstr!("Microdroid vendor")),
54 security_version: Some(vbmeta.rollback_index()),
55 ..Default::default()
56 };
57 Ok(retry_bcc_format_config_descriptor(&values)?)
58 }
59
60 // TODO(ioffe): move to libvbmeta.
find_root_digest(vbmeta: &VbMetaImage) -> Result<Option<Vec<u8>>>61 fn find_root_digest(vbmeta: &VbMetaImage) -> Result<Option<Vec<u8>>> {
62 for descriptor in vbmeta.descriptors()?.iter() {
63 if let vbmeta::Descriptor::Hashtree(_) = descriptor {
64 return Ok(Some(descriptor.to_hashtree()?.root_digest().to_vec()));
65 }
66 }
67 Ok(None)
68 }
69
dice_derivation(dice: DiceDriver, vbmeta: &VbMetaImage) -> Result<OwnedDiceArtifacts>70 fn dice_derivation(dice: DiceDriver, vbmeta: &VbMetaImage) -> Result<OwnedDiceArtifacts> {
71 let authority_hash = if let Some(pubkey) = vbmeta.public_key() {
72 hash(pubkey).context("hash pubkey")?
73 } else {
74 bail!("no public key")
75 };
76 let code_hash = if let Some(root_digest) = find_root_digest(vbmeta)? {
77 hash(root_digest.as_ref()).context("hash root_digest")?
78 } else {
79 bail!("no hashtree")
80 };
81 let desc = build_descriptor(vbmeta).context("build descriptor")?;
82 let hidden = [0; HIDDEN_SIZE];
83 // The microdroid vendor partition doesn't contribute to the debuggability of the VM, and it is
84 // a bit tricky to propagate the info on whether the VM is debuggable to
85 // derive_microdroid_dice_node binary. Given these, we just always set `is_debuggable: false`
86 // for the "Microdroid vendor" dice node. The adjacent dice nodes (pvmfw & payload) provide the
87 // accurate information on whether VM is debuggable.
88 dice.derive(code_hash, &desc, authority_hash, /* debug= */ false, hidden)
89 }
90
extract_vbmeta(block_dev: &Path) -> Result<VbMetaImage>91 fn extract_vbmeta(block_dev: &Path) -> Result<VbMetaImage> {
92 let size = blkgetsize64(block_dev).context("blkgetsize64 failed")?;
93 let file = File::open(block_dev).context("open failed")?;
94 let vbmeta = VbMetaImage::verify_reader_region(file, 0, size)?;
95 Ok(vbmeta)
96 }
97
try_main() -> Result<()>98 fn try_main() -> Result<()> {
99 let args = Args::parse();
100 let dice =
101 DiceDriver::new(&args.dice_driver, is_strict_boot()).context("Failed to load DICE")?;
102 let path = read_link(args.microdroid_vendor_disk_image).context("failed to read symlink")?;
103 let vbmeta = extract_vbmeta(&path).context("failed to extract vbmeta")?;
104 let dice_artifacts = dice_derivation(dice, &vbmeta).context("failed to derive dice chain")?;
105 let file = File::create(&args.output).context("failed to create output")?;
106 serde_cbor::to_writer(file, &dice_artifacts).context("failed to write dice artifacts")?;
107 Ok(())
108 }
109
main()110 fn main() {
111 if let Err(e) = try_main() {
112 eprintln!("failed with {:?}", e);
113 std::process::exit(1);
114 }
115 }
116