blob: fc18225f7c56ecb15017aaac547c83227d7a1763 [file] [log] [blame]
Alice Wange089a212022-07-18 11:24:03 +00001// Copyright 2022, 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//! Tool for handling AVMD blobs.
16
17use anyhow::{anyhow, bail, Result};
18use apexutil::get_payload_vbmeta_image_hash;
Alice Wang0cafa142022-09-23 15:17:02 +000019use apkverify::get_apk_digest;
Alice Wange089a212022-07-18 11:24:03 +000020use avmd::{ApkDescriptor, Avmd, Descriptor, ResourceIdentifier, VbMetaDescriptor};
21use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
22use serde::ser::Serialize;
23use std::fs::File;
24use vbmeta::VbMetaImage;
25
26fn get_vbmeta_image_hash(file: &str) -> Result<Vec<u8>> {
27 let img = VbMetaImage::verify_path(file)?;
28 Ok(img.hash().ok_or_else(|| anyhow!("No hash as VBMeta image isn't signed"))?.to_vec())
29}
30
31/// Iterate over a set of argument values, that could be empty or come in
32/// (<index>, <namespace>, <name>, <file>) tuple.
33struct NamespaceNameFileIterator<'a> {
34 indices: Option<clap::Indices<'a>>,
35 values: Option<clap::Values<'a>>,
36}
37
38impl<'a> NamespaceNameFileIterator<'a> {
39 fn new(args: &'a ArgMatches, name: &'a str) -> Self {
40 NamespaceNameFileIterator { indices: args.indices_of(name), values: args.values_of(name) }
41 }
42}
43
44impl<'a> Iterator for NamespaceNameFileIterator<'a> {
45 type Item = (usize, &'a str, &'a str, &'a str);
46
47 fn next(&mut self) -> Option<Self::Item> {
48 match (self.indices.as_mut(), self.values.as_mut()) {
49 (Some(indices), Some(values)) => {
50 match (indices.nth(2), values.next(), values.next(), values.next()) {
51 (Some(index), Some(namespace), Some(name), Some(file)) => {
52 Some((index, namespace, name, file))
53 }
54 _ => None,
55 }
56 }
57 _ => None,
58 }
59 }
60}
61
62fn create(args: &ArgMatches) -> Result<()> {
63 // Store descriptors in the order they were given in the arguments
64 // TODO: instead, group them by namespace?
65 let mut descriptors = std::collections::BTreeMap::new();
66 for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "vbmeta") {
67 descriptors.insert(
68 i,
69 Descriptor::VbMeta(VbMetaDescriptor {
70 resource: ResourceIdentifier::new(namespace, name),
71 vbmeta_digest: get_vbmeta_image_hash(file)?,
72 }),
73 );
74 }
75 for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "apk") {
76 let file = File::open(file)?;
Alice Wang9df25eb2022-09-26 08:35:16 +000077 let (signature_algorithm_id, apk_digest) = get_apk_digest(file, /*verify=*/ true)?;
Alice Wange089a212022-07-18 11:24:03 +000078 descriptors.insert(
79 i,
80 Descriptor::Apk(ApkDescriptor {
81 resource: ResourceIdentifier::new(namespace, name),
82 signature_algorithm_id,
83 apk_digest: apk_digest.to_vec(),
84 }),
85 );
86 }
87 for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "apex-payload") {
88 descriptors.insert(
89 i,
90 Descriptor::VbMeta(VbMetaDescriptor {
91 resource: ResourceIdentifier::new(namespace, name),
92 vbmeta_digest: get_payload_vbmeta_image_hash(file)?,
93 }),
94 );
95 }
96 let avmd = Avmd::new(descriptors.into_values().collect());
97 let mut bytes = Vec::new();
98 avmd.serialize(
99 &mut serde_cbor::Serializer::new(&mut serde_cbor::ser::IoWrite::new(&mut bytes))
100 .packed_format()
101 .legacy_enums(),
102 )?;
103 std::fs::write(args.value_of("file").unwrap(), &bytes)?;
104 Ok(())
105}
106
107fn dump(args: &ArgMatches) -> Result<()> {
108 let file = std::fs::read(args.value_of("file").unwrap())?;
109 let avmd: Avmd = serde_cbor::from_slice(&file)?;
110 println!("{}", avmd);
111 Ok(())
112}
113
114fn main() -> Result<()> {
115 let namespace_name_file = ["namespace", "name", "file"];
116 let app = App::new("avmdtool")
117 .setting(AppSettings::SubcommandRequiredElseHelp)
118 .subcommand(
119 SubCommand::with_name("create")
120 .setting(AppSettings::ArgRequiredElseHelp)
121 .arg(Arg::with_name("file").required(true).takes_value(true))
122 .arg(
123 Arg::with_name("vbmeta")
124 .long("vbmeta")
125 .takes_value(true)
126 .value_names(&namespace_name_file)
127 .multiple(true),
128 )
129 .arg(
130 Arg::with_name("apk")
131 .long("apk")
132 .takes_value(true)
133 .value_names(&namespace_name_file)
134 .multiple(true),
135 )
136 .arg(
137 Arg::with_name("apex-payload")
138 .long("apex-payload")
139 .takes_value(true)
140 .value_names(&namespace_name_file)
141 .multiple(true),
142 ),
143 )
144 .subcommand(
145 SubCommand::with_name("dump")
146 .setting(AppSettings::ArgRequiredElseHelp)
147 .arg(Arg::with_name("file").required(true).takes_value(true)),
148 );
149
150 let args = app.get_matches();
151 match args.subcommand() {
Jeff Vander Stoepa8dc2712022-07-29 02:33:45 +0200152 Some(("create", sub_args)) => create(sub_args)?,
153 Some(("dump", sub_args)) => dump(sub_args)?,
Alice Wange089a212022-07-18 11:24:03 +0000154 _ => bail!("Invalid arguments"),
155 }
156 Ok(())
157}