1use crate::traits::PasswordHash;
13use arcanum_core::error::{Error, Result};
14use argon2::{
15 Algorithm, Argon2 as Argon2Inner, Params, Version,
16 password_hash::{PasswordHasher, PasswordVerifier, SaltString},
17};
18use rand::rngs::OsRng;
19use serde::{Deserialize, Serialize};
20
21#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Argon2Params {
24 pub memory_cost: u32,
26 pub time_cost: u32,
28 pub parallelism: u32,
30 pub output_len: usize,
32}
33
34impl Default for Argon2Params {
35 fn default() -> Self {
36 Self::moderate()
37 }
38}
39
40impl Argon2Params {
41 pub fn new(memory_cost: u32, time_cost: u32, parallelism: u32) -> Self {
43 Self {
44 memory_cost,
45 time_cost,
46 parallelism,
47 output_len: 32,
48 }
49 }
50
51 pub fn low() -> Self {
55 Self {
56 memory_cost: 16 * 1024,
57 time_cost: 2,
58 parallelism: 1,
59 output_len: 32,
60 }
61 }
62
63 pub fn moderate() -> Self {
67 Self {
68 memory_cost: 64 * 1024,
69 time_cost: 3,
70 parallelism: 4,
71 output_len: 32,
72 }
73 }
74
75 pub fn high() -> Self {
79 Self {
80 memory_cost: 256 * 1024,
81 time_cost: 4,
82 parallelism: 4,
83 output_len: 32,
84 }
85 }
86
87 pub fn maximum() -> Self {
91 Self {
92 memory_cost: 1024 * 1024,
93 time_cost: 6,
94 parallelism: 4,
95 output_len: 32,
96 }
97 }
98
99 pub fn owasp() -> Self {
103 Self {
104 memory_cost: 19 * 1024,
105 time_cost: 2,
106 parallelism: 1,
107 output_len: 32,
108 }
109 }
110}
111
112pub struct Argon2;
114
115impl PasswordHash for Argon2 {
116 type Params = Argon2Params;
117 const ALGORITHM: &'static str = "Argon2id";
118
119 fn hash_password(password: &[u8], params: &Self::Params) -> Result<String> {
120 let salt = SaltString::generate(&mut OsRng);
121
122 let argon2_params = Params::new(
123 params.memory_cost,
124 params.time_cost,
125 params.parallelism,
126 Some(params.output_len),
127 )
128 .map_err(|e| Error::InternalError(e.to_string()))?;
129
130 let argon2 = Argon2Inner::new(Algorithm::Argon2id, Version::V0x13, argon2_params);
131
132 let hash = argon2
133 .hash_password(password, &salt)
134 .map_err(|e| Error::InternalError(e.to_string()))?;
135
136 Ok(hash.to_string())
137 }
138
139 fn verify_password(password: &[u8], hash: &str) -> Result<bool> {
140 let parsed_hash =
141 argon2::PasswordHash::new(hash).map_err(|e| Error::ParseError(e.to_string()))?;
142
143 let argon2 = Argon2Inner::default();
144
145 Ok(argon2.verify_password(password, &parsed_hash).is_ok())
146 }
147
148 fn derive_key(
149 password: &[u8],
150 salt: &[u8],
151 params: &Self::Params,
152 output_len: usize,
153 ) -> Result<Vec<u8>> {
154 let argon2_params = Params::new(
155 params.memory_cost,
156 params.time_cost,
157 params.parallelism,
158 Some(output_len),
159 )
160 .map_err(|e| Error::InternalError(e.to_string()))?;
161
162 let argon2 = Argon2Inner::new(Algorithm::Argon2id, Version::V0x13, argon2_params);
163
164 let mut output = vec![0u8; output_len];
165 argon2
166 .hash_password_into(password, salt, &mut output)
167 .map_err(|e| Error::KeyDerivationFailed)?;
168
169 Ok(output)
170 }
171}
172
173impl Argon2 {
174 pub fn hash(password: &[u8]) -> Result<String> {
176 Self::hash_password(password, &Argon2Params::default())
177 }
178
179 pub fn verify(password: &[u8], hash: &str) -> Result<bool> {
181 Self::verify_password(password, hash)
182 }
183
184 pub fn derive_key_256(password: &[u8], salt: &[u8]) -> Result<[u8; 32]> {
186 let key = Self::derive_key(password, salt, &Argon2Params::default(), 32)?;
187 let mut arr = [0u8; 32];
188 arr.copy_from_slice(&key);
189 Ok(arr)
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn test_hash_verify() {
199 let password = b"correct horse battery staple";
200 let hash = Argon2::hash_password(password, &Argon2Params::low()).unwrap();
201
202 assert!(Argon2::verify_password(password, &hash).unwrap());
204
205 assert!(!Argon2::verify_password(b"wrong password", &hash).unwrap());
207 }
208
209 #[test]
210 fn test_hash_format() {
211 let password = b"test";
212 let hash = Argon2::hash_password(password, &Argon2Params::low()).unwrap();
213
214 assert!(hash.starts_with("$argon2id$"));
216 }
217
218 #[test]
219 fn test_derive_key() {
220 let password = b"password";
221 let salt = b"somesalt12345678"; let key1 = Argon2::derive_key(password, salt, &Argon2Params::low(), 32).unwrap();
224 let key2 = Argon2::derive_key(password, salt, &Argon2Params::low(), 32).unwrap();
225
226 assert_eq!(key1, key2);
228 assert_eq!(key1.len(), 32);
229
230 let key3 = Argon2::derive_key(b"different", salt, &Argon2Params::low(), 32).unwrap();
232 assert_ne!(key1, key3);
233 }
234
235 #[test]
236 fn test_different_salts() {
237 let password = b"password";
238 let hash1 = Argon2::hash_password(password, &Argon2Params::low()).unwrap();
239 let hash2 = Argon2::hash_password(password, &Argon2Params::low()).unwrap();
240
241 assert_ne!(hash1, hash2);
243
244 assert!(Argon2::verify_password(password, &hash1).unwrap());
246 assert!(Argon2::verify_password(password, &hash2).unwrap());
247 }
248}