1use super::CommandError;
5use crate::{
6 cli::{get_auth, SubCommand},
7 context::ContextCache,
8 convert::{from_input_to_bytes, from_str_to_handle},
9 device::{self, Auth, Device, DeviceError},
10 key::Tpm2shAlgId,
11 pcr::pcr_get_bank_list,
12 uri::Uri,
13};
14use argh::FromArgs;
15use std::{cell::RefCell, rc::Rc};
16use tpm2_protocol::{
17 data::{Tpm2bEvent, TpmCc, TpmSe, TpmuHa},
18 message::TpmPcrEventCommand,
19 TpmHandle,
20};
21
22#[derive(FromArgs, Debug)]
24#[argh(
25 subcommand,
26 name = "pcr-event",
27 note = "Extends a Platform Configuration Register (PCR) with data.
28
29This command computes the digest of the provided data and uses it to
30extend the state of the specified PCR. This is a one-way operation.
31
32The output is a single, reusable policy expression that can be used
33in other commands.
34
35Example:
36 # Extend PCR 16 with the SHA256 digest of the string \"my-event\"
37 echo -n \"my-event\" | tpm2sh pcr-event 16"
38)]
39pub struct PcrEvent {
40 #[argh(positional, arg_name = "pcr-index", from_str_fn(from_str_to_handle))]
42 pub pcr_index: TpmHandle,
43
44 #[argh(positional)]
46 pub input: Option<Uri>,
47
48 #[argh(option, arg_name = "auth", short = 'a')]
51 pub auth: Option<String>,
52
53 #[argh(option, arg_name = "auth", short = 'm', long = "hmac-auth")]
56 pub hmac_auth: Option<String>,
57}
58
59impl SubCommand for PcrEvent {
60 fn run(
61 &self,
62 device: Option<Rc<RefCell<Device>>>,
63 context: &mut ContextCache,
64 _plain: bool,
65 ) -> Result<(), CommandError> {
66 let auth = match (self.auth.as_ref(), self.hmac_auth.as_ref()) {
67 (Some(_), Some(_)) => {
68 return Err(CommandError::InvalidInput(
69 "Cannot use --auth and --hmac-auth at the same time".to_string(),
70 ));
71 }
72 (Some(auth_str), None) => get_auth(
73 Some(auth_str),
74 "TPM2SH_AUTH",
75 &context.session_map,
76 &[TpmSe::Policy],
77 )?,
78 (None, Some(hmac_auth_str)) => get_auth(
79 Some(hmac_auth_str),
80 "TPM2SH_HMAC_AUTH",
81 &context.session_map,
82 &[TpmSe::Hmac],
83 )?,
84 (None, None) => {
85 let auth = get_auth(None, "TPM2SH_AUTH", &context.session_map, &[TpmSe::Policy])?;
86 if matches!(&auth, Auth::Password(p) if p.is_empty()) {
87 get_auth(
88 None,
89 "TPM2SH_HMAC_AUTH",
90 &context.session_map,
91 &[TpmSe::Hmac],
92 )?
93 } else {
94 auth
95 }
96 }
97 };
98 device::with_device(device, |device| {
99 let banks = pcr_get_bank_list(device)?;
100 let handles = [self.pcr_index.0];
101 let auths = &[auth];
102
103 let data_bytes = from_input_to_bytes(self.input.as_ref())?;
104
105 let event_data = Tpm2bEvent::try_from(data_bytes.as_slice())?;
106 let command = TpmPcrEventCommand {
107 pcr_handle: handles[0].into(),
108 event_data,
109 };
110
111 let (resp, _) = context.execute(device, &command, &handles, auths)?;
112
113 let pcr_resp = resp
114 .PcrEvent()
115 .map_err(|_| DeviceError::ResponseMismatch(TpmCc::PcrEvent))?;
116
117 let clauses: Vec<String> = banks
118 .iter()
119 .zip(pcr_resp.digests.iter())
120 .filter_map(|(bank, digest_struct)| {
121 if let TpmuHa::Digest(bytes) = digest_struct.digest {
122 Some(format!(
123 "{}:{}:{}",
124 Tpm2shAlgId(bank.alg),
125 self.pcr_index.0,
126 hex::encode(bytes)
127 ))
128 } else {
129 None
130 }
131 })
132 .collect();
133
134 writeln!(context.writer, "{}", clauses.join("+"))?;
135
136 Ok(())
137 })
138 }
139}