plan: libxcrypt-compatible sha512-crypt (16-char salt); KAT test
This commit is contained in:
1 parent
07ff88c18d
commit
bc600c27a0
1 file changed
+19
-3
+19
-3
@@ -219,11 +219,19 @@ pub fn classify_secret(s: &str) -> SecretKind {
|
||||
SecretKind::Plaintext(s.to_string())
|
||||
}
|
||||
|
||||
/// SHA512-crypt hash (what `mkpasswd -m sha-512` produces).
|
||||
/// SHA512-crypt hash compatible with glibc/libxcrypt (Dovecot, `mkpasswd -m sha-512`).
|
||||
///
|
||||
/// The salt is 12 random bytes = exactly 16 salt characters. sha-crypt 0.6's
|
||||
/// default (16 bytes -> 22 characters) hashes over all 22 characters, while
|
||||
/// libxcrypt only uses the first 16, so such hashes never verify in Dovecot
|
||||
/// (caught by the e2e test).
|
||||
pub fn sha512_crypt(password: &str) -> Result<String> {
|
||||
use rand::RngExt;
|
||||
use sha_crypt::{PasswordHasher, ShaCrypt};
|
||||
let h =
|
||||
ShaCrypt::default().hash_password(password.as_bytes()).map_err(|e| anyhow::anyhow!("hashing password: {e}"))?;
|
||||
let salt: [u8; 12] = rand::rng().random();
|
||||
let h = ShaCrypt::default()
|
||||
.hash_password_with_salt(password.as_bytes(), &salt)
|
||||
.map_err(|e| anyhow::anyhow!("hashing password: {e}"))?;
|
||||
Ok(h.as_str().to_string())
|
||||
}
|
||||
|
||||
@@ -487,6 +495,14 @@ mod tests {
|
||||
assert!(h.starts_with("$6$"));
|
||||
assert!(verify_sha_crypt("correct horse", &h));
|
||||
assert!(!verify_sha_crypt("wrong", &h));
|
||||
// glibc/libxcrypt only use 16 salt characters; longer salts never verify there.
|
||||
let salt = h.trim_start_matches("$6$").split('$').find(|f| !f.starts_with("rounds=")).unwrap();
|
||||
assert_eq!(salt.len(), 16, "{h}");
|
||||
// Algorithm matches glibc (Drepper's test vector).
|
||||
assert!(verify_sha_crypt(
|
||||
"Hello world!",
|
||||
"$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user