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