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}