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