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