argon2/
lib.rs

1//! Thin wrapper for the Argon2 C library.
2//! All public argon2 functions are mapped to functions with the `argon2` prefix
3//! and any leftover underscores after the prefix removed.
4//! e.g. `argon2_ctx` -> `ctx` and `argon2i_ctx` -> `i_ctx`
5
6#[allow(bad_style, dead_code)]
7mod sys;
8mod types;
9
10use std::convert::TryInto;
11use std::ffi::CStr;
12use types::{opt_slice_ptr_mut, opt_slice_len, opt_slice_ptr};
13
14pub use self::types::*;
15
16/// Function that gives the string representation of an argon2 Variant.
17/// If the `uppercase` parameter is true, the name of the variant is returned with the first letter
18/// uppercased.
19pub fn type2string(variant: Variant, uppercase: bool) -> &'static str {
20    unsafe {
21        let uppercase_i = if uppercase { 1 } else { 0 };
22        let str_ptr = sys::argon2_type2string(variant.to_c(), uppercase_i);
23        assert!(!str_ptr.is_null(), "null variant name.");
24        let str_cstr = CStr::from_ptr(str_ptr);
25        str_cstr.to_str().expect("Variant name is not valid UTF-8")
26    }
27}
28
29/// Function that performs memory-hard hashing with certain degree of parallelism.
30pub fn ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C, variant: Variant) -> Result<(), Error> {
31    unsafe {
32        Error::check_code(sys::argon2_ctx(&mut context.try_into()?, variant.to_c()) as _)
33    }
34}
35
36/// Argon2d: Version of Argon2 that picks memory blocks depending on the password and salt. Only
37/// for side-channel-free environment!!
38pub fn d_ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C) -> Result<(), Error> {
39    unsafe {
40        Error::check_code(sys::argon2d_ctx(&mut context.try_into()?))
41    }
42}
43
44/// Argon2i: Version of Argon2 that picks memory blocks
45/// independent on the password and salt. Good for side-channels,
46/// but worse with respect to tradeoff attacks if only one pass is used.
47pub fn i_ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C) -> Result<(), Error> {
48    unsafe {
49        Error::check_code(sys::argon2i_ctx(&mut context.try_into()?))
50    }
51}
52
53/// Argon2id: Version of Argon2 where the first half-pass over memory is
54/// password-independent, the rest are password-dependent (on the password and
55/// salt). OK against side channels (they reduce to 1/2-pass Argon2i), and
56/// better with respect to tradeoff attacks (similar to Argon2d).
57pub fn id_ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C) -> Result<(), Error> {
58    unsafe {
59        Error::check_code(sys::argon2id_ctx(&mut context.try_into()?))
60    }
61}
62
63/// Hashes a password with Argon2i, producing an encoded (string) hash.
64///
65/// # Parameters
66/// - `t_cost`: Number of iterations
67/// - `m_cost`: Sets memory usage to m_cost kibibytes
68/// - `parallelism`: Number of threads and compute lanes
69/// - `pwd`: Slice containing the password.
70/// - `salt`: Slice containing the salt.
71/// - `hashlen`: Desired length of the hash in bytes.
72/// - `encoded`: Buffer where to write the encoded hash.
73///
74/// # Notes
75///
76/// - The different parallelism levels will give different results.
77pub fn i_hash_encoded(
78    t_cost: u32,
79    m_cost: u32,
80    parallelism: u32,
81    pwd: Option<&[u8]>,
82    salt: Option<&[u8]>,
83    hashlen: usize,
84    encoded: &mut [u8]) -> Result<(), Error> {
85    unsafe {
86        Error::check_code(
87            sys::argon2i_hash_encoded(
88                t_cost, m_cost, parallelism,
89                opt_slice_ptr(&pwd) as _,
90                opt_slice_len(&pwd),
91                opt_slice_ptr(&salt) as _,
92                opt_slice_len(&salt),
93                hashlen,
94                encoded.as_mut_ptr() as _,
95                encoded.len(),
96            )
97        )
98    }
99}
100
101/// Hashes a password with Argon2i, producing a raw hash.
102///
103/// # Parameters
104/// - `t_cost`: Number of iterations
105/// - `m_cost`: Sets memory usage to m_cost kibibytes
106/// - `parallelism`: Number of threads and compute lanes
107/// - `pwd`: Slice containing the password.
108/// - `salt`: Slice containing the salt.
109/// - `hash`: Buffer where to write the raw hash.
110///
111/// # Notes
112///
113/// - The different parallelism levels will give different results.
114pub fn i_hash_raw(
115    t_cost: u32,
116    m_cost: u32,
117    parallelism: u32,
118    pwd: Option<&[u8]>,
119    salt: Option<&[u8]>,
120    hash: &mut [u8]) -> Result<(), Error> {
121    unsafe {
122        Error::check_code(
123            sys::argon2i_hash_raw(
124                t_cost, m_cost, parallelism,
125                opt_slice_ptr(&pwd) as _,
126                opt_slice_len(&pwd),
127                opt_slice_ptr(&salt) as _,
128                opt_slice_len(&salt),
129                hash.as_mut_ptr() as _,
130                hash.len(),
131            )
132        )
133    }
134}
135
136/// Hashes a password with Argon2d, producing an encoded (string) hash.
137///
138/// # Parameters
139/// - `t_cost`: Number of iterations
140/// - `m_cost`: Sets memory usage to m_cost kibibytes
141/// - `parallelism`: Number of threads and compute lanes
142/// - `pwd`: Slice containing the password.
143/// - `salt`: Slice containing the salt.
144/// - `hashlen`: Desired length of the hash in bytes.
145/// - `encoded`: Buffer where to write the encoded hash.
146///
147/// # Notes
148///
149/// - The different parallelism levels will give different results.
150pub fn d_hash_encoded(
151    t_cost: u32,
152    m_cost: u32,
153    parallelism: u32,
154    pwd: Option<&[u8]>,
155    salt: Option<&[u8]>,
156    hashlen: usize,
157    encoded: &mut [u8]) -> Result<(), Error> {
158    unsafe {
159        Error::check_code(
160            sys::argon2d_hash_encoded(
161                t_cost, m_cost, parallelism,
162                opt_slice_ptr(&pwd) as _,
163                opt_slice_len(&pwd),
164                opt_slice_ptr(&salt) as _,
165                opt_slice_len(&salt),
166                hashlen,
167                encoded.as_mut_ptr() as _,
168                encoded.len(),
169            )
170        )
171    }
172}
173
174/// Hashes a password with Argon2d, producing a raw hash.
175///
176/// # Parameters
177/// - `t_cost`: Number of iterations
178/// - `m_cost`: Sets memory usage to m_cost kibibytes
179/// - `parallelism`: Number of threads and compute lanes
180/// - `pwd`: Slice containing the password.
181/// - `salt`: Slice containing the salt.
182/// - `hash`: Buffer where to write the raw hash.
183///
184/// # Notes
185///
186/// - The different parallelism levels will give different results.
187pub fn d_hash_raw(
188    t_cost: u32,
189    m_cost: u32,
190    parallelism: u32,
191    pwd: Option<&[u8]>,
192    salt: Option<&[u8]>,
193    hash: &mut [u8]) -> Result<(), Error> {
194    unsafe {
195        Error::check_code(
196            sys::argon2d_hash_raw(
197                t_cost, m_cost, parallelism,
198                opt_slice_ptr(&pwd) as _,
199                opt_slice_len(&pwd),
200                opt_slice_ptr(&salt) as _,
201                opt_slice_len(&salt),
202                hash.as_mut_ptr() as _,
203                hash.len(),
204            )
205        )
206    }
207}
208
209/// Hashes a password with Argon2id, producing an encoded (string) hash.
210///
211/// # Parameters
212/// - `t_cost`: Number of iterations
213/// - `m_cost`: Sets memory usage to m_cost kibibytes
214/// - `parallelism`: Number of threads and compute lanes
215/// - `pwd`: Slice containing the password.
216/// - `salt`: Slice containing the salt.
217/// - `hashlen`: Desired length of the hash in bytes.
218/// - `encoded`: Buffer where to write the encoded hash.
219///
220/// # Notes
221///
222/// - The different parallelism levels will give different results.
223pub fn id_hash_encoded(
224    t_cost: u32,
225    m_cost: u32,
226    parallelism: u32,
227    pwd: Option<&[u8]>,
228    salt: Option<&[u8]>,
229    hashlen: usize,
230    encoded: &mut [u8]) -> Result<(), Error> {
231    unsafe {
232        Error::check_code(
233            sys::argon2id_hash_encoded(
234                t_cost, m_cost, parallelism,
235                opt_slice_ptr(&pwd) as _,
236                opt_slice_len(&pwd),
237                opt_slice_ptr(&salt) as _,
238                opt_slice_len(&salt),
239                hashlen,
240                encoded.as_mut_ptr() as _,
241                encoded.len(),
242            )
243        )
244    }
245}
246
247/// Hashes a password with Argon2id, producing a raw hash.
248///
249/// # Parameters
250/// - `t_cost`: Number of iterations
251/// - `m_cost`: Sets memory usage to m_cost kibibytes
252/// - `parallelism`: Number of threads and compute lanes
253/// - `pwd`: Slice containing the password.
254/// - `salt`: Slice containing the salt.
255/// - `hash`: Buffer where to write the raw hash.
256///
257/// # Notes
258///
259/// - The different parallelism levels will give different results.
260pub fn id_hash_raw(
261    t_cost: u32,
262    m_cost: u32,
263    parallelism: u32,
264    pwd: Option<&[u8]>,
265    salt: Option<&[u8]>,
266    hash: &mut [u8]) -> Result<(), Error> {
267    unsafe {
268        Error::check_code(
269            sys::argon2id_hash_raw(
270                t_cost, m_cost, parallelism,
271                opt_slice_ptr(&pwd) as _,
272                opt_slice_len(&pwd),
273                opt_slice_ptr(&salt) as _,
274                opt_slice_len(&salt),
275                hash.as_mut_ptr() as _,
276                hash.len(),
277            )
278        )
279    }
280}
281
282/// Generic Argon2 hash function.
283///
284/// # Parameters
285/// - `t_cost`: Number of iterations
286/// - `m_cost`: Sets memory usage to m_cost kibibytes
287/// - `parallelism`: Number of threads and compute lanes
288/// - `pwd`: Slice containing the password.
289/// - `salt`: Slice containing the salt.
290/// - `hash`: Buffer where to write the raw hash.
291/// - `encoded`: Buffer where to write the encoded hash (as a string).
292/// - `variant`: The variant (type) of Argon2 to use.
293/// - `version`: The version of the Argon2 algorithm to use.
294///
295/// # Notes
296///
297/// - The different parallelism levels will give different results.
298pub fn hash(
299    t_cost: u32,
300    m_cost: u32,
301    parallelism: u32,
302    pwd: Option<&[u8]>,
303    salt: Option<&[u8]>,
304    mut hash: Option<&mut [u8]>,
305    mut encoded: Option<&mut [u8]>,
306    variant: Variant,
307    version: Version) -> Result<(), Error> {
308    unsafe {
309        Error::check_code(
310            sys::argon2_hash(
311                t_cost, m_cost, parallelism,
312                opt_slice_ptr(&pwd) as _,
313                opt_slice_len(&pwd),
314                opt_slice_ptr(&salt) as _,
315                opt_slice_len(&salt),
316                opt_slice_ptr_mut(&mut hash) as _,
317                opt_slice_len(&hash),
318                opt_slice_ptr_mut(&mut encoded) as _,
319                opt_slice_len(&encoded),
320                variant.to_c() as _,
321                version.to_c() as _,
322            )
323        )
324    }
325}
326
327/// Verifies a password against an encoded string using Argon2i.
328///
329/// # Parameters
330/// - `encoded`: String encoding parameters, salt, hash.
331/// - `pwd`: Slice containing password.
332pub fn i_verify(encoded: &CStr, pwd: Option<&[u8]>) -> Result<(), Error> {
333    unsafe {
334        Error::check_code(
335            sys::argon2i_verify(
336                encoded.as_ptr() as _,
337                opt_slice_ptr(&pwd) as _,
338                opt_slice_len(&pwd),
339            )
340        )
341    }
342}
343
344/// Verifies a password against an encoded string using Argon2d.
345///
346/// # Parameters
347/// - `encoded`: String encoding parameters, salt, hash.
348/// - `pwd`: Slice containing password.
349pub fn d_verify(encoded: &CStr, pwd: Option<&[u8]>) -> Result<(), Error> {
350    unsafe {
351        Error::check_code(
352            sys::argon2d_verify(
353                encoded.as_ptr() as _,
354                opt_slice_ptr(&pwd) as _,
355                opt_slice_len(&pwd),
356            )
357        )
358    }
359}
360
361/// Verifies a password against an encoded string using Argon2id.
362///
363/// # Parameters
364/// - `encoded`: String encoding parameters, salt, hash.
365/// - `pwd`: Slice containing password.
366pub fn id_verify(encoded: &CStr, pwd: Option<&[u8]>) -> Result<(), Error> {
367    unsafe {
368        Error::check_code(
369            sys::argon2id_verify(
370                encoded.as_ptr() as _,
371                opt_slice_ptr(&pwd) as _,
372                opt_slice_len(&pwd),
373            )
374        )
375    }
376}
377
378/// Verifies a password against an encoded string.
379///
380/// # Parameters
381/// - `encoded`: String encoding parameters, salt, hash.
382/// - `pwd`: Slice containing password.
383pub fn verify(encoded: &CStr, pwd: Option<&[u8]>, variant: Variant) -> Result<(), Error> {
384    unsafe {
385        Error::check_code(
386            sys::argon2_verify(
387                encoded.as_ptr() as _,
388                opt_slice_ptr(&pwd) as _,
389                opt_slice_len(&pwd),
390                variant.to_c() as _,
391            )
392        )
393    }
394}
395
396/// Verify if a given password is correct for Argon2d hashing.
397///
398/// # Parameters
399///
400/// - `context`: The current Argon2 context.
401/// - `hash`: The password hash to verify. The length of the hash must match the length of the out
402/// parameter in context.
403pub fn d_verify_ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C, hash: &[u8]) -> Result<(), Error> {
404
405    let mut argon_context = context.try_into()?;
406    if hash.len() as u32 != argon_context.outlen {
407        return Err(Error::BadParam("hash.len"))
408    }
409
410    unsafe {
411        Error::check_code(
412            sys::argon2d_verify_ctx(
413                &mut argon_context,
414                hash.as_ptr() as _,
415            )
416        )
417    }
418}
419
420/// Verify if a given password is correct for Argon2i hashing.
421///
422/// # Parameters
423///
424/// - `context`: The current Argon2 context.
425/// - `hash`: The password hash to verify. The length of the hash must match the length of the out
426/// parameter in context.
427pub fn i_verify_ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C, hash: &[u8]) -> Result<(), Error> {
428
429    let mut argon_context = context.try_into()?;
430    if hash.len() as u32 != argon_context.outlen {
431        return Err(Error::BadParam("hash.len"))
432    }
433
434    unsafe {
435        Error::check_code(
436            sys::argon2i_verify_ctx(
437                &mut argon_context,
438                hash.as_ptr() as _,
439            )
440        )
441    }
442}
443
444/// Verify if a given password is correct for Argon2id hashing.
445///
446/// # Parameters
447///
448/// - `context`: The current Argon2 context.
449/// - `hash`: The password hash to verify. The length of the hash must match the length of the out
450/// parameter in context.
451pub fn id_verify_ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C, hash: &[u8]) -> Result<(), Error> {
452
453    let mut argon_context = context.try_into()?;
454    if hash.len() as u32 != argon_context.outlen {
455        return Err(Error::BadParam("hash.len"))
456    }
457
458    unsafe {
459        Error::check_code(
460            sys::argon2id_verify_ctx(
461                &mut argon_context,
462                hash.as_ptr() as _,
463            )
464        )
465    }
466}
467
468/// Verify if a given password is correct for a given variant of Argon2 hashing.
469///
470/// # Parameters
471///
472/// - `context`: The current Argon2 context.
473/// - `hash`: The password hash to verify. The length of the hash must match the length of the out
474/// parameter in context.
475pub fn verify_ctx<C: TryInto<sys::Argon2_Context, Error = self::Error>>(context: C, hash: &[u8], variant: Variant) -> Result<(), Error> {
476
477    let mut argon_context = context.try_into()?;
478    if hash.len() as u32 != argon_context.outlen {
479        return Err(Error::BadParam("hash.len"))
480    }
481
482    unsafe {
483        Error::check_code(
484            sys::argon2_verify_ctx(
485                &mut argon_context,
486                hash.as_ptr() as _,
487                variant.to_c() as _,
488            )
489        )
490    }
491}
492
493/// Get the associated error message for a given error code.
494pub fn error_message(code: ErrorCode) -> &'static str {
495    unsafe {
496        let str_ptr = sys::argon2_error_message(code.to_c());
497        if str_ptr.is_null() {
498            "UNKNOWN_ERROR_CODE"
499        } else {
500            let str_cstr = CStr::from_ptr(str_ptr);
501            str_cstr.to_str().expect("Variant name is not valid UTF-8")
502        }
503    }
504}
505
506/// Returns the encoded hash length for the given input parameters.
507///
508/// # Parameters
509/// `t_cost`: Number of iterations.
510/// `m_cost`: Memory usage in kibibytes.
511/// `parallelism`: Number of threads; used to compute lanes.
512/// `saltlen`: Salt size in bytes.
513/// `hashlen`: Hash size in bytes.
514/// `variant`: The Argon2 Variant that we want the encoded length for.
515///
516/// # Returns
517///
518/// The encoded hash length in bytes.
519pub fn encodedlen(
520    t_cost: u32,
521    m_cost: u32,
522    parallelism: u32,
523    saltlen: u32,
524    hashlen: u32,
525    variant: Variant) -> usize {
526    unsafe {
527        sys::argon2_encodedlen(t_cost, m_cost, parallelism, saltlen, hashlen, variant.to_c() as _)
528    }
529}
530
531/// Converts a slice of bytes to a CStr.
532/// Unlike CStr::from_bytes_with_nul this will stop at the first
533/// null byte instead of returning an error for interior null bytes.
534/// This will return an error if there are no null bytes at all.
535pub fn c_str(bytes: &[u8]) -> Result<&CStr, Error> {
536    for (idx, b) in bytes.iter().enumerate() {
537        if *b == 0 {
538            return Ok(CStr::from_bytes_with_nul(&bytes[0..(idx + 1)]).expect("Failed CStr conversion."));
539        }
540    }
541    Err(Error::BadParam("bytes"))
542}
543
544/// Converts a slice of bytes to a CStr much like `c_str` except this will allocate a C string for
545/// you instead with a terminating null byte if one cannot be found inside of the given byte
546/// string.
547pub fn c_str_cow<'a>(bytes: &'a [u8]) -> std::borrow::Cow<'a, CStr> {
548    for (idx, b) in bytes.iter().enumerate() {
549        if *b == 0 {
550            return std::borrow::Cow::Borrowed(
551                CStr::from_bytes_with_nul(&bytes[0..(idx + 1)])
552                .expect("Failed CStr conversion.")
553            );
554        }
555    }
556
557    std::borrow::Cow::Owned(
558        std::ffi::CString::new(bytes).expect("Failed to create CString.")
559    )
560}
561
562#[cfg(test)]
563mod test {
564    use super::*;
565
566    /// Make sure that all variants have names.
567    #[test]
568    fn test_variant_names() {
569        assert_eq!("argon2i", type2string(Variant::I, false));
570        assert_eq!("Argon2i", type2string(Variant::I, true));
571        assert_eq!("argon2d", type2string(Variant::D, false));
572        assert_eq!("Argon2d", type2string(Variant::D, true));
573        assert_eq!("argon2id", type2string(Variant::ID, false));
574        assert_eq!("Argon2id", type2string(Variant::ID, true));
575    }
576
577    fn hex_conv(bytes: &[u8], hex_dest: &mut [u8]) {
578        const DIGITS: &[u8] = b"0123456789abcdef";
579        for (idx, byte) in bytes.iter().enumerate() {
580            hex_dest[(idx * 2)] = DIGITS[((*byte >> 4) as usize) & 0xF];
581            hex_dest[(idx * 2) + 1] = DIGITS[(*byte as usize) & 0xF];
582        }
583    }
584
585    fn str_conv(bytes: &[u8]) -> &str {
586        std::str::from_utf8(bytes).expect("Bad UTF-8 conversion.")
587    }
588
589    fn tovec(a: &[u8]) -> Vec<u8> {
590        let mut v = Vec::with_capacity(a.len());
591        v.extend_from_slice(a);
592        return v
593    }
594
595    fn hashtest_bytes(version: Version, t: u32, m: u32, p: u32, pwd: &mut [u8], salt: &mut [u8], hexref: &mut [u8], mcfref: &mut [u8], variant: Variant) {
596        const OUTLEN: usize = 32;
597        const ENCODED_LEN: usize = 108;
598
599        let mut out = [0u8; OUTLEN];
600        let mut hex_out = [0u8; OUTLEN * 2 + 4];
601        let mut encoded = [0u8; ENCODED_LEN];
602
603        println!("HASH TEST: $v={:?} t={}, m={}, p = {}, pass={}, salt={}",
604                 version, t, m, p,
605                 unsafe { std::str::from_utf8_unchecked(pwd) },
606                 unsafe { std::str::from_utf8_unchecked(salt) },);
607
608        hash(t, 1<<m, p, Some(pwd), Some(salt), Some(&mut out), Some(&mut encoded), variant, version).expect("Test hash failed.");
609        hex_conv(&out, &mut hex_out);
610
611        assert_eq!(str_conv(hexref), str_conv(&hex_out[0..(OUTLEN * 2)]));
612
613        verify(
614            c_str(&encoded).expect("bad C string."), Some(pwd), variant
615        ).expect("Failed verify-1");
616
617        verify(
618            &c_str_cow(&mcfref), Some(pwd), variant
619        ).expect("Failed verify-1");
620    }
621
622    fn hashtest(version: Version, t: u32, m: u32, p: u32, pwd: &str, salt: &str, hexref: &str, mcfref: &str, variant: Variant) {
623        hashtest_bytes(
624            version, t, m, p,
625            &mut tovec(pwd.as_bytes()),
626            &mut tovec(salt.as_bytes()),
627            &mut tovec(hexref.as_bytes()),
628            &mut tovec(mcfref.as_bytes()),
629            variant);
630    }
631
632    macro_rules! check_error_code {
633        ($Code:ident, $Value:expr) => {
634            assert_eq!(Err(Error::Code(ErrorCode::$Code)), $Value)
635        }
636    }
637
638    #[test]
639    fn test_argon2i_0x10() {
640        println!("Test Argon2i version number: 0x{:02X}", (Version::Version10).to_int());
641        hashtest(Version::Version10, 2, 16, 1, "password", "somesalt",
642             "f6c4db4a54e2a370627aff3db6176b94a2a209a62c8e36152711802f7b30c694",
643             "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ",
644             Variant::I);
645        hashtest(Version::Version10, 2, 18, 1, "password", "somesalt",
646                 "3e689aaa3d28a77cf2bc72a51ac53166761751182f1ee292e3f677a7da4c2467",
647                 "$argon2i$m=262144,t=2,p=1$c29tZXNhbHQ$Pmiaqj0op3zyvHKlGsUxZnYXURgvHuKS4/Z3p9pMJGc",
648                 Variant::I);
649        hashtest(Version::Version10, 2, 8, 1, "password", "somesalt",
650                 "fd4dd83d762c49bdeaf57c47bdcd0c2f1babf863fdeb490df63ede9975fccf06",
651                 "$argon2i$m=256,t=2,p=1$c29tZXNhbHQ$/U3YPXYsSb3q9XxHvc0MLxur+GP960kN9j7emXX8zwY",
652                 Variant::I);
653        hashtest(Version::Version10, 2, 8, 2, "password", "somesalt",
654                 "b6c11560a6a9d61eac706b79a2f97d68b4463aa3ad87e00c07e2b01e90c564fb",
655                 "$argon2i$m=256,t=2,p=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs",
656                 Variant::I);
657        hashtest(Version::Version10, 1, 16, 1, "password", "somesalt",
658                 "81630552b8f3b1f48cdb1992c4c678643d490b2b5eb4ff6c4b3438b5621724b2",
659                 "$argon2i$m=65536,t=1,p=1$c29tZXNhbHQ$gWMFUrjzsfSM2xmSxMZ4ZD1JCytetP9sSzQ4tWIXJLI",
660                 Variant::I);
661        hashtest(Version::Version10, 4, 16, 1, "password", "somesalt",
662                 "f212f01615e6eb5d74734dc3ef40ade2d51d052468d8c69440a3a1f2c1c2847b",
663                 "$argon2i$m=65536,t=4,p=1$c29tZXNhbHQ$8hLwFhXm6110c03D70Ct4tUdBSRo2MaUQKOh8sHChHs",
664                 Variant::I);
665        hashtest(Version::Version10, 2, 16, 1, "differentpassword", "somesalt",
666                 "e9c902074b6754531a3a0be519e5baf404b30ce69b3f01ac3bf21229960109a3",
667                 "$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$6ckCB0tnVFMaOgvlGeW69ASzDOabPwGsO/ISKZYBCaM",
668                 Variant::I);
669        hashtest(Version::Version10, 2, 16, 1, "password", "diffsalt",
670                 "79a103b90fe8aef8570cb31fc8b22259778916f8336b7bdac3892569d4f1c497",
671                 "$argon2i$m=65536,t=2,p=1$ZGlmZnNhbHQ$eaEDuQ/orvhXDLMfyLIiWXeJFvgza3vaw4kladTxxJc",
672                 Variant::I);
673    }
674
675    #[test]
676    #[ignore]
677    fn test_argon2i_0x10_large_ram() {
678        hashtest(Version::Version10, 2, 20, 1, "password", "somesalt",
679                "9690ec55d28d3ed32562f2e73ea62b02b018757643a2ae6e79528459de8106e9",
680                "$argon2i$m=1048576,t=2,p=1$c29tZXNhbHQ$lpDsVdKNPtMlYvLnPqYrArAYdXZDoq5ueVKEWd6BBuk",
681                Variant::I);
682    }
683
684    #[test]
685    fn test_argon2i_0x10_errors() {
686        // Handle an invalid encoding correctly (it is missing a $)
687        check_error_code!(DecodingFail, verify(&c_str_cow(b"$argon2i$m=65536,t=2,p=1c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"),
688               Some(b"password"), Variant::I));
689
690        // Handle an invalid encoding correctly (it is missing a $)
691        check_error_code!(DecodingFail, verify(&c_str_cow(b"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"),
692               Some(b"password"), Variant::I));
693
694        // Handle an invalid encoding correctly (salt is too short)
695        check_error_code!(SaltTooShort, verify(&c_str_cow(b"$argon2i$m=65536,t=2,p=1$$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"),
696               Some(b"password"), Variant::I));
697
698        // Handle an invalid encoding correctly (the encoded password is "passwore")
699        check_error_code!(VerifyMismatch, verify(&c_str_cow(b"$argon2i$m=65536,t=2,p=1$c29tZXNhbHQ$b2G3seW+uPzerwQQC+/E1K50CLLO7YXy0JRcaTuswRo"),
700               Some(b"password"), Variant::I));
701    }
702
703    #[test]
704    fn test_argon2i_0x13() {
705        println!("Test Argon2i version number: 0x{:02X}", (Version::Version13).to_int());
706
707        hashtest(Version::Version13, 2, 16, 1, "password", "somesalt",
708                 "c1628832147d9720c5bd1cfd61367078729f6dfb6f8fea9ff98158e0d7816ed0",
709                 "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$wWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA",
710                 Variant::I);
711        hashtest(Version::Version13, 2, 18, 1, "password", "somesalt",
712                 "296dbae80b807cdceaad44ae741b506f14db0959267b183b118f9b24229bc7cb",
713                 "$argon2i$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$KW266AuAfNzqrUSudBtQbxTbCVkmexg7EY+bJCKbx8s",
714                 Variant::I);
715        hashtest(Version::Version13, 2, 8, 1, "password", "somesalt",
716                 "89e9029f4637b295beb027056a7336c414fadd43f6b208645281cb214a56452f",
717                 "$argon2i$v=19$m=256,t=2,p=1$c29tZXNhbHQ$iekCn0Y3spW+sCcFanM2xBT63UP2sghkUoHLIUpWRS8",
718                 Variant::I);
719        hashtest(Version::Version13, 2, 8, 2, "password", "somesalt",
720                 "4ff5ce2769a1d7f4c8a491df09d41a9fbe90e5eb02155a13e4c01e20cd4eab61",
721                 "$argon2i$v=19$m=256,t=2,p=2$c29tZXNhbHQ$T/XOJ2mh1/TIpJHfCdQan76Q5esCFVoT5MAeIM1Oq2E",
722                 Variant::I);
723        hashtest(Version::Version13, 1, 16, 1, "password", "somesalt",
724                 "d168075c4d985e13ebeae560cf8b94c3b5d8a16c51916b6f4ac2da3ac11bbecf",
725                 "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$0WgHXE2YXhPr6uVgz4uUw7XYoWxRkWtvSsLaOsEbvs8",
726                 Variant::I);
727        hashtest(Version::Version13, 4, 16, 1, "password", "somesalt",
728                 "aaa953d58af3706ce3df1aefd4a64a84e31d7f54175231f1285259f88174ce5b",
729                 "$argon2i$v=19$m=65536,t=4,p=1$c29tZXNhbHQ$qqlT1YrzcGzj3xrv1KZKhOMdf1QXUjHxKFJZ+IF0zls",
730                 Variant::I);
731        hashtest(Version::Version13, 2, 16, 1, "differentpassword", "somesalt",
732                 "14ae8da01afea8700c2358dcef7c5358d9021282bd88663a4562f59fb74d22ee",
733                 "$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$FK6NoBr+qHAMI1jc73xTWNkCEoK9iGY6RWL1n7dNIu4",
734                 Variant::I);
735        hashtest(Version::Version13, 2, 16, 1, "password", "diffsalt",
736                 "b0357cccfbef91f3860b0dba447b2348cbefecadaf990abfe9cc40726c521271",
737                 "$argon2i$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ$sDV8zPvvkfOGCw26RHsjSMvv7K2vmQq/6cxAcmxSEnE",
738                 Variant::I);
739    }
740
741    #[test]
742    #[ignore]
743    pub fn test_argon2i_0x13_large_ram() {
744        hashtest(Version::Version13, 2, 20, 1, "password", "somesalt",
745                 "d1587aca0922c3b5d6a83edab31bee3c4ebaef342ed6127a55d19b2351ad1f41",
746                 "$argon2i$v=19$m=1048576,t=2,p=1$c29tZXNhbHQ$0Vh6ygkiw7XWqD7asxvuPE667zQu1hJ6VdGbI1GtH0E",
747                 Variant::I);
748    }
749
750    #[test]
751    fn test_argon2i_0x13_errors() {
752        // Handle an invalid encoding correctly (it is missing a $)
753        check_error_code!(DecodingFail, verify(
754                &c_str_cow(b"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQwWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"),
755                Some(b"password"), Variant::I));
756
757        // Handle an invalid encoding correctly (it is missing a $)
758        check_error_code!(DecodingFail, verify(
759                &c_str_cow(b"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQwWKIMhR9lyDFvRz9YTZweHKfbftvj+qf+YFY4NeBbtA"),
760                Some(b"password"), Variant::I));
761
762        // Handle an invalid encoding correctly (salt is too short)
763        check_error_code!(SaltTooShort, verify(
764                &c_str_cow(b"$argon2i$v=19$m=65536,t=2,p=1$$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ"),
765                Some(b"password"), Variant::I));
766
767        // Handle an invalid encoding correctly (the encoded password is "passwore")
768        check_error_code!(VerifyMismatch, verify(
769                &c_str_cow(b"$argon2i$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$8iIuixkI73Js3G1uMbezQXD0b8LG4SXGsOwoQkdAQIM"),
770                Some(b"password"), Variant::I));
771    }
772
773    #[test]
774    fn test_argon2id_0x13() {
775        println!("Test Argon2id version number: 0x{:02X}", (Version::Version13).to_int());
776
777        hashtest(Version::Version13, 2, 16, 1, "password", "somesalt",
778                 "09316115d5cf24ed5a15a31a3ba326e5cf32edc24702987c02b6566f61913cf7",
779                 "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc", Variant::ID);
780        hashtest(Version::Version13, 2, 18, 1, "password", "somesalt",
781                 "78fe1ec91fb3aa5657d72e710854e4c3d9b9198c742f9616c2f085bed95b2e8c",
782                 "$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow", Variant::ID);
783        hashtest(Version::Version13, 2, 8, 1, "password", "somesalt",
784                 "9dfeb910e80bad0311fee20f9c0e2b12c17987b4cac90c2ef54d5b3021c68bfe",
785                 "$argon2id$v=19$m=256,t=2,p=1$c29tZXNhbHQ$nf65EOgLrQMR/uIPnA4rEsF5h7TKyQwu9U1bMCHGi/4", Variant::ID);
786        hashtest(Version::Version13, 2, 8, 2, "password", "somesalt",
787                 "6d093c501fd5999645e0ea3bf620d7b8be7fd2db59c20d9fff9539da2bf57037",
788                 "$argon2id$v=19$m=256,t=2,p=2$c29tZXNhbHQ$bQk8UB/VmZZF4Oo79iDXuL5/0ttZwg2f/5U52iv1cDc", Variant::ID);
789        hashtest(Version::Version13, 1, 16, 1, "password", "somesalt",
790                 "f6a5adc1ba723dddef9b5ac1d464e180fcd9dffc9d1cbf76cca2fed795d9ca98",
791                 "$argon2id$v=19$m=65536,t=1,p=1$c29tZXNhbHQ$9qWtwbpyPd3vm1rB1GThgPzZ3/ydHL92zKL+15XZypg", Variant::ID);
792        hashtest(Version::Version13, 4, 16, 1, "password", "somesalt",
793                 "9025d48e68ef7395cca9079da4c4ec3affb3c8911fe4f86d1a2520856f63172c",
794                 "$argon2id$v=19$m=65536,t=4,p=1$c29tZXNhbHQ$kCXUjmjvc5XMqQedpMTsOv+zyJEf5PhtGiUghW9jFyw", Variant::ID);
795        hashtest(Version::Version13, 2, 16, 1, "differentpassword", "somesalt",
796                 "0b84d652cf6b0c4beaef0dfe278ba6a80df6696281d7e0d2891b817d8c458fde",
797                 "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$C4TWUs9rDEvq7w3+J4umqA32aWKB1+DSiRuBfYxFj94", Variant::ID);
798        hashtest(Version::Version13, 2, 16, 1, "password", "diffsalt",
799                 "bdf32b05ccc42eb15d58fd19b1f856b113da1e9a5874fdcc544308565aa8141c",
800                 "$argon2id$v=19$m=65536,t=2,p=1$ZGlmZnNhbHQ$vfMrBczELrFdWP0ZsfhWsRPaHppYdP3MVEMIVlqoFBw", Variant::ID);
801    }
802
803    #[test]
804    fn test_common_error_states() {
805        const OUTLEN: usize = 32;
806        let mut out = [0u8; OUTLEN];
807
808        check_error_code!(MemoryTooLittle, hash(2, 1, 1,
809                                                Some(b"password"), Some(b"diffsalt"),
810                                                Some(&mut out), None,
811                                                Variant::ID, Version::Version13));
812        check_error_code!(SaltTooShort, hash(2, 1 << 12, 1,
813                                                Some(b"password"), Some(b"s"),
814                                                Some(&mut out), None,
815                                                Variant::ID, Version::Version13));
816
817        // @NOTE This test is missing because it's not possible to pass a mismatched length/pointer
818        // pair to this function :)
819        //
820        //     ret = argon2_hash(2, 1 << 12, 1, NULL, strlen("password"),
821        //                "diffsalt", strlen("diffsalt"),
822        //                out, OUT_LEN, NULL, 0, Argon2_id, version);
823        //
824        // It would look something like this:
825        //
826        //     check_error_code!(PwdPtrMismatch, hash(2, 1 << 12, 1,
827        //                                            Some(b"password"), Some(b"diffsalt"),
828        //                                            Some(&mut out), None,
829        //                                            Variant::ID, Version::Version13));
830    }
831}