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}