blob: 8d7cb57d12fb9951e397d5e6ddc16e827f8a3f58 [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};
Andrew Walbranaa1efc42022-08-10 13:33:57 +000021use clap::{
22 builder::ValueParser,
23 parser::{Indices, ValuesRef},
24 Arg, ArgAction, ArgMatches, Command,
25};
Alice Wange089a212022-07-18 11:24:03 +000026use serde::ser::Serialize;
Andrew Walbranaa1efc42022-08-10 13:33:57 +000027use std::{fs::File, path::PathBuf};
Alice Wange089a212022-07-18 11:24:03 +000028use vbmeta::VbMetaImage;
29
30fn get_vbmeta_image_hash(file: &str) -> Result<Vec<u8>> {
31 let img = VbMetaImage::verify_path(file)?;
32 Ok(img.hash().ok_or_else(|| anyhow!("No hash as VBMeta image isn't signed"))?.to_vec())
33}
34
35/// Iterate over a set of argument values, that could be empty or come in
36/// (<index>, <namespace>, <name>, <file>) tuple.
37struct NamespaceNameFileIterator<'a> {
Andrew Walbranaa1efc42022-08-10 13:33:57 +000038 indices: Option<Indices<'a>>,
39 values: Option<ValuesRef<'a, String>>,
Alice Wange089a212022-07-18 11:24:03 +000040}
41
42impl<'a> NamespaceNameFileIterator<'a> {
43 fn new(args: &'a ArgMatches, name: &'a str) -> Self {
Andrew Walbranaa1efc42022-08-10 13:33:57 +000044 NamespaceNameFileIterator { indices: args.indices_of(name), values: args.get_many(name) }
Alice Wange089a212022-07-18 11:24:03 +000045 }
46}
47
48impl<'a> Iterator for NamespaceNameFileIterator<'a> {
49 type Item = (usize, &'a str, &'a str, &'a str);
50
51 fn next(&mut self) -> Option<Self::Item> {
52 match (self.indices.as_mut(), self.values.as_mut()) {
53 (Some(indices), Some(values)) => {
54 match (indices.nth(2), values.next(), values.next(), values.next()) {
55 (Some(index), Some(namespace), Some(name), Some(file)) => {
56 Some((index, namespace, name, file))
57 }
58 _ => None,
59 }
60 }
61 _ => None,
62 }
63 }
64}
65
66fn create(args: &ArgMatches) -> Result<()> {
67 // Store descriptors in the order they were given in the arguments
68 // TODO: instead, group them by namespace?
69 let mut descriptors = std::collections::BTreeMap::new();
70 for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "vbmeta") {
71 descriptors.insert(
72 i,
73 Descriptor::VbMeta(VbMetaDescriptor {
74 resource: ResourceIdentifier::new(namespace, name),
75 vbmeta_digest: get_vbmeta_image_hash(file)?,
76 }),
77 );
78 }
79 for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "apk") {
80 let file = File::open(file)?;
Alice Wang9df25eb2022-09-26 08:35:16 +000081 let (signature_algorithm_id, apk_digest) = get_apk_digest(file, /*verify=*/ true)?;
Alice Wange089a212022-07-18 11:24:03 +000082 descriptors.insert(
83 i,
84 Descriptor::Apk(ApkDescriptor {
85 resource: ResourceIdentifier::new(namespace, name),
86 signature_algorithm_id,
87 apk_digest: apk_digest.to_vec(),
88 }),
89 );
90 }
91 for (i, namespace, name, file) in NamespaceNameFileIterator::new(args, "apex-payload") {
92 descriptors.insert(
93 i,
94 Descriptor::VbMeta(VbMetaDescriptor {
95 resource: ResourceIdentifier::new(namespace, name),
96 vbmeta_digest: get_payload_vbmeta_image_hash(file)?,
97 }),
98 );
99 }
100 let avmd = Avmd::new(descriptors.into_values().collect());
101 let mut bytes = Vec::new();
102 avmd.serialize(
103 &mut serde_cbor::Serializer::new(&mut serde_cbor::ser::IoWrite::new(&mut bytes))
104 .packed_format()
105 .legacy_enums(),
106 )?;
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000107 std::fs::write(args.get_one::<PathBuf>("file").unwrap(), &bytes)?;
Alice Wange089a212022-07-18 11:24:03 +0000108 Ok(())
109}
110
111fn dump(args: &ArgMatches) -> Result<()> {
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000112 let file = std::fs::read(args.get_one::<PathBuf>("file").unwrap())?;
Alice Wange089a212022-07-18 11:24:03 +0000113 let avmd: Avmd = serde_cbor::from_slice(&file)?;
114 println!("{}", avmd);
115 Ok(())
116}
117
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000118fn clap_command() -> Command {
Alice Wange089a212022-07-18 11:24:03 +0000119 let namespace_name_file = ["namespace", "name", "file"];
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000120
121 Command::new("avmdtool")
122 .subcommand_required(true)
123 .arg_required_else_help(true)
Alice Wange089a212022-07-18 11:24:03 +0000124 .subcommand(
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000125 Command::new("create")
126 .arg_required_else_help(true)
127 .arg(Arg::new("file").value_parser(ValueParser::path_buf()).required(true))
Alice Wange089a212022-07-18 11:24:03 +0000128 .arg(
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000129 Arg::new("vbmeta")
Alice Wange089a212022-07-18 11:24:03 +0000130 .long("vbmeta")
Chris Wailes75269622022-12-05 23:01:44 -0800131 .value_names(namespace_name_file)
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000132 .num_args(3)
133 .action(ArgAction::Append),
Alice Wange089a212022-07-18 11:24:03 +0000134 )
135 .arg(
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000136 Arg::new("apk")
Alice Wange089a212022-07-18 11:24:03 +0000137 .long("apk")
Chris Wailes75269622022-12-05 23:01:44 -0800138 .value_names(namespace_name_file)
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000139 .num_args(3)
140 .action(ArgAction::Append),
Alice Wange089a212022-07-18 11:24:03 +0000141 )
142 .arg(
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000143 Arg::new("apex-payload")
Alice Wange089a212022-07-18 11:24:03 +0000144 .long("apex-payload")
Chris Wailes75269622022-12-05 23:01:44 -0800145 .value_names(namespace_name_file)
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000146 .num_args(3)
147 .action(ArgAction::Append),
Alice Wange089a212022-07-18 11:24:03 +0000148 ),
149 )
150 .subcommand(
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000151 Command::new("dump")
152 .arg_required_else_help(true)
153 .arg(Arg::new("file").value_parser(ValueParser::path_buf()).required(true)),
154 )
155}
Alice Wange089a212022-07-18 11:24:03 +0000156
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000157fn main() -> Result<()> {
158 let args = clap_command().get_matches();
Alice Wange089a212022-07-18 11:24:03 +0000159 match args.subcommand() {
Jeff Vander Stoepa8dc2712022-07-29 02:33:45 +0200160 Some(("create", sub_args)) => create(sub_args)?,
161 Some(("dump", sub_args)) => dump(sub_args)?,
Alice Wange089a212022-07-18 11:24:03 +0000162 _ => bail!("Invalid arguments"),
163 }
164 Ok(())
165}
Andrew Walbranaa1efc42022-08-10 13:33:57 +0000166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn verify_command() {
173 // Check that the command parsing has been configured in a valid way.
174 clap_command().debug_assert();
175 }
176}