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