//! Test vectors for Argon2 password hashes in the PHC string format //! //! Adapted from: #![cfg(all(feature = "alloc", feature = "password-hash"))] use argon2::{ Algorithm, Argon2, AssociatedData, KeyId, ParamsBuilder, PasswordHash, PasswordHasher, PasswordVerifier, Version, }; use password_hash::{ errors::{Error, InvalidValue}, SaltString, }; /// Valid password pub const VALID_PASSWORD: &[u8] = b"password"; /// Invalid password pub const INVALID_PASSWORD: &[u8] = b"sassword"; /// Password hashes for "password" pub const VALID_PASSWORD_HASHES: &[&str] = &[ "$argon2i$v=19$m=65536,t=1,p=1$c29tZXNhbHQAAAAAAAAAAA$+r0d29hqEB0yasKr55ZgICsQGSkl0v0kgwhd+U3wyRo", "$argon2id$v=19$m=262144,t=2,p=1$c29tZXNhbHQ$eP4eyR+zqlZX1y5xCFTkw9m5GYx0L5YWwvCFvtlbLow", "$argon2id$v=19$m=65536,t=2,p=1$c29tZXNhbHQ$CTFhFdXPJO1aFaMaO6Mm5c8y7cJHAph8ArZWb2GRPPc", "$argon2d$v=19$m=65536,t=2,p=1$YzI5dFpYTmhiSFFBQUFBQUFBQUFBQQ$Jxy74cswY2mq9y+u+iJcJy8EqOp4t/C7DWDzGwGB3IM", // Password with optional keyid "$argon2d$v=19$m=65536,t=2,p=1,keyid=8PDw8A$YzI5dFpYTmhiSFFBQUFBQUFBQUFBQQ$Jxy74cswY2mq9y+u+iJcJy8EqOp4t/C7DWDzGwGB3IM", // Password with optional data "$argon2d$v=16$m=32,t=2,p=3,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o", // Password with optional keyid&data "$argon2d$v=16$m=32,t=2,p=3,keyid=8PDw8A,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o", ]; #[test] fn verifies_correct_password() { for hash_string in VALID_PASSWORD_HASHES { let hash = PasswordHash::new(hash_string).unwrap(); assert_eq!( Argon2::default().verify_password(VALID_PASSWORD, &hash), Ok(()) ); } } #[test] fn rejects_incorrect_password() { for hash_string in VALID_PASSWORD_HASHES { let hash = PasswordHash::new(hash_string).unwrap(); assert!(Argon2::default() .verify_password(INVALID_PASSWORD, &hash) .is_err()); } } // Test PHC string format according to spec // see: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#argon2-encoding macro_rules! testcase_bad_encoding { ($name: ident, $err: expr, $hash: expr) => { #[test] fn $name() { let hash = PasswordHash::new($hash).unwrap(); assert_eq!( Argon2::default().verify_password(b"password", &hash), Err($err) ); } }; } macro_rules! ignored_testcase_bad_encoding { ($name: ident, $err: expr, $hash: expr) => { #[test] #[ignore] fn $name() { let hash = PasswordHash::new($hash).unwrap(); assert_eq!( Argon2::default().verify_password(b"password", &hash), Err($err) ); } }; } testcase_bad_encoding!( argon2i_invalid_encoding_invalid_version, Error::Version, "$argon2i$v=42$m=65536,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_m_not_a_number, Error::ParamValueInvalid(InvalidValue::InvalidChar('d')), "$argon2i$m=dummy,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_m_too_small, Error::ParamValueInvalid(InvalidValue::TooShort), "$argon2i$m=0,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_m_too_big, Error::ParamValueInvalid(InvalidValue::InvalidFormat), "$argon2i$m=4294967296,t=2,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_t_not_a_number, Error::ParamValueInvalid(InvalidValue::InvalidChar('d')), "$argon2i$m=65536,t=dummy,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_t_too_small, Error::ParamValueInvalid(InvalidValue::TooShort), "$argon2i$m=65536,t=0,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_t_too_big, Error::ParamValueInvalid(InvalidValue::InvalidFormat), "$argon2i$m=65536,t=4294967296,p=1$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_p_not_a_number, Error::ParamValueInvalid(InvalidValue::InvalidChar('d')), "$argon2i$m=65536,t=2,p=dummy$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_p_too_small, Error::ParamValueInvalid(InvalidValue::TooShort), "$argon2i$m=65536,t=2,p=0$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); // TODO: Wrong error is returned, this should be changed in the `password-hash` crate ignored_testcase_bad_encoding!( argon2i_invalid_encoding_p_too_big, Error::ParamValueInvalid(InvalidValue::InvalidFormat), "$argon2i$m=65536,t=2,p=256$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_keyid_not_b64, Error::B64Encoding(base64ct::Error::InvalidEncoding), "$argon2i$m=65536,t=2,p=1,keyid=dummy$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); testcase_bad_encoding!( argon2i_invalid_encoding_data_not_b64, Error::B64Encoding(base64ct::Error::InvalidEncoding), "$argon2i$m=65536,t=2,p=1,data=dummy$c29tZXNhbHQ$9sTbSlTio3Biev89thdrlKKiCaYsjjYVJxGAL3swxpQ" ); #[test] fn check_decoding_supports_out_of_order_parameters() { // parameters order is not mandatory let hash = "$argon2d$v=16$m=32,t=2,p=3,keyid=8PDw8A,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o"; let hash = PasswordHash::new(hash).unwrap(); assert!(Argon2::default() .verify_password(b"password", &hash) .is_ok()); } // TODO: Fix default parameters for decoding ! #[test] #[ignore] fn check_decoding_supports_default_parameters() { // parameters order is not mandatory let hash = "$argon2i$p=2,m=256,t=2$c29tZXNhbHQ$tsEVYKap1h6scGt5ovl9aLRGOqOth+AMB+KwHpDFZPs"; let hash = PasswordHash::new(hash).unwrap(); assert!(Argon2::default() .verify_password(b"password", &hash) .is_ok()); } // m/t/p parameters are NOT optional according to spec // TODO: Wrong error is returned, this should be changed in the `password-hash` crate ignored_testcase_bad_encoding!( argon2i_invalid_encoding_missing_m, Error::ParamValueInvalid(InvalidValue::InvalidFormat), "$argon2d$v=16$t=2,p=3$8PDw8PDw8PA$Xv5daH0zPuKO3c9tMBG/WOIUsDrPqq815/xyQTukNxY" ); // TODO: Wrong error is returned, this should be changed in the `password-hash` crate ignored_testcase_bad_encoding!( argon2i_invalid_encoding_missing_t, Error::ParamValueInvalid(InvalidValue::InvalidFormat), "$argon2d$v=16$m=32,p=3$8PDw8PDw8PA$Xv5daH0zPuKO3c9tMBG/WOIUsDrPqq815/xyQTukNxY" ); // TODO: Wrong error is returned, this should be changed in the `password-hash` crate ignored_testcase_bad_encoding!( argon2i_invalid_encoding_missing_p, Error::ParamValueInvalid(InvalidValue::InvalidFormat), "$argon2d$v=16$m=32,t=2$8PDw8PDw8PA$Xv5daH0zPuKO3c9tMBG/WOIUsDrPqq815/xyQTukNxY" ); // Missing&invalid id/salt/hash fields is handled by `PasswordHash` so no need to test that here #[test] fn check_hash_encoding_parameters_order() { let params = ParamsBuilder::new() .m_cost(32) .t_cost(2) .p_cost(3) .data(AssociatedData::new(&[0x0f; 6]).unwrap()) .keyid(KeyId::new(&[0xf0; 4]).unwrap()) .build() .unwrap(); let ctx = Argon2::new(Algorithm::Argon2d, Version::V0x10, params); let salt = vec![0; 8]; let password = b"password"; let salt_string = SaltString::encode_b64(&salt).unwrap(); let password_hash = ctx .hash_password(password, &salt_string) .unwrap() .to_string(); // The parameters shall appear in the m,t,p,keyid,data order assert_eq!(password_hash, "$argon2d$v=16$m=32,t=2,p=3,keyid=8PDw8A,data=Dw8PDw8P$AAAAAAAAAAA$KnH4gniiaFnDvlA1xev3yovC4cnrrI6tnHOYtmja90o"); }