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