bc_components/
nonce.rs

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