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 
build_descriptor(vbmeta: &VbMetaImage) -> Result<Vec<u8>>50 fn build_descriptor(vbmeta: &VbMetaImage) -> Result<Vec<u8>> {
51     let values = DiceConfigValues {
52         component_name: Some(cstr!("Microdroid vendor")),
53         security_version: Some(vbmeta.rollback_index()),
54         ..Default::default()
55     };
56     Ok(retry_bcc_format_config_descriptor(&values)?)
57 }
58 
59 // TODO(ioffe): move to libvbmeta.
find_root_digest(vbmeta: &VbMetaImage) -> Result<Option<Vec<u8>>>60 fn find_root_digest(vbmeta: &VbMetaImage) -> Result<Option<Vec<u8>>> {
61     for descriptor in vbmeta.descriptors()?.iter() {
62         if let vbmeta::Descriptor::Hashtree(_) = descriptor {
63             return Ok(Some(descriptor.to_hashtree()?.root_digest().to_vec()));
64         }
65     }
66     Ok(None)
67 }
68 
dice_derivation(dice: DiceDriver, vbmeta: &VbMetaImage) -> Result<OwnedDiceArtifacts>69 fn dice_derivation(dice: DiceDriver, vbmeta: &VbMetaImage) -> Result<OwnedDiceArtifacts> {
70     let authority_hash = if let Some(pubkey) = vbmeta.public_key() {
71         hash(pubkey).context("hash pubkey")?
72     } else {
73         bail!("no public key")
74     };
75     let code_hash = if let Some(root_digest) = find_root_digest(vbmeta)? {
76         hash(root_digest.as_ref()).context("hash root_digest")?
77     } else {
78         bail!("no hashtree")
79     };
80     let desc = build_descriptor(vbmeta).context("build descriptor")?;
81     let hidden = [0; HIDDEN_SIZE];
82     // The microdroid vendor partition doesn't contribute to the debuggability of the VM, and it is
83     // a bit tricky to propagate the info on whether the VM is debuggable to
84     // derive_microdroid_dice_node binary. Given these, we just always set `is_debuggable: false`
85     // for the "Microdroid vendor" dice node. The adjacent dice nodes (pvmfw & payload) provide the
86     // accurate information on whether VM is debuggable.
87     dice.derive(code_hash, &desc, authority_hash, /* debug= */ false, hidden)
88 }
89 
extract_vbmeta(block_dev: &Path) -> Result<VbMetaImage>90 fn extract_vbmeta(block_dev: &Path) -> Result<VbMetaImage> {
91     let size = blkgetsize64(block_dev).context("blkgetsize64  failed")?;
92     let file = File::open(block_dev).context("open failed")?;
93     let vbmeta = VbMetaImage::verify_reader_region(file, 0, size)?;
94     Ok(vbmeta)
95 }
96 
try_main() -> Result<()>97 fn try_main() -> Result<()> {
98     let args = Args::parse();
99     let dice =
100         DiceDriver::new(&args.dice_driver, is_strict_boot()).context("Failed to load DICE")?;
101     let path = read_link(args.microdroid_vendor_disk_image).context("failed to read symlink")?;
102     let vbmeta = extract_vbmeta(&path).context("failed to extract vbmeta")?;
103     let dice_artifacts = dice_derivation(dice, &vbmeta).context("failed to derive dice chain")?;
104     let file = File::create(&args.output).context("failed to create output")?;
105     serde_cbor::to_writer(file, &dice_artifacts).context("failed to write dice artifacts")?;
106     Ok(())
107 }
108 
main()109 fn main() {
110     if let Err(e) = try_main() {
111         eprintln!("failed with {:?}", e);
112         std::process::exit(1);
113     }
114 }
115