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}