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