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