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