Skip to main content

tpm2_protocol/basic/
integer.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, TpmMarshal, TpmResult, TpmSized, TpmUnmarshal, TpmWireBytes, TpmWriter,
7};
8use core::{
9    cmp::Ordering,
10    convert::TryFrom,
11    fmt::{Debug, Display, Formatter, LowerHex, UpperHex},
12    hash::{Hash, Hasher},
13    marker::PhantomData,
14};
15
16/// Native integer that can occupy an `N`-byte TPM wire field.
17trait TpmIntBytes<const N: usize>: Copy + Ord {
18    fn to_be_bytes(self) -> [u8; N];
19    fn from_be_bytes(bytes: [u8; N]) -> Self;
20}
21
22/// Big-endian TPM integer of native type `T` and wire width `N`.
23#[repr(transparent)]
24pub struct TpmInt<T, const N: usize>([u8; N], PhantomData<T>);
25
26impl<T, const N: usize> Copy for TpmInt<T, N> {}
27
28impl<T, const N: usize> Clone for TpmInt<T, N> {
29    fn clone(&self) -> Self {
30        *self
31    }
32}
33
34impl<T, const N: usize> TpmInt<T, N> {
35    #[must_use]
36    pub const fn from_be_bytes(bytes: [u8; N]) -> Self {
37        Self(bytes, PhantomData)
38    }
39
40    #[must_use]
41    pub const fn as_bytes(&self) -> &[u8; N] {
42        &self.0
43    }
44
45    #[must_use]
46    pub fn as_bytes_mut(&mut self) -> &mut [u8; N] {
47        &mut self.0
48    }
49
50    #[must_use]
51    pub const fn to_be_bytes(self) -> [u8; N] {
52        self.0
53    }
54
55    /// Casts a byte slice into a TPM integer wire view.
56    ///
57    /// # Errors
58    ///
59    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
60    /// `buf` is smaller than this integer's wire size.
61    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
62    /// `buf` is larger than this integer's wire size.
63    pub fn cast(buf: &[u8]) -> TpmResult<&Self> {
64        Self::validate(buf)?;
65
66        // SAFETY: The validation above guarantees the exact byte length
67        // required by this transparent integer view.
68        Ok(unsafe { Self::cast_unchecked(buf) })
69    }
70
71    /// Validates an exact TPM integer wire view.
72    ///
73    /// # Errors
74    ///
75    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
76    /// `buf` is smaller than this integer's wire size.
77    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
78    /// `buf` is larger than this integer's wire size.
79    pub fn validate(buf: &[u8]) -> TpmResult<()> {
80        TpmWireBytes::<N>::validate(buf)
81    }
82
83    /// Validates that `buf` starts with a TPM integer wire view.
84    ///
85    /// # Errors
86    ///
87    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
88    /// `buf` is smaller than this integer's wire size.
89    pub fn validate_prefix(buf: &[u8]) -> TpmResult<()> {
90        TpmWireBytes::<N>::validate_prefix(buf)
91    }
92
93    /// Casts the first bytes in a slice into a TPM integer wire view.
94    ///
95    /// # Errors
96    ///
97    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
98    /// `buf` is smaller than this integer's wire size.
99    pub fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
100        Self::validate_prefix(buf)?;
101        let (head, tail) = buf.split_at(N);
102
103        // SAFETY: The validation above guarantees that `head` has exactly
104        // the byte length required by this transparent integer view.
105        Ok((unsafe { Self::cast_unchecked(head) }, tail))
106    }
107
108    /// Casts a mutable byte slice into a mutable TPM integer wire view.
109    ///
110    /// # Errors
111    ///
112    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
113    /// `buf` is smaller than this integer's wire size.
114    /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
115    /// `buf` is larger than this integer's wire size.
116    pub fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
117        Self::validate(buf)?;
118
119        // SAFETY: The validation above guarantees the exact
120        // byte length required by this transparent integer view.
121        Ok(unsafe { Self::cast_mut_unchecked(buf) })
122    }
123
124    /// Casts the first mutable bytes in a slice into a TPM integer wire view.
125    ///
126    /// # Errors
127    ///
128    /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
129    /// `buf` is smaller than this integer's wire size.
130    pub fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
131        Self::validate_prefix(buf)?;
132        let (head, tail) = buf.split_at_mut(N);
133
134        // SAFETY: The validation above guarantees that `head` has exactly
135        // the byte length required by this transparent integer view.
136        Ok((unsafe { Self::cast_mut_unchecked(head) }, tail))
137    }
138}
139
140crate::tpm_byte_view!(array TpmInt<T, const N: usize>);
141
142impl<T, const N: usize> Default for TpmInt<T, N> {
143    fn default() -> Self {
144        Self::from_be_bytes([0; N])
145    }
146}
147
148impl<T, const N: usize> PartialEq for TpmInt<T, N> {
149    fn eq(&self, other: &Self) -> bool {
150        self.0 == other.0
151    }
152}
153
154impl<T, const N: usize> Eq for TpmInt<T, N> {}
155
156impl<T, const N: usize> Hash for TpmInt<T, N> {
157    fn hash<H: Hasher>(&self, state: &mut H) {
158        self.0.hash(state);
159    }
160}
161
162impl<T: TpmIntBytes<N>, const N: usize> PartialOrd for TpmInt<T, N> {
163    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
164        Some(self.cmp(other))
165    }
166}
167
168impl<T: TpmIntBytes<N>, const N: usize> Ord for TpmInt<T, N> {
169    fn cmp(&self, other: &Self) -> Ordering {
170        T::from_be_bytes(self.0).cmp(&T::from_be_bytes(other.0))
171    }
172}
173
174impl<T: TpmIntBytes<N> + Debug, const N: usize> Debug for TpmInt<T, N> {
175    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
176        f.debug_tuple("TpmInt")
177            .field(&T::from_be_bytes(self.0))
178            .finish()
179    }
180}
181
182impl<T: TpmIntBytes<N>, const N: usize> From<T> for TpmInt<T, N> {
183    fn from(value: T) -> Self {
184        Self::from_be_bytes(value.to_be_bytes())
185    }
186}
187
188impl<T: TpmIntBytes<N> + Display, const N: usize> Display for TpmInt<T, N> {
189    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
190        Display::fmt(&T::from_be_bytes(self.0), f)
191    }
192}
193
194impl<T: TpmIntBytes<N> + LowerHex, const N: usize> LowerHex for TpmInt<T, N> {
195    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
196        LowerHex::fmt(&T::from_be_bytes(self.0), f)
197    }
198}
199
200impl<T: TpmIntBytes<N> + UpperHex, const N: usize> UpperHex for TpmInt<T, N> {
201    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
202        UpperHex::fmt(&T::from_be_bytes(self.0), f)
203    }
204}
205
206impl<T, const N: usize> TpmSized for TpmInt<T, N> {
207    const SIZE: usize = N;
208
209    fn len(&self) -> usize {
210        Self::SIZE
211    }
212}
213
214impl<T, const N: usize> TpmMarshal for TpmInt<T, N> {
215    fn marshal(&self, writer: &mut TpmWriter) -> TpmResult<()> {
216        writer.write_bytes(self.as_bytes())
217    }
218}
219
220impl<T, const N: usize> TpmUnmarshal for TpmInt<T, N> {
221    fn unmarshal(buffer: &[u8]) -> TpmResult<(Self, &[u8])> {
222        let (value, remainder) = TpmWireBytes::<N>::cast_prefix(buffer)?;
223        Ok((Self::from_be_bytes(*value.as_bytes()), remainder))
224    }
225}
226
227impl<T, const N: usize> TpmCast for TpmInt<T, N> {
228    fn cast(buf: &[u8]) -> TpmResult<&Self> {
229        Self::cast(buf)
230    }
231
232    fn cast_prefix(buf: &[u8]) -> TpmResult<(&Self, &[u8])> {
233        Self::cast_prefix(buf)
234    }
235
236    unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
237        // SAFETY: The caller upholds the unchecked cast contract for `TpmInt`.
238        unsafe { Self::cast_unchecked(buf) }
239    }
240}
241
242impl<T, const N: usize> TpmCastMut for TpmInt<T, N> {
243    fn cast_mut(buf: &mut [u8]) -> TpmResult<&mut Self> {
244        Self::cast_mut(buf)
245    }
246
247    fn cast_prefix_mut(buf: &mut [u8]) -> TpmResult<(&mut Self, &mut [u8])> {
248        Self::cast_prefix_mut(buf)
249    }
250
251    unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
252        // SAFETY: The caller upholds the unchecked mutable cast contract for `TpmInt`.
253        unsafe { Self::cast_mut_unchecked(buf) }
254    }
255}
256
257impl<T, const N: usize> TryFrom<usize> for TpmInt<T, N>
258where
259    T: TpmIntBytes<N> + TryFrom<usize>,
260{
261    type Error = T::Error;
262
263    fn try_from(value: usize) -> Result<Self, Self::Error> {
264        T::try_from(value).map(Self::from)
265    }
266}
267
268macro_rules! tpm_int {
269    ($name:ident, $raw:ty, $n:literal) => {
270        pub type $name = TpmInt<$raw, $n>;
271
272        impl TpmIntBytes<$n> for $raw {
273            fn to_be_bytes(self) -> [u8; $n] {
274                <$raw>::to_be_bytes(self)
275            }
276
277            fn from_be_bytes(bytes: [u8; $n]) -> Self {
278                <$raw>::from_be_bytes(bytes)
279            }
280        }
281
282        impl TpmInt<$raw, $n> {
283            #[must_use]
284            pub const fn new(value: $raw) -> Self {
285                Self::from_be_bytes(<$raw>::to_be_bytes(value))
286            }
287
288            #[must_use]
289            pub const fn value(self) -> $raw {
290                <$raw>::from_be_bytes(self.to_be_bytes())
291            }
292
293            pub const fn set(&mut self, value: $raw) {
294                *self = Self::new(value);
295            }
296        }
297
298        impl From<TpmInt<$raw, $n>> for $raw {
299            fn from(value: TpmInt<$raw, $n>) -> $raw {
300                value.value()
301            }
302        }
303    };
304}
305
306tpm_int!(TpmUint8, u8, 1);
307tpm_int!(TpmInt8, i8, 1);
308tpm_int!(TpmUint16, u16, 2);
309tpm_int!(TpmUint32, u32, 4);
310tpm_int!(TpmUint64, u64, 8);
311tpm_int!(TpmInt32, i32, 4);
312
313pub type TpmHandle = TpmUint32;