Skip to main content

bcx_core/
ids.rs

1use crate::ValidationError;
2
3/// Fixed-width digest used for protocol commitments.
4#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
5pub struct Digest([u8; Self::LEN]);
6
7impl Digest {
8    /// Digest byte length for the first BCX profile.
9    pub const LEN: usize = 32;
10
11    /// Creates a digest from raw bytes.
12    #[must_use]
13    pub const fn new(bytes: [u8; Self::LEN]) -> Self {
14        Self(bytes)
15    }
16
17    /// Returns the digest as bytes.
18    #[must_use]
19    pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
20        &self.0
21    }
22
23    /// Returns true when every byte is zero.
24    #[must_use]
25    pub const fn is_zero(&self) -> bool {
26        let mut accumulated = 0;
27        let mut index = 0;
28        while index < Self::LEN {
29            accumulated |= self.0[index];
30            index += 1;
31        }
32        accumulated == 0
33    }
34
35    /// Compares two digests without data-dependent early exit.
36    ///
37    /// Use this method in security-sensitive paths. The derived `PartialEq`
38    /// implementation remains available for ordinary structural comparisons.
39    #[must_use]
40    pub const fn ct_eq(&self, other: &Self) -> bool {
41        let mut accumulated = 0;
42        let mut index = 0;
43        while index < Self::LEN {
44            accumulated |= self.0[index] ^ other.0[index];
45            index += 1;
46        }
47        accumulated == 0
48    }
49}
50
51impl core::fmt::Debug for Digest {
52    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        formatter.write_str("Digest(..)")
54    }
55}
56
57/// Globally unique event identifier within a trust domain.
58#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
59pub struct EventId(Digest);
60
61impl EventId {
62    /// Creates an event identifier when the digest is non-zero.
63    pub const fn new(digest: Digest) -> Result<Self, ValidationError> {
64        if digest.is_zero() {
65            Err(ValidationError::ZeroValue)
66        } else {
67            Ok(Self(digest))
68        }
69    }
70
71    /// Returns the underlying digest commitment.
72    #[must_use]
73    pub const fn digest(&self) -> Digest {
74        self.0
75    }
76}
77
78/// Reference to a capability object or capability commitment.
79#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
80pub struct CapabilityRef(Digest);
81
82impl CapabilityRef {
83    /// Creates a capability reference when the digest is non-zero.
84    pub const fn new(digest: Digest) -> Result<Self, ValidationError> {
85        if digest.is_zero() {
86            Err(ValidationError::ZeroValue)
87        } else {
88            Ok(Self(digest))
89        }
90    }
91
92    /// Returns the underlying digest commitment.
93    #[must_use]
94    pub const fn digest(&self) -> Digest {
95        self.0
96    }
97}
98
99/// Reference to a policy epoch.
100#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
101pub struct PolicyEpoch(Digest);
102
103impl PolicyEpoch {
104    /// Creates a policy epoch when the digest is non-zero.
105    pub const fn new(digest: Digest) -> Result<Self, ValidationError> {
106        if digest.is_zero() {
107            Err(ValidationError::ZeroValue)
108        } else {
109            Ok(Self(digest))
110        }
111    }
112
113    /// Returns the underlying digest commitment.
114    #[must_use]
115    pub const fn digest(&self) -> Digest {
116        self.0
117    }
118}
119
120/// Per-issuer operation sequence used for replay detection.
121#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
122pub struct OperationSequence(u64);
123
124impl OperationSequence {
125    /// Creates a non-zero operation sequence.
126    pub const fn new(value: u64) -> Result<Self, ValidationError> {
127        if value == 0 {
128            Err(ValidationError::ZeroValue)
129        } else {
130            Ok(Self(value))
131        }
132    }
133
134    /// Returns the raw sequence number.
135    #[must_use]
136    pub const fn get(self) -> u64 {
137        self.0
138    }
139}
140
141/// Nonce bytes carried by signed invocations and WHY queries.
142#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
143pub struct Nonce([u8; Self::LEN]);
144
145impl Nonce {
146    /// Nonce byte length for the first BCX profile.
147    pub const LEN: usize = 16;
148
149    /// Creates a nonce from raw bytes.
150    #[must_use]
151    pub const fn new(bytes: [u8; Self::LEN]) -> Self {
152        Self(bytes)
153    }
154
155    /// Returns the nonce as bytes.
156    #[must_use]
157    pub const fn as_bytes(&self) -> &[u8; Self::LEN] {
158        &self.0
159    }
160}
161
162impl core::fmt::Debug for Nonce {
163    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164        formatter.write_str("Nonce(..)")
165    }
166}