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}