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