1use crate::{
6 auth::Auth,
7 cli::SubCommand,
8 command::{print_table, CommandError, Tabled},
9 device::{self, Device},
10 handle::Handle,
11 job::Job,
12 key::{
13 format_alg_from_public, KeyError, Tpm2shAlgId, OID_ECDSA_WITH_SHA256,
14 OID_ECDSA_WITH_SHA384, OID_ECDSA_WITH_SHA512, OID_RSA_ENCRYPTION,
15 OID_SHA1_WITH_RSA_ENCRYPTION, OID_SHA256_WITH_RSA_ENCRYPTION,
16 OID_SHA384_WITH_RSA_ENCRYPTION, OID_SHA512_WITH_RSA_ENCRYPTION, SECP_256_R_1, SECP_384_R_1,
17 SECP_521_R_1,
18 },
19};
20use clap::Args;
21use num_bigint::ToBigInt;
22use rasn::{
23 types::{BitString, Integer, ObjectIdentifier, SequenceOf},
24 AsnType, Decode, Decoder,
25};
26use strum::Display;
27use tpm2_protocol::{
28 data::{TpmAlgId, TpmHt, TpmPt},
29 TpmHandle,
30};
31
32#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
33#[strum(serialize_all = "kebab-case")]
34enum MemoryHandleType {
35 Transient,
36 Persistent,
37 Session,
38 Certificate,
39}
40
41struct MemoryRow {
42 handle: String,
43 class: String,
44 details: String,
45}
46
47impl Tabled for MemoryRow {
48 fn headers() -> Vec<String> {
49 vec![
50 "HANDLE".to_string(),
51 "TYPE".to_string(),
52 "DETAILS".to_string(),
53 ]
54 }
55
56 fn row(&self) -> Vec<String> {
57 vec![
58 self.handle.clone(),
59 self.class.clone(),
60 self.details.clone(),
61 ]
62 }
63}
64
65#[derive(Args, Debug)]
67#[command(about = "Lists objects inside TPM memory")]
68pub struct Memory {}
69
70impl SubCommand for Memory {
71 fn run(&self, job: &mut Job) -> Result<(), CommandError> {
72 device::with_device(job.device.clone(), |device| {
73 let mut rows: Vec<MemoryRow> = Vec::new();
74 Self::fetch_rows(
75 device,
76 &mut rows,
77 TpmHt::Persistent,
78 MemoryHandleType::Persistent,
79 Self::fetch_details,
80 )?;
81 Self::fetch_rows(
82 device,
83 &mut rows,
84 TpmHt::Transient,
85 MemoryHandleType::Transient,
86 Self::fetch_details,
87 )?;
88 Self::fetch_rows(
89 device,
90 &mut rows,
91 TpmHt::LoadedSession,
92 MemoryHandleType::Session,
93 |_, handle| {
94 let ht = TpmHt::try_from(handle)?;
95 let detail = if ht == TpmHt::HmacSession {
96 "hmac"
97 } else {
98 "policy"
99 };
100 Ok(detail.to_string())
101 },
102 )?;
103
104 Self::fetch_rows(
105 device,
106 &mut rows,
107 TpmHt::SavedSession,
108 MemoryHandleType::Session,
109 |_, _| Ok("saved".to_string()),
110 )?;
111
112 let max_read_size = device.get_tpm_property(TpmPt::NvBufferMax).unwrap_or(0) as usize;
113
114 if max_read_size > 0 {
115 Self::fetch_rows(
116 device,
117 &mut rows,
118 TpmHt::NvIndex,
119 MemoryHandleType::Certificate,
120 |device, handle| {
121 let handle_val = handle.value();
122 if !(0x01C0_0000..=0x01C0_FFFF).contains(&handle_val) {
123 return Err(CommandError::InvalidInput("Not a certificate".into()));
124 }
125 let auths = vec![Auth::default()];
126 let cert_bytes = job
127 .read_certificate(device, &auths, handle_val, max_read_size)?
128 .ok_or(CommandError::InvalidInput("No certificate data".into()))?;
129 if cert_bytes.is_empty() || u32::from(cert_bytes[0]) != (0x30) {
130 return Err(CommandError::InvalidInput("Not a DER certificate".into()));
131 }
132 Ok(Memory::fetch_alg_name(&cert_bytes)?)
133 },
134 )?;
135 }
136 rows.sort_unstable_by(|a, b| a.handle.cmp(&b.handle));
137 print_table(&mut job.writer, &rows)?;
138 Ok(())
139 })
140 }
141}
142
143fn default_bool_false() -> bool {
144 false
145}
146
147#[derive(AsnType, Decode, Debug)]
148struct AlgorithmIdentifier {
149 algorithm: ObjectIdentifier,
150 parameters: Option<rasn::types::Any>,
151}
152
153#[derive(AsnType, Decode, Debug)]
154struct SubjectPublicKeyInfo {
155 algorithm: AlgorithmIdentifier,
156 subject_public_key: BitString,
157}
158
159#[derive(AsnType, Decode, Debug)]
160struct RsaPublicKey {
161 modulus: Integer,
162 _public_exponent: Integer,
163}
164
165#[derive(AsnType, Decode, Debug)]
166struct Extension {
167 _extn_id: ObjectIdentifier,
168 #[rasn(default = "default_bool_false")]
169 _critical: bool,
170 _extn_value: rasn::types::OctetString,
171}
172
173#[derive(AsnType, Decode, Debug)]
174struct TbsCertificate {
175 #[rasn(tag(explicit(context, 0)))]
176 _version: Option<Integer>,
177 _serial_number: Integer,
178 signature: AlgorithmIdentifier,
179 _issuer: rasn::types::Any,
180 _validity: rasn::types::Any,
181 _subject: rasn::types::Any,
182 subject_public_key_info: SubjectPublicKeyInfo,
183 #[rasn(tag(context, 1))]
184 _issuer_unique_id: Option<BitString>,
185 #[rasn(tag(context, 2))]
186 _subject_unique_id: Option<BitString>,
187 #[rasn(tag(explicit(context, 3)))]
188 _extensions: Option<SequenceOf<Extension>>,
189}
190
191#[derive(AsnType, Decode, Debug)]
192struct Certificate {
193 tbs_cert: TbsCertificate,
194 _signature_algorithm: AlgorithmIdentifier,
195 _signature_value: BitString,
196}
197
198impl Memory {
199 fn fetch_rows<F>(
200 device: &mut Device,
201 rows: &mut Vec<MemoryRow>,
202 class: TpmHt,
203 display_type: MemoryHandleType,
204 mut get_details: F,
205 ) -> Result<(), CommandError>
206 where
207 F: FnMut(&mut Device, Handle) -> Result<String, CommandError>,
208 {
209 for handle in device.fetch_handles((class as u32) << 24)? {
210 match get_details(device, handle) {
211 Ok(details) => {
212 rows.push(MemoryRow {
213 handle: format!("{:08x}", handle.value()),
214 class: display_type.to_string(),
215 details,
216 });
217 }
218 Err(e) => log::debug!("{:08x}: {e}", handle.value()),
219 }
220 }
221 Ok(())
222 }
223
224 fn fetch_details(device: &mut Device, handle: Handle) -> Result<String, CommandError> {
225 let tpm_handle = TpmHandle(handle.value());
226 let (public, _) = device.read_public(tpm_handle)?;
227 Ok(format_alg_from_public(&public))
228 }
229
230 fn fetch_hash_alg(oid: &ObjectIdentifier) -> Result<TpmAlgId, KeyError> {
231 if oid == &OID_SHA1_WITH_RSA_ENCRYPTION {
232 Ok(TpmAlgId::Sha1)
233 } else if oid == &OID_SHA256_WITH_RSA_ENCRYPTION || oid == &OID_ECDSA_WITH_SHA256 {
234 Ok(TpmAlgId::Sha256)
235 } else if oid == &OID_SHA384_WITH_RSA_ENCRYPTION || oid == &OID_ECDSA_WITH_SHA384 {
236 Ok(TpmAlgId::Sha384)
237 } else if oid == &OID_SHA512_WITH_RSA_ENCRYPTION || oid == &OID_ECDSA_WITH_SHA512 {
238 Ok(TpmAlgId::Sha512)
239 } else {
240 Err(KeyError::UnsupportedOid(oid.to_string()))
241 }
242 }
243
244 fn fetch_alg_name(cert_der: &[u8]) -> Result<String, KeyError> {
245 let cert: Certificate = rasn::der::decode(cert_der)?;
246 let tbs = cert.tbs_cert;
247 let spki = tbs.subject_public_key_info;
248 let sig_alg = Memory::fetch_hash_alg(&tbs.signature.algorithm)?;
249 let sig_alg_str = Tpm2shAlgId(sig_alg).to_string();
250
251 let key_oid = &spki.algorithm.algorithm;
252 if key_oid == &OID_RSA_ENCRYPTION {
253 let key: RsaPublicKey = rasn::der::decode(spki.subject_public_key.as_raw_slice())?;
254 let modulus = key
255 .modulus
256 .to_bigint()
257 .ok_or_else(|| KeyError::InvalidRsaModulus(key.modulus.to_string()))?;
258 let key_bits = u16::try_from(modulus.bits())
259 .map_err(|_| KeyError::InvalidRsaModulus(modulus.to_string()))?;
260 Ok(format!("rsa-{key_bits}:{sig_alg_str}"))
261 } else if key_oid == &crate::key::OID_EC_PUBLIC_KEY {
262 let curve_param_oid = spki
263 .algorithm
264 .parameters
265 .as_ref()
266 .and_then(|any| rasn::der::decode::<ObjectIdentifier>(any.as_ref()).ok());
267 let curve_str = if curve_param_oid.as_ref() == Some(&SECP_256_R_1) {
268 "nist-p256"
269 } else if curve_param_oid.as_ref() == Some(&SECP_384_R_1) {
270 "nist-p384"
271 } else if curve_param_oid.as_ref() == Some(&SECP_521_R_1) {
272 "nist-p521"
273 } else if let Some(oid) = curve_param_oid.as_ref() {
274 return Err(KeyError::UnsupportedOid(oid.to_string()));
275 } else {
276 return Err(KeyError::InvalidOid);
277 };
278 Ok(format!("ecc-{curve_str}:{sig_alg_str}"))
279 } else {
280 Err(KeyError::UnsupportedOid(key_oid.to_string()))
281 }
282 }
283}