bc_components/nonce.rs
1use std::rc::Rc;
2use anyhow::bail;
3use bc_rand::fill_random_data;
4use dcbor::prelude::*;
5use crate::tags;
6
7/// A random nonce ("number used once").
8///
9/// A `Nonce` is a cryptographic primitive consisting of a random or pseudo-random number that
10/// is used only once in a cryptographic communication. Nonces are often used in authentication
11/// protocols, encryption algorithms, and digital signatures to prevent replay attacks and ensure
12/// the uniqueness of encrypted messages.
13///
14/// In this implementation, a `Nonce` is a 12-byte random value. The size is chosen to be sufficiently
15/// large to prevent collisions while remaining efficient for storage and transmission.
16///
17/// # CBOR Serialization
18///
19/// `Nonce` implements the `CBORTaggedCodable` trait, which means it can be serialized to and
20/// deserialized from CBOR with a specific tag. The tag used is `TAG_NONCE` defined in the `tags` module.
21///
22/// # UR Serialization
23///
24/// When serialized as a Uniform Resource (UR), a `Nonce` is represented as a binary blob with the type "nonce".
25///
26/// # Common Uses
27///
28/// - In authenticated encryption schemes like AES-GCM
29/// - For initializing counters in counter-mode block ciphers
30/// - In challenge-response authentication protocols
31/// - To prevent replay attacks in secure communications
32///
33/// # Examples
34///
35/// Creating a new random nonce:
36///
37/// ```
38/// use bc_components::Nonce;
39///
40/// // Generate a new random nonce
41/// let nonce = Nonce::new();
42/// ```
43///
44/// Creating a nonce from existing data:
45///
46/// ```
47/// use bc_components::Nonce;
48///
49/// // Create a nonce from a byte array
50/// let data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
51/// let nonce = Nonce::from_data(data);
52///
53/// // Access the nonce data
54/// let nonce_data = nonce.data();
55/// ```
56///
57/// Converting to and from hexadecimal representation:
58///
59/// ```
60/// use bc_components::Nonce;
61///
62/// // Create a nonce and convert to hex
63/// let nonce = Nonce::new();
64/// let hex_string = nonce.hex();
65///
66/// // Create a nonce from hex
67/// let nonce_from_hex = Nonce::from_hex(&hex_string);
68/// assert_eq!(nonce, nonce_from_hex);
69/// ```
70#[derive(Clone, Eq, PartialEq)]
71pub struct Nonce([u8; Self::NONCE_SIZE]);
72
73impl Nonce {
74 pub const NONCE_SIZE: usize = 12;
75
76 /// Create a new random nonce.
77 pub fn new() -> Self {
78 let mut data = [0u8; Self::NONCE_SIZE];
79 fill_random_data(&mut data);
80 Self(data)
81 }
82
83 /// Restores a nonce from data.
84 pub const fn from_data(data: [u8; Self::NONCE_SIZE]) -> Self {
85 Self(data)
86 }
87
88 /// Restores a nonce from data.
89 pub fn from_data_ref(data: impl AsRef<[u8]>) -> anyhow::Result<Self> {
90 let data = data.as_ref();
91 if data.len() != Self::NONCE_SIZE {
92 bail!("Invalid nonce size");
93 }
94 let mut arr = [0u8; Self::NONCE_SIZE];
95 arr.copy_from_slice(data);
96 Ok(Self::from_data(arr))
97 }
98
99 /// Get the data of the nonce.
100 pub fn data(&self) -> &[u8; Self::NONCE_SIZE] {
101 self.into()
102 }
103
104 /// Create a new nonce from the given hexadecimal string.
105 ///
106 /// # Panics
107 /// Panics if the string is not exactly 24 hexadecimal digits.
108 pub fn from_hex(hex: impl AsRef<str>) -> Self {
109 Self::from_data_ref(hex::decode(hex.as_ref()).unwrap()).unwrap()
110 }
111
112 /// The data as a hexadecimal string.
113 pub fn hex(&self) -> String {
114 hex::encode(self.data())
115 }
116}
117
118/// Provides a default implementation that creates a new random nonce.
119impl Default for Nonce {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125/// Converts an Rc-wrapped Nonce into a Nonce by cloning the inner value.
126impl From<Rc<Nonce>> for Nonce {
127 fn from(value: Rc<Nonce>) -> Self {
128 value.as_ref().clone()
129 }
130}
131
132/// Allows accessing the underlying data as a fixed-size byte array reference.
133impl<'a> From<&'a Nonce> for &'a [u8; Nonce::NONCE_SIZE] {
134 fn from(value: &'a Nonce) -> Self {
135 &value.0
136 }
137}
138
139/// Provides a self-reference, enabling API consistency with other types.
140impl AsRef<Nonce> for Nonce {
141 fn as_ref(&self) -> &Self {
142 self
143 }
144}
145
146/// Identifies the CBOR tags used for Nonce serialization.
147impl CBORTagged for Nonce {
148 fn cbor_tags() -> Vec<Tag> {
149 tags_for_values(&[tags::TAG_NONCE])
150 }
151}
152
153/// Enables conversion of a Nonce into a tagged CBOR value.
154impl From<Nonce> for CBOR {
155 fn from(value: Nonce) -> Self {
156 value.tagged_cbor()
157 }
158}
159
160/// Defines how a Nonce is encoded as CBOR (as a byte string).
161impl CBORTaggedEncodable for Nonce {
162 fn untagged_cbor(&self) -> CBOR {
163 CBOR::to_byte_string(self.data())
164 }
165}
166
167/// Enables conversion from CBOR to Nonce, with proper error handling.
168impl TryFrom<CBOR> for Nonce {
169 type Error = dcbor::Error;
170
171 fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
172 Self::from_tagged_cbor(cbor)
173 }
174}
175
176/// Defines how a Nonce is decoded from CBOR.
177impl CBORTaggedDecodable for Nonce {
178 fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
179 let data = CBOR::try_into_byte_string(untagged_cbor)?;
180 Ok(Self::from_data_ref(data)?)
181 }
182}
183
184/// Provides a debug representation showing the nonce's hexadecimal value.
185impl std::fmt::Debug for Nonce {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 write!(f, "Nonce({})", self.hex())
188 }
189}
190
191/// Enables cloning a Nonce from a reference using From trait.
192impl From<&Nonce> for Nonce {
193 fn from(nonce: &Nonce) -> Self {
194 nonce.clone()
195 }
196}
197
198/// Converts a Nonce into a `Vec<u8>` containing the nonce bytes.
199impl From<Nonce> for Vec<u8> {
200 fn from(nonce: Nonce) -> Self {
201 nonce.0.to_vec()
202 }
203}
204
205/// Converts a Nonce reference into a `Vec<u8>` containing the nonce bytes.
206impl From<&Nonce> for Vec<u8> {
207 fn from(nonce: &Nonce) -> Self {
208 nonce.0.to_vec()
209 }
210}
211
212#[cfg(test)]
213mod test {
214 use super::Nonce;
215 use dcbor::prelude::*;
216
217 #[test]
218 fn test_nonce_raw() {
219 let nonce_raw = [0u8; Nonce::NONCE_SIZE];
220 let nonce = Nonce::from_data(nonce_raw);
221 assert_eq!(nonce.data(), &nonce_raw);
222 }
223
224 #[test]
225 fn test_nonce_from_raw_data() {
226 let raw_data = vec![0u8; Nonce::NONCE_SIZE];
227 let nonce = Nonce::from_data_ref(&raw_data).unwrap();
228 assert_eq!(nonce.data(), &raw_data[..]);
229 }
230
231 #[test]
232 fn test_nonce_size() {
233 let raw_data = vec![0u8; Nonce::NONCE_SIZE + 1];
234 let nonce = Nonce::from_data_ref(raw_data);
235 assert!(nonce.is_err());
236 }
237
238 #[test]
239 fn test_nonce_new() {
240 let nonce1 = Nonce::new();
241 let nonce2 = Nonce::new();
242 assert_ne!(nonce1.data(), nonce2.data());
243 }
244
245 #[test]
246 fn test_nonce_hex_roundtrip() {
247 let nonce = Nonce::new();
248 let hex_string = nonce.hex();
249 let nonce_from_hex = Nonce::from_hex(hex_string);
250 assert_eq!(nonce, nonce_from_hex);
251 }
252
253 #[test]
254 fn test_nonce_cbor_roundtrip() {
255 let nonce = Nonce::new();
256 let cbor: CBOR = nonce.clone().into();
257 let decoded_nonce = Nonce::try_from(cbor).unwrap();
258 assert_eq!(nonce, decoded_nonce);
259 }
260}