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 them, the default values 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/// For max security the `p_cost` should be set to `1` and the `t_cost` could be anything between `8` and `30`.
71/// That also depends on the `m_cost` which is the most important parameter.
72/// The higher the `m_cost` the more secure the hash is but the time it takes to compute it increases linearly.
73///
74/// ## Presets
75///
76/// There are some presets for the `Argon2` struct that you can use.
77///
78/// - `Argon2::very_fast()`
79/// - `Argon2::fast()`
80/// - `Argon2::balanced()`
81/// - `Argon2::slow()`
82/// - `Argon2::very_slow()`
83#[derive(Default, Clone, Debug)]
84pub struct Argon2 {
85    pub m_cost: u32,
86    pub t_cost: u32,
87    pub p_cost: u32,
88    pub hash_length: u64,
89    /// By default we use the Argon2id
90    pub algorithm: Algorithm,
91    /// By default we use the version 0x13
92    pub version: Version,
93}
94
95impl Argon2 {
96    /// Create a new Argon2 instance with the given parameters.
97    ///
98    /// By default it will use the `Argon2id` with a `64 byte` hash length.
99    ///
100    /// ## Arguments
101    ///
102    /// - `m_cost` - The memory cost in kibibytes
103    /// - `t_cost` - Iteration cost
104    /// - `p_cost` - Parallelization
105    pub fn new(m_cost: u32, t_cost: u32, p_cost: u32) -> Self {
106        Self {
107            m_cost,
108            t_cost,
109            p_cost,
110            hash_length: RECOMMENDED_HASH_LENGTH,
111            ..Default::default()
112        }
113    }
114
115    pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self {
116        self.algorithm = algorithm;
117        self
118    }
119
120    pub fn with_version(mut self, version: Version) -> Self {
121        self.version = version;
122        self
123    }
124
125    pub fn with_hash_length(mut self, hash_length: u64) -> Self {
126        self.hash_length = hash_length;
127        self
128    }
129
130    /// Hashes the given password
131    ///
132    /// ## Arguments
133    ///
134    /// - `password` - The password to hash
135    /// - `salt` - The salt to use for hashing
136    ///
137    ///
138    /// ## Returns
139    ///
140    /// The hash of the password in its raw byte form
141    pub fn hash_password(&self, password: &str, mut salt: Vec<u8>) -> Result<Vec<u8>, Error> {
142        let mut hash_buffer = vec![0u8; self.hash_length as usize];
143
144        let mut context = argon2_context {
145            out: hash_buffer.as_mut_ptr(),
146            outlen: self.hash_length as u32,
147            pwd: password.as_bytes().as_ptr() as *mut u8,
148            pwdlen: password.len() as u32,
149            salt: salt.as_mut_ptr(),
150            saltlen: salt.len() as u32,
151            secret: std::ptr::null_mut(),
152            secretlen: 0,
153            ad: std::ptr::null_mut(),
154            adlen: 0,
155            t_cost: self.t_cost,
156            m_cost: self.m_cost,
157            lanes: self.p_cost,
158            threads: self.p_cost,
159            version: self.version as u32,
160            allocate_cbk: None,
161            free_cbk: None,
162            flags: ARGON2_DEFAULT_FLAGS,
163        };
164
165        let code = unsafe { argon2_ctx(&mut context, self.algorithm as u32) };
166
167        #[cfg(feature = "zeroize")]
168        salt.zeroize();
169
170        if code != 0 {
171            return Err(Error::Argon2(map_argon2_error(code)));
172        }
173
174        Ok(hash_buffer)
175    }
176}
177
178// Argon2 Presets
179impl Argon2 {
180    pub fn very_fast() -> Self {
181        Self {
182            m_cost: 128_000,
183            t_cost: 8,
184            p_cost: 1,
185            hash_length: RECOMMENDED_HASH_LENGTH,
186            ..Default::default()
187        }
188    }
189
190    pub fn fast() -> Self {
191        Self {
192            m_cost: 256_000,
193            t_cost: 16,
194            p_cost: 1,
195            hash_length: RECOMMENDED_HASH_LENGTH,
196            ..Default::default()
197        }
198    }
199
200    pub fn balanced() -> Self {
201        Self {
202            m_cost: 1024_000,
203            t_cost: 8,
204            p_cost: 1,
205            hash_length: RECOMMENDED_HASH_LENGTH,
206            ..Default::default()
207        }
208    }
209
210    pub fn slow() -> Self {
211        Self {
212            m_cost: 2048_000,
213            t_cost: 8,
214            p_cost: 1,
215            hash_length: RECOMMENDED_HASH_LENGTH,
216            ..Default::default()
217        }
218    }
219
220    pub fn very_slow() -> Self {
221        Self {
222            m_cost: 3072_000,
223            t_cost: 8,
224            p_cost: 1,
225            hash_length: RECOMMENDED_HASH_LENGTH,
226            ..Default::default()
227        }
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234
235    #[test]
236    fn test_argon2() {
237        let argon2 = Argon2::very_fast();
238        let salt = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
239        let hash = argon2.hash_password("password", salt).unwrap();
240        assert_eq!(hash.len(), 64);
241    }
242}