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