1use alloc::{vec, vec::Vec};
31
32use spin::Mutex;
33
34use super::tpm_commands::{
35 TpmGetRandomCommand, TpmPcrExtendCommand, TpmPcrReadCommand, TpmResponseHeader,
36 TpmStartupCommand, TpmStartupType,
37};
38use crate::error::KernelError;
39
40pub mod mmio {
42 pub const TPM2_BASE: usize = 0xFED40000;
44
45 #[cfg(target_arch = "aarch64")]
47 pub const TPM2_BASE_AARCH64: usize = 0x0C000000;
48
49 pub const CRB_LOC_STATE: usize = 0x0000;
51 pub const CRB_LOC_CTRL: usize = 0x0008;
53 pub const CRB_LOC_STS: usize = 0x000C;
55
56 pub const TPM_ACCESS: usize = 0x0000;
58 pub const TPM_STS: usize = 0x0018;
60 pub const TPM_DATA_FIFO: usize = 0x0024;
62 pub const TPM_INTERFACE_ID: usize = 0x0030;
64
65 pub const CRB_CTRL_REQ: usize = 0x0040;
67 pub const CRB_CTRL_STS: usize = 0x0044;
69 pub const CRB_CTRL_CANCEL: usize = 0x0048;
71 pub const CRB_CTRL_START: usize = 0x004C;
73
74 pub const CRB_CTRL_CMD_SIZE: usize = 0x0058;
76 pub const CRB_CTRL_CMD_LADDR: usize = 0x005C;
78 pub const CRB_CTRL_CMD_HADDR: usize = 0x0060;
80 pub const CRB_CTRL_RSP_SIZE: usize = 0x0064;
82 pub const CRB_CTRL_RSP_ADDR: usize = 0x0068;
84
85 pub const CRB_DATA_BUFFER: usize = 0x0080;
87
88 pub const CRB_BUFFER_SIZE: usize = 3968; pub const STS_COMMAND_READY: u32 = 1 << 6;
94 pub const STS_EXPECT: u32 = 1 << 3;
96 pub const STS_DATA_AVAIL: u32 = 1 << 4;
98 pub const STS_VALID: u32 = 1 << 7;
100
101 pub const CRB_START: u32 = 1;
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub enum TpmInterfaceType {
109 Mmio,
111 I2c,
113 Spi,
115 Firmware,
117 Software,
119 None,
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125#[repr(u32)]
126pub enum TpmCommand {
127 Startup = 0x144,
128 GetRandom = 0x17B,
129 PCRRead = 0x17E,
130 PCRExtend = 0x182,
131 CreatePrimary = 0x131,
132 Create = 0x153,
133 Load = 0x157,
134 Sign = 0x15D,
135 VerifySignature = 0x177,
136 Quote = 0x158,
137 Unseal = 0x15E,
138}
139
140pub type PcrIndex = u8;
142
143pub type TpmHandle = u32;
145
146pub type TpmResult<T> = Result<T, TpmError>;
148
149const PCR_COUNT: usize = 24;
151
152const MAX_SEALED_ENTRIES: usize = 4;
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum TpmError {
160 NotInitialized,
161 CommandFailed,
162 InvalidHandle,
163 AuthFailed,
164 NotSupported,
165 HardwareError,
166 InvalidPcr,
167 BufferTooSmall,
168 SealStorageFull,
169 UnsealFailed,
170 Timeout,
171}
172
173struct SoftPcrBank {
175 values: [[u8; 32]; PCR_COUNT],
177 extended: [bool; PCR_COUNT],
179}
180
181impl SoftPcrBank {
182 const fn new() -> Self {
183 Self {
184 values: [[0u8; 32]; PCR_COUNT],
185 extended: [false; PCR_COUNT],
186 }
187 }
188
189 fn extend(&mut self, index: usize, measurement: &[u8; 32]) {
191 use crate::crypto::hash::sha256;
192
193 let mut concat = [0u8; 64];
194 concat[..32].copy_from_slice(&self.values[index]);
195 concat[32..].copy_from_slice(measurement);
196
197 let hash = sha256(&concat);
198 self.values[index].copy_from_slice(hash.as_bytes());
199 self.extended[index] = true;
200 }
201
202 fn read(&self, index: usize) -> [u8; 32] {
204 self.values[index]
205 }
206}
207
208struct SealedEntry {
210 handle: u32,
212 pcr_policy: [[u8; 32]; PCR_COUNT],
214 pcr_mask: [bool; PCR_COUNT],
216 _sealed_data: Vec<u8>,
218 _salt: [u8; 32],
220 active: bool,
222}
223
224impl SealedEntry {
225 const fn empty() -> Self {
226 Self {
227 handle: 0,
228 pcr_policy: [[0u8; 32]; PCR_COUNT],
229 pcr_mask: [false; PCR_COUNT],
230 _sealed_data: Vec::new(),
231 _salt: [0u8; 32],
232 active: false,
233 }
234 }
235}
236
237pub struct Tpm {
241 initialized: bool,
242 locality: u8,
243 interface_type: TpmInterfaceType,
244 mmio_base: Option<usize>,
246 soft_pcrs: SoftPcrBank,
248 sealed_entries: [SealedEntry; MAX_SEALED_ENTRIES],
250 next_seal_handle: u32,
252}
253
254impl Tpm {
255 pub fn new() -> Self {
257 Self {
258 initialized: false,
259 locality: 0,
260 interface_type: TpmInterfaceType::None,
261 mmio_base: None,
262 soft_pcrs: SoftPcrBank::new(),
263 sealed_entries: [
264 SealedEntry::empty(),
265 SealedEntry::empty(),
266 SealedEntry::empty(),
267 SealedEntry::empty(),
268 ],
269 next_seal_handle: 0x80000100,
270 }
271 }
272
273 pub fn detect_hardware(&mut self) -> TpmResult<TpmInterfaceType> {
275 #[cfg(target_arch = "x86_64")]
277 {
278 if let Some(base) = self.try_detect_mmio(mmio::TPM2_BASE) {
279 crate::println!("[TPM] Detected MMIO TPM at 0x{:X}", base);
280 self.interface_type = TpmInterfaceType::Mmio;
281 self.mmio_base = Some(base);
282 return Ok(TpmInterfaceType::Mmio);
283 }
284 }
285
286 #[cfg(target_arch = "aarch64")]
288 {
289 crate::println!("[TPM] AArch64 TPM detection: device tree probe not implemented");
291 }
292
293 #[cfg(target_arch = "riscv64")]
295 {
296 crate::println!("[TPM] RISC-V TPM detection: device tree probe not implemented");
297 }
298
299 crate::println!("[TPM] No hardware TPM detected, using software emulation");
301 self.interface_type = TpmInterfaceType::Software;
302 Ok(TpmInterfaceType::Software)
303 }
304
305 #[cfg(target_arch = "x86_64")]
317 fn try_detect_mmio(&self, base: usize) -> Option<usize> {
318 let virt_base = tpm_map_mmio(base);
320 if virt_base == 0 {
321 return None;
322 }
323 if probe_hardware_tpm(virt_base) {
324 Some(virt_base)
325 } else {
326 None
327 }
328 }
329
330 fn request_locality(&mut self, locality: u8) -> TpmResult<()> {
335 self.locality = locality;
336
337 if let Some(base) = self.mmio_base {
338 if self.interface_type == TpmInterfaceType::Mmio {
339 let locality_offset = (locality as usize) * 0x1000;
340 let loc_ctrl = base + locality_offset + mmio::CRB_LOC_CTRL;
341
342 unsafe {
344 let ptr = loc_ctrl as *mut u32;
345 core::ptr::write_volatile(ptr, 1); }
347
348 let loc_sts = base + locality_offset + mmio::CRB_LOC_STS;
350 let mut retries = 1000u32;
351 loop {
352 let sts = unsafe { core::ptr::read_volatile(loc_sts as *const u32) };
354 if sts & 1 != 0 {
355 return Ok(());
357 }
358 retries -= 1;
359 if retries == 0 {
360 return Err(TpmError::Timeout);
361 }
362 }
363 }
364 }
365
366 Ok(())
368 }
369
370 fn send_command(&self, command: &[u8]) -> TpmResult<Vec<u8>> {
375 let base = match self.mmio_base {
376 Some(b) => b,
377 None => return Err(TpmError::HardwareError),
378 };
379
380 if command.len() > mmio::CRB_BUFFER_SIZE {
381 return Err(TpmError::BufferTooSmall);
382 }
383
384 let locality_offset = (self.locality as usize) * 0x1000;
385 let buf_addr = base + locality_offset + mmio::CRB_DATA_BUFFER;
386 let ctrl_start = base + locality_offset + mmio::CRB_CTRL_START;
387 let ctrl_sts = base + locality_offset + mmio::CRB_CTRL_STS;
388
389 unsafe {
392 for (i, &byte) in command.iter().enumerate() {
393 let ptr = (buf_addr + i) as *mut u8;
394 core::ptr::write_volatile(ptr, byte);
395 }
396 }
397
398 unsafe {
401 core::ptr::write_volatile(ctrl_start as *mut u32, mmio::CRB_START);
402 }
403
404 let mut retries = 100_000u32;
406 loop {
407 let start_val = unsafe { core::ptr::read_volatile(ctrl_start as *const u32) };
409 if start_val == 0 {
410 break; }
412 retries -= 1;
413 if retries == 0 {
414 return Err(TpmError::Timeout);
415 }
416 }
417
418 let sts = unsafe { core::ptr::read_volatile(ctrl_sts as *const u32) };
421 if sts & 1 != 0 {
422 return Err(TpmError::CommandFailed);
423 }
424
425 let mut header_bytes = [0u8; 10];
428 unsafe {
430 for (i, byte) in header_bytes.iter_mut().enumerate() {
431 *byte = core::ptr::read_volatile((buf_addr + i) as *const u8);
432 }
433 }
434
435 let response_header =
436 TpmResponseHeader::parse(&header_bytes).ok_or(TpmError::CommandFailed)?;
437
438 let response_size = response_header.size as usize;
439 if !(10..=mmio::CRB_BUFFER_SIZE).contains(&response_size) {
440 return Err(TpmError::CommandFailed);
441 }
442
443 let mut response = vec![0u8; response_size];
445 response[..10].copy_from_slice(&header_bytes);
446 #[allow(clippy::needless_range_loop)]
450 unsafe {
451 for i in 10..response_size {
452 response[i] = core::ptr::read_volatile((buf_addr + i) as *const u8);
453 }
454 }
455
456 Ok(response)
457 }
458
459 pub fn startup(&mut self) -> TpmResult<()> {
461 if self.interface_type == TpmInterfaceType::None {
463 self.detect_hardware()?;
464 }
465
466 match self.interface_type {
467 TpmInterfaceType::Mmio => {
468 crate::println!("[TPM] Performing CRB startup sequence...");
469
470 self.request_locality(0)?;
472
473 let cmd = TpmStartupCommand::new(TpmStartupType::Clear);
475 let cmd_bytes = cmd.to_bytes();
476
477 match self.send_command(&cmd_bytes) {
478 Ok(response) => {
479 let header =
480 TpmResponseHeader::parse(&response).ok_or(TpmError::CommandFailed)?;
481 if !header.response_code().is_success() {
482 let _rc = header.response_code;
483 crate::println!(
484 "[TPM] TPM2_Startup failed with response code 0x{:08X}",
485 _rc
486 );
487 return Err(TpmError::CommandFailed);
488 }
489 crate::println!("[TPM] TPM2_Startup(Clear) succeeded via CRB");
490 }
491 Err(e) => {
492 crate::println!("[TPM] TPM2_Startup command send failed: {:?}", e);
493 return Err(e);
494 }
495 }
496 }
497 TpmInterfaceType::Software => {
498 crate::println!("[TPM] Software TPM emulation active (no hardware)");
499 }
501 TpmInterfaceType::I2c | TpmInterfaceType::Spi => {
502 crate::println!("[TPM] I2C/SPI startup not yet implemented");
503 }
504 TpmInterfaceType::Firmware => {
505 crate::println!("[TPM] Firmware interface startup not yet implemented");
506 }
507 TpmInterfaceType::None => {
508 crate::println!("[TPM] No TPM interface available");
509 return Err(TpmError::NotSupported);
510 }
511 }
512
513 self.initialized = true;
514 crate::println!("[TPM] TPM 2.0 startup complete");
515 Ok(())
516 }
517
518 pub fn get_random(&self, num_bytes: usize) -> TpmResult<Vec<u8>> {
520 if !self.initialized {
521 return Err(TpmError::NotInitialized);
522 }
523
524 if num_bytes == 0 {
525 return Ok(Vec::new());
526 }
527
528 match self.interface_type {
529 TpmInterfaceType::Mmio => {
530 let mut result = Vec::with_capacity(num_bytes);
532 let mut remaining = num_bytes;
533
534 while remaining > 0 {
536 let request_size = core::cmp::min(remaining, 48) as u16;
537 let cmd = TpmGetRandomCommand::new(request_size);
538 let cmd_bytes = cmd.to_bytes();
539
540 let response = self.send_command(&cmd_bytes)?;
541 let header =
542 TpmResponseHeader::parse(&response).ok_or(TpmError::CommandFailed)?;
543
544 if !header.response_code().is_success() {
545 return Err(TpmError::CommandFailed);
546 }
547
548 if response.len() < 12 {
551 return Err(TpmError::CommandFailed);
552 }
553 let bytes_returned = u16::from_be_bytes([response[10], response[11]]) as usize;
554 if response.len() < 12 + bytes_returned {
555 return Err(TpmError::CommandFailed);
556 }
557 result.extend_from_slice(&response[12..12 + bytes_returned]);
558 remaining -= bytes_returned;
559 }
560
561 result.truncate(num_bytes);
562 Ok(result)
563 }
564 TpmInterfaceType::Software => {
565 let mut buffer = vec![0u8; num_bytes];
567 let rng = crate::crypto::random::get_random();
568 rng.fill_bytes(&mut buffer)
569 .map_err(|_| TpmError::HardwareError)?;
570 Ok(buffer)
571 }
572 _ => {
573 let mut buffer = vec![0u8; num_bytes];
575 let rng = crate::crypto::random::get_random();
576 rng.fill_bytes(&mut buffer)
577 .map_err(|_| TpmError::HardwareError)?;
578 Ok(buffer)
579 }
580 }
581 }
582
583 pub fn pcr_read(&self, pcr_index: PcrIndex) -> TpmResult<[u8; 32]> {
587 if !self.initialized {
588 return Err(TpmError::NotInitialized);
589 }
590
591 if pcr_index as usize >= PCR_COUNT {
592 return Err(TpmError::InvalidPcr);
593 }
594
595 match self.interface_type {
596 TpmInterfaceType::Mmio => {
597 let cmd =
599 TpmPcrReadCommand::new(super::tpm_commands::hash_alg::SHA256, &[pcr_index]);
600 let cmd_bytes = cmd.to_bytes();
601
602 let response = self.send_command(&cmd_bytes)?;
603 let header = TpmResponseHeader::parse(&response).ok_or(TpmError::CommandFailed)?;
604
605 if !header.response_code().is_success() {
606 return Err(TpmError::CommandFailed);
607 }
608
609 if response.len() < 10 + 4 + 8 + 4 + 32 {
614 return Err(TpmError::CommandFailed);
615 }
616
617 let digest_offset = response.len() - 32;
621 let mut pcr_value = [0u8; 32];
622 pcr_value.copy_from_slice(&response[digest_offset..]);
623 Ok(pcr_value)
624 }
625 TpmInterfaceType::Software => {
626 crate::println!("[TPM] Reading PCR {} (software)", pcr_index);
627 Ok(self.soft_pcrs.read(pcr_index as usize))
628 }
629 _ => {
630 crate::println!("[TPM] Reading PCR {} (unsupported interface)", pcr_index);
631 Ok([0u8; 32])
632 }
633 }
634 }
635
636 pub fn pcr_extend(&mut self, pcr_index: PcrIndex, data: &[u8; 32]) -> TpmResult<()> {
640 if !self.initialized {
641 return Err(TpmError::NotInitialized);
642 }
643
644 if pcr_index as usize >= PCR_COUNT {
645 return Err(TpmError::InvalidPcr);
646 }
647
648 crate::println!("[TPM] Extending PCR {} with measurement", pcr_index);
649
650 match self.interface_type {
651 TpmInterfaceType::Mmio => {
652 let cmd = TpmPcrExtendCommand::new(pcr_index, data);
654 let cmd_bytes = cmd.to_bytes();
655
656 let response = self.send_command(&cmd_bytes)?;
657 let header = TpmResponseHeader::parse(&response).ok_or(TpmError::CommandFailed)?;
658
659 if !header.response_code().is_success() {
660 return Err(TpmError::CommandFailed);
661 }
662
663 crate::println!("[TPM] PCR {} extended via hardware", pcr_index);
664 Ok(())
665 }
666 TpmInterfaceType::Software => {
667 self.soft_pcrs.extend(pcr_index as usize, data);
668 crate::println!("[TPM] PCR {} extended (software)", pcr_index);
669 Ok(())
670 }
671 _ => {
672 crate::println!("[TPM] PCR extend unsupported on {:?}", self.interface_type);
673 Ok(())
674 }
675 }
676 }
677
678 pub fn quote(&self, pcr_selection: &[PcrIndex], nonce: &[u8; 32]) -> TpmResult<Vec<u8>> {
682 if !self.initialized {
683 return Err(TpmError::NotInitialized);
684 }
685
686 crate::println!(
687 "[TPM] Creating attestation quote for {} PCRs",
688 pcr_selection.len()
689 );
690
691 let mut quote_data = Vec::with_capacity(32 + pcr_selection.len() * 32);
693 quote_data.extend_from_slice(nonce);
694
695 for &pcr_idx in pcr_selection {
696 let pcr_value = self.pcr_read(pcr_idx)?;
697 quote_data.extend_from_slice(&pcr_value);
698 }
699
700 let digest = crate::crypto::hash::sha256("e_data);
702 let mut result = Vec::with_capacity(64);
703 result.extend_from_slice(digest.as_bytes());
704 result.extend_from_slice("e_data);
706
707 Ok(result)
708 }
709
710 pub fn seal(&mut self, data: &[u8], pcr_selection: &[PcrIndex]) -> TpmResult<Vec<u8>> {
716 if !self.initialized {
717 return Err(TpmError::NotInitialized);
718 }
719
720 crate::println!(
721 "[TPM] Sealing {} bytes to {} PCRs",
722 data.len(),
723 pcr_selection.len()
724 );
725
726 let slot = self
728 .sealed_entries
729 .iter()
730 .position(|e| !e.active)
731 .ok_or(TpmError::SealStorageFull)?;
732
733 let mut salt = [0u8; 32];
735 let rng = crate::crypto::random::get_random();
736 rng.fill_bytes(&mut salt)
737 .map_err(|_| TpmError::HardwareError)?;
738
739 let mut pcr_policy = [[0u8; 32]; PCR_COUNT];
741 let mut pcr_mask = [false; PCR_COUNT];
742 for &pcr_idx in pcr_selection {
743 if (pcr_idx as usize) < PCR_COUNT {
744 pcr_policy[pcr_idx as usize] = self.pcr_read(pcr_idx)?;
745 pcr_mask[pcr_idx as usize] = true;
746 }
747 }
748
749 let key = self.derive_seal_key(&salt, &pcr_policy, &pcr_mask);
751
752 let sealed_data = xor_with_keystream(data, &key);
754
755 let handle = self.next_seal_handle;
756 self.next_seal_handle += 1;
757
758 self.sealed_entries[slot] = SealedEntry {
759 handle,
760 pcr_policy,
761 pcr_mask,
762 _sealed_data: sealed_data.clone(),
763 _salt: salt,
764 active: true,
765 };
766
767 let mut blob = Vec::with_capacity(4 + 32 + sealed_data.len());
769 blob.extend_from_slice(&handle.to_be_bytes());
770 blob.extend_from_slice(&salt);
771 blob.extend_from_slice(&sealed_data);
772
773 crate::println!(
774 "[TPM] Data sealed to handle 0x{:08X} ({} bytes)",
775 handle,
776 blob.len()
777 );
778
779 Ok(blob)
780 }
781
782 pub fn unseal(&self, sealed_blob: &[u8]) -> TpmResult<Vec<u8>> {
787 if !self.initialized {
788 return Err(TpmError::NotInitialized);
789 }
790
791 if sealed_blob.len() < 36 {
792 return Err(TpmError::UnsealFailed);
793 }
794
795 crate::println!("[TPM] Unsealing data blob ({} bytes)", sealed_blob.len());
796
797 let handle = u32::from_be_bytes([
799 sealed_blob[0],
800 sealed_blob[1],
801 sealed_blob[2],
802 sealed_blob[3],
803 ]);
804 let mut salt = [0u8; 32];
805 salt.copy_from_slice(&sealed_blob[4..36]);
806 let sealed_data = &sealed_blob[36..];
807
808 let entry = self
810 .sealed_entries
811 .iter()
812 .find(|e| e.active && e.handle == handle)
813 .ok_or(TpmError::InvalidHandle)?;
814
815 for i in 0..PCR_COUNT {
817 if entry.pcr_mask[i] {
818 let current = self.pcr_read(i as u8)?;
819 if current != entry.pcr_policy[i] {
820 crate::println!("[TPM] Unseal failed: PCR {} mismatch (policy violation)", i);
821 return Err(TpmError::AuthFailed);
822 }
823 }
824 }
825
826 let key = self.derive_seal_key(&salt, &entry.pcr_policy, &entry.pcr_mask);
828 let plaintext = xor_with_keystream(sealed_data, &key);
829
830 crate::println!(
831 "[TPM] Data unsealed from handle 0x{:08X} ({} bytes)",
832 handle,
833 plaintext.len()
834 );
835
836 Ok(plaintext)
837 }
838
839 fn derive_seal_key(
841 &self,
842 salt: &[u8; 32],
843 pcr_policy: &[[u8; 32]; PCR_COUNT],
844 pcr_mask: &[bool; PCR_COUNT],
845 ) -> [u8; 32] {
846 use crate::crypto::hash::sha256;
847
848 let mut key_material = Vec::with_capacity(32 + PCR_COUNT * 32 + 17);
851 key_material.extend_from_slice(salt);
852 for i in 0..PCR_COUNT {
853 if pcr_mask[i] {
854 key_material.extend_from_slice(&pcr_policy[i]);
855 }
856 }
857 key_material.extend_from_slice(b"veridian-tpm-seal");
858
859 let hash = sha256(&key_material);
860 *hash.as_bytes()
861 }
862
863 pub fn create_signing_key(&self) -> TpmResult<TpmHandle> {
865 if !self.initialized {
866 return Err(TpmError::NotInitialized);
867 }
868
869 crate::println!("[TPM] Creating signing key");
870
871 let handle: TpmHandle = 0x80000001;
873 Ok(handle)
874 }
875
876 pub fn sign(&self, handle: TpmHandle, data: &[u8]) -> TpmResult<Vec<u8>> {
878 if !self.initialized {
879 return Err(TpmError::NotInitialized);
880 }
881
882 crate::println!(
883 "[TPM] Signing {} bytes with key handle 0x{:08X}",
884 data.len(),
885 handle
886 );
887
888 let mut sign_input = Vec::with_capacity(4 + data.len() + 16);
890 sign_input.extend_from_slice(&handle.to_be_bytes());
891 sign_input.extend_from_slice(data);
892 sign_input.extend_from_slice(b"veridian-tpm-sig");
893
894 let hash = crate::crypto::hash::sha256(&sign_input);
895 Ok(hash.as_bytes().to_vec())
896 }
897
898 pub fn verify_signature(
900 &self,
901 handle: TpmHandle,
902 data: &[u8],
903 signature: &[u8],
904 ) -> TpmResult<bool> {
905 if !self.initialized {
906 return Err(TpmError::NotInitialized);
907 }
908
909 crate::println!("[TPM] Verifying signature with key handle 0x{:08X}", handle);
910
911 let expected = self.sign(handle, data)?;
913 Ok(expected == signature)
914 }
915
916 pub fn is_software_emulation(&self) -> bool {
918 self.interface_type == TpmInterfaceType::Software
919 }
920
921 pub fn is_initialized(&self) -> bool {
923 self.initialized
924 }
925}
926
927impl Default for Tpm {
928 fn default() -> Self {
929 Self::new()
930 }
931}
932
933fn xor_with_keystream(data: &[u8], key: &[u8; 32]) -> Vec<u8> {
938 use crate::crypto::hash::sha256;
939
940 let mut result = Vec::with_capacity(data.len());
941 let mut counter: u64 = 0;
942 let mut offset = 0;
943
944 while offset < data.len() {
945 let mut block_input = [0u8; 40]; block_input[..32].copy_from_slice(key);
948 block_input[32..40].copy_from_slice(&counter.to_le_bytes());
949
950 let keystream_hash = sha256(&block_input);
951 let keystream = keystream_hash.as_bytes();
952
953 let chunk_len = core::cmp::min(32, data.len() - offset);
954 for i in 0..chunk_len {
955 result.push(data[offset + i] ^ keystream[i]);
956 }
957
958 offset += chunk_len;
959 counter += 1;
960 }
961
962 result
963}
964
965static TPM: Mutex<Option<Tpm>> = Mutex::new(None);
967
968pub fn init() -> Result<(), KernelError> {
970 let mut tpm = Tpm::new();
971
972 match tpm.startup() {
974 Ok(()) => {
975 let _mode = if tpm.is_software_emulation() {
976 "software emulation"
977 } else {
978 "hardware"
979 };
980 crate::println!("[TPM] TPM 2.0 support initialized ({})", _mode);
981 *TPM.lock() = Some(tpm);
982 Ok(())
983 }
984 Err(_) => {
985 crate::println!("[TPM] TPM hardware not available (continuing without TPM support)");
987 Ok(())
988 }
989 }
990}
991
992pub fn with_tpm<R, F: FnOnce(&Tpm) -> R>(f: F) -> Option<R> {
996 let guard = TPM.lock();
997 guard.as_ref().map(f)
998}
999
1000pub fn with_tpm_mut<R, F: FnOnce(&mut Tpm) -> R>(f: F) -> Option<R> {
1004 let mut guard = TPM.lock();
1005 guard.as_mut().map(f)
1006}
1007
1008pub fn is_available() -> bool {
1010 TPM.lock().is_some()
1011}
1012
1013#[cfg(target_arch = "x86_64")]
1014fn tpm_map_mmio(phys_base: usize) -> usize {
1020 if phys_base == 0 {
1021 return 0;
1022 }
1023 crate::mm::phys_to_virt_addr(phys_base as u64) as usize
1024}
1025
1026#[cfg(target_arch = "x86_64")]
1027fn tpm_read_register(virt_base: usize, offset: usize) -> u32 {
1029 let ptr = (virt_base + offset) as *const u32;
1030 unsafe { core::ptr::read_volatile(ptr) }
1033}
1034
1035#[cfg(target_arch = "x86_64")]
1036#[allow(dead_code)]
1038fn tpm_write_register(virt_base: usize, offset: usize, value: u32) {
1039 let ptr = (virt_base + offset) as *mut u32;
1040 unsafe { core::ptr::write_volatile(ptr, value) }
1042}
1043
1044#[cfg(target_arch = "x86_64")]
1045fn probe_hardware_tpm(virt_base: usize) -> bool {
1050 let access = tpm_read_register(virt_base, 0x0000);
1052 if access == 0xFFFF_FFFF || access == 0 {
1055 return false;
1056 }
1057
1058 let intf_id = tpm_read_register(virt_base, 0x0030);
1060 let intf_type = intf_id & 0xF;
1062 if intf_type > 1 {
1063 return false; }
1065
1066 crate::println!(
1067 "[TPM] Hardware TPM detected: access=0x{:08x}, intf_id=0x{:08x}, type={}",
1068 access,
1069 intf_id,
1070 if intf_type == 0 { "TIS" } else { "CRB" }
1071 );
1072 true
1073}
1074
1075pub fn pcr_extend(pcr_index: PcrIndex, measurement: &[u8; 32]) -> Result<(), TpmError> {
1081 match with_tpm_mut(|tpm| tpm.pcr_extend(pcr_index, measurement)) {
1082 Some(result) => result,
1083 None => Ok(()), }
1085}
1086
1087pub fn pcr_read(pcr_index: PcrIndex) -> Result<[u8; 32], TpmError> {
1089 match with_tpm(|tpm| tpm.pcr_read(pcr_index)) {
1090 Some(result) => result,
1091 None => Err(TpmError::NotInitialized),
1092 }
1093}
1094
1095#[cfg(test)]
1096mod tests {
1097 use super::*;
1098
1099 #[test]
1100 fn test_tpm_creation() {
1101 let tpm = Tpm::new();
1102 assert!(!tpm.initialized);
1103 }
1104
1105 #[test]
1106 fn test_soft_pcr_extend() {
1107 let mut bank = SoftPcrBank::new();
1108 let measurement = [0x42u8; 32];
1109
1110 assert_eq!(bank.read(0), [0u8; 32]);
1112
1113 bank.extend(0, &measurement);
1115 assert!(bank.extended[0]);
1116 assert_ne!(bank.read(0), [0u8; 32]);
1118 }
1119
1120 #[test]
1121 fn test_xor_keystream_roundtrip() {
1122 let key = [0xABu8; 32];
1123 let plaintext = b"Hello, VeridianOS TPM!";
1124
1125 let encrypted = xor_with_keystream(plaintext, &key);
1126 assert_ne!(&encrypted[..], &plaintext[..]);
1127
1128 let decrypted = xor_with_keystream(&encrypted, &key);
1129 assert_eq!(&decrypted[..], &plaintext[..]);
1130 }
1131}