1use crate::{
6 cli::{get_auth, SubCommand},
7 command::CommandError,
8 context::ContextCache,
9 device::{self, Device},
10 key, x509,
11};
12use argh::FromArgs;
13use rasn::types::Tag;
14use std::{cell::RefCell, rc::Rc};
15use strum::Display;
16use tabled::Tabled;
17use tpm2_protocol::data::{TpmHt, TpmPt, TpmSe};
18
19#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
20#[strum(serialize_all = "kebab-case")]
21enum MemoryHandleType {
22 Transient,
23 Persistent,
24 Session,
25 Certificate,
26}
27
28#[derive(Tabled)]
29struct MemoryRow {
30 #[tabled(rename = "HANDLE")]
31 handle: String,
32 #[tabled(rename = "TYPE")]
33 handle_type: String,
34 #[tabled(rename = "DETAILS")]
35 details: String,
36}
37
38#[derive(FromArgs, Debug)]
40#[argh(subcommand, name = "memory", note = "Lists objects inside TPM memory")]
41pub struct Memory {
42 #[argh(option, arg_name = "auth", short = 'p')]
45 pub parent_auth: Option<String>,
46
47 #[argh(option, arg_name = "auth", short = 'a')]
50 pub auth: Option<String>,
51
52 #[argh(option, arg_name = "auth", short = 'm', long = "hmac-auth")]
55 pub hmac_auth: Option<String>,
56}
57
58impl Memory {
59 fn fetch_rows<F>(
64 device: &mut Device,
65 rows: &mut Vec<MemoryRow>,
66 handle_type_to_query: u32,
67 display_type: MemoryHandleType,
68 mut get_details: F,
69 ) -> Result<(), CommandError>
70 where
71 F: FnMut(&mut Device, u32) -> Result<String, CommandError>,
72 {
73 for handle in device.get_all_handles(handle_type_to_query << 24)? {
74 match get_details(device, handle) {
75 Ok(details) => {
76 rows.push(MemoryRow {
77 handle: format!("{handle:08x}"),
78 handle_type: display_type.to_string(),
79 details,
80 });
81 }
82 Err(e) => {
83 log::debug!("Could not retrieve details for handle {handle:08x}: {e}");
84 }
85 }
86 }
87 Ok(())
88 }
89
90 fn fetch_details(device: &mut Device, handle: u32) -> Result<String, CommandError> {
92 let (public, _) = device.read_public(handle.into())?;
93 Ok(key::format_alg_from_public(&public))
94 }
95}
96
97impl SubCommand for Memory {
98 fn run(
99 &self,
100 device: Option<Rc<RefCell<Device>>>,
101 context: &mut ContextCache,
102 plain: bool,
103 ) -> Result<(), CommandError> {
104 let nv_auth = get_auth(
105 self.parent_auth.as_ref(),
106 "TPM2SH_PARENT_AUTH",
107 &context.session_map,
108 &[TpmSe::Policy],
109 )?;
110 device::with_device(device, |device| {
111 let mut rows: Vec<MemoryRow> = Vec::new();
112
113 Self::fetch_rows(
114 device,
115 &mut rows,
116 TpmHt::Persistent as u32,
117 MemoryHandleType::Persistent,
118 Self::fetch_details,
119 )?;
120
121 Self::fetch_rows(
122 device,
123 &mut rows,
124 TpmHt::Transient as u32,
125 MemoryHandleType::Transient,
126 Self::fetch_details,
127 )?;
128
129 Self::fetch_rows(
130 device,
131 &mut rows,
132 TpmHt::HmacSession as u32,
133 MemoryHandleType::Session,
134 |_, handle| {
135 let mso = (handle >> 24) as u8;
136 let detail = if mso == TpmHt::HmacSession as u8 {
137 "hmac"
138 } else {
139 "policy"
140 };
141 Ok(detail.to_string())
142 },
143 )?;
144
145 Self::fetch_rows(
146 device,
147 &mut rows,
148 TpmHt::PolicySession as u32,
149 MemoryHandleType::Session,
150 |_, _| Ok("saved".to_string()),
151 )?;
152
153 let max_read_size = device.get_tpm_property(TpmPt::NvBufferMax).unwrap_or(0) as usize;
154
155 if max_read_size > 0 {
156 Self::fetch_rows(
157 device,
158 &mut rows,
159 TpmHt::NvIndex as u32,
160 MemoryHandleType::Certificate,
161 |device, handle| {
162 if !(0x01C0_0000..=0x01C0_FFFF).contains(&handle) {
163 return Err(CommandError::InvalidInput("Not a certificate".into()));
164 }
165 let cert_bytes = context
166 .read_certificate(
167 device,
168 std::slice::from_ref(&nv_auth),
169 handle,
170 max_read_size,
171 )?
172 .ok_or(CommandError::InvalidInput("No certificate data".into()))?;
173
174 if cert_bytes.is_empty()
175 || u32::from(cert_bytes[0]) != (0x20 | Tag::SEQUENCE.value)
176 {
177 return Err(CommandError::InvalidInput("Not a DER certificate".into()));
178 }
179 Ok(x509::get_algorithm(&cert_bytes)?)
180 },
181 )?;
182 }
183
184 rows.sort_unstable_by(|a, b| a.handle.cmp(&b.handle));
185
186 super::print_table(&mut context.writer, rows, plain)?;
187
188 Ok(())
189 })
190 }
191}