tpm2_protocol/macro/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
5#[macro_export]
6macro_rules! integer {
7 ($name:ident, $raw:ty, $bytes:expr) => {
8 #[derive(Default, Clone, Copy, PartialEq, Eq, Hash)]
9 #[repr(transparent)]
10 pub struct $name([u8; $bytes]);
11
12 impl $name {
13 #[must_use]
14 pub const fn new(value: $raw) -> Self {
15 Self(value.to_be_bytes())
16 }
17
18 #[must_use]
19 pub const fn value(self) -> $raw {
20 <$raw>::from_be_bytes(self.0)
21 }
22
23 #[must_use]
24 pub const fn get(&self) -> $raw {
25 <$raw>::from_be_bytes(self.0)
26 }
27
28 pub const fn set(&mut self, value: $raw) {
29 self.0 = value.to_be_bytes();
30 }
31
32 #[must_use]
33 pub const fn as_bytes(&self) -> &[u8; $bytes] {
34 &self.0
35 }
36
37 #[must_use]
38 pub fn as_bytes_mut(&mut self) -> &mut [u8; $bytes] {
39 &mut self.0
40 }
41
42 #[must_use]
43 pub fn to_be_bytes(self) -> [u8; $bytes] {
44 self.0
45 }
46
47 #[must_use]
48 pub const fn from_be_bytes(bytes: [u8; $bytes]) -> Self {
49 Self(bytes)
50 }
51
52 /// Casts a byte slice into a TPM integer wire view.
53 ///
54 /// # Errors
55 ///
56 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
57 /// `buf` is smaller than this integer's wire size.
58 /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
59 /// `buf` is larger than this integer's wire size.
60 pub fn cast(buf: &[u8]) -> $crate::TpmResult<&Self> {
61 Self::validate(buf)?;
62
63 // SAFETY: The validation above guarantees the exact byte length
64 // required by this transparent integer view.
65 Ok(unsafe { Self::cast_unchecked(buf) })
66 }
67
68 /// Validates an exact TPM integer wire view.
69 ///
70 /// # Errors
71 ///
72 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
73 /// `buf` is smaller than this integer's wire size.
74 /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
75 /// `buf` is larger than this integer's wire size.
76 pub fn validate(buf: &[u8]) -> $crate::TpmResult<()> {
77 $crate::TpmWireBytes::<$bytes>::validate(buf)
78 }
79
80 /// Validates that `buf` starts with a TPM integer wire view.
81 ///
82 /// # Errors
83 ///
84 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
85 /// `buf` is smaller than this integer's wire size.
86 pub fn validate_prefix(buf: &[u8]) -> $crate::TpmResult<()> {
87 $crate::TpmWireBytes::<$bytes>::validate_prefix(buf)
88 }
89
90 /// Casts the first bytes in a slice into a TPM integer wire view.
91 ///
92 /// # Errors
93 ///
94 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
95 /// `buf` is smaller than this integer's wire size.
96 pub fn cast_prefix(buf: &[u8]) -> $crate::TpmResult<(&Self, &[u8])> {
97 Self::validate_prefix(buf)?;
98 let (head, tail) = buf.split_at($bytes);
99
100 // SAFETY: The validation above guarantees that `head` has exactly
101 // the byte length required by this transparent integer view.
102 Ok((unsafe { Self::cast_unchecked(head) }, tail))
103 }
104
105 /// Casts a byte slice into a TPM integer wire view without validation.
106 ///
107 /// # Safety
108 ///
109 /// The caller must ensure that `buf.len()` equals this integer's
110 /// wire size and that any containing protocol structure has been
111 /// validated as needed.
112 #[must_use]
113 pub unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
114 let ptr = buf.as_ptr().cast::<Self>();
115
116 // SAFETY: `$name` is `repr(transparent)` over `[u8; $bytes]`.
117 // The caller guarantees exact size.
118 unsafe { &*ptr }
119 }
120
121 /// Casts a mutable byte slice into a mutable TPM integer wire view.
122 ///
123 /// # Errors
124 ///
125 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
126 /// `buf` is smaller than this integer's wire size.
127 /// Returns [`TrailingData`](crate::TpmError::TrailingData) when
128 /// `buf` is larger than this integer's wire size.
129 pub fn cast_mut(buf: &mut [u8]) -> $crate::TpmResult<&mut Self> {
130 Self::validate(buf)?;
131
132 // SAFETY: The validation above guarantees the exact
133 // byte length required by this transparent integer view.
134 Ok(unsafe { Self::cast_mut_unchecked(buf) })
135 }
136
137 /// Casts the first mutable bytes in a slice into a TPM integer wire view.
138 ///
139 /// # Errors
140 ///
141 /// Returns [`UnexpectedEnd`](crate::TpmError::UnexpectedEnd) when
142 /// `buf` is smaller than this integer's wire size.
143 pub fn cast_prefix_mut(buf: &mut [u8]) -> $crate::TpmResult<(&mut Self, &mut [u8])> {
144 Self::validate_prefix(buf)?;
145 let (head, tail) = buf.split_at_mut($bytes);
146
147 // SAFETY: The validation above guarantees that `head` has exactly
148 // the byte length required by this transparent integer view.
149 Ok((unsafe { Self::cast_mut_unchecked(head) }, tail))
150 }
151
152 /// Casts a mutable byte slice into a mutable TPM integer wire view without validation.
153 ///
154 /// # Safety
155 ///
156 /// The caller must ensure that `buf.len()` equals this integer's
157 /// wire size and that any containing protocol structure has been
158 /// validated as needed. The returned reference inherits the
159 /// exclusive access represented by `buf`.
160 #[must_use]
161 pub unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
162 let ptr = buf.as_mut_ptr().cast::<Self>();
163
164 // SAFETY: `$name` is `repr(transparent)` over `[u8; $bytes]`.
165 // The caller guarantees exact size.
166 unsafe { &mut *ptr }
167 }
168 }
169
170 impl core::fmt::Debug for $name {
171 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
172 f.debug_tuple(stringify!($name)).field(&self.get()).finish()
173 }
174 }
175
176 impl core::cmp::PartialOrd for $name {
177 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
178 Some(self.cmp(other))
179 }
180 }
181
182 impl core::cmp::Ord for $name {
183 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
184 self.get().cmp(&other.get())
185 }
186 }
187
188 impl From<$raw> for $name {
189 fn from(value: $raw) -> Self {
190 Self::new(value)
191 }
192 }
193
194 impl From<$name> for $raw {
195 fn from(value: $name) -> $raw {
196 value.value()
197 }
198 }
199
200 impl core::fmt::Display for $name {
201 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
202 core::fmt::Display::fmt(&self.get(), f)
203 }
204 }
205
206 impl core::fmt::LowerHex for $name {
207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208 core::fmt::LowerHex::fmt(&self.get(), f)
209 }
210 }
211
212 impl core::fmt::UpperHex for $name {
213 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214 core::fmt::UpperHex::fmt(&self.get(), f)
215 }
216 }
217
218 impl $crate::TpmSized for $name {
219 const SIZE: usize = $bytes;
220 fn len(&self) -> usize {
221 Self::SIZE
222 }
223 }
224
225 impl $crate::TpmMarshal for $name {
226 fn marshal(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
227 writer.write_bytes(self.as_bytes())
228 }
229 }
230
231 impl $crate::TpmCast for $name {
232 fn cast(buf: &[u8]) -> $crate::TpmResult<&Self> {
233 Self::cast(buf)
234 }
235
236 fn cast_prefix(buf: &[u8]) -> $crate::TpmResult<(&Self, &[u8])> {
237 Self::cast_prefix(buf)
238 }
239
240 unsafe fn cast_unchecked(buf: &[u8]) -> &Self {
241 // SAFETY: The caller upholds the unchecked cast contract for `$name`.
242 unsafe { Self::cast_unchecked(buf) }
243 }
244 }
245
246 impl $crate::TpmCastMut for $name {
247 fn cast_mut(buf: &mut [u8]) -> $crate::TpmResult<&mut Self> {
248 Self::cast_mut(buf)
249 }
250
251 fn cast_prefix_mut(buf: &mut [u8]) -> $crate::TpmResult<(&mut Self, &mut [u8])> {
252 Self::cast_prefix_mut(buf)
253 }
254
255 unsafe fn cast_mut_unchecked(buf: &mut [u8]) -> &mut Self {
256 // SAFETY: The caller upholds the unchecked mutable cast contract for `$name`.
257 unsafe { Self::cast_mut_unchecked(buf) }
258 }
259 }
260
261 impl TryFrom<usize> for $name
262 where
263 $raw: TryFrom<usize>,
264 {
265 type Error = <$raw as TryFrom<usize>>::Error;
266 fn try_from(value: usize) -> Result<Self, Self::Error> {
267 <$raw>::try_from(value).map(Self::new)
268 }
269 }
270 };
271}