1use crate::config::Config;
10use crate::context::Context;
11use crate::core;
12use crate::encoding;
13use crate::memory::Memory;
14use crate::result::Result;
15use crate::thread_mode::ThreadMode;
16use crate::variant::Variant;
17use crate::version::Version;
18
19use constant_time_eq::constant_time_eq;
20
21#[rustfmt::skip]
43pub fn encoded_len(
44 variant: Variant,
45 mem_cost: u32,
46 time_cost: u32,
47 parallelism: u32,
48 salt_len: u32,
49 hash_len: u32
50) -> u32 {
51 ("$$v=$m=,t=,p=$$".len() as u32) +
52 (variant.as_lowercase_str().len() as u32) +
53 encoding::num_len(Version::default().as_u32()) +
54 encoding::num_len(mem_cost) +
55 encoding::num_len(time_cost) +
56 encoding::num_len(parallelism) +
57 encoding::base64_len(salt_len) +
58 encoding::base64_len(hash_len)
59}
60
61#[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")]
87#[cfg_attr(
88 feature = "crossbeam-utils",
89 doc = "config.thread_mode = ThreadMode::Parallel;"
90)]
91#[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")]
92#[cfg_attr(
93 not(feature = "crossbeam-utils"),
94 doc = "config.thread_mode = ThreadMode::Sequential;"
95)]
96pub fn hash_encoded(pwd: &[u8], salt: &[u8], config: &Config) -> Result<String> {
99 let context = Context::new(config.clone(), pwd, salt)?;
100 let hash = run(&context);
101 let encoded = encoding::encode_string(&context, &hash);
102 Ok(encoded)
103}
104
105#[cfg_attr(feature = "crossbeam-utils", doc = "config.lanes = 4;")]
131#[cfg_attr(
132 feature = "crossbeam-utils",
133 doc = "config.thread_mode = ThreadMode::Parallel;"
134)]
135#[cfg_attr(not(feature = "crossbeam-utils"), doc = "config.lanes = 1;")]
136#[cfg_attr(
137 not(feature = "crossbeam-utils"),
138 doc = "config.thread_mode = ThreadMode::Sequential;"
139)]
140pub fn hash_raw(pwd: &[u8], salt: &[u8], config: &Config) -> Result<Vec<u8>> {
143 let context = Context::new(config.clone(), pwd, salt)?;
144 let hash = run(&context);
145 Ok(hash)
146}
147
148pub fn verify_encoded(encoded: &str, pwd: &[u8]) -> Result<bool> {
162 verify_encoded_ext(encoded, pwd, &[], &[])
163}
164
165pub fn verify_encoded_ext(encoded: &str, pwd: &[u8], secret: &[u8], ad: &[u8]) -> Result<bool> {
181 let decoded = encoding::decode_string(encoded)?;
182 let threads = if cfg!(feature = "crossbeam-utils") {
183 decoded.parallelism
184 } else {
185 1
186 };
187 let config = Config {
188 variant: decoded.variant,
189 version: decoded.version,
190 mem_cost: decoded.mem_cost,
191 time_cost: decoded.time_cost,
192 lanes: decoded.parallelism,
193 thread_mode: ThreadMode::from_threads(threads),
194 secret,
195 ad,
196 hash_length: decoded.hash.len() as u32,
197 };
198 verify_raw(pwd, &decoded.salt, &decoded.hash, &config)
199}
200
201pub fn verify_raw(pwd: &[u8], salt: &[u8], hash: &[u8], config: &Config) -> Result<bool> {
218 let config = Config {
219 hash_length: hash.len() as u32,
220 ..config.clone()
221 };
222 let context = Context::new(config, pwd, salt)?;
223 let calculated_hash = run(&context);
224 Ok(constant_time_eq(hash, &calculated_hash))
225}
226
227fn run(context: &Context) -> Vec<u8> {
228 let mut memory = Memory::new(context.config.lanes, context.lane_length);
229 core::initialize(context, &mut memory);
230 unsafe { core::fill_memory_blocks(context, &mut memory) };
232 core::finalize(context, &memory)
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 #[test]
240 fn single_thread_verification_multi_lane_hash() {
241 let hash = "$argon2i$v=19$m=4096,t=3,p=4$YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo$\
242 BvBk2OaSofBHfbrUW61nHrWB/43xgfs/QJJ5DkMAd8I";
243 let res = verify_encoded(hash, b"foo").unwrap();
244 assert!(res);
245 }
246
247 #[test]
248 fn test_argon2id_for_miri() {
249 let hash = "$argon2id$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
250 0gasyPnKXiBHQ5bft/bd4jrmy2DdtrLTX3JR9co7fRY";
251 let res = verify_encoded(hash, b"password").unwrap();
252 assert!(res);
253 }
254
255 #[test]
256 fn test_argon2id_for_miri_2() {
257 let hash = "$argon2id$v=19$m=512,t=2,p=8$c29tZXNhbHQ$\
258 qgW4yz2jO7oklapDpVwzUYgfDLzfwkppGTvhRDDBjkY";
259 let res = verify_encoded(hash, b"password").unwrap();
260 assert!(res);
261 }
262
263 #[test]
264 fn test_argon2d_for_miri() {
265 let hash = "$argon2d$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
266 doW5kZ/0cTwqwbYTwr9JD0wNwy3tMyJMMk9ojGsC8bk";
267 let res = verify_encoded(hash, b"password").unwrap();
268 assert!(res);
269 }
270
271 #[test]
272 fn test_argon2i_for_miri() {
273 let hash = "$argon2i$v=19$m=256,t=2,p=16$c29tZXNhbHQ$\
274 c1suSp12ZBNLSuyhD8pJriM2r5jP2kgZ5QdDAk3+HaY";
275 let res = verify_encoded(hash, b"password").unwrap();
276 assert!(res);
277 }
278}