Skip to main content

tpm2_protocol/basic/
buffer.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5use crate::{
6    TpmCast, TpmCastMut, TpmError, TpmMarshal, TpmResult, TpmSized, TpmWriter, basic::TpmUint16,
7};
8use core::{
9    convert::TryFrom,
10    fmt::Debug,
11    hash::{Hash, Hasher},
12    mem::{MaybeUninit, size_of},
13    ops::Deref,
14    slice,
15};
16
17const TPM2B_SIZE_LEN: usize = size_of::<TpmUint16>();
18
19/// A zero-copy TPM2B wire view over caller-owned bytes.
20#[repr(transparent)]
21pub struct Tpm2b<const CAPACITY: usize>([u8]);
22
23impl<const CAPACITY: usize> Tpm2b<CAPACITY> {
24    /// Casts a byte slice into a TPM2B wire view.
25    ///
26    /// # Errors
27    ///
28    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
29    /// `buf` is shorter than the TPM2B header or declared payload size.
30    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
31    /// `buf` contains bytes after the declared payload.
32    /// Returns [`TooManyBytes`](crate::TpmError::TooManyBytes) when
33    /// the declared payload exceeds `CAPACITY`.
34    pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
35        Self::validate(buf)?;
36
37        // SAFETY: `validate` checked the complete TPM2B byte range and size
38        // limit for this transparent wire view.
39        Ok(unsafe { Self::cast_unchecked(buf) })
40    }
41
42    /// Casts the first TPM2B value in a byte slice into a wire view.
43    ///
44    /// # Errors
45    ///
46    /// Returns `Err(TpmError)` when the first TPM2B value is malformed.
47    pub fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
48        let wire_len = Self::validate_prefix(buf)?;
49        let (head, tail) = buf.split_at(wire_len);
50
51        // SAFETY: `validate_prefix` checked the complete TPM2B byte range and
52        // size limit for `head`.
53        Ok((unsafe { Self::cast_unchecked(head) }, tail))
54    }
55
56    /// Casts a byte slice into a TPM2B wire view without validation.
57    ///
58    /// # Safety
59    ///
60    /// The caller must ensure that `buf` contains exactly one complete TPM2B
61    /// value and that its declared payload length does not exceed `CAPACITY`.
62    #[must_use]
63    pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
64        let ptr = core::ptr::from_ref(buf) as *const Self;
65
66        // SAFETY: `Tpm2b` is `repr(transparent)` over `[u8]`, so it has the
67        // same layout, metadata, and alignment as the referenced slice.
68        unsafe { &*ptr }
69    }
70
71    /// Casts a mutable byte slice into a mutable TPM2B wire view.
72    ///
73    /// # Errors
74    ///
75    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
76    /// `buf` is shorter than the TPM2B header or declared payload size.
77    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
78    /// `buf` contains bytes after the declared payload.
79    /// Returns [`TooManyBytes`](crate::TpmError::TooManyBytes) when
80    /// the declared payload exceeds `CAPACITY`.
81    pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
82        Self::validate(buf)?;
83
84        // SAFETY: `validate` checked the complete TPM2B byte range and size
85        // limit for this transparent wire view. The `&mut` input provides
86        // exclusive access.
87        Ok(unsafe { Self::cast_mut_unchecked(buf) })
88    }
89
90    /// Casts the first mutable TPM2B value in a byte slice into a wire view.
91    ///
92    /// # Errors
93    ///
94    /// Returns `Err(TpmError)` when the first TPM2B value is malformed.
95    pub fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
96        let wire_len = Self::validate_prefix(buf)?;
97        let (head, tail) = buf.split_at_mut(wire_len);
98
99        // SAFETY: `validate_prefix` checked the complete TPM2B byte range and
100        // size limit for `head`.
101        Ok((unsafe { Self::cast_mut_unchecked(head) }, tail))
102    }
103
104    /// Casts a mutable byte slice into a mutable TPM2B wire view without validation.
105    ///
106    /// # Safety
107    ///
108    /// The caller must ensure that `buf` contains exactly one complete TPM2B
109    /// value and that its declared payload length does not exceed `CAPACITY`.
110    /// The returned reference inherits the exclusive access represented by
111    /// `buf`.
112    #[must_use]
113    pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
114        let ptr = core::ptr::from_mut(buf) as *mut Self;
115
116        // SAFETY: `Tpm2b` is `repr(transparent)` over `[u8]`, so it has the
117        // same layout, metadata, and alignment as the referenced slice.
118        unsafe { &mut *ptr }
119    }
120
121    /// Returns the complete TPM2B byte representation.
122    #[must_use]
123    pub const fn as_bytes(&self) -> &[u8] {
124        &self.0
125    }
126
127    /// Returns the complete mutable TPM2B byte representation.
128    #[must_use]
129    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
130        &mut self.0
131    }
132
133    /// Returns the declared payload size.
134    #[must_use]
135    pub fn size(&self) -> usize {
136        Self::read_size(&self.0)
137    }
138
139    /// Returns the payload bytes.
140    #[must_use]
141    pub fn data(&self) -> &[u8] {
142        &self.0[TPM2B_SIZE_LEN..]
143    }
144
145    /// Returns the payload bytes, as an alias for [`Self::data`].
146    #[must_use]
147    pub fn payload(&self) -> &[u8] {
148        self.data()
149    }
150
151    /// Returns the mutable payload bytes.
152    #[must_use]
153    pub fn data_mut(&mut self) -> &mut [u8] {
154        &mut self.0[TPM2B_SIZE_LEN..]
155    }
156
157    /// Returns the mutable payload bytes, as an alias for [`Self::data_mut`].
158    #[must_use]
159    pub fn payload_mut(&mut self) -> &mut [u8] {
160        self.data_mut()
161    }
162
163    /// Returns the complete TPM2B wire length.
164    #[must_use]
165    pub const fn len(&self) -> usize {
166        self.0.len()
167    }
168
169    /// Returns `true` when the TPM2B payload is empty.
170    #[must_use]
171    pub fn is_empty(&self) -> bool {
172        self.size() == 0
173    }
174
175    /// Validates an exact TPM2B wire value.
176    ///
177    /// # Errors
178    ///
179    /// Returns `Err(TpmError)` when the TPM2B value is malformed or has
180    /// trailing data.
181    pub fn validate(buf: &[u8]) -> TpmResult<()> {
182        let wire_len = Self::validate_prefix(buf)?;
183
184        if buf.len() > wire_len {
185            return Err(TpmError::TrailingData(
186                crate::TpmErrorValue::new(wire_len).actual(buf.len() - wire_len),
187            ));
188        }
189
190        Ok(())
191    }
192
193    /// Validates the first TPM2B wire value and returns its wire length.
194    ///
195    /// # Errors
196    ///
197    /// Returns `Err(TpmError)` when the first TPM2B value is malformed.
198    pub fn validate_prefix(buf: &[u8]) -> TpmResult<usize> {
199        if buf.len() < TPM2B_SIZE_LEN {
200            return Err(TpmError::UnexpectedEnd(
201                crate::TpmErrorValue::new(0).size(TPM2B_SIZE_LEN, buf.len()),
202            ));
203        }
204
205        let payload_len = Self::read_size(buf);
206        if payload_len > CAPACITY {
207            return Err(TpmError::TooManyBytes(
208                crate::TpmErrorValue::new(0).limit(CAPACITY, payload_len),
209            ));
210        }
211
212        let wire_len = TPM2B_SIZE_LEN
213            .checked_add(payload_len)
214            .ok_or(TpmError::IntegerTooLarge(
215                crate::TpmErrorValue::new(0).value_usize(payload_len),
216            ))?;
217
218        if buf.len() < wire_len {
219            return Err(TpmError::UnexpectedEnd(
220                crate::TpmErrorValue::new(TPM2B_SIZE_LEN)
221                    .size(payload_len, buf.len().saturating_sub(TPM2B_SIZE_LEN)),
222            ));
223        }
224
225        Ok(wire_len)
226    }
227
228    fn read_size(buf: &[u8]) -> usize {
229        usize::from(u16::from_be_bytes([buf[0], buf[1]]))
230    }
231}
232
233impl<const CAPACITY: usize> TpmCast for Tpm2b<CAPACITY> {
234    fn cast(buf: &[u8]) -> TpmResult<&Self> {
235        Self::cast(buf)
236    }
237
238    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
239        Self::cast_prefix(buf)
240    }
241
242    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
243        // SAFETY: The caller upholds the unchecked cast contract for `Tpm2b`.
244        unsafe { Self::cast_unchecked(buf) }
245    }
246}
247
248impl<const CAPACITY: usize> TpmCastMut for Tpm2b<CAPACITY> {
249    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
250        Self::cast_mut(buf)
251    }
252
253    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
254        Self::cast_prefix_mut(buf)
255    }
256
257    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
258        // SAFETY: The caller upholds the unchecked mutable cast contract for
259        // `Tpm2b`.
260        unsafe { Self::cast_mut_unchecked(buf) }
261    }
262}
263
264impl<'a, const CAPACITY: usize> crate::TpmField<'a> for TpmBuffer<CAPACITY> {
265    type View = &'a Tpm2b<CAPACITY>;
266
267    fn cast_prefix_field(buf: &'a [u8]) -> TpmResult<(Self::View, &'a [u8])> {
268        Tpm2b::<CAPACITY>::cast_prefix(buf)
269    }
270}
271
272impl<const CAPACITY: usize> AsRef<[u8]> for Tpm2b<CAPACITY> {
273    fn as_ref(&self) -> &[u8] {
274        self.as_bytes()
275    }
276}
277
278impl<const CAPACITY: usize> AsMut<[u8]> for Tpm2b<CAPACITY> {
279    fn as_mut(&mut self) -> &mut [u8] {
280        self.as_bytes_mut()
281    }
282}
283
284/// A buffer in the TPM2B wire format.
285///
286/// The `size` field is stored in native endian and converted to big-endian
287/// only during marshaling.
288#[derive(Clone, Copy)]
289pub struct TpmBuffer<const CAPACITY: usize> {
290    size: u16,
291    data: [MaybeUninit<u8>; CAPACITY],
292}
293
294impl<const CAPACITY: usize> TpmBuffer<CAPACITY> {
295    /// Creates a new, empty `TpmBuffer`.
296    #[must_use]
297    pub const fn new() -> Self {
298        Self {
299            size: 0,
300            data: [const { MaybeUninit::uninit() }; CAPACITY],
301        }
302    }
303
304    /// Appends a byte to the buffer.
305    ///
306    /// # Errors
307    ///
308    /// Returns [`BufferOverflow`](crate::TpmError::BufferOverflow) when the
309    /// buffer is full or the size exceeds `u16::MAX`.
310    pub fn try_push(&mut self, byte: u8) -> TpmResult<()> {
311        if (self.size as usize) >= CAPACITY || self.size == u16::MAX {
312            return Err(TpmError::BufferOverflow(
313                crate::TpmErrorValue::new(self.size as usize)
314                    .limit(CAPACITY, self.size as usize + 1),
315            ));
316        }
317        self.data[self.size as usize].write(byte);
318        self.size += 1;
319        Ok(())
320    }
321
322    /// Appends a slice of bytes to the buffer.
323    ///
324    /// # Errors
325    ///
326    /// Returns [`BufferOverflow`](crate::TpmError::BufferOverflow) when the
327    /// resulting size exceeds the buffer capacity or `u16::MAX`.
328    pub fn try_extend_from_slice(&mut self, slice: &[u8]) -> TpmResult<()> {
329        let current_len = self.size as usize;
330        let new_len = current_len
331            .checked_add(slice.len())
332            .ok_or(TpmError::BufferOverflow(
333                crate::TpmErrorValue::new(current_len)
334                    .size(slice.len(), CAPACITY.saturating_sub(current_len)),
335            ))?;
336
337        if new_len > CAPACITY {
338            return Err(TpmError::BufferOverflow(
339                crate::TpmErrorValue::new(current_len).limit(CAPACITY, new_len),
340            ));
341        }
342
343        self.size = u16::try_from(new_len).map_err(|_| {
344            TpmError::BufferOverflow(
345                crate::TpmErrorValue::new(current_len).limit(u16::MAX as usize, new_len),
346            )
347        })?;
348
349        for (dest, src) in self.data[current_len..new_len].iter_mut().zip(slice) {
350            dest.write(*src);
351        }
352        Ok(())
353    }
354}
355
356impl<const CAPACITY: usize> Deref for TpmBuffer<CAPACITY> {
357    type Target = [u8];
358
359    fn deref(&self) -> &Self::Target {
360        let size = self.size as usize;
361
362        // SAFETY: The first `size` bytes are initialized by the mutation APIs,
363        // and `MaybeUninit<u8>` has the same layout as `u8`.
364        unsafe { slice::from_raw_parts(self.data.as_ptr().cast::<u8>(), size) }
365    }
366}
367
368impl<const CAPACITY: usize> Default for TpmBuffer<CAPACITY> {
369    fn default() -> Self {
370        Self::new()
371    }
372}
373
374impl<const CAPACITY: usize> PartialEq for TpmBuffer<CAPACITY> {
375    fn eq(&self, other: &Self) -> bool {
376        **self == **other
377    }
378}
379
380impl<const CAPACITY: usize> Eq for TpmBuffer<CAPACITY> {}
381
382impl<const CAPACITY: usize> Hash for TpmBuffer<CAPACITY> {
383    fn hash<H: Hasher>(&self, state: &mut H) {
384        (**self).hash(state);
385    }
386}
387
388impl<const CAPACITY: usize> TpmSized for TpmBuffer<CAPACITY> {
389    const SIZE: usize = size_of::<TpmUint16>() + CAPACITY;
390    fn len(&self) -> usize {
391        size_of::<TpmUint16>() + self.size as usize
392    }
393}
394
395impl<const CAPACITY: usize> TpmMarshal for TpmBuffer<CAPACITY> {
396    fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()> {
397        TpmUint16::from(self.size).marshal(writer)?;
398        writer.write_bytes(self)
399    }
400}
401
402impl<const CAPACITY: usize> TryFrom<&[u8]> for TpmBuffer<CAPACITY> {
403    type Error = TpmError;
404
405    fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
406        if slice.len() > CAPACITY {
407            return Err(TpmError::TooManyBytes(
408                crate::TpmErrorValue::new(0).limit(CAPACITY, slice.len()),
409            ));
410        }
411        let mut buffer = Self::new();
412        buffer.try_extend_from_slice(slice)?;
413        Ok(buffer)
414    }
415}
416
417impl<const CAPACITY: usize> AsRef<[u8]> for TpmBuffer<CAPACITY> {
418    fn as_ref(&self) -> &[u8] {
419        self
420    }
421}
422
423impl<const CAPACITY: usize> Debug for TpmBuffer<CAPACITY> {
424    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
425        write!(f, "TpmBuffer(")?;
426        for byte in self.iter() {
427            write!(f, "{byte:02X}")?;
428        }
429        write!(f, ")")
430    }
431}