Skip to main content

tpm2_device/
lib.rs

1// SPDX-License-Identifier: GPL-3-0-or-later
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5#![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/// Errors that can occur when talking to a TPM device.
49#[derive(Debug, Error)]
50pub enum TpmDeviceError {
51    /// The TPM device is already mutably borrowed.
52    #[error("device is already borrowed")]
53    AlreadyBorrowed,
54
55    /// The requested capability is not available from the TPM.
56    #[error("capability not found: {0}")]
57    CapabilityMissing(TpmCap),
58
59    #[error("operation interrupted by user")]
60    Interrupted,
61
62    /// An invalid command code was used.
63    #[error("invalid CC: {0}")]
64    InvalidCc(tpm2_protocol::data::TpmCc),
65
66    /// The TPM returned an invalid or malformed response.
67    #[error("invalid response")]
68    InvalidResponse,
69
70    /// An I/O error occurred when accessing the TPM device.
71    #[error("I/O: {0}")]
72    Io(#[from] std::io::Error),
73
74    /// Marshaling a TPM protocol encoded object failed.
75    #[error("marshal: {0}")]
76    Marshal(TpmError),
77
78    /// No TPM device is available.
79    #[error("device not available")]
80    NotAvailable,
81
82    /// The requested operation could not be completed.
83    #[error("operation failed")]
84    OperationFailed,
85
86    /// No PCR banks are available on the TPM.
87    #[error("PCR banks not available")]
88    PcrBanksNotAvailable,
89
90    /// The PCR selection masks differ between active banks.
91    #[error("PCR bank selection mismatch")]
92    PcrBankSelectionMismatch,
93
94    /// The TPM response did not match the expected command code.
95    #[error("response mismatch: {0}")]
96    ResponseMismatch(TpmCc),
97
98    /// The TPM command timed out.
99    #[error("TPM command timed out")]
100    Timeout,
101
102    /// The TPM returned an error code.
103    #[error("TPM return code: {0}")]
104    TpmRc(TpmRc),
105
106    /// Trailing data after the response.
107    #[error("trailing data")]
108    TrailingData,
109
110    /// Unmarshaling a TPM protocol encoded object failed.
111    #[error("unmarshal: {0}")]
112    Unmarshal(TpmError),
113
114    /// An unexpected end-of-file was encountered.
115    #[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
131/// Executes a closure with a mutable reference to a `TpmDevice`.
132///
133/// This helper function centralizes the boilerplate for safely acquiring a
134/// mutable borrow of a `TpmDevice` from the shared `Rc<RefCell<...>>`.
135///
136/// # Errors
137///
138/// Returns [`NotAvailable`](crate::TpmDeviceError::NotAvailable) when no device
139/// is present.
140/// Returns [`AlreadyBorrowed`](crate::TpmDeviceError::AlreadyBorrowed) when the
141/// device is already mutably borrowed.
142/// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants depending
143/// on function.
144pub 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
156/// A builder for constructing a `TpmDevice`.
157pub 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    /// Sets the device file path.
175    #[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    /// Sets the operation timeout.
182    #[must_use]
183    pub fn with_timeout(mut self, timeout: Duration) -> Self {
184        self.timeout = timeout;
185        self
186    }
187
188    /// Sets the interruption check callback.
189    #[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    /// Opens the TPM device file and constructs the `TpmDevice`.
199    ///
200    /// # Errors
201    ///
202    /// Returns [`Io`](crate::TpmDeviceError::Io) when the device file cannot be
203    /// opened or when configuring the file descriptor flags fails.
204    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    /// Creates a new builder for `TpmDevice`.
248    #[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    /// Performs the whole TPM command transmission process.
289    ///
290    /// # Errors
291    ///
292    /// Returns [`Interrupted`](crate::TpmDeviceError::Interrupted) when the
293    /// interrupt callback requests cancellation.
294    /// Returns [`Io`](crate::TpmDeviceError::Io) when a write, flush, or read
295    /// operation on the device file fails, or when polling the device file
296    /// descriptor fails.
297    /// Returns [`Marshal`](crate::TpmDeviceError::Marshal) when marshal
298    /// operation on TPM protocol compliant data fails.
299    /// Returns [`Timeout`](crate::TpmDeviceError::Timeout) when the TPM does
300    /// not respond within the configured timeout.
301    /// Returns [`TpmRc`](crate::TpmDeviceError::TpmRc) when the TPM returns an
302    /// error code.
303    /// Returns [`Unmarshal`](crate::TpmDeviceError::Unmarshal) when unmarshal
304    /// operation on TPM protocol compliant data fails.
305    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    /// Fetches a complete list of capabilities from the TPM, handling
388    /// pagination.
389    ///
390    /// # Errors
391    ///
392    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
393    /// when receiving unepected TPM response.
394    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
395    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
396    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    /// Retrieves all algorithm properties supported by the TPM.
431    ///
432    /// # Errors
433    ///
434    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
435    /// when receiving unepected TPM response.
436    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
437    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
438    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    /// Retrieves all handles of a specific type from the TPM.
452    ///
453    /// # Errors
454    ///
455    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
456    /// when receiving unepected TPM response.
457    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
458    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
459    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    /// Retrieves all available ECC curves supported by the TPM.
474    ///
475    /// # Errors
476    ///
477    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
478    /// when receiving unepected TPM response.
479    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
480    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
481    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    /// Retrieves the list of active PCR banks and the bank selection mask.
495    ///
496    /// # Errors
497    ///
498    /// Returns
499    /// [`PcrBanksNotAvailable`](crate::TpmDeviceError::PcrBanksNotAvailable)
500    /// when no PCR banks are available.
501    /// Return
502    /// [`PcrBankSelectionMismatch`](crate::TpmDeviceError::PcrBankSelectionMismatch)
503    /// when the PCR selection masks differ between active banks.
504    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
505    /// when receiving unepected TPM response.
506    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
507    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
508    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    /// Fetches and returns one page of capabilities of a certain type from the
556    /// TPM.
557    ///
558    /// # Errors
559    ///
560    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
561    /// when receiving unepected TPM response.
562    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
563    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
564    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    /// Reads a specific TPM property.
587    ///
588    /// # Errors
589    ///
590    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
591    /// when receiving unepected TPM response.
592    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
593    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
594    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    /// Reads the public area of a TPM object.
613    ///
614    /// # Errors
615    ///
616    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
617    /// when receiving unepected TPM response.
618    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
619    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
620    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    /// Finds a persistent handle by its `Tpm2bName`.
636    ///
637    /// # Errors
638    ///
639    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
640    /// when receiving unepected TPM response.
641    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
642    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
643    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    /// Saves the context of a transient object or session.
668    ///
669    /// # Errors
670    ///
671    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
672    /// when receiving unepected TPM response.
673    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
674    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
675    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    /// Loads a TPM context and returns the handle.
688    ///
689    /// # Errors
690    ///
691    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch)
692    /// when receiving unepected TPM response.
693    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
694    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
695    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    /// Flushes a transient object or session from the TPM and removes it from
710    /// the cache.
711    ///
712    /// # Errors
713    ///
714    /// Returns [`TpmDeviceError`](crate::TpmDeviceError) variants when
715    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
716    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    /// Loads a session context and then flushes the resulting handle.
726    ///
727    /// # Errors
728    ///
729    /// Returns [`TpmDeviceError`](crate::TpmDeviceError) variants when
730    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
731    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
1205/// A builder for creating a TPM policy session.
1206pub 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    /// Opens the policy session on the provided device.
1279    ///
1280    /// # Errors
1281    ///
1282    /// Returns [`ResponseMismatch`](crate::TpmDeviceError::ResponseMismatch) if
1283    /// the TPM response is unexpected.
1284    /// Returns [`Unmarshal`](crate::TpmDeviceError::Unmarshal) when unmarshal
1285    /// operation on TPM protocol compliant data fails.
1286    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants depending
1287    /// on function.
1288    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/// Represents an active TPM policy session.
1326#[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    /// Creates a new builder for `TpmPolicySession`.
1336    #[must_use]
1337    pub fn builder() -> TpmPolicySessionBuilder {
1338        TpmPolicySessionBuilder::new()
1339    }
1340
1341    /// Returns the session handle.
1342    #[must_use]
1343    pub fn handle(&self) -> TpmHandle {
1344        self.handle
1345    }
1346
1347    /// Returns the session attributes.
1348    #[must_use]
1349    pub fn attributes(&self) -> TpmaSession {
1350        self.attributes
1351    }
1352
1353    /// Returns the hash algorithm used by the session.
1354    #[must_use]
1355    pub fn hash_alg(&self) -> TpmAlgId {
1356        self.hash_alg
1357    }
1358
1359    /// Returns the nonce generated by the TPM.
1360    #[must_use]
1361    pub fn nonce_tpm(&self) -> &Tpm2bNonce {
1362        &self.nonce_tpm
1363    }
1364
1365    /// Applies a list of policy commands to this session.
1366    ///
1367    /// This method iterates through the provided commands, updates the first handle
1368    /// of each command (or second for `PolicySecret`) to point to this session,
1369    /// and transmits them to the device.
1370    ///
1371    /// # Errors
1372    ///
1373    /// Returns [`InvalidCc`](crate::TpmDeviceError::InvalidCc) when a command is not
1374    /// a supported policy command.
1375    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
1376    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
1377    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    /// Flushes the session context from the TPM.
1402    ///
1403    /// # Errors
1404    ///
1405    /// Returns other [`TpmDeviceError`](crate::TpmDeviceError) variants when
1406    /// [`TpmDevice::transmit`](crate::TpmDevice::transmit) fails.
1407    pub fn flush(&self, device: &mut TpmDevice) -> Result<(), TpmDeviceError> {
1408        device.flush_context(self.handle)
1409    }
1410}