bc_components/
nonce.rs

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