1#![deny(clippy::all)]
6#![deny(clippy::pedantic)]
7
8use nix::{
9 fcntl,
10 poll::{poll, PollFd, PollFlags},
11};
12use rand::{thread_rng, RngCore};
13use std::{
14 cell::RefCell,
15 fs::{File, OpenOptions},
16 io::{Read, Write},
17 os::fd::{AsFd, AsRawFd},
18 path::{Path, PathBuf},
19 rc::Rc,
20 time::{Duration, Instant},
21};
22
23use thiserror::Error;
24use tpm2_crypto::TpmHash;
25use tpm2_protocol::{
26 basic::{Tpm2b as Tpm2bWire, TpmBuffer, TpmHandle, TpmList, TpmUint16, TpmUint32, TpmUint64},
27 constant::{MAX_HANDLES, TPM_MAX_COMMAND_SIZE},
28 data::{
29 Tpm2bDigest, Tpm2bEccParameter, Tpm2bEncryptedSecret, Tpm2bName, Tpm2bNonce,
30 Tpm2bPublicKeyRsa, Tpm2bSymKey, TpmAlgId, TpmCap, TpmCc, TpmEccCurve, TpmHt, TpmPt, TpmRc,
31 TpmRcBase, TpmRh, TpmSe, TpmSt, TpmaAlgorithm, TpmaCc, TpmaObject, TpmaSession, TpmiYesNo,
32 TpmsAlgProperty, TpmsAuthCommand, TpmsCapabilityData, TpmsContext, TpmsEccParms,
33 TpmsEccPoint, TpmsKeyedhashParms, TpmsPcrSelect, TpmsPcrSelection, TpmsRsaParms,
34 TpmsSchemeHash, TpmsSchemeXor, TpmsSymcipherParms, TpmsTaggedProperty, TpmtEccScheme,
35 TpmtKdfScheme, TpmtKeyedhashScheme, TpmtPublic, TpmtRsaScheme, TpmtSymDefObject,
36 TpmuAsymScheme, TpmuCapabilities, TpmuKdfScheme, TpmuKeyedhashScheme, TpmuPublicId,
37 TpmuPublicParms, TpmuSymKeyBits, TpmuSymMode,
38 },
39 frame::{
40 tpm_marshal_command, TpmAuthCommands, TpmCommandValue as TpmCommand, TpmContextLoadCommand,
41 TpmContextSaveCommand, TpmFlushContextCommand, TpmFrame, TpmGetCapabilityCommand,
42 TpmReadPublicCommand, TpmResponse, TpmResponseView, TpmStartAuthSessionCommand,
43 },
44 TpmCast, TpmError, TpmField, TpmWriter,
45};
46use tracing::{debug, trace};
47
48#[derive(Debug, Error)]
50pub enum TpmDeviceError {
51 #[error("device is already borrowed")]
53 AlreadyBorrowed,
54
55 #[error("capability not found: {0}")]
57 CapabilityMissing(TpmCap),
58
59 #[error("operation interrupted by user")]
60 Interrupted,
61
62 #[error("invalid CC: {0}")]
64 InvalidCc(tpm2_protocol::data::TpmCc),
65
66 #[error("invalid response")]
68 InvalidResponse,
69
70 #[error("I/O: {0}")]
72 Io(#[from] std::io::Error),
73
74 #[error("marshal: {0}")]
76 Marshal(TpmError),
77
78 #[error("device not available")]
80 NotAvailable,
81
82 #[error("operation failed")]
84 OperationFailed,
85
86 #[error("PCR banks not available")]
88 PcrBanksNotAvailable,
89
90 #[error("PCR bank selection mismatch")]
92 PcrBankSelectionMismatch,
93
94 #[error("response mismatch: {0}")]
96 ResponseMismatch(TpmCc),
97
98 #[error("TPM command timed out")]
100 Timeout,
101
102 #[error("TPM return code: {0}")]
104 TpmRc(TpmRc),
105
106 #[error("trailing data")]
108 TrailingData,
109
110 #[error("unmarshal: {0}")]
112 Unmarshal(TpmError),
113
114 #[error("unexpected EOF")]
116 UnexpectedEof,
117}
118
119impl From<TpmRc> for TpmDeviceError {
120 fn from(rc: TpmRc) -> Self {
121 Self::TpmRc(rc)
122 }
123}
124
125impl From<nix::Error> for TpmDeviceError {
126 fn from(err: nix::Error) -> Self {
127 Self::Io(std::io::Error::from_raw_os_error(err as i32))
128 }
129}
130
131pub fn with_device<F, T, E>(device: Option<Rc<RefCell<TpmDevice>>>, function: F) -> Result<T, E>
145where
146 F: FnOnce(&mut TpmDevice) -> Result<T, E>,
147 E: From<TpmDeviceError>,
148{
149 let device_rc = device.ok_or(TpmDeviceError::NotAvailable)?;
150 let mut device_guard = device_rc
151 .try_borrow_mut()
152 .map_err(|_| TpmDeviceError::AlreadyBorrowed)?;
153 function(&mut device_guard)
154}
155
156pub struct TpmDeviceBuilder {
158 path: PathBuf,
159 timeout: Duration,
160 interrupted: Box<dyn Fn() -> bool>,
161}
162
163impl Default for TpmDeviceBuilder {
164 fn default() -> Self {
165 Self {
166 path: PathBuf::from("/dev/tpmrm0"),
167 timeout: Duration::from_secs(120),
168 interrupted: Box::new(|| false),
169 }
170 }
171}
172
173impl TpmDeviceBuilder {
174 #[must_use]
176 pub fn with_path<P: AsRef<Path>>(mut self, path: P) -> Self {
177 self.path = path.as_ref().to_path_buf();
178 self
179 }
180
181 #[must_use]
183 pub fn with_timeout(mut self, timeout: Duration) -> Self {
184 self.timeout = timeout;
185 self
186 }
187
188 #[must_use]
190 pub fn with_interrupted<F>(mut self, handler: F) -> Self
191 where
192 F: Fn() -> bool + 'static,
193 {
194 self.interrupted = Box::new(handler);
195 self
196 }
197
198 pub fn build(self) -> Result<TpmDevice, TpmDeviceError> {
205 let file = OpenOptions::new()
206 .read(true)
207 .write(true)
208 .open(&self.path)
209 .map_err(TpmDeviceError::Io)?;
210
211 let fd = file.as_raw_fd();
212 let flags = fcntl::fcntl(fd, fcntl::FcntlArg::F_GETFL)?;
213 let mut oflags = fcntl::OFlag::from_bits_truncate(flags);
214 oflags.insert(fcntl::OFlag::O_NONBLOCK);
215 fcntl::fcntl(fd, fcntl::FcntlArg::F_SETFL(oflags))?;
216
217 Ok(TpmDevice {
218 file,
219 interrupted: self.interrupted,
220 timeout: self.timeout,
221 command: Vec::with_capacity(TPM_MAX_COMMAND_SIZE),
222 response: Vec::with_capacity(TPM_MAX_COMMAND_SIZE),
223 })
224 }
225}
226
227pub struct TpmDevice {
228 file: File,
229 interrupted: Box<dyn Fn() -> bool>,
230 timeout: Duration,
231 command: Vec<u8>,
232 response: Vec<u8>,
233}
234
235impl std::fmt::Debug for TpmDevice {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 f.debug_struct("Device")
238 .field("file", &self.file)
239 .field("timeout", &self.timeout)
240 .finish_non_exhaustive()
241 }
242}
243
244impl TpmDevice {
245 const NO_SESSIONS: &'static [TpmsAuthCommand] = &[];
246
247 #[must_use]
249 pub fn builder() -> TpmDeviceBuilder {
250 TpmDeviceBuilder::default()
251 }
252
253 fn receive(&mut self, buf: &mut [u8]) -> Result<usize, TpmDeviceError> {
254 let fd = self.file.as_fd();
255 let mut fds = [PollFd::new(fd, PollFlags::POLLIN)];
256
257 let num_events = match poll(&mut fds, 100u16) {
258 Ok(num) => num,
259 Err(nix::Error::EINTR) => return Ok(0),
260 Err(e) => return Err(e.into()),
261 };
262
263 if num_events == 0 {
264 return Ok(0);
265 }
266
267 let revents = fds[0].revents().unwrap_or(PollFlags::empty());
268
269 if revents.intersects(PollFlags::POLLERR | PollFlags::POLLNVAL) {
270 return Err(TpmDeviceError::UnexpectedEof);
271 }
272
273 if revents.contains(PollFlags::POLLIN) {
274 match self.file.read(buf) {
275 Ok(0) => Err(TpmDeviceError::UnexpectedEof),
276 Ok(n) => Ok(n),
277 Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(0),
278 Err(e) if e.kind() == std::io::ErrorKind::Interrupted => Ok(0),
279 Err(e) => Err(e.into()),
280 }
281 } else if revents.contains(PollFlags::POLLHUP) {
282 Err(TpmDeviceError::UnexpectedEof)
283 } else {
284 Ok(0)
285 }
286 }
287
288 pub fn transmit<C: TpmFrame>(
306 &mut self,
307 command: &C,
308 sessions: &[TpmsAuthCommand],
309 ) -> Result<&TpmResponse, TpmDeviceError> {
310 self.prepare_command(command, sessions)?;
311 let cc = command.cc();
312
313 self.file.write_all(&self.command)?;
314 self.file.flush()?;
315
316 let start_time = Instant::now();
317 self.response.clear();
318 let mut total_size: Option<usize> = None;
319 let mut temp_buf = [0u8; 1024];
320
321 loop {
322 if (self.interrupted)() {
323 return Err(TpmDeviceError::Interrupted);
324 }
325 if start_time.elapsed() > self.timeout {
326 return Err(TpmDeviceError::Timeout);
327 }
328
329 let n = self.receive(&mut temp_buf)?;
330 if n > 0 {
331 self.response.extend_from_slice(&temp_buf[..n]);
332 }
333
334 if total_size.is_none() && self.response.len() >= 10 {
335 let Ok(size_bytes): Result<[u8; 4], _> = self.response[2..6].try_into() else {
336 return Err(TpmDeviceError::OperationFailed);
337 };
338 let size = u32::from_be_bytes(size_bytes) as usize;
339 if !(10..={ TPM_MAX_COMMAND_SIZE }).contains(&size) {
340 return Err(TpmDeviceError::OperationFailed);
341 }
342 total_size = Some(size);
343 }
344
345 if let Some(size) = total_size {
346 if self.response.len() == size {
347 break;
348 }
349 if self.response.len() > size {
350 return Err(TpmDeviceError::TrailingData);
351 }
352 }
353 }
354
355 let response = TpmResponse::cast(&self.response).map_err(TpmDeviceError::Unmarshal)?;
356 let result = TpmResponseView::cast(cc, response).map_err(TpmDeviceError::Unmarshal)?;
357 trace!("{} R: {}", cc, hex::encode(&self.response));
358 result.map(|_| response).map_err(TpmDeviceError::TpmRc)
359 }
360
361 fn prepare_command<C: TpmFrame>(
362 &mut self,
363 command: &C,
364 sessions: &[TpmsAuthCommand],
365 ) -> Result<(), TpmDeviceError> {
366 let cc = command.cc();
367 let tag = if sessions.is_empty() {
368 TpmSt::NoSessions
369 } else {
370 TpmSt::Sessions
371 };
372
373 self.command.resize(TPM_MAX_COMMAND_SIZE, 0);
374
375 let len = {
376 let mut writer = TpmWriter::new(&mut self.command);
377 tpm_marshal_command(command, tag, sessions, &mut writer)
378 .map_err(TpmDeviceError::Marshal)?;
379 writer.len()
380 };
381 self.command.truncate(len);
382
383 trace!("{} C: {}", cc, hex::encode(&self.command));
384 Ok(())
385 }
386
387 fn get_capability<T, F, N>(
397 &mut self,
398 cap: TpmCap,
399 property_start: u32,
400 count: u32,
401 mut extract: F,
402 next_prop: N,
403 ) -> Result<Vec<T>, TpmDeviceError>
404 where
405 T: Copy,
406 F: for<'a> FnMut(&'a TpmuCapabilities) -> Result<&'a [T], TpmDeviceError>,
407 N: Fn(&T) -> u32,
408 {
409 let mut results = Vec::new();
410 let mut prop = property_start;
411 loop {
412 let (more_data, cap_data) =
413 self.get_capability_page(cap, TpmUint32::from(prop), TpmUint32::from(count))?;
414 let items: &[T] = extract(&cap_data.data)?;
415 results.extend_from_slice(items);
416
417 if more_data {
418 if let Some(last) = items.last() {
419 prop = next_prop(last);
420 } else {
421 break;
422 }
423 } else {
424 break;
425 }
426 }
427 Ok(results)
428 }
429
430 pub fn fetch_algorithm_properties(&mut self) -> Result<Vec<TpmsAlgProperty>, TpmDeviceError> {
439 self.get_capability(
440 TpmCap::Algs,
441 0,
442 u32::try_from(MAX_HANDLES).map_err(|_| TpmDeviceError::OperationFailed)?,
443 |caps| match caps {
444 TpmuCapabilities::Algs(algs) => Ok(algs),
445 _ => Err(TpmDeviceError::CapabilityMissing(TpmCap::Algs)),
446 },
447 |last| u32::from(last.alg.value()) + 1,
448 )
449 }
450
451 pub fn fetch_handles(&mut self, class: TpmHt) -> Result<Vec<TpmHandle>, TpmDeviceError> {
460 self.get_capability(
461 TpmCap::Handles,
462 (class as u32) << 24,
463 u32::try_from(MAX_HANDLES).map_err(|_| TpmDeviceError::OperationFailed)?,
464 |caps| match caps {
465 TpmuCapabilities::Handles(handles) => Ok(handles),
466 _ => Err(TpmDeviceError::CapabilityMissing(TpmCap::Handles)),
467 },
468 |last| last.value() + 1,
469 )
470 .map(|handles| handles.into_iter().collect())
471 }
472
473 pub fn fetch_ecc_curves(&mut self) -> Result<Vec<TpmEccCurve>, TpmDeviceError> {
482 self.get_capability(
483 TpmCap::EccCurves,
484 0,
485 u32::try_from(MAX_HANDLES).map_err(|_| TpmDeviceError::OperationFailed)?,
486 |caps| match caps {
487 TpmuCapabilities::EccCurves(curves) => Ok(curves),
488 _ => Err(TpmDeviceError::CapabilityMissing(TpmCap::EccCurves)),
489 },
490 |last| u32::from(last.value()) + 1,
491 )
492 }
493
494 pub fn fetch_pcr_bank_list(
509 &mut self,
510 ) -> Result<(Vec<TpmAlgId>, TpmsPcrSelect), TpmDeviceError> {
511 let pcrs: Vec<TpmsPcrSelection> = self.get_capability(
512 TpmCap::Pcrs,
513 0,
514 u32::try_from(MAX_HANDLES).map_err(|_| TpmDeviceError::OperationFailed)?,
515 |caps| match caps {
516 TpmuCapabilities::Pcrs(pcrs) => Ok(pcrs),
517 _ => Err(TpmDeviceError::CapabilityMissing(TpmCap::Pcrs)),
518 },
519 |last| last.hash as u32 + 1,
520 )?;
521
522 if pcrs.is_empty() {
523 return Err(TpmDeviceError::PcrBanksNotAvailable);
524 }
525
526 let mut common_select: Option<TpmsPcrSelect> = None;
527 let mut algs = Vec::with_capacity(pcrs.len());
528
529 for bank in pcrs {
530 if bank.pcr_select.iter().all(|&b| b == 0) {
531 debug!(
532 "skipping unallocated bank {:?} (mask: {})",
533 bank.hash,
534 hex::encode(&*bank.pcr_select)
535 );
536 continue;
537 }
538
539 if let Some(ref select) = common_select {
540 if bank.pcr_select != *select {
541 return Err(TpmDeviceError::PcrBankSelectionMismatch);
542 }
543 } else {
544 common_select = Some(bank.pcr_select);
545 }
546 algs.push(bank.hash);
547 }
548
549 let select = common_select.ok_or(TpmDeviceError::PcrBanksNotAvailable)?;
550
551 algs.sort();
552 Ok((algs, select))
553 }
554
555 fn get_capability_page(
565 &mut self,
566 cap: TpmCap,
567 property: TpmUint32,
568 property_count: TpmUint32,
569 ) -> Result<(bool, TpmsCapabilityData), TpmDeviceError> {
570 let cmd = TpmGetCapabilityCommand {
571 cap,
572 property,
573 property_count,
574 handles: [],
575 };
576
577 let response = self.transmit(&cmd, Self::NO_SESSIONS)?;
578 let (_, parameters) = response_parts(response, 0)?;
579 let (more_data, parameters) = parse_field_value::<TpmiYesNo>(parameters)?;
580 let (capability_data, rest) = parse_capability_data(parameters)?;
581 ensure_empty(rest)?;
582
583 Ok((more_data.into(), capability_data))
584 }
585
586 pub fn get_tpm_property(&mut self, property: TpmPt) -> Result<TpmUint32, TpmDeviceError> {
595 let (_, cap_data) = self.get_capability_page(
596 TpmCap::TpmProperties,
597 TpmUint32::from(property as u32),
598 TpmUint32::from(1),
599 )?;
600
601 let TpmuCapabilities::TpmProperties(props) = &cap_data.data else {
602 return Err(TpmDeviceError::CapabilityMissing(TpmCap::TpmProperties));
603 };
604
605 let Some(prop) = props.iter().find(|prop| prop.property == property) else {
606 return Err(TpmDeviceError::CapabilityMissing(TpmCap::TpmProperties));
607 };
608
609 Ok(prop.value)
610 }
611
612 pub fn read_public(
621 &mut self,
622 handle: TpmHandle,
623 ) -> Result<(TpmtPublic, Tpm2bName), TpmDeviceError> {
624 let cmd = TpmReadPublicCommand { handles: [handle] };
625 let response = self.transmit(&cmd, Self::NO_SESSIONS)?;
626 let (_, parameters) = response_parts(response, 0)?;
627 let (public, parameters) = parse_tpm2b_public(parameters)?;
628 let (name, parameters): (Tpm2bName, _) = parse_tpm2b_buffer(parameters)?;
629 let (_qualified_name, rest): (Tpm2bName, _) = parse_tpm2b_buffer(parameters)?;
630 ensure_empty(rest)?;
631
632 Ok((public, name))
633 }
634
635 pub fn find_persistent(
644 &mut self,
645 target_name: &Tpm2bName,
646 ) -> Result<Option<TpmHandle>, TpmDeviceError> {
647 for handle in self.fetch_handles(TpmHt::Persistent)? {
648 match self.read_public(handle) {
649 Ok((_, name)) => {
650 if name == *target_name {
651 return Ok(Some(handle));
652 }
653 }
654 Err(TpmDeviceError::TpmRc(rc)) => {
655 let base = rc.base();
656 if base == TpmRcBase::ReferenceH0 || base == TpmRcBase::Handle {
657 continue;
658 }
659 return Err(TpmDeviceError::TpmRc(rc));
660 }
661 Err(e) => return Err(e),
662 }
663 }
664 Ok(None)
665 }
666
667 pub fn save_context(&mut self, save_handle: TpmHandle) -> Result<TpmsContext, TpmDeviceError> {
676 let cmd = TpmContextSaveCommand {
677 handles: [save_handle],
678 };
679 let response = self.transmit(&cmd, Self::NO_SESSIONS)?;
680 let (_, parameters) = response_parts(response, 0)?;
681 let (context, rest) = parse_tpms_context(parameters)?;
682 ensure_empty(rest)?;
683
684 Ok(context)
685 }
686
687 pub fn load_context(&mut self, context: TpmsContext) -> Result<TpmHandle, TpmDeviceError> {
696 let cmd = TpmContextLoadCommand {
697 context,
698 handles: [],
699 };
700 let response = self.transmit(&cmd, Self::NO_SESSIONS)?;
701 let (handles, parameters) = response_parts(response, 1)?;
702 let (handle, rest) = parse_wire_copy::<TpmHandle>(handles)?;
703 ensure_empty(rest)?;
704 ensure_empty(parameters)?;
705
706 Ok(handle)
707 }
708
709 pub fn flush_context(&mut self, handle: TpmHandle) -> Result<(), TpmDeviceError> {
717 let cmd = TpmFlushContextCommand {
718 flush_handle: handle,
719 handles: [],
720 };
721 self.transmit(&cmd, Self::NO_SESSIONS)?;
722 Ok(())
723 }
724
725 pub fn flush_session(&mut self, context: TpmsContext) -> Result<(), TpmDeviceError> {
732 match self.load_context(context) {
733 Ok(handle) => self.flush_context(handle),
734 Err(TpmDeviceError::TpmRc(rc)) => {
735 let base = rc.base();
736 if base == TpmRcBase::ReferenceH0 || base == TpmRcBase::Handle {
737 Ok(())
738 } else {
739 Err(TpmDeviceError::TpmRc(rc))
740 }
741 }
742 Err(e) => Err(e),
743 }
744 }
745}
746
747fn ensure_empty(buf: &[u8]) -> Result<(), TpmDeviceError> {
748 if buf.is_empty() {
749 Ok(())
750 } else {
751 Err(TpmDeviceError::TrailingData)
752 }
753}
754
755fn response_parts(
756 response: &TpmResponse,
757 response_handles: usize,
758) -> Result<(&[u8], &[u8]), TpmDeviceError> {
759 let handle_len = response_handles
760 .checked_mul(core::mem::size_of::<TpmHandle>())
761 .ok_or(TpmDeviceError::InvalidResponse)?;
762 let body = response.body();
763 if body.len() < handle_len {
764 return Err(TpmDeviceError::InvalidResponse);
765 }
766
767 let (handles, after_handles) = body.split_at(handle_len);
768 if response.tag().map_err(TpmDeviceError::Unmarshal)? != TpmSt::Sessions {
769 return Ok((handles, after_handles));
770 }
771
772 let (parameter_size, after_size) = parse_wire_copy::<TpmUint32>(after_handles)?;
773 let parameter_size =
774 usize::try_from(parameter_size.get()).map_err(|_| TpmDeviceError::InvalidResponse)?;
775 if after_size.len() < parameter_size {
776 return Err(TpmDeviceError::InvalidResponse);
777 }
778
779 let (parameters, _auth_area) = after_size.split_at(parameter_size);
780 Ok((handles, parameters))
781}
782
783fn parse_wire_copy<'a, T>(buf: &'a [u8]) -> Result<(T, &'a [u8]), TpmDeviceError>
784where
785 T: TpmCast + Copy + 'a,
786{
787 let (value, rest) = T::cast_prefix(buf).map_err(TpmDeviceError::Unmarshal)?;
788 Ok((*value, rest))
789}
790
791fn parse_field_value<'a, T>(buf: &'a [u8]) -> Result<(T, &'a [u8]), TpmDeviceError>
792where
793 T: TpmField<'a, View = T>,
794{
795 <T as TpmField<'a>>::cast_prefix_field(buf).map_err(TpmDeviceError::Unmarshal)
796}
797
798fn parse_tpm2b_buffer<const CAPACITY: usize>(
799 buf: &[u8],
800) -> Result<(TpmBuffer<CAPACITY>, &[u8]), TpmDeviceError> {
801 let (value, rest) =
802 Tpm2bWire::<CAPACITY>::cast_prefix(buf).map_err(TpmDeviceError::Unmarshal)?;
803 let value =
804 TpmBuffer::<CAPACITY>::try_from(value.payload()).map_err(TpmDeviceError::Unmarshal)?;
805
806 Ok((value, rest))
807}
808
809fn parse_tpms_scheme_hash(buf: &[u8]) -> Result<(TpmsSchemeHash, &[u8]), TpmDeviceError> {
810 let (hash_alg, rest) = parse_field_value::<TpmAlgId>(buf)?;
811
812 Ok((TpmsSchemeHash { hash_alg }, rest))
813}
814
815fn parse_tpmt_kdf_scheme(buf: &[u8]) -> Result<(TpmtKdfScheme, &[u8]), TpmDeviceError> {
816 let (scheme, buf) = parse_field_value::<TpmAlgId>(buf)?;
817 let (details, rest) = parse_tpmu_kdf_scheme(scheme, buf)?;
818
819 Ok((TpmtKdfScheme { scheme, details }, rest))
820}
821
822fn parse_tpmu_kdf_scheme(
823 scheme: TpmAlgId,
824 buf: &[u8],
825) -> Result<(TpmuKdfScheme, &[u8]), TpmDeviceError> {
826 match scheme {
827 TpmAlgId::Mgf1 => {
828 let (details, rest) = parse_tpms_scheme_hash(buf)?;
829 Ok((TpmuKdfScheme::Mgf1(details), rest))
830 }
831 TpmAlgId::Kdf1Sp800_56A => {
832 let (details, rest) = parse_tpms_scheme_hash(buf)?;
833 Ok((TpmuKdfScheme::Kdf1Sp800_56a(details), rest))
834 }
835 TpmAlgId::Kdf2 => {
836 let (details, rest) = parse_tpms_scheme_hash(buf)?;
837 Ok((TpmuKdfScheme::Kdf2(details), rest))
838 }
839 TpmAlgId::Kdf1Sp800_108 => {
840 let (details, rest) = parse_tpms_scheme_hash(buf)?;
841 Ok((TpmuKdfScheme::Kdf1Sp800_108(details), rest))
842 }
843 TpmAlgId::Null => Ok((TpmuKdfScheme::Null, buf)),
844 _ => Err(TpmDeviceError::InvalidResponse),
845 }
846}
847
848fn parse_tpms_scheme_xor(buf: &[u8]) -> Result<(TpmsSchemeXor, &[u8]), TpmDeviceError> {
849 let (hash_alg, buf) = parse_field_value::<TpmAlgId>(buf)?;
850 let (kdf, rest) = parse_tpmt_kdf_scheme(buf)?;
851
852 Ok((TpmsSchemeXor { hash_alg, kdf }, rest))
853}
854
855fn parse_tpmu_keyedhash_scheme(
856 scheme: TpmAlgId,
857 buf: &[u8],
858) -> Result<(TpmuKeyedhashScheme, &[u8]), TpmDeviceError> {
859 match scheme {
860 TpmAlgId::Hmac => {
861 let (details, rest) = parse_tpms_scheme_hash(buf)?;
862 Ok((TpmuKeyedhashScheme::Hmac(details), rest))
863 }
864 TpmAlgId::Xor => {
865 let (details, rest) = parse_tpms_scheme_xor(buf)?;
866 Ok((TpmuKeyedhashScheme::Xor(details), rest))
867 }
868 TpmAlgId::Null => Ok((TpmuKeyedhashScheme::Null, buf)),
869 _ => Err(TpmDeviceError::InvalidResponse),
870 }
871}
872
873fn parse_tpmt_keyedhash_scheme(buf: &[u8]) -> Result<(TpmtKeyedhashScheme, &[u8]), TpmDeviceError> {
874 let (scheme, buf) = parse_field_value::<TpmAlgId>(buf)?;
875 let (details, rest) = parse_tpmu_keyedhash_scheme(scheme, buf)?;
876
877 Ok((TpmtKeyedhashScheme { scheme, details }, rest))
878}
879
880fn parse_tpmu_asym_scheme(
881 scheme: TpmAlgId,
882 buf: &[u8],
883) -> Result<(TpmuAsymScheme, &[u8]), TpmDeviceError> {
884 match scheme {
885 TpmAlgId::Rsassa
886 | TpmAlgId::Rsapss
887 | TpmAlgId::Ecdsa
888 | TpmAlgId::Ecdaa
889 | TpmAlgId::Sm2
890 | TpmAlgId::Ecschnorr
891 | TpmAlgId::Oaep
892 | TpmAlgId::Ecdh
893 | TpmAlgId::Ecmqv => {
894 let (details, rest) = parse_tpms_scheme_hash(buf)?;
895 Ok((TpmuAsymScheme::Hash(details), rest))
896 }
897 TpmAlgId::Rsaes | TpmAlgId::Null => Ok((TpmuAsymScheme::Null, buf)),
898 _ => Err(TpmDeviceError::InvalidResponse),
899 }
900}
901
902fn parse_tpmt_rsa_scheme(buf: &[u8]) -> Result<(TpmtRsaScheme, &[u8]), TpmDeviceError> {
903 let (scheme, buf) = parse_field_value::<TpmAlgId>(buf)?;
904 let (details, rest) = parse_tpmu_asym_scheme(scheme, buf)?;
905
906 Ok((TpmtRsaScheme { scheme, details }, rest))
907}
908
909fn parse_tpmt_ecc_scheme(buf: &[u8]) -> Result<(TpmtEccScheme, &[u8]), TpmDeviceError> {
910 let (scheme, buf) = parse_field_value::<TpmAlgId>(buf)?;
911 let (details, rest) = parse_tpmu_asym_scheme(scheme, buf)?;
912
913 Ok((TpmtEccScheme { scheme, details }, rest))
914}
915
916fn parse_tpmu_sym_key_bits(
917 algorithm: TpmAlgId,
918 buf: &[u8],
919) -> Result<(TpmuSymKeyBits, &[u8]), TpmDeviceError> {
920 match algorithm {
921 TpmAlgId::Aes => {
922 let (value, rest) = parse_wire_copy::<TpmUint16>(buf)?;
923 Ok((TpmuSymKeyBits::Aes(value), rest))
924 }
925 TpmAlgId::Sm4 => {
926 let (value, rest) = parse_wire_copy::<TpmUint16>(buf)?;
927 Ok((TpmuSymKeyBits::Sm4(value), rest))
928 }
929 TpmAlgId::Camellia => {
930 let (value, rest) = parse_wire_copy::<TpmUint16>(buf)?;
931 Ok((TpmuSymKeyBits::Camellia(value), rest))
932 }
933 TpmAlgId::Xor => {
934 let (value, rest) = parse_field_value::<TpmAlgId>(buf)?;
935 Ok((TpmuSymKeyBits::Xor(value), rest))
936 }
937 TpmAlgId::Null => Ok((TpmuSymKeyBits::Null, buf)),
938 _ => Err(TpmDeviceError::InvalidResponse),
939 }
940}
941
942fn parse_tpmu_sym_mode(
943 algorithm: TpmAlgId,
944 buf: &[u8],
945) -> Result<(TpmuSymMode, &[u8]), TpmDeviceError> {
946 match algorithm {
947 TpmAlgId::Aes => {
948 let (value, rest) = parse_field_value::<TpmAlgId>(buf)?;
949 Ok((TpmuSymMode::Aes(value), rest))
950 }
951 TpmAlgId::Sm4 => {
952 let (value, rest) = parse_field_value::<TpmAlgId>(buf)?;
953 Ok((TpmuSymMode::Sm4(value), rest))
954 }
955 TpmAlgId::Camellia => {
956 let (value, rest) = parse_field_value::<TpmAlgId>(buf)?;
957 Ok((TpmuSymMode::Camellia(value), rest))
958 }
959 TpmAlgId::Xor => {
960 let (value, rest) = parse_field_value::<TpmAlgId>(buf)?;
961 Ok((TpmuSymMode::Xor(value), rest))
962 }
963 TpmAlgId::Null => Ok((TpmuSymMode::Null, buf)),
964 _ => Err(TpmDeviceError::InvalidResponse),
965 }
966}
967
968fn parse_tpmt_sym_def(buf: &[u8]) -> Result<(TpmtSymDefObject, &[u8]), TpmDeviceError> {
969 let (algorithm, buf) = parse_field_value::<TpmAlgId>(buf)?;
970 if algorithm == TpmAlgId::Null {
971 return Ok((TpmtSymDefObject::default(), buf));
972 }
973
974 let (key_bits, buf) = parse_tpmu_sym_key_bits(algorithm, buf)?;
975 let (mode, rest) = parse_tpmu_sym_mode(algorithm, buf)?;
976
977 Ok((
978 TpmtSymDefObject {
979 algorithm,
980 key_bits,
981 mode,
982 },
983 rest,
984 ))
985}
986
987fn parse_tpmu_public_parms(
988 object_type: TpmAlgId,
989 buf: &[u8],
990) -> Result<(TpmuPublicParms, &[u8]), TpmDeviceError> {
991 match object_type {
992 TpmAlgId::KeyedHash => {
993 let (scheme, rest) = parse_tpmt_keyedhash_scheme(buf)?;
994 Ok((
995 TpmuPublicParms::KeyedHash(TpmsKeyedhashParms { scheme }),
996 rest,
997 ))
998 }
999 TpmAlgId::SymCipher => {
1000 let (sym, rest) = parse_tpmt_sym_def(buf)?;
1001 Ok((TpmuPublicParms::SymCipher(TpmsSymcipherParms { sym }), rest))
1002 }
1003 TpmAlgId::Rsa => {
1004 let (symmetric, buf) = parse_tpmt_sym_def(buf)?;
1005 let (scheme, buf) = parse_tpmt_rsa_scheme(buf)?;
1006 let (key_bits, buf) = parse_wire_copy::<TpmUint16>(buf)?;
1007 let (exponent, rest) = parse_wire_copy::<TpmUint32>(buf)?;
1008 Ok((
1009 TpmuPublicParms::Rsa(TpmsRsaParms {
1010 symmetric,
1011 scheme,
1012 key_bits,
1013 exponent,
1014 }),
1015 rest,
1016 ))
1017 }
1018 TpmAlgId::Ecc => {
1019 let (symmetric, buf) = parse_tpmt_sym_def(buf)?;
1020 let (scheme, buf) = parse_tpmt_ecc_scheme(buf)?;
1021 let (curve_id, buf) = parse_field_value::<TpmEccCurve>(buf)?;
1022 let (kdf, rest) = parse_tpmt_kdf_scheme(buf)?;
1023 Ok((
1024 TpmuPublicParms::Ecc(TpmsEccParms {
1025 symmetric,
1026 scheme,
1027 curve_id,
1028 kdf,
1029 }),
1030 rest,
1031 ))
1032 }
1033 TpmAlgId::Null => Ok((TpmuPublicParms::Null, buf)),
1034 _ => Err(TpmDeviceError::InvalidResponse),
1035 }
1036}
1037
1038fn parse_tpms_ecc_point(buf: &[u8]) -> Result<(TpmsEccPoint, &[u8]), TpmDeviceError> {
1039 let (x, buf): (Tpm2bEccParameter, _) = parse_tpm2b_buffer(buf)?;
1040 let (y, rest): (Tpm2bEccParameter, _) = parse_tpm2b_buffer(buf)?;
1041
1042 Ok((TpmsEccPoint { x, y }, rest))
1043}
1044
1045fn parse_tpmu_public_id(
1046 object_type: TpmAlgId,
1047 buf: &[u8],
1048) -> Result<(TpmuPublicId, &[u8]), TpmDeviceError> {
1049 match object_type {
1050 TpmAlgId::KeyedHash => {
1051 let (value, rest): (Tpm2bDigest, _) = parse_tpm2b_buffer(buf)?;
1052 Ok((TpmuPublicId::KeyedHash(value), rest))
1053 }
1054 TpmAlgId::SymCipher => {
1055 let (value, rest): (Tpm2bSymKey, _) = parse_tpm2b_buffer(buf)?;
1056 Ok((TpmuPublicId::SymCipher(value), rest))
1057 }
1058 TpmAlgId::Rsa => {
1059 let (value, rest): (Tpm2bPublicKeyRsa, _) = parse_tpm2b_buffer(buf)?;
1060 Ok((TpmuPublicId::Rsa(value), rest))
1061 }
1062 TpmAlgId::Ecc => {
1063 let (value, rest) = parse_tpms_ecc_point(buf)?;
1064 Ok((TpmuPublicId::Ecc(value), rest))
1065 }
1066 TpmAlgId::Null => Ok((TpmuPublicId::Null, buf)),
1067 _ => Err(TpmDeviceError::InvalidResponse),
1068 }
1069}
1070
1071fn parse_tpmt_public(buf: &[u8]) -> Result<(TpmtPublic, &[u8]), TpmDeviceError> {
1072 let (object_type, buf) = parse_field_value::<TpmAlgId>(buf)?;
1073 let (name_alg, buf) = parse_field_value::<TpmAlgId>(buf)?;
1074 let (object_attributes, buf) = parse_field_value::<TpmaObject>(buf)?;
1075 let (auth_policy, buf): (Tpm2bDigest, _) = parse_tpm2b_buffer(buf)?;
1076 let (parameters, buf) = parse_tpmu_public_parms(object_type, buf)?;
1077 let (unique, rest) = parse_tpmu_public_id(object_type, buf)?;
1078
1079 Ok((
1080 TpmtPublic {
1081 object_type,
1082 name_alg,
1083 object_attributes,
1084 auth_policy,
1085 parameters,
1086 unique,
1087 },
1088 rest,
1089 ))
1090}
1091
1092fn parse_tpm2b_public(buf: &[u8]) -> Result<(TpmtPublic, &[u8]), TpmDeviceError> {
1093 let (size, buf) = parse_wire_copy::<TpmUint16>(buf)?;
1094 let size = usize::from(size.get());
1095 if buf.len() < size {
1096 return Err(TpmDeviceError::InvalidResponse);
1097 }
1098
1099 let (public, rest) = buf.split_at(size);
1100 let (public, public_rest) = parse_tpmt_public(public)?;
1101 ensure_empty(public_rest)?;
1102
1103 Ok((public, rest))
1104}
1105
1106fn parse_tpms_context(buf: &[u8]) -> Result<(TpmsContext, &[u8]), TpmDeviceError> {
1107 let (sequence, buf) = parse_wire_copy::<TpmUint64>(buf)?;
1108 let (saved_handle, buf) = parse_wire_copy::<TpmHandle>(buf)?;
1109 let (hierarchy, buf) = parse_field_value::<TpmRh>(buf)?;
1110 let (context_blob, rest): (TpmBuffer<TPM_MAX_COMMAND_SIZE>, _) = parse_tpm2b_buffer(buf)?;
1111
1112 Ok((
1113 TpmsContext {
1114 sequence,
1115 saved_handle,
1116 hierarchy,
1117 context_blob,
1118 },
1119 rest,
1120 ))
1121}
1122
1123fn parse_list<'a, T, const CAPACITY: usize>(
1124 buf: &'a [u8],
1125 mut parse_item: impl FnMut(&'a [u8]) -> Result<(T, &'a [u8]), TpmDeviceError>,
1126) -> Result<(TpmList<T, CAPACITY>, &'a [u8]), TpmDeviceError>
1127where
1128 T: Copy,
1129{
1130 let (count, mut cursor) = parse_wire_copy::<TpmUint32>(buf)?;
1131 let mut list = TpmList::<T, CAPACITY>::new();
1132
1133 for _ in 0..count.get() {
1134 let (item, rest) = parse_item(cursor)?;
1135 list.try_push(item).map_err(TpmDeviceError::Unmarshal)?;
1136 cursor = rest;
1137 }
1138
1139 Ok((list, cursor))
1140}
1141
1142fn parse_tpms_alg_property(buf: &[u8]) -> Result<(TpmsAlgProperty, &[u8]), TpmDeviceError> {
1143 let (alg, buf) = parse_field_value::<TpmAlgId>(buf)?;
1144 let (alg_properties, rest) = parse_field_value::<TpmaAlgorithm>(buf)?;
1145
1146 Ok((
1147 TpmsAlgProperty {
1148 alg,
1149 alg_properties,
1150 },
1151 rest,
1152 ))
1153}
1154
1155fn parse_tpms_tagged_property(buf: &[u8]) -> Result<(TpmsTaggedProperty, &[u8]), TpmDeviceError> {
1156 let (property, buf) = parse_field_value::<TpmPt>(buf)?;
1157 let (value, rest) = parse_wire_copy::<TpmUint32>(buf)?;
1158
1159 Ok((TpmsTaggedProperty { property, value }, rest))
1160}
1161
1162fn parse_tpms_pcr_selection(buf: &[u8]) -> Result<(TpmsPcrSelection, &[u8]), TpmDeviceError> {
1163 let (hash, buf) = parse_field_value::<TpmAlgId>(buf)?;
1164 let (pcr_select, rest) =
1165 <TpmsPcrSelect as TpmField>::cast_prefix_field(buf).map_err(TpmDeviceError::Unmarshal)?;
1166 let pcr_select = TpmsPcrSelect::try_from(pcr_select).map_err(TpmDeviceError::Unmarshal)?;
1167
1168 Ok((TpmsPcrSelection { hash, pcr_select }, rest))
1169}
1170
1171fn parse_capability_data(buf: &[u8]) -> Result<(TpmsCapabilityData, &[u8]), TpmDeviceError> {
1172 let (capability, buf) = parse_field_value::<TpmCap>(buf)?;
1173 let (data, rest) = match capability {
1174 TpmCap::Algs => {
1175 let (list, rest) = parse_list::<TpmsAlgProperty, 64>(buf, parse_tpms_alg_property)?;
1176 (TpmuCapabilities::Algs(list), rest)
1177 }
1178 TpmCap::Handles => {
1179 let (list, rest) = parse_list::<TpmHandle, 128>(buf, parse_wire_copy::<TpmHandle>)?;
1180 (TpmuCapabilities::Handles(list), rest)
1181 }
1182 TpmCap::Pcrs => {
1183 let (list, rest) = parse_list::<TpmsPcrSelection, 8>(buf, parse_tpms_pcr_selection)?;
1184 (TpmuCapabilities::Pcrs(list), rest)
1185 }
1186 TpmCap::Commands => {
1187 let (list, rest) = parse_list::<TpmaCc, 256>(buf, parse_field_value::<TpmaCc>)?;
1188 (TpmuCapabilities::Commands(list), rest)
1189 }
1190 TpmCap::TpmProperties => {
1191 let (list, rest) =
1192 parse_list::<TpmsTaggedProperty, 64>(buf, parse_tpms_tagged_property)?;
1193 (TpmuCapabilities::TpmProperties(list), rest)
1194 }
1195 TpmCap::EccCurves => {
1196 let (list, rest) =
1197 parse_list::<TpmEccCurve, 64>(buf, parse_field_value::<TpmEccCurve>)?;
1198 (TpmuCapabilities::EccCurves(list), rest)
1199 }
1200 };
1201
1202 Ok((TpmsCapabilityData { capability, data }, rest))
1203}
1204
1205pub struct TpmPolicySessionBuilder {
1207 bind: TpmHandle,
1208 tpm_key: TpmHandle,
1209 nonce_caller: Option<Tpm2bNonce>,
1210 encrypted_salt: Option<Tpm2bEncryptedSecret>,
1211 session_type: TpmSe,
1212 symmetric: TpmtSymDefObject,
1213 auth_hash: TpmAlgId,
1214}
1215
1216impl Default for TpmPolicySessionBuilder {
1217 fn default() -> Self {
1218 Self {
1219 bind: (TpmRh::Null as u32).into(),
1220 tpm_key: (TpmRh::Null as u32).into(),
1221 nonce_caller: None,
1222 encrypted_salt: None,
1223 session_type: TpmSe::Policy,
1224 symmetric: TpmtSymDefObject::default(),
1225 auth_hash: TpmAlgId::Sha256,
1226 }
1227 }
1228}
1229
1230impl TpmPolicySessionBuilder {
1231 #[must_use]
1232 pub fn new() -> Self {
1233 Self::default()
1234 }
1235
1236 #[must_use]
1237 pub fn with_bind(mut self, bind: TpmHandle) -> Self {
1238 self.bind = bind;
1239 self
1240 }
1241
1242 #[must_use]
1243 pub fn with_tpm_key(mut self, tpm_key: TpmHandle) -> Self {
1244 self.tpm_key = tpm_key;
1245 self
1246 }
1247
1248 #[must_use]
1249 pub fn with_nonce_caller(mut self, nonce: Tpm2bNonce) -> Self {
1250 self.nonce_caller = Some(nonce);
1251 self
1252 }
1253
1254 #[must_use]
1255 pub fn with_encrypted_salt(mut self, salt: Tpm2bEncryptedSecret) -> Self {
1256 self.encrypted_salt = Some(salt);
1257 self
1258 }
1259
1260 #[must_use]
1261 pub fn with_session_type(mut self, session_type: TpmSe) -> Self {
1262 self.session_type = session_type;
1263 self
1264 }
1265
1266 #[must_use]
1267 pub fn with_symmetric(mut self, symmetric: TpmtSymDefObject) -> Self {
1268 self.symmetric = symmetric;
1269 self
1270 }
1271
1272 #[must_use]
1273 pub fn with_auth_hash(mut self, auth_hash: TpmAlgId) -> Self {
1274 self.auth_hash = auth_hash;
1275 self
1276 }
1277
1278 pub fn open(self, device: &mut TpmDevice) -> Result<TpmPolicySession, TpmDeviceError> {
1289 let nonce_caller = if let Some(nonce) = self.nonce_caller {
1290 nonce
1291 } else {
1292 let digest_len = TpmHash::try_from(self.auth_hash)
1293 .map_err(|_| TpmDeviceError::OperationFailed)?
1294 .size();
1295 let mut nonce_bytes = vec![0; digest_len];
1296 thread_rng().fill_bytes(&mut nonce_bytes);
1297 Tpm2bNonce::try_from(nonce_bytes.as_slice()).map_err(TpmDeviceError::Unmarshal)?
1298 };
1299
1300 let cmd = TpmStartAuthSessionCommand {
1301 nonce_caller,
1302 encrypted_salt: self.encrypted_salt.unwrap_or_default(),
1303 session_type: self.session_type,
1304 symmetric: self.symmetric,
1305 auth_hash: self.auth_hash,
1306 handles: [self.tpm_key, self.bind],
1307 };
1308
1309 let response = device.transmit(&cmd, TpmDevice::NO_SESSIONS)?;
1310 let (handles, parameters) = response_parts(response, 1)?;
1311 let (handle, rest) = parse_wire_copy::<TpmHandle>(handles)?;
1312 ensure_empty(rest)?;
1313 let (nonce_tpm, rest): (Tpm2bNonce, _) = parse_tpm2b_buffer(parameters)?;
1314 ensure_empty(rest)?;
1315
1316 Ok(TpmPolicySession {
1317 handle,
1318 attributes: TpmaSession::CONTINUE_SESSION,
1319 hash_alg: self.auth_hash,
1320 nonce_tpm,
1321 })
1322 }
1323}
1324
1325#[derive(Debug, Clone)]
1327pub struct TpmPolicySession {
1328 handle: TpmHandle,
1329 attributes: TpmaSession,
1330 hash_alg: TpmAlgId,
1331 nonce_tpm: Tpm2bNonce,
1332}
1333
1334impl TpmPolicySession {
1335 #[must_use]
1337 pub fn builder() -> TpmPolicySessionBuilder {
1338 TpmPolicySessionBuilder::new()
1339 }
1340
1341 #[must_use]
1343 pub fn handle(&self) -> TpmHandle {
1344 self.handle
1345 }
1346
1347 #[must_use]
1349 pub fn attributes(&self) -> TpmaSession {
1350 self.attributes
1351 }
1352
1353 #[must_use]
1355 pub fn hash_alg(&self) -> TpmAlgId {
1356 self.hash_alg
1357 }
1358
1359 #[must_use]
1361 pub fn nonce_tpm(&self) -> &Tpm2bNonce {
1362 &self.nonce_tpm
1363 }
1364
1365 pub fn run(
1378 &self,
1379 device: &mut TpmDevice,
1380 commands: Vec<(TpmCommand, TpmAuthCommands)>,
1381 ) -> Result<(), TpmDeviceError> {
1382 for (mut command_body, auth_sessions) in commands {
1383 match &mut command_body {
1384 TpmCommand::PolicyPcr(cmd) => cmd.handles[0] = self.handle,
1385 TpmCommand::PolicyOr(cmd) => cmd.handles[0] = self.handle,
1386 TpmCommand::PolicyRestart(cmd) => {
1387 cmd.handles[0] = self.handle;
1388 }
1389 TpmCommand::PolicySecret(cmd) => {
1390 cmd.handles[1] = self.handle;
1391 }
1392 _ => {
1393 return Err(TpmDeviceError::InvalidCc(command_body.cc()));
1394 }
1395 }
1396 device.transmit(&command_body, auth_sessions.as_ref())?;
1397 }
1398 Ok(())
1399 }
1400
1401 pub fn flush(&self, device: &mut TpmDevice) -> Result<(), TpmDeviceError> {
1408 device.flush_context(self.handle)
1409 }
1410}