argon2_rs/
lib.rs

1pub mod error;
2use error::*;
3
4use argon2_sys::{ARGON2_DEFAULT_FLAGS, argon2_context, argon2_ctx};
5
6#[cfg(feature = "zeroize")]
7use zeroize::Zeroize;
8
9pub const RECOMMENDED_HASH_LENGTH: u64 = 64;
10
11/// Argon2 primitive type: variants of the algorithm.
12#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
13#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Default, Ord)]
14pub enum Algorithm {
15    /// Optimizes against GPU cracking attacks but vulnerable to side-channels.
16    ///
17    /// Accesses the memory array in a password dependent order, reducing the
18    /// possibility of time–memory tradeoff (TMTO) attacks.
19    Argon2d = 0,
20
21    /// Optimized to resist side-channel attacks.
22    ///
23    /// Accesses the memory array in a password independent order, increasing the
24    /// possibility of time-memory tradeoff (TMTO) attacks.
25    Argon2i = 1,
26
27    /// Hybrid that mixes Argon2i and Argon2d passes (*default*).
28    ///
29    /// Uses the Argon2i approach for the first half pass over memory and
30    /// Argon2d approach for subsequent passes. This effectively places it in
31    /// the "middle" between the other two: it doesn't provide as good
32    /// TMTO/GPU cracking resistance as Argon2d, nor as good of side-channel
33    /// resistance as Argon2i, but overall provides the most well-rounded
34    /// approach to both classes of attacks.
35    #[default]
36    Argon2id = 2,
37}
38
39/// Version of the algorithm.
40#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
41#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd, Ord)]
42#[repr(u32)]
43pub enum Version {
44    /// Version 16 (0x10 in hex)
45    ///
46    /// Performs overwrite internally
47    V0x10 = 0x10,
48
49    /// Version 19 (0x13 in hex, default)
50    ///
51    /// Performs XOR internally
52    #[default]
53    V0x13 = 0x13,
54}
55
56/// Argon2 instance
57///
58/// # Parameters
59///
60/// - `m_cost` - The memory cost in kibibytes
61/// - `t_cost` - Iteration cost
62/// - `p_cost` - Parallelization
63/// - `hash_length` - The length of the hash in bytes
64/// - `algorithm` - The algorithm to use
65/// - `version` - The version of the algorithm to use
66///
67/// By default it will use the `Argon2id` with a `64 byte` hash length (maximum).
68///
69/// It is not recomended to change these specific values, they are fine for most use cases.
70///
71/// Generally speaking you don't want to mess with the `t_cost` and `p_cost` parameters a lot.
72///
73/// ## About the `m_cost`, `t_cost` and `p_cost` parameters
74///
75/// ### `m_cost`
76///
77/// You should mostly adjust the `m_cost` if you really want to increase the security of the hash since this is
78/// the major bottleneck for GPUs and ASICs.
79///
80/// Anything from `1024_000` and beyond is considered very secure, if you are paranoid you should increase it
81/// to the max physical RAM of the machine this hash will be computed on.
82///
83/// ### `t_cost`
84/// For most use cases a good value is between `8` and `30`.
85///
86/// Increasing the `t_cost` will increase the time it takes to compute the hash linearly.
87///
88/// For example if the hash takes 10 seconds to compute with `t_cost` set to `8` and you increase it to `16` it will take roughly twice the time.
89///
90/// ### `p_cost`
91/// 
92/// For max security the `p_cost` should be set to `1`.
93///
94/// Increasing the `p_cost` will decrease the time it takes to compute the hash linearly.
95///
96/// For example if the hash takes 10 seconds to compute with `p_cost` set to `1` and you increase it to `2` it will take roughly half the time.
97///
98/// Keep in mind increasing the `p_cost` beyond the machine's physical cores will not increase the speed of the hash computation
99/// but in case of a brute-force attack the attacker will be able to use more cores to compute the hash and thus giving him leverage.
100///
101/// ## Presets
102///
103/// There are some presets for the `Argon2` struct that you can use.
104///
105/// - `Argon2::very_fast()`
106/// - `Argon2::fast()`
107/// - `Argon2::balanced()`
108/// - `Argon2::slow()`
109/// - `Argon2::very_slow()`
110#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
111#[derive(Default, Clone, Debug)]
112pub struct Argon2 {
113    pub m_cost: u32,
114    pub t_cost: u32,
115    pub p_cost: u32,
116    pub hash_length: u64,
117    /// By default we use the Argon2id
118    pub algorithm: Algorithm,
119    /// By default we use the version 0x13
120    pub version: Version,
121}
122
123impl Argon2 {
124    /// Create a new Argon2 instance with the given parameters.
125    ///
126    /// By default it will use the `Argon2id` with a `64 byte` hash length.
127    ///
128    /// ## Arguments
129    ///
130    /// - `m_cost` - The memory cost in kibibytes
131    /// - `t_cost` - Iteration cost
132    /// - `p_cost` - Parallelization
133    pub fn new(m_cost: u32, t_cost: u32, p_cost: u32) -> Self {
134        Self {
135            m_cost,
136            t_cost,
137            p_cost,
138            hash_length: RECOMMENDED_HASH_LENGTH,
139            ..Default::default()
140        }
141    }
142
143    pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self {
144        self.algorithm = algorithm;
145        self
146    }
147
148    pub fn with_version(mut self, version: Version) -> Self {
149        self.version = version;
150        self
151    }
152
153    pub fn with_hash_length(mut self, hash_length: u64) -> Self {
154        self.hash_length = hash_length;
155        self
156    }
157
158    /// Hashes the given password
159    ///
160    /// ## Arguments
161    ///
162    /// - `password` - The password to hash
163    /// - `salt` - The salt to use for hashing
164    ///
165    ///
166    /// ## Returns
167    ///
168    /// The hash of the password in its raw byte form
169    pub fn hash_password(&self, password: &str, mut salt: Vec<u8>) -> Result<Vec<u8>, Error> {
170        let mut hash_buffer = vec![0u8; self.hash_length as usize];
171
172        let mut context = argon2_context {
173            out: hash_buffer.as_mut_ptr(),
174            outlen: self.hash_length as u32,
175            pwd: password.as_bytes().as_ptr() as *mut u8,
176            pwdlen: password.len() as u32,
177            salt: salt.as_mut_ptr(),
178            saltlen: salt.len() as u32,
179            secret: std::ptr::null_mut(),
180            secretlen: 0,
181            ad: std::ptr::null_mut(),
182            adlen: 0,
183            t_cost: self.t_cost,
184            m_cost: self.m_cost,
185            lanes: self.p_cost,
186            threads: self.p_cost,
187            version: self.version as u32,
188            allocate_cbk: None,
189            free_cbk: None,
190            flags: ARGON2_DEFAULT_FLAGS,
191        };
192
193        let code = unsafe { argon2_ctx(&mut context, self.algorithm as u32) };
194
195        #[cfg(feature = "zeroize")]
196        salt.zeroize();
197
198        if code != 0 {
199            return Err(Error::Argon2(map_argon2_error(code)));
200        }
201
202        Ok(hash_buffer)
203    }
204}
205
206// Argon2 Presets
207impl Argon2 {
208    pub fn very_fast() -> Self {
209        Self {
210            m_cost: 128_000,
211            t_cost: 8,
212            p_cost: 1,
213            hash_length: RECOMMENDED_HASH_LENGTH,
214            ..Default::default()
215        }
216    }
217
218    pub fn fast() -> Self {
219        Self {
220            m_cost: 256_000,
221            t_cost: 16,
222            p_cost: 1,
223            hash_length: RECOMMENDED_HASH_LENGTH,
224            ..Default::default()
225        }
226    }
227
228    pub fn balanced() -> Self {
229        Self {
230            m_cost: 1024_000,
231            t_cost: 8,
232            p_cost: 1,
233            hash_length: RECOMMENDED_HASH_LENGTH,
234            ..Default::default()
235        }
236    }
237
238    pub fn slow() -> Self {
239        Self {
240            m_cost: 2048_000,
241            t_cost: 8,
242            p_cost: 1,
243            hash_length: RECOMMENDED_HASH_LENGTH,
244            ..Default::default()
245        }
246    }
247
248    pub fn very_slow() -> Self {
249        Self {
250            m_cost: 3072_000,
251            t_cost: 8,
252            p_cost: 1,
253            hash_length: RECOMMENDED_HASH_LENGTH,
254            ..Default::default()
255        }
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_argon2() {
265        let argon2 = Argon2::very_fast();
266        let salt = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
267        let hash = argon2.hash_password("password", salt).unwrap();
268        assert_eq!(hash.len(), 64);
269    }
270}