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