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