1use crate::ValidationError;
2use subtle::{Choice, ConstantTimeEq};
3use zeroize::Zeroize;
4
5#[derive(Eq)]
6struct IdentifierBytes<const MIN: usize, const MAX: usize> {
7 bytes: [u8; MAX],
8 len: u8,
9}
10
11impl<const MIN: usize, const MAX: usize> IdentifierBytes<MIN, MAX> {
12 fn new(bytes: &[u8]) -> Result<Self, ValidationError> {
13 if bytes.is_empty() {
14 return Err(ValidationError::Empty);
15 }
16 if bytes.len() < MIN {
17 return Err(ValidationError::Malformed);
18 }
19 if bytes.len() > MAX {
20 return Err(ValidationError::TooLarge);
21 }
22 if bytes.iter().all(|byte| *byte == 0) {
23 return Err(ValidationError::ZeroValue);
24 }
25
26 let len = u8::try_from(bytes.len()).map_err(|_| ValidationError::TooLarge)?;
27 let mut stored = [0; MAX];
28 stored[..bytes.len()].copy_from_slice(bytes);
29 Ok(Self { bytes: stored, len })
30 }
31
32 fn as_bytes(&self) -> &[u8] {
33 self.bytes.split_at(self.len as usize).0
34 }
35
36 const fn len(&self) -> usize {
37 self.len as usize
38 }
39
40 fn ct_eq(&self, other: &Self) -> bool {
41 let len_eq: Choice = self.len.ct_eq(&other.len);
42 let bytes_eq: Choice = self.bytes.ct_eq(&other.bytes);
43 bool::from(len_eq & bytes_eq)
44 }
45}
46
47impl<const MIN: usize, const MAX: usize> PartialEq for IdentifierBytes<MIN, MAX> {
48 fn eq(&self, other: &Self) -> bool {
49 self.ct_eq(other)
50 }
51}
52
53impl<const MIN: usize, const MAX: usize> Drop for IdentifierBytes<MIN, MAX> {
54 fn drop(&mut self) {
55 self.bytes.zeroize();
56 self.len.zeroize();
57 }
58}
59
60macro_rules! define_identifier {
61 ($(#[$meta:meta])* $name:ident, $min:expr, $max:expr) => {
62 $(#[$meta])*
63 #[derive(Eq, PartialEq)]
64 pub struct $name(IdentifierBytes<{ $min }, { $max }>);
65
66 impl $name {
67 pub const MIN_LEN: usize = $min;
69 pub const MAX_LEN: usize = $max;
71
72 pub fn new(bytes: &[u8]) -> Result<Self, ValidationError> {
74 IdentifierBytes::new(bytes).map(Self)
75 }
76
77 #[must_use]
79 pub fn as_bytes(&self) -> &[u8] {
80 self.0.as_bytes()
81 }
82
83 #[must_use]
85 pub const fn len(&self) -> usize {
86 self.0.len()
87 }
88
89 #[must_use]
91 pub const fn is_empty(&self) -> bool {
92 false
93 }
94 }
95
96 impl core::fmt::Debug for $name {
97 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
98 formatter.write_str(concat!(stringify!($name), "(..)"))
99 }
100 }
101 };
102}
103
104#[derive(Clone, Copy, Eq)]
106pub struct Digest([u8; Self::LEN]);
107
108impl Digest {
109 pub const LEN: usize = 32;
111
112 #[must_use]
114 pub const fn new(bytes: [u8; Self::LEN]) -> Self {
115 Self(bytes)
116 }
117
118 #[must_use]
120 pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
121 &self.0
122 }
123
124 #[must_use]
126 pub fn is_zero(&self) -> bool {
127 bool::from(self.0.ct_eq(&[0u8; Self::LEN]))
128 }
129
130 #[must_use]
134 pub fn ct_eq(&self, other: &Self) -> bool {
135 bool::from(self.0.ct_eq(&other.0))
136 }
137}
138
139impl PartialEq for Digest {
140 fn eq(&self, other: &Self) -> bool {
141 self.ct_eq(other)
142 }
143}
144
145impl core::hash::Hash for Digest {
146 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
147 core::hash::Hash::hash(&self.0, state);
148 }
149}
150
151impl core::fmt::Debug for Digest {
152 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153 formatter.write_str("Digest(..)")
154 }
155}
156
157define_identifier!(
158 StatementId,
160 Digest::LEN,
161 Digest::LEN
162);
163
164define_identifier!(
165 SubjectId,
167 1,
168 64
169);
170
171define_identifier!(
172 RealmId,
174 1,
175 64
176);
177
178define_identifier!(
179 ProfileId,
181 1,
182 32
183);
184
185define_identifier!(
186 ProofSuiteId,
188 1,
189 32
190);
191
192define_identifier!(
193 PolicyId,
195 1,
196 32
197);
198
199define_identifier!(
200 CheckpointId,
202 Digest::LEN,
203 Digest::LEN
204);
205
206define_identifier!(
207 NativeBindingId,
209 1,
210 64
211);
212
213#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
215pub struct EventId(Digest);
216
217impl EventId {
218 pub fn new(digest: Digest) -> Result<Self, ValidationError> {
220 if digest.is_zero() {
221 Err(ValidationError::ZeroValue)
222 } else {
223 Ok(Self(digest))
224 }
225 }
226
227 #[must_use]
229 pub const fn digest(&self) -> Digest {
230 self.0
231 }
232}
233
234#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
236pub struct CapabilityRef(Digest);
237
238impl CapabilityRef {
239 pub fn new(digest: Digest) -> Result<Self, ValidationError> {
241 if digest.is_zero() {
242 Err(ValidationError::ZeroValue)
243 } else {
244 Ok(Self(digest))
245 }
246 }
247
248 #[must_use]
250 pub const fn digest(&self) -> Digest {
251 self.0
252 }
253}
254
255#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
257pub struct PolicyEpoch(Digest);
258
259impl PolicyEpoch {
260 pub fn new(digest: Digest) -> Result<Self, ValidationError> {
262 if digest.is_zero() {
263 Err(ValidationError::ZeroValue)
264 } else {
265 Ok(Self(digest))
266 }
267 }
268
269 #[must_use]
271 pub const fn digest(&self) -> Digest {
272 self.0
273 }
274}
275
276#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
278pub struct OperationSequence(u64);
279
280impl OperationSequence {
281 pub const fn new(value: u64) -> Result<Self, ValidationError> {
283 if value == 0 {
284 Err(ValidationError::ZeroValue)
285 } else {
286 Ok(Self(value))
287 }
288 }
289
290 #[must_use]
292 pub const fn get(self) -> u64 {
293 self.0
294 }
295
296 pub const fn next(self) -> Result<Self, ValidationError> {
298 match self.0.checked_add(1) {
299 Some(value) => Ok(Self(value)),
300 None => Err(ValidationError::TooLarge),
301 }
302 }
303
304 #[must_use]
306 pub const fn immediately_follows(self, previous: Self) -> bool {
307 match previous.0.checked_add(1) {
308 Some(expected) => self.0 == expected,
309 None => false,
310 }
311 }
312}
313
314#[derive(Eq)]
322pub struct Nonce([u8; Self::LEN]);
323
324impl Nonce {
325 pub const LEN: usize = 16;
327
328 pub fn new(bytes: [u8; Self::LEN]) -> Result<Self, ValidationError> {
330 if bool::from(bytes.ct_eq(&[0u8; Self::LEN])) {
331 Err(ValidationError::ZeroValue)
332 } else {
333 Ok(Self(bytes))
334 }
335 }
336
337 #[must_use]
339 pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
340 &self.0
341 }
342
343 #[must_use]
345 pub fn ct_eq(&self, other: &Self) -> bool {
346 bool::from(self.0.ct_eq(&other.0))
347 }
348}
349
350impl PartialEq for Nonce {
351 fn eq(&self, other: &Self) -> bool {
352 self.ct_eq(other)
353 }
354}
355
356impl Drop for Nonce {
357 fn drop(&mut self) {
358 self.0.zeroize();
359 }
360}
361
362impl core::fmt::Debug for Nonce {
363 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364 formatter.write_str("Nonce(..)")
365 }
366}