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}