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}