Skip to main content

tpm2_protocol/frame/
wire.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2026 Jarkko Sakkinen
4
5use super::{TPM_DISPATCH_TABLE, TPM_HEADER_SIZE};
6use crate::{
7    TpmCast, TpmCastMut, TpmError, TpmResult,
8    constant::MAX_SESSIONS,
9    data::{TpmCc, TpmRc, TpmRcBase, TpmSt},
10};
11use core::{mem::size_of, ops::Range};
12
13const HEADER_SIZE: usize = TPM_HEADER_SIZE as usize;
14const TAG_OFFSET: usize = 0;
15const SIZE_OFFSET: usize = 2;
16const CODE_OFFSET: usize = 6;
17
18/// A zero-copy TPM command wire view over caller-owned bytes.
19#[repr(transparent)]
20pub struct TpmCommand([u8]);
21
22impl TpmCommand {
23    /// Casts a byte slice into a TPM command wire view.
24    ///
25    /// # Errors
26    ///
27    /// Returns `Err(TpmError)` when the command envelope is malformed.
28    pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
29        Self::validate_envelope(buf)?;
30
31        // SAFETY: `validate_envelope` checked the command frame bounds and
32        // dispatch invariants required for this transparent wire view.
33        Ok(unsafe { Self::cast_unchecked(buf) })
34    }
35
36    /// Casts the first TPM command frame in a byte slice into a wire view.
37    ///
38    /// # Errors
39    ///
40    /// Returns `Err(TpmError)` when the command envelope is malformed or
41    /// incomplete.
42    pub fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
43        let frame_len = frame_prefix_size(buf)?;
44        let (frame, tail) = buf.split_at(frame_len);
45
46        Self::validate_envelope(frame)?;
47
48        // SAFETY: `validate_envelope` checked the complete command frame.
49        Ok((unsafe { Self::cast_unchecked(frame) }, tail))
50    }
51
52    /// Casts a byte slice into a TPM command wire view without validation.
53    ///
54    /// # Safety
55    ///
56    /// The caller must ensure that `buf` contains exactly one complete TPM
57    /// command frame with a valid command code and handle area layout.
58    #[must_use]
59    pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
60        let ptr = core::ptr::from_ref(buf) as *const Self;
61
62        // SAFETY: `TpmCommand` is `repr(transparent)` over `[u8]`, so it has
63        // the same layout, metadata, and alignment as the referenced slice.
64        unsafe { &*ptr }
65    }
66
67    /// Casts a mutable byte slice into a mutable TPM command wire view.
68    ///
69    /// # Errors
70    ///
71    /// Returns `Err(TpmError)` when the command envelope is malformed.
72    pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
73        Self::validate_envelope(buf)?;
74
75        // SAFETY: `validate_envelope` checked the command frame bounds and
76        // dispatch invariants required for this transparent wire view. The
77        // `&mut` input provides exclusive access.
78        Ok(unsafe { Self::cast_mut_unchecked(buf) })
79    }
80
81    /// Casts the first mutable TPM command frame in a byte slice into a wire view.
82    ///
83    /// # Errors
84    ///
85    /// Returns `Err(TpmError)` when the command envelope is malformed or
86    /// incomplete.
87    pub fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
88        let frame_len = frame_prefix_size(buf)?;
89        let (frame, tail) = buf.split_at_mut(frame_len);
90
91        Self::validate_envelope(frame)?;
92
93        // SAFETY: `validate_envelope` checked the complete command frame.
94        Ok((unsafe { Self::cast_mut_unchecked(frame) }, tail))
95    }
96
97    /// Casts a mutable byte slice into a mutable TPM command wire view without validation.
98    ///
99    /// # Safety
100    ///
101    /// The caller must ensure that `buf` contains exactly one complete TPM
102    /// command frame with a valid command code and handle area layout. The
103    /// returned reference inherits the exclusive access represented by `buf`.
104    #[must_use]
105    pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
106        let ptr = core::ptr::from_mut(buf) as *mut Self;
107
108        // SAFETY: `TpmCommand` is `repr(transparent)` over `[u8]`, so it has
109        // the same layout, metadata, and alignment as the referenced slice.
110        unsafe { &mut *ptr }
111    }
112
113    /// Returns the complete command frame bytes.
114    #[must_use]
115    pub const fn as_bytes(&self) -> &[u8] {
116        &self.0
117    }
118
119    /// Returns the mutable command frame bytes.
120    #[must_use]
121    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
122        &mut self.0
123    }
124
125    /// Returns the command tag.
126    ///
127    /// # Errors
128    ///
129    /// Returns [`VariantNotAvailable`](crate::TpmError::VariantNotAvailable)
130    /// when the tag value is not defined.
131    pub fn tag(&self) -> TpmResult<TpmSt> {
132        let raw = read_u16(&self.0, TAG_OFFSET);
133
134        TpmSt::try_from(raw).map_err(|_| {
135            TpmError::InvalidTag(crate::TpmErrorValue::new(TAG_OFFSET).value(u64::from(raw)))
136        })
137    }
138
139    /// Returns the command frame size field.
140    #[must_use]
141    pub fn size(&self) -> u32 {
142        read_u32(&self.0, SIZE_OFFSET)
143    }
144
145    /// Returns the command code.
146    ///
147    /// # Errors
148    ///
149    /// Returns [`InvalidCc`](crate::TpmError::InvalidCc) when the
150    /// command code has no dispatch entry.
151    pub fn cc(&self) -> TpmResult<TpmCc> {
152        command_code(&self.0)
153    }
154
155    /// Sets the command tag without changing the frame shape.
156    pub fn set_tag(&mut self, tag: TpmSt) {
157        write_u16(&mut self.0, TAG_OFFSET, tag.value());
158    }
159
160    /// Sets the command code without changing the frame shape.
161    ///
162    /// # Errors
163    ///
164    /// Returns [`InvalidCc`](crate::TpmError::InvalidCc) when the
165    /// command code has no dispatch entry.
166    pub fn set_cc(&mut self, cc: TpmCc) -> TpmResult<()> {
167        let _ = dispatch_for(cc)?;
168
169        write_u32(&mut self.0, CODE_OFFSET, cc.value());
170        Ok(())
171    }
172
173    /// Returns the command handle area bytes.
174    ///
175    /// # Errors
176    ///
177    /// Returns `Err(TpmError)` when the command envelope is malformed.
178    pub fn handles(&self) -> TpmResult<&[u8]> {
179        let range = self.handle_area_range()?;
180
181        Ok(&self.0[range])
182    }
183
184    /// Returns the mutable command handle area bytes.
185    ///
186    /// # Errors
187    ///
188    /// Returns `Err(TpmError)` when the command envelope is malformed.
189    pub fn handles_mut(&mut self) -> TpmResult<&mut [u8]> {
190        let range = self.handle_area_range()?;
191
192        Ok(&mut self.0[range])
193    }
194
195    /// Returns the command authorization area bytes.
196    ///
197    /// # Errors
198    ///
199    /// Returns `Err(TpmError)` when the command has no sessions or its
200    /// authorization area is malformed.
201    pub fn auth_area(&self) -> TpmResult<&[u8]> {
202        let (auth_area, _) = self.session_and_parameter_ranges()?;
203
204        Ok(&self.0[auth_area])
205    }
206
207    /// Returns the mutable command authorization area bytes.
208    ///
209    /// # Errors
210    ///
211    /// Returns `Err(TpmError)` when the command has no sessions or its
212    /// authorization area is malformed.
213    pub fn auth_area_mut(&mut self) -> TpmResult<&mut [u8]> {
214        let (auth_area, _) = self.session_and_parameter_ranges()?;
215
216        Ok(&mut self.0[auth_area])
217    }
218
219    /// Returns the command parameter area bytes.
220    ///
221    /// # Errors
222    ///
223    /// Returns `Err(TpmError)` when the command envelope is malformed.
224    pub fn parameters(&self) -> TpmResult<&[u8]> {
225        let (_, parameters) = self.session_and_parameter_ranges()?;
226
227        Ok(&self.0[parameters])
228    }
229
230    /// Returns the mutable command parameter area bytes.
231    ///
232    /// # Errors
233    ///
234    /// Returns `Err(TpmError)` when the command envelope is malformed.
235    pub fn parameters_mut(&mut self) -> TpmResult<&mut [u8]> {
236        let (_, parameters) = self.session_and_parameter_ranges()?;
237
238        Ok(&mut self.0[parameters])
239    }
240
241    /// Validates command frame structure without constructing an owned command body.
242    ///
243    /// # Errors
244    ///
245    /// Returns `Err(TpmError)` when the command frame is malformed.
246    pub fn validate(&self) -> TpmResult<()> {
247        Self::validate_envelope(&self.0)?;
248        let auth_area = self.auth_area()?;
249
250        validate_auth_commands(&self.0, auth_area)
251    }
252
253    /// Returns `true` when the command frame contains no bytes.
254    #[must_use]
255    pub const fn is_empty(&self) -> bool {
256        self.0.is_empty()
257    }
258
259    /// Returns the command frame length.
260    #[must_use]
261    pub const fn len(&self) -> usize {
262        self.0.len()
263    }
264
265    fn handle_area_range(&self) -> TpmResult<Range<usize>> {
266        let dispatch = dispatch_for(self.cc()?)?;
267        let handle_area_size = handle_area_size(dispatch.handles, HEADER_SIZE)?;
268        let handle_area_end =
269            HEADER_SIZE
270                .checked_add(handle_area_size)
271                .ok_or(TpmError::IntegerTooLarge(
272                    crate::TpmErrorValue::new(HEADER_SIZE).value_usize(handle_area_size),
273                ))?;
274
275        if self.0.len() < handle_area_end {
276            return Err(TpmError::UnexpectedEnd(
277                crate::TpmErrorValue::new(HEADER_SIZE)
278                    .size(handle_area_size, self.0.len().saturating_sub(HEADER_SIZE)),
279            ));
280        }
281
282        Ok(HEADER_SIZE..handle_area_end)
283    }
284
285    fn session_and_parameter_ranges(&self) -> TpmResult<(Range<usize>, Range<usize>)> {
286        let handle_area = self.handle_area_range()?;
287        let tag = self.tag()?;
288        let after_handles_start = handle_area.end;
289
290        if tag != TpmSt::Sessions {
291            return Ok((
292                after_handles_start..after_handles_start,
293                after_handles_start..self.0.len(),
294            ));
295        }
296
297        let after_handles = &self.0[after_handles_start..];
298
299        if after_handles.len() < size_of::<u32>() {
300            return Err(TpmError::UnexpectedEnd(
301                crate::TpmErrorValue::new(after_handles_start)
302                    .size(size_of::<u32>(), after_handles.len()),
303            ));
304        }
305
306        let auth_size = read_u32(after_handles, 0) as usize;
307        let auth_start = size_of::<u32>();
308        let auth_end = auth_start
309            .checked_add(auth_size)
310            .ok_or(TpmError::IntegerTooLarge(
311                crate::TpmErrorValue::new(after_handles_start).value_usize(auth_size),
312            ))?;
313
314        if after_handles.len() < auth_end {
315            return Err(TpmError::UnexpectedEnd(
316                crate::TpmErrorValue::new(after_handles_start + auth_start)
317                    .size(auth_size, after_handles.len().saturating_sub(auth_start)),
318            ));
319        }
320
321        let auth_start = after_handles_start + auth_start;
322        let auth_end = after_handles_start + auth_end;
323
324        Ok((auth_start..auth_end, auth_end..self.0.len()))
325    }
326
327    fn validate_envelope(buf: &[u8]) -> TpmResult<()> {
328        validate_frame_size(buf)?;
329
330        let raw_tag = read_u16(buf, TAG_OFFSET);
331        let tag = TpmSt::try_from(raw_tag).map_err(|_| {
332            TpmError::InvalidTag(crate::TpmErrorValue::new(TAG_OFFSET).value(u64::from(raw_tag)))
333        })?;
334        if tag != TpmSt::NoSessions && tag != TpmSt::Sessions {
335            return Err(TpmError::InvalidTag(
336                crate::TpmErrorValue::new(TAG_OFFSET).value(u64::from(raw_tag)),
337            ));
338        }
339
340        let dispatch = dispatch_for(command_code(buf)?)?;
341        let body = &buf[HEADER_SIZE..];
342        let handle_area_size = handle_area_size(dispatch.handles, HEADER_SIZE)?;
343
344        if body.len() < handle_area_size {
345            return Err(TpmError::UnexpectedEnd(
346                crate::TpmErrorValue::new(HEADER_SIZE).size(handle_area_size, body.len()),
347            ));
348        }
349
350        if tag == TpmSt::Sessions {
351            let after_handles = &body[handle_area_size..];
352            if after_handles.len() < size_of::<u32>() {
353                return Err(TpmError::UnexpectedEnd(
354                    crate::TpmErrorValue::new(HEADER_SIZE + handle_area_size)
355                        .size(size_of::<u32>(), after_handles.len()),
356                ));
357            }
358
359            let auth_size = read_u32(after_handles, 0) as usize;
360            let auth_end =
361                size_of::<u32>()
362                    .checked_add(auth_size)
363                    .ok_or(TpmError::IntegerTooLarge(
364                        crate::TpmErrorValue::new(HEADER_SIZE + handle_area_size)
365                            .value_usize(auth_size),
366                    ))?;
367
368            if after_handles.len() < auth_end {
369                return Err(TpmError::UnexpectedEnd(
370                    crate::TpmErrorValue::new(HEADER_SIZE + handle_area_size + size_of::<u32>())
371                        .size(
372                            auth_size,
373                            after_handles.len().saturating_sub(size_of::<u32>()),
374                        ),
375                ));
376            }
377        }
378
379        Ok(())
380    }
381}
382
383impl TpmCast for TpmCommand {
384    fn cast(buf: &[u8]) -> TpmResult<&Self> {
385        Self::cast(buf)
386    }
387
388    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
389        Self::cast_prefix(buf)
390    }
391
392    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
393        // SAFETY: The caller upholds the unchecked cast contract for `TpmCommand`.
394        unsafe { Self::cast_unchecked(buf) }
395    }
396}
397
398impl TpmCastMut for TpmCommand {
399    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
400        Self::cast_mut(buf)
401    }
402
403    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
404        Self::cast_prefix_mut(buf)
405    }
406
407    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
408        // SAFETY: The caller upholds the unchecked mutable cast contract for
409        // `TpmCommand`.
410        unsafe { Self::cast_mut_unchecked(buf) }
411    }
412}
413
414impl AsRef<[u8]> for TpmCommand {
415    fn as_ref(&self) -> &[u8] {
416        self.as_bytes()
417    }
418}
419
420impl AsMut<[u8]> for TpmCommand {
421    fn as_mut(&mut self) -> &mut [u8] {
422        self.as_bytes_mut()
423    }
424}
425
426/// A zero-copy TPM response wire view over caller-owned bytes.
427#[repr(transparent)]
428pub struct TpmResponse([u8]);
429
430impl TpmResponse {
431    /// Casts a byte slice into a TPM response wire view.
432    ///
433    /// # Errors
434    ///
435    /// Returns `Err(TpmError)` when the response envelope is malformed.
436    pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
437        Self::validate_envelope(buf)?;
438
439        // SAFETY: `validate_envelope` checked the response frame bounds
440        // required for this transparent wire view.
441        Ok(unsafe { Self::cast_unchecked(buf) })
442    }
443
444    /// Casts the first TPM response frame in a byte slice into a wire view.
445    ///
446    /// # Errors
447    ///
448    /// Returns `Err(TpmError)` when the response envelope is malformed or
449    /// incomplete.
450    pub fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
451        let frame_len = frame_prefix_size(buf)?;
452        let (frame, tail) = buf.split_at(frame_len);
453
454        Self::validate_envelope(frame)?;
455
456        // SAFETY: `validate_envelope` checked the complete response frame.
457        Ok((unsafe { Self::cast_unchecked(frame) }, tail))
458    }
459
460    /// Casts a byte slice into a TPM response wire view without validation.
461    ///
462    /// # Safety
463    ///
464    /// The caller must ensure that `buf` contains exactly one complete TPM
465    /// response frame.
466    #[must_use]
467    pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
468        let ptr = core::ptr::from_ref(buf) as *const Self;
469
470        // SAFETY: `TpmResponse` is `repr(transparent)` over `[u8]`, so it has
471        // the same layout, metadata, and alignment as the referenced slice.
472        unsafe { &*ptr }
473    }
474
475    /// Casts a mutable byte slice into a mutable TPM response wire view.
476    ///
477    /// # Errors
478    ///
479    /// Returns `Err(TpmError)` when the response envelope is malformed.
480    pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
481        Self::validate_envelope(buf)?;
482
483        // SAFETY: `validate_envelope` checked the response frame bounds
484        // required for this transparent wire view. The `&mut` input provides
485        // exclusive access.
486        Ok(unsafe { Self::cast_mut_unchecked(buf) })
487    }
488
489    /// Casts the first mutable TPM response frame in a byte slice into a wire view.
490    ///
491    /// # Errors
492    ///
493    /// Returns `Err(TpmError)` when the response envelope is malformed or
494    /// incomplete.
495    pub fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
496        let frame_len = frame_prefix_size(buf)?;
497        let (frame, tail) = buf.split_at_mut(frame_len);
498
499        Self::validate_envelope(frame)?;
500
501        // SAFETY: `validate_envelope` checked the complete response frame.
502        Ok((unsafe { Self::cast_mut_unchecked(frame) }, tail))
503    }
504
505    /// Casts a mutable byte slice into a mutable TPM response wire view without validation.
506    ///
507    /// # Safety
508    ///
509    /// The caller must ensure that `buf` contains exactly one complete TPM
510    /// response frame. The returned reference inherits the exclusive access
511    /// represented by `buf`.
512    #[must_use]
513    pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
514        let ptr = core::ptr::from_mut(buf) as *mut Self;
515
516        // SAFETY: `TpmResponse` is `repr(transparent)` over `[u8]`, so it has
517        // the same layout, metadata, and alignment as the referenced slice.
518        unsafe { &mut *ptr }
519    }
520
521    /// Returns the complete response frame bytes.
522    #[must_use]
523    pub const fn as_bytes(&self) -> &[u8] {
524        &self.0
525    }
526
527    /// Returns the mutable response frame bytes.
528    #[must_use]
529    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
530        &mut self.0
531    }
532
533    /// Returns the response tag.
534    ///
535    /// # Errors
536    ///
537    /// Returns [`VariantNotAvailable`](crate::TpmError::VariantNotAvailable)
538    /// when the tag value is not defined.
539    pub fn tag(&self) -> TpmResult<TpmSt> {
540        let raw = read_u16(&self.0, TAG_OFFSET);
541
542        TpmSt::try_from(raw).map_err(|_| {
543            TpmError::InvalidTag(crate::TpmErrorValue::new(TAG_OFFSET).value(u64::from(raw)))
544        })
545    }
546
547    /// Returns the response frame size field.
548    #[must_use]
549    pub fn size(&self) -> u32 {
550        read_u32(&self.0, SIZE_OFFSET)
551    }
552
553    /// Returns the response code.
554    ///
555    /// # Errors
556    ///
557    /// Returns `Err(TpmError)` when the response code is malformed.
558    pub fn rc(&self) -> TpmResult<TpmRc> {
559        let raw = read_u32(&self.0, CODE_OFFSET);
560
561        TpmRc::try_from(raw).map_err(|_| {
562            TpmError::InvalidRc(crate::TpmErrorValue::new(CODE_OFFSET).value(u64::from(raw)))
563        })
564    }
565
566    /// Sets the response tag without changing the frame shape.
567    pub fn set_tag(&mut self, tag: TpmSt) {
568        write_u16(&mut self.0, TAG_OFFSET, tag.value());
569    }
570
571    /// Sets the response code without changing the frame shape.
572    pub fn set_rc(&mut self, rc: TpmRc) {
573        write_u32(&mut self.0, CODE_OFFSET, rc.value());
574    }
575
576    /// Returns the response body bytes after the TPM header.
577    #[must_use]
578    pub fn body(&self) -> &[u8] {
579        &self.0[HEADER_SIZE..]
580    }
581
582    /// Returns the mutable response body bytes after the TPM header.
583    #[must_use]
584    pub fn body_mut(&mut self) -> &mut [u8] {
585        &mut self.0[HEADER_SIZE..]
586    }
587
588    /// Validates response frame structure without constructing an owned response body.
589    ///
590    /// # Errors
591    ///
592    /// Returns `Err(TpmError)` when the response frame is malformed or
593    /// `cc` has no dispatch entry.
594    pub fn validate(&self, cc: TpmCc) -> TpmResult<()> {
595        Self::validate_envelope(&self.0)?;
596        let dispatch = dispatch_for(cc)?;
597
598        if !matches!(self.rc()?, TpmRc::Fmt0(TpmRcBase::Success)) {
599            return Ok(());
600        }
601
602        if self.tag()? != TpmSt::Sessions {
603            return Ok(());
604        }
605
606        let handle_area_size = handle_area_size(dispatch.response_handles, HEADER_SIZE)?;
607        let body = self.body();
608        if body.len() < handle_area_size {
609            return Err(TpmError::UnexpectedEnd(
610                crate::TpmErrorValue::new(HEADER_SIZE).size(handle_area_size, body.len()),
611            ));
612        }
613
614        let after_handles = &body[handle_area_size..];
615        if after_handles.len() < size_of::<u32>() {
616            return Err(TpmError::UnexpectedEnd(
617                crate::TpmErrorValue::new(HEADER_SIZE + handle_area_size)
618                    .size(size_of::<u32>(), after_handles.len()),
619            ));
620        }
621
622        let params_len = read_u32(after_handles, 0) as usize;
623        let sessions_start =
624            size_of::<u32>()
625                .checked_add(params_len)
626                .ok_or(TpmError::IntegerTooLarge(
627                    crate::TpmErrorValue::new(HEADER_SIZE + handle_area_size)
628                        .value_usize(params_len),
629                ))?;
630
631        if after_handles.len() < sessions_start {
632            return Err(TpmError::UnexpectedEnd(
633                crate::TpmErrorValue::new(HEADER_SIZE + handle_area_size + size_of::<u32>()).size(
634                    params_len,
635                    after_handles.len().saturating_sub(size_of::<u32>()),
636                ),
637            ));
638        }
639
640        validate_auth_responses(&self.0, &after_handles[sessions_start..])
641    }
642
643    /// Returns `true` when the response frame contains no bytes.
644    #[must_use]
645    pub const fn is_empty(&self) -> bool {
646        self.0.is_empty()
647    }
648
649    /// Returns the response frame length.
650    #[must_use]
651    pub const fn len(&self) -> usize {
652        self.0.len()
653    }
654
655    fn validate_envelope(buf: &[u8]) -> TpmResult<()> {
656        validate_frame_size(buf)?;
657        let raw_tag = read_u16(buf, TAG_OFFSET);
658        let _ = TpmSt::try_from(raw_tag).map_err(|_| {
659            TpmError::InvalidTag(crate::TpmErrorValue::new(TAG_OFFSET).value(u64::from(raw_tag)))
660        })?;
661        let raw_rc = read_u32(buf, CODE_OFFSET);
662        let _ = TpmRc::try_from(raw_rc).map_err(|_| {
663            TpmError::InvalidRc(crate::TpmErrorValue::new(CODE_OFFSET).value(u64::from(raw_rc)))
664        })?;
665
666        Ok(())
667    }
668}
669
670impl TpmCast for TpmResponse {
671    fn cast(buf: &[u8]) -> TpmResult<&Self> {
672        Self::cast(buf)
673    }
674
675    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
676        Self::cast_prefix(buf)
677    }
678
679    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
680        // SAFETY: The caller upholds the unchecked cast contract for `TpmResponse`.
681        unsafe { Self::cast_unchecked(buf) }
682    }
683}
684
685impl TpmCastMut for TpmResponse {
686    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
687        Self::cast_mut(buf)
688    }
689
690    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
691        Self::cast_prefix_mut(buf)
692    }
693
694    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
695        // SAFETY: The caller upholds the unchecked mutable cast contract for
696        // `TpmResponse`.
697        unsafe { Self::cast_mut_unchecked(buf) }
698    }
699}
700
701impl AsRef<[u8]> for TpmResponse {
702    fn as_ref(&self) -> &[u8] {
703        self.as_bytes()
704    }
705}
706
707impl AsMut<[u8]> for TpmResponse {
708    fn as_mut(&mut self) -> &mut [u8] {
709        self.as_bytes_mut()
710    }
711}
712
713fn command_code(buf: &[u8]) -> TpmResult<TpmCc> {
714    let raw = read_u32(buf, CODE_OFFSET);
715
716    TpmCc::try_from(raw).map_err(|_| {
717        TpmError::InvalidCc(crate::TpmErrorValue::new(CODE_OFFSET).value(u64::from(raw)))
718    })
719}
720
721fn dispatch_for(cc: TpmCc) -> TpmResult<&'static super::TpmDispatch> {
722    TPM_DISPATCH_TABLE
723        .binary_search_by_key(&cc, |d| d.cc)
724        .map(|index| &TPM_DISPATCH_TABLE[index])
725        .map_err(|_| TpmError::InvalidCc(crate::TpmErrorValue::new(0).value(u64::from(cc.value()))))
726}
727
728fn validate_frame_size(buf: &[u8]) -> TpmResult<()> {
729    let size = frame_prefix_size(buf)?;
730
731    if buf.len() > size {
732        return Err(TpmError::TrailingData(
733            crate::TpmErrorValue::new(size).actual(buf.len() - size),
734        ));
735    }
736
737    Ok(())
738}
739
740fn handle_area_size(handles: usize, offset: usize) -> TpmResult<usize> {
741    handles
742        .checked_mul(size_of::<u32>())
743        .ok_or(TpmError::IntegerTooLarge(
744            crate::TpmErrorValue::new(offset).value_usize(handles),
745        ))
746}
747
748fn frame_prefix_size(buf: &[u8]) -> TpmResult<usize> {
749    if buf.len() < HEADER_SIZE {
750        return Err(TpmError::UnexpectedEnd(
751            crate::TpmErrorValue::new(0).size(HEADER_SIZE, buf.len()),
752        ));
753    }
754
755    let size = read_u32(buf, SIZE_OFFSET) as usize;
756    if buf.len() < size {
757        return Err(TpmError::UnexpectedEnd(
758            crate::TpmErrorValue::new(buf.len()).size(size - buf.len(), 0),
759        ));
760    }
761
762    Ok(size)
763}
764
765fn read_u16(buf: &[u8], offset: usize) -> u16 {
766    u16::from_be_bytes([buf[offset], buf[offset + 1]])
767}
768
769fn read_u32(buf: &[u8], offset: usize) -> u32 {
770    u32::from_be_bytes([
771        buf[offset],
772        buf[offset + 1],
773        buf[offset + 2],
774        buf[offset + 3],
775    ])
776}
777
778fn write_u16(buf: &mut [u8], offset: usize, value: u16) {
779    buf[offset..offset + size_of::<u16>()].copy_from_slice(&value.to_be_bytes());
780}
781
782fn write_u32(buf: &mut [u8], offset: usize, value: u32) {
783    buf[offset..offset + size_of::<u32>()].copy_from_slice(&value.to_be_bytes());
784}
785
786fn validate_auth_commands(base: &[u8], mut buf: &[u8]) -> TpmResult<()> {
787    let mut count = 0;
788
789    while !buf.is_empty() {
790        if count >= MAX_SESSIONS {
791            return Err(TpmError::TooManyItems(
792                crate::TpmErrorValue::at(base, buf).limit(MAX_SESSIONS, count + 1),
793            ));
794        }
795
796        if buf.len() < size_of::<u32>() {
797            return Err(TpmError::UnexpectedEnd(
798                crate::TpmErrorValue::at(base, buf).size(size_of::<u32>(), buf.len()),
799            ));
800        }
801
802        buf = &buf[size_of::<u32>()..];
803        buf = skip_tpm2b(base, buf)?;
804
805        if buf.is_empty() {
806            return Err(TpmError::UnexpectedEnd(
807                crate::TpmErrorValue::at(base, buf).size(1, 0),
808            ));
809        }
810
811        buf = &buf[1..];
812        buf = skip_tpm2b(base, buf)?;
813        count += 1;
814    }
815
816    Ok(())
817}
818
819fn validate_auth_responses(base: &[u8], mut buf: &[u8]) -> TpmResult<()> {
820    let mut count = 0;
821
822    while !buf.is_empty() {
823        if count >= MAX_SESSIONS {
824            return Err(TpmError::TooManyItems(
825                crate::TpmErrorValue::at(base, buf).limit(MAX_SESSIONS, count + 1),
826            ));
827        }
828
829        buf = skip_tpm2b(base, buf)?;
830
831        if buf.is_empty() {
832            return Err(TpmError::UnexpectedEnd(
833                crate::TpmErrorValue::at(base, buf).size(1, 0),
834            ));
835        }
836
837        buf = &buf[1..];
838        buf = skip_tpm2b(base, buf)?;
839        count += 1;
840    }
841
842    Ok(())
843}
844
845fn skip_tpm2b<'a>(base: &[u8], buf: &'a [u8]) -> TpmResult<&'a [u8]> {
846    if buf.len() < size_of::<u16>() {
847        return Err(TpmError::UnexpectedEnd(
848            crate::TpmErrorValue::at(base, buf).size(size_of::<u16>(), buf.len()),
849        ));
850    }
851
852    let size = read_u16(buf, 0) as usize;
853    let end = size_of::<u16>()
854        .checked_add(size)
855        .ok_or(TpmError::IntegerTooLarge(
856            crate::TpmErrorValue::at(base, buf).value_usize(size),
857        ))?;
858
859    if buf.len() < end {
860        return Err(TpmError::UnexpectedEnd(
861            crate::TpmErrorValue::at(base, buf).size(end, buf.len()),
862        ));
863    }
864
865    Ok(&buf[end..])
866}