mirror of
https://github.com/RustCrypto/password-hashes.git
synced 2026-01-24 19:56:34 +00:00
pbkdf2: heapless MCF hash verification support (#816)
Adds an `alloc` feature which is needed to enable MCF hashing functionality. The `PasswordVerifier<mcf::PasswordHashRef>` impl now works without any dependency on liballoc.
This commit is contained in:
6
.github/workflows/pbkdf2.yml
vendored
6
.github/workflows/pbkdf2.yml
vendored
@@ -55,6 +55,6 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
- run: cargo test --no-default-features
|
||||
- run: cargo test
|
||||
- run: cargo test --all-features
|
||||
- uses: RustCrypto/actions/cargo-hack-install@master
|
||||
- run: cargo hack test --feature-powerset
|
||||
- run: cargo test --all-features --release
|
||||
|
||||
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -282,8 +282,9 @@ checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
|
||||
|
||||
[[package]]
|
||||
name = "mcf"
|
||||
version = "0.6.0-rc.2"
|
||||
source = "git+https://github.com/RustCrypto/formats?branch=base64ct%2Fpbkdf2-alphabet#a2b37fd6975e49ecdee08d640d6258f4b0f488c7"
|
||||
version = "0.6.0-rc.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "423dc04b93e27ab6399fd28615305105c1621cebb78cbe24f64cb942d440733a"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
@@ -20,5 +20,3 @@ opt-level = 2
|
||||
argon2 = { path = "./argon2" }
|
||||
pbkdf2 = { path = "./pbkdf2" }
|
||||
scrypt = { path = "./scrypt" }
|
||||
|
||||
mcf = { git = "https://github.com/RustCrypto/formats", branch = "base64ct/pbkdf2-alphabet" }
|
||||
|
||||
@@ -17,8 +17,8 @@ rust-version = "1.85"
|
||||
digest = { version = "0.11.0-rc.4", features = ["mac"] }
|
||||
|
||||
# optional dependencies
|
||||
hmac = { version = "0.13.0-rc.3", default-features = false, optional = true }
|
||||
mcf = { version = "0.6.0-rc.2", optional = true }
|
||||
hmac = { version = "0.13.0-rc.3", optional = true, default-features = false }
|
||||
mcf = { version = "0.6.0-rc.3", optional = true, default-features = false, features = ["base64"] }
|
||||
password-hash = { version = "0.6.0-rc.8", default-features = false, optional = true }
|
||||
sha1 = { version = "0.11.0-rc.3", default-features = false, optional = true }
|
||||
sha2 = { version = "0.11.0-rc.3", default-features = false, optional = true }
|
||||
@@ -33,8 +33,9 @@ belt-hash = "0.2.0-rc.3"
|
||||
|
||||
[features]
|
||||
default = ["hmac"]
|
||||
alloc = ["mcf?/alloc", "password-hash?/alloc"]
|
||||
getrandom = ["password-hash/getrandom"]
|
||||
mcf = ["hmac", "password-hash/alloc", "dep:mcf", "sha2"]
|
||||
mcf = ["hmac", "sha2", "dep:password-hash", "dep:mcf"]
|
||||
phc = ["hmac", "password-hash/phc", "sha2"]
|
||||
rand_core = ["password-hash/rand_core"]
|
||||
|
||||
|
||||
@@ -7,18 +7,24 @@
|
||||
//! field contains `rounds=` or not: if the number of rounds does NOT contain `rounds=`, but just a
|
||||
//! bare number of rounds, then it's MCF format. If it DOES contain `rounds=`, then it's PHC.
|
||||
|
||||
pub use mcf::{PasswordHash, PasswordHashRef};
|
||||
pub use mcf::PasswordHashRef;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use mcf::PasswordHash;
|
||||
|
||||
use crate::{Algorithm, Params, Pbkdf2, pbkdf2_hmac};
|
||||
use mcf::Base64;
|
||||
use password_hash::{
|
||||
CustomizedPasswordHasher, Error, PasswordHasher, PasswordVerifier, Result, Version,
|
||||
};
|
||||
use password_hash::{Error, PasswordVerifier, Result};
|
||||
use sha2::{Sha256, Sha512};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use password_hash::{CustomizedPasswordHasher, PasswordHasher, Version};
|
||||
#[cfg(feature = "sha1")]
|
||||
use sha1::Sha1;
|
||||
|
||||
const MAX_SALT_LEN: usize = 64;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
type Params = Params;
|
||||
|
||||
@@ -63,12 +69,14 @@ impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl PasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
fn hash_password_with_salt(&self, password: &[u8], salt: &[u8]) -> Result<PasswordHash> {
|
||||
self.hash_password_customized(password, salt, None, None, self.params)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl PasswordVerifier<PasswordHash> for Pbkdf2 {
|
||||
fn verify_password(&self, password: &[u8], hash: &PasswordHash) -> Result<()> {
|
||||
self.verify_password(password, hash.as_password_hash_ref())
|
||||
@@ -89,15 +97,17 @@ impl PasswordVerifier<PasswordHashRef> for Pbkdf2 {
|
||||
}
|
||||
|
||||
// decode salt
|
||||
let mut salt_buf = [0u8; MAX_SALT_LEN];
|
||||
let salt = next
|
||||
.decode_base64(Base64::Pbkdf2)
|
||||
.decode_base64_into(Base64::Pbkdf2, &mut salt_buf)
|
||||
.map_err(|_| Error::EncodingInvalid)?;
|
||||
|
||||
// decode expected password hash
|
||||
let mut expected_buf = [0u8; Params::MAX_OUTPUT_LENGTH];
|
||||
let expected = fields
|
||||
.next()
|
||||
.ok_or(Error::EncodingInvalid)?
|
||||
.decode_base64(Base64::Pbkdf2)
|
||||
.decode_base64_into(Base64::Pbkdf2, &mut expected_buf)
|
||||
.map_err(|_| Error::EncodingInvalid)?;
|
||||
|
||||
// should be the last field
|
||||
@@ -105,8 +115,8 @@ impl PasswordVerifier<PasswordHashRef> for Pbkdf2 {
|
||||
return Err(Error::EncodingInvalid);
|
||||
}
|
||||
|
||||
let mut buffer = [0u8; Params::MAX_OUTPUT_LENGTH];
|
||||
let out = buffer.get_mut(..expected.len()).ok_or(Error::OutputSize)?;
|
||||
let mut out_buf = [0u8; Params::MAX_OUTPUT_LENGTH];
|
||||
let out = out_buf.get_mut(..expected.len()).ok_or(Error::OutputSize)?;
|
||||
|
||||
let f = match algorithm {
|
||||
#[cfg(feature = "sha1")]
|
||||
@@ -134,14 +144,21 @@ impl PasswordVerifier<PasswordHashRef> for Pbkdf2 {
|
||||
// TODO(tarcieri): tests for SHA-1
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Error;
|
||||
use crate::{Params, Pbkdf2};
|
||||
use mcf::{Base64, PasswordHash};
|
||||
use password_hash::{CustomizedPasswordHasher, PasswordVerifier};
|
||||
use crate::Pbkdf2;
|
||||
use mcf::PasswordHashRef;
|
||||
use password_hash::{Error, PasswordVerifier};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use {
|
||||
crate::Params,
|
||||
mcf::{Base64, PasswordHash},
|
||||
password_hash::CustomizedPasswordHasher,
|
||||
};
|
||||
|
||||
// Example adapted from:
|
||||
// <https://passlib.readthedocs.io/en/stable/lib/passlib.hash.pbkdf2_digest.html>
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn hash_password_sha256() {
|
||||
const EXAMPLE_PASSWORD: &[u8] = b"password";
|
||||
const EXAMPLE_ROUNDS: u32 = 8000;
|
||||
@@ -173,6 +190,7 @@ mod tests {
|
||||
// Example adapted from:
|
||||
// <https://github.com/hlandau/passlib/blob/8f820e0/hash/pbkdf2/pbkdf2_test.go>
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn hash_password_sha512() {
|
||||
const EXAMPLE_PASSWORD: &[u8] = b"abcdefghijklmnop";
|
||||
const EXAMPLE_ROUNDS: u32 = 25000;
|
||||
@@ -199,4 +217,23 @@ mod tests {
|
||||
Err(Error::PasswordInvalid)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_password_sha256() {
|
||||
const EXAMPLE_PASSWORD: &[u8] = b"password";
|
||||
const EXAMPLE_HASH: &str =
|
||||
"$pbkdf2-sha256$8000$XAuBMIYQQogxRg$tRRlz8hYn63B9LYiCd6PRo6FMiunY9ozmMMI3srxeRE";
|
||||
|
||||
let pwhash = PasswordHashRef::new(EXAMPLE_HASH).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Pbkdf2::SHA256.verify_password(EXAMPLE_PASSWORD, pwhash),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Pbkdf2::SHA256.verify_password(b"bogus", pwhash),
|
||||
Err(Error::PasswordInvalid)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,10 @@ use core::{
|
||||
fmt::{self, Display},
|
||||
str::FromStr,
|
||||
};
|
||||
use password_hash::{Error, Result};
|
||||
|
||||
#[cfg(feature = "phc")]
|
||||
use password_hash::{
|
||||
Error, Result,
|
||||
phc::{self, Decimal, ParamsString},
|
||||
};
|
||||
use password_hash::phc::{self, Decimal, ParamsString};
|
||||
|
||||
/// PBKDF2 params
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
|
||||
Reference in New Issue
Block a user