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