xref: /aosp_15_r20/tools/security/remote_provisioning/hwtrust/src/main.rs (revision d9ecfb0f4d734c9ce41cde8ac4d585b094fd4222)
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