bc_components/salt.rs
1use std::ops::RangeInclusive;
2use bc_ur::prelude::*;
3use bc_rand::{ rng_next_in_closed_range, rng_random_data, RandomNumberGenerator, SecureRandomNumberGenerator };
4use crate::tags;
5use anyhow::{ bail, Result };
6
7/// Random salt used to decorrelate other information.
8///
9/// A `Salt` is a cryptographic primitive consisting of random data that is used to modify the
10/// output of a cryptographic function. Salts are primarily used in password hashing to defend
11/// against dictionary attacks, rainbow table attacks, and pre-computation attacks. They are also
12/// used in other cryptographic contexts to ensure uniqueness and prevent correlation between
13/// different parts of a cryptosystem.
14///
15/// Unlike a [`Nonce`](crate::Nonce) which has a fixed size, a `Salt` in this implementation can
16/// have a variable length (minimum 8 bytes). Different salt creation methods are provided to
17/// generate salts of appropriate sizes for different use cases.
18///
19/// # Minimum Size Requirement
20///
21/// For security reasons, salts must be at least 8 bytes long. Attempting to create a salt
22/// with fewer than 8 bytes will result in an error.
23///
24/// # CBOR Serialization
25///
26/// `Salt` implements the `CBORTaggedCodable` trait, which means it can be serialized to and
27/// deserialized from CBOR with a specific tag. The tag used is `TAG_SALT` defined in the `tags` module.
28///
29/// # UR Serialization
30///
31/// When serialized as a Uniform Resource (UR), a `Salt` is represented as a binary blob with the type "salt".
32///
33/// # Common Uses
34///
35/// - Password hashing and key derivation functions
36/// - Preventing correlation in cryptographic protocols
37/// - Randomizing data before encryption to prevent pattern recognition
38/// - Adding entropy to improve security in various cryptographic functions
39///
40/// # Examples
41///
42/// Creating a salt with a specific length:
43///
44/// ```
45/// use bc_components::Salt;
46///
47/// // Generate a salt with 16 bytes
48/// let salt = Salt::new_with_len(16).unwrap();
49/// assert_eq!(salt.len(), 16);
50/// ```
51///
52/// Creating a salt with a length proportional to data size:
53///
54/// ```
55/// use bc_components::Salt;
56///
57/// // Generate a salt proportional to 100 bytes of data
58/// let salt = Salt::new_for_size(100);
59///
60/// // Salts for larger data will be larger (but still efficient)
61/// let big_salt = Salt::new_for_size(1000);
62/// assert!(big_salt.len() > salt.len());
63/// ```
64///
65/// Creating a salt with a length in a specific range:
66///
67/// ```
68/// use bc_components::Salt;
69///
70/// // Generate a salt with length between 16 and 32 bytes
71/// let salt = Salt::new_in_range(16..=32).unwrap();
72/// assert!(salt.len() >= 16 && salt.len() <= 32);
73/// ```
74#[derive(Clone, Eq, PartialEq)]
75pub struct Salt(Vec<u8>);
76
77impl Salt {
78 /// Return the length of the salt.
79 pub fn len(&self) -> usize {
80 self.0.len()
81 }
82
83 /// Return true if the salt is empty (this is not recommended).
84 pub fn is_empty(&self) -> bool {
85 self.0.is_empty()
86 }
87
88 /// Create a new salt from data.
89 pub fn from_data(data: impl Into<Vec<u8>>) -> Self {
90 Self(data.into())
91 }
92
93 /// Return the data of the salt.
94 pub fn data(&self) -> &Vec<u8> {
95 &self.0
96 }
97
98 /// Create a specific number of bytes of salt.
99 ///
100 /// If the number of bytes is less than 8, this will return `None`.
101 pub fn new_with_len(count: usize) -> Result<Self> {
102 let mut rng = SecureRandomNumberGenerator;
103 Self::new_with_len_using(count, &mut rng)
104 }
105
106 /// Create a specific number of bytes of salt.
107 ///
108 /// If the number of bytes is less than 8, this will return `None`.
109 pub fn new_with_len_using(count: usize, rng: &mut impl RandomNumberGenerator) -> Result<Self> {
110 if count < 8 {
111 bail!("Salt length is too short");
112 }
113 Ok(Self::from_data(rng_random_data(rng, count)))
114 }
115
116 /// Create a number of bytes of salt chosen randomly from the given range.
117 ///
118 /// If the minimum number of bytes is less than 8, this will return `None`.
119 pub fn new_in_range(range: RangeInclusive<usize>) -> Result<Self> {
120 if range.start() < &8 {
121 bail!("Salt length is too short");
122 }
123 let mut rng = SecureRandomNumberGenerator;
124 Self::new_in_range_using(&range, &mut rng)
125 }
126
127 /// Create a number of bytes of salt chosen randomly from the given range.
128 ///
129 /// If the minimum number of bytes is less than 8, this will return `None`.
130 pub fn new_in_range_using(
131 range: &RangeInclusive<usize>,
132 rng: &mut impl RandomNumberGenerator
133 ) -> Result<Self> {
134 if range.start() < &8 {
135 bail!("Salt length is too short");
136 }
137 let count = rng_next_in_closed_range(rng, range);
138 Self::new_with_len_using(count, rng)
139 }
140
141 /// Create a number of bytes of salt generally proportionate to the size of the object being salted.
142 pub fn new_for_size(size: usize) -> Self {
143 let mut rng = SecureRandomNumberGenerator;
144 Self::new_for_size_using(size, &mut rng)
145 }
146
147 /// Create a number of bytes of salt generally proportionate to the size of the object being salted.
148 pub fn new_for_size_using(size: usize, rng: &mut impl RandomNumberGenerator) -> Self {
149 let count = size as f64;
150 let min_size = std::cmp::max(8, (count * 0.05).ceil() as usize);
151 let max_size = std::cmp::max(min_size + 8, (count * 0.25).ceil() as usize);
152 Self::new_in_range_using(&(min_size..=max_size), rng).unwrap()
153 }
154
155 /// Create a new salt from the given hexadecimal string.
156 pub fn from_hex(hex: impl AsRef<str>) -> Self {
157 Self::from_data(hex::decode(hex.as_ref()).unwrap())
158 }
159
160 /// The data as a hexadecimal string.
161 pub fn hex(&self) -> String {
162 hex::encode(self.data())
163 }
164}
165
166/// Allows accessing the underlying data as a byte slice reference.
167impl<'a> From<&'a Salt> for &'a [u8] {
168 fn from(value: &'a Salt) -> Self {
169 value.data()
170 }
171}
172
173/// Allows using a Salt as a reference to a byte slice.
174impl AsRef<[u8]> for Salt {
175 fn as_ref(&self) -> &[u8] {
176 self.data()
177 }
178}
179
180/// Provides a self-reference, enabling API consistency with other types.
181impl AsRef<Salt> for Salt {
182 fn as_ref(&self) -> &Salt {
183 self
184 }
185}
186
187/// Identifies the CBOR tags used for Salt serialization.
188impl CBORTagged for Salt {
189 fn cbor_tags() -> Vec<Tag> {
190 tags_for_values(&[tags::TAG_SALT])
191 }
192}
193
194/// Enables conversion of a Salt into a tagged CBOR value.
195impl From<Salt> for CBOR {
196 fn from(value: Salt) -> Self {
197 value.tagged_cbor()
198 }
199}
200
201/// Defines how a Salt is encoded as CBOR (as a byte string).
202impl CBORTaggedEncodable for Salt {
203 fn untagged_cbor(&self) -> CBOR {
204 CBOR::to_byte_string(self.data())
205 }
206}
207
208/// Enables conversion from CBOR to Salt, with proper error handling.
209impl TryFrom<CBOR> for Salt {
210 type Error = dcbor::Error;
211
212 fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
213 Self::from_tagged_cbor(cbor)
214 }
215}
216
217/// Defines how a Salt is decoded from CBOR.
218impl CBORTaggedDecodable for Salt {
219 fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
220 let data = CBOR::try_into_byte_string(untagged_cbor)?;
221 let instance = Self::from_data(data);
222 Ok(instance)
223 }
224}
225
226/// Provides a debug representation showing the salt's length.
227impl std::fmt::Debug for Salt {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 write!(f, "Salt({})", self.len())
230 }
231}
232
233/// Enables cloning a Salt from a reference using From trait.
234impl From<&Salt> for Salt {
235 fn from(salt: &Salt) -> Self {
236 salt.clone()
237 }
238}
239
240/// Converts a Salt into a `Vec<u8>` containing the salt bytes.
241impl From<Salt> for Vec<u8> {
242 fn from(salt: Salt) -> Self {
243 salt.0.to_vec()
244 }
245}
246
247/// Converts a Salt reference into a `Vec<u8>` containing the salt bytes.
248impl From<&Salt> for Vec<u8> {
249 fn from(salt: &Salt) -> Self {
250 salt.0.to_vec()
251 }
252}