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)]
110pub struct Digest([u8; Self::LEN]);
111
112impl Digest {
113 pub const LEN: usize = 32;
115
116 #[must_use]
118 pub const fn new(bytes: [u8; Self::LEN]) -> Self {
119 Self(bytes)
120 }
121
122 #[must_use]
124 pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
125 &self.0
126 }
127
128 #[must_use]
130 pub fn is_zero(&self) -> bool {
131 bool::from(self.0.ct_eq(&[0u8; Self::LEN]))
132 }
133
134 #[must_use]
138 pub fn ct_eq(&self, other: &Self) -> bool {
139 bool::from(self.0.ct_eq(&other.0))
140 }
141}
142
143impl PartialEq for Digest {
144 fn eq(&self, other: &Self) -> bool {
145 self.ct_eq(other)
146 }
147}
148
149impl core::hash::Hash for Digest {
150 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
151 core::hash::Hash::hash(&self.0, state);
152 }
153}
154
155impl core::fmt::Debug for Digest {
156 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
157 formatter.write_str("Digest(..)")
158 }
159}
160
161#[derive(Eq)]
167pub struct ZeroizedDigest(Digest);
168
169impl ZeroizedDigest {
170 #[must_use]
172 pub const fn new(digest: Digest) -> Self {
173 Self(digest)
174 }
175
176 #[must_use]
181 pub const fn digest(&self) -> Digest {
182 self.0
183 }
184
185 #[must_use]
187 pub const fn as_bytes(&self) -> &[u8; Digest::LEN] {
188 self.0.as_bytes()
189 }
190
191 #[must_use]
193 pub fn ct_eq(&self, other: &Self) -> bool {
194 self.0.ct_eq(&other.0)
195 }
196}
197
198impl From<Digest> for ZeroizedDigest {
199 fn from(digest: Digest) -> Self {
200 Self::new(digest)
201 }
202}
203
204impl PartialEq for ZeroizedDigest {
205 fn eq(&self, other: &Self) -> bool {
206 self.ct_eq(other)
207 }
208}
209
210impl Drop for ZeroizedDigest {
211 fn drop(&mut self) {
212 self.0.0.zeroize();
213 }
214}
215
216impl core::fmt::Debug for ZeroizedDigest {
217 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
218 formatter.write_str("ZeroizedDigest(..)")
219 }
220}
221
222define_identifier!(
223 StatementId,
225 Digest::LEN,
226 Digest::LEN
227);
228
229define_identifier!(
230 SubjectId,
232 1,
233 64
234);
235
236define_identifier!(
237 RealmId,
239 1,
240 64
241);
242
243define_identifier!(
244 ProfileId,
246 1,
247 32
248);
249
250define_identifier!(
251 ProofSuiteId,
253 1,
254 32
255);
256
257define_identifier!(
258 PolicyId,
260 1,
261 32
262);
263
264define_identifier!(
265 CheckpointId,
267 Digest::LEN,
268 Digest::LEN
269);
270
271define_identifier!(
272 NativeBindingId,
274 1,
275 64
276);
277
278#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
280pub struct EventId(Digest);
281
282impl EventId {
283 pub fn new(digest: Digest) -> Result<Self, ValidationError> {
285 if digest.is_zero() {
286 Err(ValidationError::ZeroValue)
287 } else {
288 Ok(Self(digest))
289 }
290 }
291
292 #[must_use]
294 pub const fn digest(&self) -> Digest {
295 self.0
296 }
297}
298
299#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
301pub struct CapabilityRef(Digest);
302
303impl CapabilityRef {
304 pub fn new(digest: Digest) -> Result<Self, ValidationError> {
306 if digest.is_zero() {
307 Err(ValidationError::ZeroValue)
308 } else {
309 Ok(Self(digest))
310 }
311 }
312
313 #[must_use]
315 pub const fn digest(&self) -> Digest {
316 self.0
317 }
318}
319
320#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
322pub struct PolicyEpoch(Digest);
323
324impl PolicyEpoch {
325 pub fn new(digest: Digest) -> Result<Self, ValidationError> {
327 if digest.is_zero() {
328 Err(ValidationError::ZeroValue)
329 } else {
330 Ok(Self(digest))
331 }
332 }
333
334 #[must_use]
336 pub const fn digest(&self) -> Digest {
337 self.0
338 }
339}
340
341#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
343pub struct OperationSequence(u64);
344
345impl OperationSequence {
346 pub const fn new(value: u64) -> Result<Self, ValidationError> {
348 if value == 0 {
349 Err(ValidationError::ZeroValue)
350 } else {
351 Ok(Self(value))
352 }
353 }
354
355 #[must_use]
357 pub const fn get(self) -> u64 {
358 self.0
359 }
360
361 pub const fn next(self) -> Result<Self, ValidationError> {
363 match self.0.checked_add(1) {
364 Some(value) => Ok(Self(value)),
365 None => Err(ValidationError::TooLarge),
366 }
367 }
368
369 #[must_use]
371 pub const fn immediately_follows(self, previous: Self) -> bool {
372 match previous.0.checked_add(1) {
373 Some(expected) => self.0 == expected,
374 None => false,
375 }
376 }
377}
378
379#[derive(Eq)]
387pub struct Nonce([u8; Self::LEN]);
388
389impl Nonce {
390 pub const LEN: usize = 16;
392
393 pub fn new(bytes: [u8; Self::LEN]) -> Result<Self, ValidationError> {
395 if bool::from(bytes.ct_eq(&[0u8; Self::LEN])) {
396 Err(ValidationError::ZeroValue)
397 } else {
398 Ok(Self(bytes))
399 }
400 }
401
402 #[must_use]
404 pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
405 &self.0
406 }
407
408 #[must_use]
410 pub fn ct_eq(&self, other: &Self) -> bool {
411 bool::from(self.0.ct_eq(&other.0))
412 }
413}
414
415impl PartialEq for Nonce {
416 fn eq(&self, other: &Self) -> bool {
417 self.ct_eq(other)
418 }
419}
420
421impl Drop for Nonce {
422 fn drop(&mut self) {
423 self.0.zeroize();
424 }
425}
426
427impl core::fmt::Debug for Nonce {
428 fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
429 formatter.write_str("Nonce(..)")
430 }
431}