1*d9ecfb0fSAndroid Build Coastguard Worker //! A tool for handling data related to the hardware root-of-trust.
2*d9ecfb0fSAndroid Build Coastguard Worker
3*d9ecfb0fSAndroid Build Coastguard Worker use anyhow::{bail, Result};
4*d9ecfb0fSAndroid Build Coastguard Worker use clap::{Parser, Subcommand, ValueEnum};
5*d9ecfb0fSAndroid Build Coastguard Worker use hwtrust::dice;
6*d9ecfb0fSAndroid Build Coastguard Worker use hwtrust::dice::ChainForm;
7*d9ecfb0fSAndroid Build Coastguard Worker use hwtrust::rkp;
8*d9ecfb0fSAndroid Build Coastguard Worker use hwtrust::session::{Options, RkpInstance, Session};
9*d9ecfb0fSAndroid Build Coastguard Worker use std::io::BufRead;
10*d9ecfb0fSAndroid Build Coastguard Worker use std::{fs, io};
11*d9ecfb0fSAndroid Build Coastguard Worker
12*d9ecfb0fSAndroid Build Coastguard Worker #[derive(Parser)]
13*d9ecfb0fSAndroid Build Coastguard Worker /// A tool for handling data related to the hardware root-of-trust
14*d9ecfb0fSAndroid Build Coastguard Worker #[clap(name = "hwtrust")]
15*d9ecfb0fSAndroid Build Coastguard Worker struct Args {
16*d9ecfb0fSAndroid Build Coastguard Worker #[clap(subcommand)]
17*d9ecfb0fSAndroid Build Coastguard Worker action: Action,
18*d9ecfb0fSAndroid Build Coastguard Worker
19*d9ecfb0fSAndroid Build Coastguard Worker /// Verbose output, including parsed data structures.
20*d9ecfb0fSAndroid Build Coastguard Worker #[clap(long)]
21*d9ecfb0fSAndroid Build Coastguard Worker verbose: bool,
22*d9ecfb0fSAndroid Build Coastguard Worker
23*d9ecfb0fSAndroid Build Coastguard Worker /// The VSR version to validate against. If omitted, the set of rules that are used have no
24*d9ecfb0fSAndroid Build Coastguard Worker /// compromises or workarounds and new implementations should validate against them as it will
25*d9ecfb0fSAndroid Build Coastguard Worker /// be the basis for future VSR versions.
26*d9ecfb0fSAndroid Build Coastguard Worker #[clap(long, value_enum)]
27*d9ecfb0fSAndroid Build Coastguard Worker vsr: Option<VsrVersion>,
28*d9ecfb0fSAndroid Build Coastguard Worker }
29*d9ecfb0fSAndroid Build Coastguard Worker
30*d9ecfb0fSAndroid Build Coastguard Worker #[derive(Subcommand)]
31*d9ecfb0fSAndroid Build Coastguard Worker enum Action {
32*d9ecfb0fSAndroid Build Coastguard Worker /// Deprecated alias of dice-chain
33*d9ecfb0fSAndroid Build Coastguard Worker VerifyDiceChain(DiceChainArgs),
34*d9ecfb0fSAndroid Build Coastguard Worker DiceChain(DiceChainArgs),
35*d9ecfb0fSAndroid Build Coastguard Worker FactoryCsr(FactoryCsrArgs),
36*d9ecfb0fSAndroid Build Coastguard Worker Csr(CsrArgs),
37*d9ecfb0fSAndroid Build Coastguard Worker }
38*d9ecfb0fSAndroid Build Coastguard Worker
39*d9ecfb0fSAndroid Build Coastguard Worker #[derive(Parser)]
40*d9ecfb0fSAndroid Build Coastguard Worker /// Verify that a DICE chain is well-formed
41*d9ecfb0fSAndroid Build Coastguard Worker ///
42*d9ecfb0fSAndroid Build Coastguard Worker /// DICE chains are expected to follow the specification of the RKP HAL [1] which is based on the
43*d9ecfb0fSAndroid Build Coastguard Worker /// Open Profile for DICE [2].
44*d9ecfb0fSAndroid Build Coastguard Worker ///
45*d9ecfb0fSAndroid Build Coastguard Worker /// [1] -- https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
46*d9ecfb0fSAndroid Build Coastguard Worker /// [2] -- https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md
47*d9ecfb0fSAndroid Build Coastguard Worker struct DiceChainArgs {
48*d9ecfb0fSAndroid Build Coastguard Worker /// Path to a file containing a DICE chain.
49*d9ecfb0fSAndroid Build Coastguard Worker chain: String,
50*d9ecfb0fSAndroid Build Coastguard Worker /// Allow non-normal DICE chain modes.
51*d9ecfb0fSAndroid Build Coastguard Worker #[clap(long)]
52*d9ecfb0fSAndroid Build Coastguard Worker allow_any_mode: bool,
53*d9ecfb0fSAndroid Build Coastguard Worker /// Validate the chain against the requirements of a specific RKP instance.
54*d9ecfb0fSAndroid Build Coastguard Worker /// If not specified, the default RKP instance is used.
55*d9ecfb0fSAndroid Build Coastguard Worker #[clap(value_enum, long, default_value = "default")]
56*d9ecfb0fSAndroid Build Coastguard Worker rkp_instance: RkpInstance,
57*d9ecfb0fSAndroid Build Coastguard Worker }
58*d9ecfb0fSAndroid Build Coastguard Worker
59*d9ecfb0fSAndroid Build Coastguard Worker #[derive(Parser)]
60*d9ecfb0fSAndroid Build Coastguard Worker /// Verify a CSR generated by the rkp_factory_extraction_tool
61*d9ecfb0fSAndroid Build Coastguard Worker ///
62*d9ecfb0fSAndroid Build Coastguard Worker /// "v1" CSRs are also decrypted using the factory EEK.
63*d9ecfb0fSAndroid Build Coastguard Worker struct FactoryCsrArgs {
64*d9ecfb0fSAndroid Build Coastguard Worker /// Path to a file containing one or more CSRs, in the "csr+json" format as defined by
65*d9ecfb0fSAndroid Build Coastguard Worker /// rkp_factory_extraction_tool. Each line is interpreted as a separate JSON blob containing
66*d9ecfb0fSAndroid Build Coastguard Worker /// a base64-encoded CSR.
67*d9ecfb0fSAndroid Build Coastguard Worker csr_file: String,
68*d9ecfb0fSAndroid Build Coastguard Worker /// Allow non-normal DICE chain modes.
69*d9ecfb0fSAndroid Build Coastguard Worker #[clap(long)]
70*d9ecfb0fSAndroid Build Coastguard Worker allow_any_mode: bool,
71*d9ecfb0fSAndroid Build Coastguard Worker }
72*d9ecfb0fSAndroid Build Coastguard Worker
73*d9ecfb0fSAndroid Build Coastguard Worker #[derive(Parser)]
74*d9ecfb0fSAndroid Build Coastguard Worker /// Parse and verify a request payload that is suitable for the RKP server's SignCertificates API.
75*d9ecfb0fSAndroid Build Coastguard Worker /// In HALv3, this is the output of generateCertificateRequestV2. For previous HAL versions,
76*d9ecfb0fSAndroid Build Coastguard Worker /// the CSR is constructed by the remote provisioning service client, but is constructed from the
77*d9ecfb0fSAndroid Build Coastguard Worker /// outputs of generateCertificateRequest.
78*d9ecfb0fSAndroid Build Coastguard Worker struct CsrArgs {
79*d9ecfb0fSAndroid Build Coastguard Worker /// Path to a file containing a single CSR, encoded as CBOR.
80*d9ecfb0fSAndroid Build Coastguard Worker csr_file: String,
81*d9ecfb0fSAndroid Build Coastguard Worker /// Allow non-normal DICE chain modes.
82*d9ecfb0fSAndroid Build Coastguard Worker #[clap(long)]
83*d9ecfb0fSAndroid Build Coastguard Worker allow_any_mode: bool,
84*d9ecfb0fSAndroid Build Coastguard Worker /// Validate the chain against the requirements of a specific RKP instance.
85*d9ecfb0fSAndroid Build Coastguard Worker /// If not specified, the default RKP instance is used.
86*d9ecfb0fSAndroid Build Coastguard Worker #[clap(value_enum, long, default_value = "default")]
87*d9ecfb0fSAndroid Build Coastguard Worker rkp_instance: RkpInstance,
88*d9ecfb0fSAndroid Build Coastguard Worker }
89*d9ecfb0fSAndroid Build Coastguard Worker
90*d9ecfb0fSAndroid Build Coastguard Worker #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
91*d9ecfb0fSAndroid Build Coastguard Worker enum VsrVersion {
92*d9ecfb0fSAndroid Build Coastguard Worker /// VSR 13 / Android T / 2022
93*d9ecfb0fSAndroid Build Coastguard Worker Vsr13,
94*d9ecfb0fSAndroid Build Coastguard Worker /// VSR 14 / Android U / 2023
95*d9ecfb0fSAndroid Build Coastguard Worker Vsr14,
96*d9ecfb0fSAndroid Build Coastguard Worker /// VSR 15 / Android V / 2024
97*d9ecfb0fSAndroid Build Coastguard Worker Vsr15,
98*d9ecfb0fSAndroid Build Coastguard Worker /// VSR 16 / Android W / 2025
99*d9ecfb0fSAndroid Build Coastguard Worker Vsr16,
100*d9ecfb0fSAndroid Build Coastguard Worker }
101*d9ecfb0fSAndroid Build Coastguard Worker
session_from_vsr(vsr: Option<VsrVersion>) -> Session102*d9ecfb0fSAndroid Build Coastguard Worker fn session_from_vsr(vsr: Option<VsrVersion>) -> Session {
103*d9ecfb0fSAndroid Build Coastguard Worker Session {
104*d9ecfb0fSAndroid Build Coastguard Worker options: match vsr {
105*d9ecfb0fSAndroid Build Coastguard Worker Some(VsrVersion::Vsr13) => Options::vsr13(),
106*d9ecfb0fSAndroid Build Coastguard Worker Some(VsrVersion::Vsr14) => Options::vsr14(),
107*d9ecfb0fSAndroid Build Coastguard Worker Some(VsrVersion::Vsr15) => Options::vsr15(),
108*d9ecfb0fSAndroid Build Coastguard Worker Some(VsrVersion::Vsr16) => {
109*d9ecfb0fSAndroid Build Coastguard Worker println!();
110*d9ecfb0fSAndroid Build Coastguard Worker println!();
111*d9ecfb0fSAndroid Build Coastguard Worker println!(" ********************************************************************");
112*d9ecfb0fSAndroid Build Coastguard Worker println!(" ! The selected VSR is not finalized and is subject to change. !");
113*d9ecfb0fSAndroid Build Coastguard Worker println!(" ! Please contact your TAM if you intend to depend on the !");
114*d9ecfb0fSAndroid Build Coastguard Worker println!(" ! validation rules use for the selected VSR. !");
115*d9ecfb0fSAndroid Build Coastguard Worker println!(" ********************************************************************");
116*d9ecfb0fSAndroid Build Coastguard Worker println!();
117*d9ecfb0fSAndroid Build Coastguard Worker println!();
118*d9ecfb0fSAndroid Build Coastguard Worker Options::vsr16()
119*d9ecfb0fSAndroid Build Coastguard Worker }
120*d9ecfb0fSAndroid Build Coastguard Worker None => Options::default(),
121*d9ecfb0fSAndroid Build Coastguard Worker },
122*d9ecfb0fSAndroid Build Coastguard Worker }
123*d9ecfb0fSAndroid Build Coastguard Worker }
124*d9ecfb0fSAndroid Build Coastguard Worker
main() -> Result<()>125*d9ecfb0fSAndroid Build Coastguard Worker fn main() -> Result<()> {
126*d9ecfb0fSAndroid Build Coastguard Worker let args = Args::parse();
127*d9ecfb0fSAndroid Build Coastguard Worker let message = match &args.action {
128*d9ecfb0fSAndroid Build Coastguard Worker Action::VerifyDiceChain(sub_args) => {
129*d9ecfb0fSAndroid Build Coastguard Worker println!();
130*d9ecfb0fSAndroid Build Coastguard Worker println!(" ********************************************************************");
131*d9ecfb0fSAndroid Build Coastguard Worker println!(" ! 'verify-dice-chain' has been deprecated in favor of 'dice-chain'.!");
132*d9ecfb0fSAndroid Build Coastguard Worker println!(" ********************************************************************");
133*d9ecfb0fSAndroid Build Coastguard Worker println!();
134*d9ecfb0fSAndroid Build Coastguard Worker verify_dice_chain(&args, sub_args)?
135*d9ecfb0fSAndroid Build Coastguard Worker }
136*d9ecfb0fSAndroid Build Coastguard Worker Action::DiceChain(sub_args) => verify_dice_chain(&args, sub_args)?,
137*d9ecfb0fSAndroid Build Coastguard Worker Action::FactoryCsr(sub_args) => parse_factory_csr(&args, sub_args)?,
138*d9ecfb0fSAndroid Build Coastguard Worker Action::Csr(sub_args) => parse_csr(&args, sub_args)?,
139*d9ecfb0fSAndroid Build Coastguard Worker };
140*d9ecfb0fSAndroid Build Coastguard Worker println!("{}", message.unwrap_or(String::from("Success")));
141*d9ecfb0fSAndroid Build Coastguard Worker Ok(())
142*d9ecfb0fSAndroid Build Coastguard Worker }
143*d9ecfb0fSAndroid Build Coastguard Worker
verify_dice_chain(args: &Args, sub_args: &DiceChainArgs) -> Result<Option<String>>144*d9ecfb0fSAndroid Build Coastguard Worker fn verify_dice_chain(args: &Args, sub_args: &DiceChainArgs) -> Result<Option<String>> {
145*d9ecfb0fSAndroid Build Coastguard Worker let mut session = session_from_vsr(args.vsr);
146*d9ecfb0fSAndroid Build Coastguard Worker session.set_allow_any_mode(sub_args.allow_any_mode);
147*d9ecfb0fSAndroid Build Coastguard Worker session.set_rkp_instance(sub_args.rkp_instance);
148*d9ecfb0fSAndroid Build Coastguard Worker let chain = dice::ChainForm::from_cbor(&session, &fs::read(&sub_args.chain)?)?;
149*d9ecfb0fSAndroid Build Coastguard Worker if args.verbose {
150*d9ecfb0fSAndroid Build Coastguard Worker println!("{chain:#?}");
151*d9ecfb0fSAndroid Build Coastguard Worker }
152*d9ecfb0fSAndroid Build Coastguard Worker if let ChainForm::Degenerate(_) = chain {
153*d9ecfb0fSAndroid Build Coastguard Worker return Ok(Some(String::from(
154*d9ecfb0fSAndroid Build Coastguard Worker "WARNING!
155*d9ecfb0fSAndroid Build Coastguard Worker The given 'degenerate' DICE chain is valid. However, the degenerate chain form is deprecated in
156*d9ecfb0fSAndroid Build Coastguard Worker favor of full DICE chains, rooted in ROM, that measure the system's boot components.",
157*d9ecfb0fSAndroid Build Coastguard Worker )));
158*d9ecfb0fSAndroid Build Coastguard Worker }
159*d9ecfb0fSAndroid Build Coastguard Worker Ok(None)
160*d9ecfb0fSAndroid Build Coastguard Worker }
161*d9ecfb0fSAndroid Build Coastguard Worker
parse_factory_csr(args: &Args, sub_args: &FactoryCsrArgs) -> Result<Option<String>>162*d9ecfb0fSAndroid Build Coastguard Worker fn parse_factory_csr(args: &Args, sub_args: &FactoryCsrArgs) -> Result<Option<String>> {
163*d9ecfb0fSAndroid Build Coastguard Worker let mut session = session_from_vsr(args.vsr);
164*d9ecfb0fSAndroid Build Coastguard Worker session.set_allow_any_mode(sub_args.allow_any_mode);
165*d9ecfb0fSAndroid Build Coastguard Worker let input = &fs::File::open(&sub_args.csr_file)?;
166*d9ecfb0fSAndroid Build Coastguard Worker let mut csr_count = 0;
167*d9ecfb0fSAndroid Build Coastguard Worker for line in io::BufReader::new(input).lines() {
168*d9ecfb0fSAndroid Build Coastguard Worker let line = line?;
169*d9ecfb0fSAndroid Build Coastguard Worker if line.is_empty() {
170*d9ecfb0fSAndroid Build Coastguard Worker continue;
171*d9ecfb0fSAndroid Build Coastguard Worker }
172*d9ecfb0fSAndroid Build Coastguard Worker let csr = rkp::FactoryCsr::from_json(&session, &line)?;
173*d9ecfb0fSAndroid Build Coastguard Worker csr_count += 1;
174*d9ecfb0fSAndroid Build Coastguard Worker if args.verbose {
175*d9ecfb0fSAndroid Build Coastguard Worker println!("{csr_count}: {csr:#?}");
176*d9ecfb0fSAndroid Build Coastguard Worker }
177*d9ecfb0fSAndroid Build Coastguard Worker }
178*d9ecfb0fSAndroid Build Coastguard Worker if csr_count == 0 {
179*d9ecfb0fSAndroid Build Coastguard Worker bail!("No CSRs found in the input file '{}'", sub_args.csr_file);
180*d9ecfb0fSAndroid Build Coastguard Worker }
181*d9ecfb0fSAndroid Build Coastguard Worker Ok(None)
182*d9ecfb0fSAndroid Build Coastguard Worker }
183*d9ecfb0fSAndroid Build Coastguard Worker
parse_csr(args: &Args, sub_args: &CsrArgs) -> Result<Option<String>>184*d9ecfb0fSAndroid Build Coastguard Worker fn parse_csr(args: &Args, sub_args: &CsrArgs) -> Result<Option<String>> {
185*d9ecfb0fSAndroid Build Coastguard Worker let mut session = session_from_vsr(args.vsr);
186*d9ecfb0fSAndroid Build Coastguard Worker session.set_allow_any_mode(sub_args.allow_any_mode);
187*d9ecfb0fSAndroid Build Coastguard Worker session.set_rkp_instance(sub_args.rkp_instance);
188*d9ecfb0fSAndroid Build Coastguard Worker let input = &fs::File::open(&sub_args.csr_file)?;
189*d9ecfb0fSAndroid Build Coastguard Worker let csr = rkp::Csr::from_cbor(&session, input)?;
190*d9ecfb0fSAndroid Build Coastguard Worker if args.verbose {
191*d9ecfb0fSAndroid Build Coastguard Worker print!("{csr:#?}");
192*d9ecfb0fSAndroid Build Coastguard Worker }
193*d9ecfb0fSAndroid Build Coastguard Worker Ok(None)
194*d9ecfb0fSAndroid Build Coastguard Worker }
195*d9ecfb0fSAndroid Build Coastguard Worker
196*d9ecfb0fSAndroid Build Coastguard Worker #[cfg(test)]
197*d9ecfb0fSAndroid Build Coastguard Worker mod tests {
198*d9ecfb0fSAndroid Build Coastguard Worker use super::*;
199*d9ecfb0fSAndroid Build Coastguard Worker use clap::CommandFactory;
200*d9ecfb0fSAndroid Build Coastguard Worker
201*d9ecfb0fSAndroid Build Coastguard Worker #[test]
verify_command()202*d9ecfb0fSAndroid Build Coastguard Worker fn verify_command() {
203*d9ecfb0fSAndroid Build Coastguard Worker Args::command().debug_assert();
204*d9ecfb0fSAndroid Build Coastguard Worker }
205*d9ecfb0fSAndroid Build Coastguard Worker }
206