1use alloc::{vec, vec::Vec};
29
30use crate::error::KernelError;
31
32#[repr(u16)]
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum TpmStructureTag {
36 NoSessions = 0x8001,
38 Sessions = 0x8002,
40}
41
42#[repr(u32)]
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum TpmCommandCode {
46 Startup = 0x00000144,
47 Shutdown = 0x00000145,
48 SelfTest = 0x00000143,
49 GetCapability = 0x0000017A,
50 GetRandom = 0x0000017B,
51 PcrRead = 0x0000017E,
52 PcrExtend = 0x00000182,
53 Create = 0x00000153,
54 Load = 0x00000157,
55 Sign = 0x0000015D,
56 VerifySignature = 0x00000177,
57 Quote = 0x00000158,
58 CreatePrimary = 0x00000131,
59}
60
61#[repr(u32)]
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum TpmResponseCode {
65 Success = 0x00000000,
66 Failure = 0x00000101,
67 BadTag = 0x0000001E,
68 Retry = 0x00000922,
69 Yielded = 0x00000908,
70 Canceled = 0x00000909,
71}
72
73impl TpmResponseCode {
74 pub fn from_u32(value: u32) -> Self {
75 match value {
76 0x00000000 => TpmResponseCode::Success,
77 0x00000101 => TpmResponseCode::Failure,
78 0x0000001E => TpmResponseCode::BadTag,
79 0x00000922 => TpmResponseCode::Retry,
80 0x00000908 => TpmResponseCode::Yielded,
81 0x00000909 => TpmResponseCode::Canceled,
82 _ => TpmResponseCode::Failure,
83 }
84 }
85
86 pub fn is_success(&self) -> bool {
87 matches!(self, TpmResponseCode::Success)
88 }
89}
90
91#[repr(u16)]
93#[derive(Debug, Clone, Copy)]
94pub enum TpmStartupType {
95 Clear = 0x0000,
97 State = 0x0001,
99}
100
101#[repr(u16)]
103#[derive(Debug, Clone, Copy)]
104pub enum TpmShutdownType {
105 Clear = 0x0000,
107 State = 0x0001,
109}
110
111fn marshal_header(tag: TpmStructureTag, command: TpmCommandCode, total_size: u32) -> Vec<u8> {
119 let mut bytes = Vec::with_capacity(10);
120 bytes.extend_from_slice(&(tag as u16).to_be_bytes());
121 bytes.extend_from_slice(&total_size.to_be_bytes());
122 bytes.extend_from_slice(&(command as u32).to_be_bytes());
123 bytes
124}
125
126pub fn marshal_command(tag: TpmStructureTag, command: TpmCommandCode, params: &[u8]) -> Vec<u8> {
131 let total_size = (10 + params.len()) as u32;
132 let mut bytes = marshal_header(tag, command, total_size);
133 bytes.extend_from_slice(params);
134 bytes
135}
136
137pub fn parse_response(data: &[u8]) -> Option<(TpmResponseCode, &[u8])> {
142 let header = TpmResponseHeader::parse(data)?;
143 let total_size = header.size as usize;
144
145 if data.len() < total_size || total_size < 10 {
146 return None;
147 }
148
149 let code = header.response_code();
150 let payload = &data[10..total_size];
151 Some((code, payload))
152}
153
154#[repr(C, packed)]
160#[derive(Debug, Clone, Copy)]
161pub struct TpmCommandHeader {
162 pub tag: u16, pub size: u32, pub command: u32, }
166
167impl TpmCommandHeader {
168 pub fn new(tag: TpmStructureTag, command: TpmCommandCode, size: u32) -> Self {
169 Self {
170 tag: tag as u16,
171 size: size.to_be(),
172 command: (command as u32).to_be(),
173 }
174 }
175}
176
177#[repr(C, packed)]
179#[derive(Debug, Clone, Copy)]
180pub struct TpmResponseHeader {
181 pub tag: u16,
182 pub size: u32,
183 pub response_code: u32,
184}
185
186impl TpmResponseHeader {
187 pub fn parse(data: &[u8]) -> Option<Self> {
189 if data.len() < 10 {
190 return None;
191 }
192
193 Some(Self {
194 tag: u16::from_be_bytes([data[0], data[1]]),
195 size: u32::from_be_bytes([data[2], data[3], data[4], data[5]]),
196 response_code: u32::from_be_bytes([data[6], data[7], data[8], data[9]]),
197 })
198 }
199
200 pub fn response_code(&self) -> TpmResponseCode {
202 TpmResponseCode::from_u32(self.response_code)
204 }
205}
206
207pub struct TpmStartupCommand {
213 startup_type: TpmStartupType,
214}
215
216impl TpmStartupCommand {
217 pub fn new(startup_type: TpmStartupType) -> Self {
218 Self { startup_type }
219 }
220
221 pub fn to_bytes(&self) -> Vec<u8> {
222 let params = (self.startup_type as u16).to_be_bytes();
223 marshal_command(
224 TpmStructureTag::NoSessions,
225 TpmCommandCode::Startup,
226 ¶ms,
227 )
228 }
229}
230
231pub struct TpmShutdownCommand {
237 shutdown_type: TpmShutdownType,
238}
239
240impl TpmShutdownCommand {
241 pub fn new(shutdown_type: TpmShutdownType) -> Self {
242 Self { shutdown_type }
243 }
244
245 pub fn to_bytes(&self) -> Vec<u8> {
246 let params = (self.shutdown_type as u16).to_be_bytes();
247 marshal_command(
248 TpmStructureTag::NoSessions,
249 TpmCommandCode::Shutdown,
250 ¶ms,
251 )
252 }
253}
254
255pub struct TpmSelfTestCommand {
261 full_test: bool,
263}
264
265impl TpmSelfTestCommand {
266 pub fn new(full_test: bool) -> Self {
267 Self { full_test }
268 }
269
270 pub fn to_bytes(&self) -> Vec<u8> {
271 let params = [if self.full_test { 1u8 } else { 0u8 }];
272 marshal_command(
273 TpmStructureTag::NoSessions,
274 TpmCommandCode::SelfTest,
275 ¶ms,
276 )
277 }
278}
279
280pub struct TpmGetRandomCommand {
286 bytes_requested: u16,
287}
288
289impl TpmGetRandomCommand {
290 pub fn new(bytes_requested: u16) -> Self {
291 Self { bytes_requested }
292 }
293
294 pub fn to_bytes(&self) -> Vec<u8> {
295 let params = self.bytes_requested.to_be_bytes();
296 marshal_command(
297 TpmStructureTag::NoSessions,
298 TpmCommandCode::GetRandom,
299 ¶ms,
300 )
301 }
302}
303
304pub struct TpmGetRandomResponse {
306 pub random_bytes: Vec<u8>,
307}
308
309impl TpmGetRandomResponse {
310 pub fn parse(data: &[u8]) -> Result<Self, KernelError> {
314 if data.len() < 12 {
315 return Err(KernelError::InvalidArgument {
316 name: "tpm_response",
317 value: "response too short",
318 });
319 }
320
321 let header = TpmResponseHeader::parse(data).ok_or(KernelError::InvalidArgument {
322 name: "tpm_response",
323 value: "invalid response header",
324 })?;
325
326 if !header.response_code().is_success() {
327 return Err(KernelError::HardwareError {
328 device: "TPM",
329 code: header.response_code() as u32,
330 });
331 }
332
333 let bytes_len = u16::from_be_bytes([data[10], data[11]]) as usize;
334
335 if data.len() < 12 + bytes_len {
336 return Err(KernelError::InvalidArgument {
337 name: "tpm_response",
338 value: "invalid random bytes length",
339 });
340 }
341
342 let random_bytes = data[12..12 + bytes_len].to_vec();
343
344 Ok(Self { random_bytes })
345 }
346}
347
348#[derive(Debug, Clone)]
354pub struct PcrSelection {
355 pub hash_alg: u16,
357 pub pcr_bitmap: Vec<u8>,
359}
360
361pub struct TpmPcrReadCommand {
363 pcr_selection: PcrSelection,
364}
365
366impl TpmPcrReadCommand {
367 pub fn new(hash_alg: u16, pcr_indices: &[u8]) -> Self {
369 let mut bitmap = vec![0u8; 3];
370
371 for &pcr in pcr_indices {
372 if (pcr as usize) < 24 {
373 let byte_idx = (pcr / 8) as usize;
374 let bit_idx = pcr % 8;
375 bitmap[byte_idx] |= 1 << bit_idx;
376 }
377 }
378
379 Self {
380 pcr_selection: PcrSelection {
381 hash_alg,
382 pcr_bitmap: bitmap,
383 },
384 }
385 }
386
387 pub fn to_bytes(&self) -> Vec<u8> {
388 let mut params = Vec::new();
391
392 params.extend_from_slice(&1u32.to_be_bytes());
394
395 params.extend_from_slice(&self.pcr_selection.hash_alg.to_be_bytes());
397
398 params.push(self.pcr_selection.pcr_bitmap.len() as u8);
400
401 params.extend_from_slice(&self.pcr_selection.pcr_bitmap);
403
404 marshal_command(
405 TpmStructureTag::NoSessions,
406 TpmCommandCode::PcrRead,
407 ¶ms,
408 )
409 }
410}
411
412pub struct TpmPcrReadResponse {
414 pub pcr_update_counter: u32,
416 pub pcr_values: Vec<Vec<u8>>,
418}
419
420impl TpmPcrReadResponse {
421 pub fn parse(data: &[u8]) -> Result<Self, KernelError> {
426 if data.len() < 14 {
427 return Err(KernelError::InvalidArgument {
428 name: "tpm_pcr_response",
429 value: "response too short for PCR_Read",
430 });
431 }
432
433 let header = TpmResponseHeader::parse(data).ok_or(KernelError::InvalidArgument {
434 name: "tpm_pcr_response",
435 value: "invalid response header",
436 })?;
437 if !header.response_code().is_success() {
438 return Err(KernelError::HardwareError {
439 device: "TPM",
440 code: header.response_code() as u32,
441 });
442 }
443
444 let pcr_update_counter = u32::from_be_bytes([data[10], data[11], data[12], data[13]]);
445
446 let mut offset = 14;
448 if data.len() < offset + 4 {
449 return Err(KernelError::InvalidArgument {
450 name: "tpm_pcr_response",
451 value: "response too short for selection count",
452 });
453 }
454 let sel_count = u32::from_be_bytes([
455 data[offset],
456 data[offset + 1],
457 data[offset + 2],
458 data[offset + 3],
459 ]) as usize;
460 offset += 4;
461
462 for _ in 0..sel_count {
463 if data.len() < offset + 3 {
464 return Err(KernelError::InvalidArgument {
465 name: "tpm_pcr_response",
466 value: "response too short for selection entry",
467 });
468 }
469 offset += 2; let select_size = data[offset] as usize;
471 offset += 1 + select_size;
472 }
473
474 if data.len() < offset + 4 {
476 return Err(KernelError::InvalidArgument {
477 name: "tpm_pcr_response",
478 value: "response too short for digest count",
479 });
480 }
481 let digest_count = u32::from_be_bytes([
482 data[offset],
483 data[offset + 1],
484 data[offset + 2],
485 data[offset + 3],
486 ]) as usize;
487 offset += 4;
488
489 let mut pcr_values = Vec::with_capacity(digest_count);
490 for _ in 0..digest_count {
491 if data.len() < offset + 2 {
492 return Err(KernelError::InvalidArgument {
493 name: "tpm_pcr_response",
494 value: "response too short for digest size",
495 });
496 }
497 let digest_size = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
498 offset += 2;
499 if data.len() < offset + digest_size {
500 return Err(KernelError::InvalidArgument {
501 name: "tpm_pcr_response",
502 value: "response too short for digest data",
503 });
504 }
505 pcr_values.push(data[offset..offset + digest_size].to_vec());
506 offset += digest_size;
507 }
508
509 Ok(Self {
510 pcr_update_counter,
511 pcr_values,
512 })
513 }
514}
515
516pub struct TpmPcrExtendCommand {
526 pcr_index: u8,
528 digest: [u8; 32],
530}
531
532impl TpmPcrExtendCommand {
533 pub fn new(pcr_index: u8, digest: &[u8; 32]) -> Self {
534 Self {
535 pcr_index,
536 digest: *digest,
537 }
538 }
539
540 pub fn to_bytes(&self) -> Vec<u8> {
546 let mut params = Vec::new();
547
548 let pcr_handle: u32 = self.pcr_index as u32;
550 params.extend_from_slice(&pcr_handle.to_be_bytes());
551
552 let auth_session_size: u32 = 9; params.extend_from_slice(&auth_session_size.to_be_bytes());
561
562 params.extend_from_slice(&0x40000009u32.to_be_bytes());
564 params.extend_from_slice(&0u16.to_be_bytes());
566 params.push(0x01);
568 params.extend_from_slice(&0u16.to_be_bytes());
570
571 let digest_count: u32 = 1; params.extend_from_slice(&digest_count.to_be_bytes());
574
575 params.extend_from_slice(&super::tpm_commands::hash_alg::SHA256.to_be_bytes());
577
578 params.extend_from_slice(&self.digest);
580
581 marshal_command(
582 TpmStructureTag::Sessions,
583 TpmCommandCode::PcrExtend,
584 ¶ms,
585 )
586 }
587}
588
589pub struct TpmGetCapabilityCommand {
595 capability: u32,
597 property: u32,
599 property_count: u32,
601}
602
603impl TpmGetCapabilityCommand {
604 pub fn new(capability: u32, property: u32, property_count: u32) -> Self {
605 Self {
606 capability,
607 property,
608 property_count,
609 }
610 }
611
612 pub fn to_bytes(&self) -> Vec<u8> {
613 let mut params = Vec::new();
614 params.extend_from_slice(&self.capability.to_be_bytes());
615 params.extend_from_slice(&self.property.to_be_bytes());
616 params.extend_from_slice(&self.property_count.to_be_bytes());
617
618 marshal_command(
619 TpmStructureTag::NoSessions,
620 TpmCommandCode::GetCapability,
621 ¶ms,
622 )
623 }
624}
625
626pub mod capability {
628 pub const TPM_CAP_TPM_PROPERTIES: u32 = 0x00000006;
630 pub const TPM_CAP_ALGS: u32 = 0x00000000;
632 pub const TPM_CAP_PCRS: u32 = 0x00000005;
634
635 pub const PT_FAMILY_INDICATOR: u32 = 0x00000100;
637 pub const PT_FIRMWARE_VERSION_1: u32 = 0x00000111;
639 pub const PT_FIRMWARE_VERSION_2: u32 = 0x00000112;
641 pub const PT_MANUFACTURER: u32 = 0x00000105;
643}
644
645pub mod hash_alg {
647 pub const SHA1: u16 = 0x0004;
648 pub const SHA256: u16 = 0x000B;
649 pub const SHA384: u16 = 0x000C;
650 pub const SHA512: u16 = 0x000D;
651}
652
653#[cfg(test)]
654mod tests {
655 use super::*;
656
657 #[test]
658 fn test_startup_command() {
659 let cmd = TpmStartupCommand::new(TpmStartupType::Clear);
660 let bytes = cmd.to_bytes();
661
662 assert_eq!(bytes.len(), 12);
663 assert_eq!(u16::from_be_bytes([bytes[0], bytes[1]]), 0x8001); assert_eq!(
666 u32::from_be_bytes([bytes[2], bytes[3], bytes[4], bytes[5]]),
667 12
668 );
669 assert_eq!(
671 u32::from_be_bytes([bytes[6], bytes[7], bytes[8], bytes[9]]),
672 0x144
673 );
674 assert_eq!(u16::from_be_bytes([bytes[10], bytes[11]]), 0x0000);
676 }
677
678 #[test]
679 fn test_shutdown_command() {
680 let cmd = TpmShutdownCommand::new(TpmShutdownType::State);
681 let bytes = cmd.to_bytes();
682
683 assert_eq!(bytes.len(), 12);
684 assert_eq!(
686 u32::from_be_bytes([bytes[6], bytes[7], bytes[8], bytes[9]]),
687 0x145
688 );
689 assert_eq!(u16::from_be_bytes([bytes[10], bytes[11]]), 0x0001);
691 }
692
693 #[test]
694 fn test_get_random_command() {
695 let cmd = TpmGetRandomCommand::new(32);
696 let bytes = cmd.to_bytes();
697
698 assert_eq!(bytes.len(), 12);
699 assert_eq!(u16::from_be_bytes([bytes[10], bytes[11]]), 32);
700 }
701
702 #[test]
703 fn test_pcr_extend_command() {
704 let digest = [0xABu8; 32];
705 let cmd = TpmPcrExtendCommand::new(7, &digest);
706 let bytes = cmd.to_bytes();
707
708 assert_eq!(u16::from_be_bytes([bytes[0], bytes[1]]), 0x8002);
710
711 assert_eq!(
713 u32::from_be_bytes([bytes[6], bytes[7], bytes[8], bytes[9]]),
714 0x182
715 );
716
717 assert_eq!(
719 u32::from_be_bytes([bytes[10], bytes[11], bytes[12], bytes[13]]),
720 7
721 );
722 }
723
724 #[test]
725 fn test_response_header_parsing() {
726 let data = [
727 0x80, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, ];
731
732 let header = TpmResponseHeader::parse(&data).unwrap();
733 assert!(header.response_code().is_success());
734 }
735
736 #[test]
737 fn test_marshal_command_generic() {
738 let params = [0x01, 0x02, 0x03, 0x04];
739 let bytes = marshal_command(
740 TpmStructureTag::NoSessions,
741 TpmCommandCode::GetCapability,
742 ¶ms,
743 );
744
745 assert_eq!(bytes.len(), 14); assert_eq!(
747 u32::from_be_bytes([bytes[2], bytes[3], bytes[4], bytes[5]]),
748 14 );
750 }
751
752 #[test]
753 fn test_parse_response() {
754 let data = [
755 0x80, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF, ];
760
761 let (code, payload) = parse_response(&data).unwrap();
762 assert!(code.is_success());
763 assert_eq!(payload, &[0xDE, 0xAD, 0xBE, 0xEF]);
764 }
765
766 #[test]
767 fn test_get_random_response_parse() {
768 let data = [
769 0x80, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xDE, 0xAD, 0xBE, 0xEF, ];
775
776 let response = TpmGetRandomResponse::parse(&data).unwrap();
777 assert_eq!(response.random_bytes, vec![0xDE, 0xAD, 0xBE, 0xEF]);
778 }
779
780 #[test]
781 fn test_self_test_command() {
782 let cmd = TpmSelfTestCommand::new(true);
783 let bytes = cmd.to_bytes();
784
785 assert_eq!(bytes.len(), 11); assert_eq!(
787 u32::from_be_bytes([bytes[6], bytes[7], bytes[8], bytes[9]]),
788 0x143 );
790 assert_eq!(bytes[10], 1); }
792}