mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 13:40:49 +02:00
Rust wrapper: implement password-hash traits
This commit is contained in:
+41
@@ -22,6 +22,12 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.72.1"
|
||||
@@ -105,6 +111,12 @@ dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
@@ -125,6 +137,15 @@ dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||
dependencies = [
|
||||
"cmov",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.2"
|
||||
@@ -229,6 +250,25 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
|
||||
dependencies = [
|
||||
"phc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phc"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.37"
|
||||
@@ -424,6 +464,7 @@ dependencies = [
|
||||
"bindgen",
|
||||
"cipher",
|
||||
"digest",
|
||||
"password-hash",
|
||||
"rand_core 0.10.0",
|
||||
"regex",
|
||||
"signature",
|
||||
|
||||
@@ -17,6 +17,7 @@ aead = ["dep:aead"]
|
||||
cipher = ["dep:cipher"]
|
||||
digest = ["dep:digest"]
|
||||
signature = ["dep:signature"]
|
||||
password-hash = ["dep:password-hash", "password-hash/phc"]
|
||||
|
||||
[dependencies]
|
||||
rand_core = { version = "0.10", optional = true, default-features = false }
|
||||
@@ -25,12 +26,14 @@ cipher = { version = "0.5", optional = true, default-features = false }
|
||||
digest = { version = "0.11", optional = true, default-features = false, features = ["block-api"] }
|
||||
signature = { version = "2.2", optional = true, default-features = false }
|
||||
zeroize = { version = "1.3", default-features = false, features = ["derive"] }
|
||||
password-hash = { version = "0.6.1", optional = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
aead = { version = "0.5", features = ["alloc", "dev"] }
|
||||
cipher = "0.5"
|
||||
digest = { version = "0.11", features = ["dev"] }
|
||||
signature = "2.2"
|
||||
password-hash = { version = "0.6.1", features = ["phc"] }
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.72.1"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FEATURES := rand_core,aead,cipher,digest,signature
|
||||
FEATURES := rand_core,aead,cipher,digest,signature,password-hash
|
||||
CARGO_FEATURE_FLAGS := --features $(FEATURES)
|
||||
|
||||
.PHONY: all
|
||||
|
||||
@@ -64,6 +64,8 @@ pub mod rsa;
|
||||
#[cfg(feature = "signature")]
|
||||
pub mod rsa_pkcs1v15;
|
||||
pub mod sha;
|
||||
#[cfg(feature = "password-hash")]
|
||||
pub mod pbkdf2_password_hash;
|
||||
#[cfg(feature = "digest")]
|
||||
pub mod sha_digest;
|
||||
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfSSL.
|
||||
*
|
||||
* wolfSSL is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfSSL is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
/*!
|
||||
RustCrypto `password-hash` trait implementations for wolfCrypt PBKDF2.
|
||||
|
||||
This module provides [`Pbkdf2`], a type that implements the
|
||||
[`PasswordHasher`] and [`CustomizedPasswordHasher`] traits from the
|
||||
`password-hash` crate, backed by the wolfCrypt PBKDF2 implementation.
|
||||
The blanket [`PasswordVerifier`] implementation is also available,
|
||||
allowing verification of existing password hashes.
|
||||
|
||||
Password hashes are represented in the
|
||||
[PHC string format](https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md):
|
||||
|
||||
```text
|
||||
$pbkdf2-sha256$i=600000$<salt>$<hash>
|
||||
```
|
||||
|
||||
# Supported algorithms
|
||||
|
||||
| Algorithm ID | Hash function |
|
||||
|-----------------|---------------|
|
||||
| `pbkdf2-sha256` | HMAC-SHA-256 |
|
||||
| `pbkdf2-sha384` | HMAC-SHA-384 |
|
||||
| `pbkdf2-sha512` | HMAC-SHA-512 |
|
||||
|
||||
[`PasswordHasher`]: password_hash::PasswordHasher
|
||||
[`CustomizedPasswordHasher`]: password_hash::CustomizedPasswordHasher
|
||||
[`PasswordVerifier`]: password_hash::PasswordVerifier
|
||||
*/
|
||||
|
||||
#![cfg(all(feature = "password-hash", hmac, kdf_pbkdf2))]
|
||||
|
||||
use password_hash::phc::{Ident, Output, ParamsString, PasswordHash, Salt};
|
||||
use password_hash::{CustomizedPasswordHasher, Error, Result, Version};
|
||||
|
||||
use crate::hmac::HMAC;
|
||||
use crate::kdf;
|
||||
|
||||
const PBKDF2_SHA256_IDENT: Ident = Ident::new_unwrap("pbkdf2-sha256");
|
||||
const PBKDF2_SHA384_IDENT: Ident = Ident::new_unwrap("pbkdf2-sha384");
|
||||
const PBKDF2_SHA512_IDENT: Ident = Ident::new_unwrap("pbkdf2-sha512");
|
||||
|
||||
/// Minimum number of PBKDF2 rounds.
|
||||
pub const MIN_ROUNDS: u32 = 1_000;
|
||||
|
||||
/// Default number of PBKDF2 rounds (OWASP recommendation for SHA-256).
|
||||
pub const DEFAULT_ROUNDS: u32 = 600_000;
|
||||
|
||||
/// Default output length in bytes.
|
||||
pub const DEFAULT_OUTPUT_LEN: usize = 32;
|
||||
|
||||
/// PBKDF2 algorithm variant.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub enum Algorithm {
|
||||
/// PBKDF2 with HMAC-SHA-256.
|
||||
#[default]
|
||||
Pbkdf2Sha256,
|
||||
/// PBKDF2 with HMAC-SHA-384.
|
||||
Pbkdf2Sha384,
|
||||
/// PBKDF2 with HMAC-SHA-512.
|
||||
Pbkdf2Sha512,
|
||||
}
|
||||
|
||||
impl Algorithm {
|
||||
/// Get the PHC string format identifier for this algorithm.
|
||||
pub fn ident(self) -> Ident {
|
||||
match self {
|
||||
Algorithm::Pbkdf2Sha256 => PBKDF2_SHA256_IDENT,
|
||||
Algorithm::Pbkdf2Sha384 => PBKDF2_SHA384_IDENT,
|
||||
Algorithm::Pbkdf2Sha512 => PBKDF2_SHA512_IDENT,
|
||||
}
|
||||
}
|
||||
|
||||
fn hmac_type(self) -> i32 {
|
||||
match self {
|
||||
Algorithm::Pbkdf2Sha256 => HMAC::TYPE_SHA256,
|
||||
Algorithm::Pbkdf2Sha384 => HMAC::TYPE_SHA384,
|
||||
Algorithm::Pbkdf2Sha512 => HMAC::TYPE_SHA512,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Ident> for Algorithm {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(ident: Ident) -> Result<Self> {
|
||||
if ident == PBKDF2_SHA256_IDENT {
|
||||
Ok(Algorithm::Pbkdf2Sha256)
|
||||
} else if ident == PBKDF2_SHA384_IDENT {
|
||||
Ok(Algorithm::Pbkdf2Sha384)
|
||||
} else if ident == PBKDF2_SHA512_IDENT {
|
||||
Ok(Algorithm::Pbkdf2Sha512)
|
||||
} else {
|
||||
Err(Error::Algorithm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// PBKDF2 parameters.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Params {
|
||||
/// Number of iterations (rounds).
|
||||
pub rounds: u32,
|
||||
/// Desired output hash length in bytes.
|
||||
pub output_len: usize,
|
||||
}
|
||||
|
||||
impl Default for Params {
|
||||
fn default() -> Self {
|
||||
Params {
|
||||
rounds: DEFAULT_ROUNDS,
|
||||
output_len: DEFAULT_OUTPUT_LEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&PasswordHash> for Params {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(hash: &PasswordHash) -> Result<Self> {
|
||||
let rounds = hash
|
||||
.params
|
||||
.get_decimal("i")
|
||||
.ok_or(Error::ParamInvalid { name: "i" })?;
|
||||
|
||||
if rounds < MIN_ROUNDS {
|
||||
return Err(Error::ParamInvalid { name: "i" });
|
||||
}
|
||||
|
||||
let output_len = if let Some(ref h) = hash.hash {
|
||||
h.len()
|
||||
} else if let Some(l) = hash.params.get_decimal("l") {
|
||||
l as usize
|
||||
} else {
|
||||
return Err(Error::ParamInvalid { name: "l" });
|
||||
};
|
||||
|
||||
Ok(Params { rounds, output_len })
|
||||
}
|
||||
}
|
||||
|
||||
/// PBKDF2 password hasher backed by wolfCrypt.
|
||||
///
|
||||
/// Implements the [`PasswordHasher`](password_hash::PasswordHasher) and
|
||||
/// [`CustomizedPasswordHasher`] traits. A blanket
|
||||
/// [`PasswordVerifier`](password_hash::PasswordVerifier) implementation is
|
||||
/// provided by the `password-hash` crate.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// #[cfg(all(hmac, kdf_pbkdf2))]
|
||||
/// {
|
||||
/// use password_hash::PasswordHasher;
|
||||
/// use wolfssl_wolfcrypt::pbkdf2_password_hash::Pbkdf2;
|
||||
///
|
||||
/// let hasher = Pbkdf2::default();
|
||||
/// let salt = b"0123456789abcdef"; // 16 bytes
|
||||
/// let hash = hasher.hash_password_with_salt(b"password", salt)
|
||||
/// .expect("hashing failed");
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Pbkdf2 {
|
||||
/// Algorithm to use for hashing.
|
||||
pub algorithm: Algorithm,
|
||||
/// Default parameters.
|
||||
pub params: Params,
|
||||
}
|
||||
|
||||
impl password_hash::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.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl password_hash::CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
type Params = Params;
|
||||
|
||||
fn hash_password_customized(
|
||||
&self,
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
algorithm: Option<&str>,
|
||||
version: Option<Version>,
|
||||
params: Params,
|
||||
) -> Result<PasswordHash> {
|
||||
if version.is_some() {
|
||||
return Err(Error::Version);
|
||||
}
|
||||
|
||||
let algorithm = match algorithm {
|
||||
Some(s) => {
|
||||
let ident = Ident::new(s).map_err(|_| Error::Algorithm)?;
|
||||
Algorithm::try_from(ident)?
|
||||
}
|
||||
None => self.algorithm,
|
||||
};
|
||||
|
||||
if params.rounds < MIN_ROUNDS {
|
||||
return Err(Error::ParamInvalid { name: "i" });
|
||||
}
|
||||
|
||||
let iterations = i32::try_from(params.rounds)
|
||||
.map_err(|_| Error::ParamInvalid { name: "i" })?;
|
||||
|
||||
let salt = Salt::new(salt)?;
|
||||
|
||||
let mut out_buf = [0u8; Output::MAX_LENGTH];
|
||||
let out_slice = &mut out_buf[..params.output_len];
|
||||
kdf::pbkdf2(password, salt.as_ref(), iterations, algorithm.hmac_type(), out_slice)
|
||||
.map_err(|_| Error::Crypto)?;
|
||||
let output = Output::new(out_slice)?;
|
||||
|
||||
let mut phc_params = ParamsString::new();
|
||||
phc_params.add_decimal("i", params.rounds)?;
|
||||
|
||||
Ok(PasswordHash {
|
||||
algorithm: algorithm.ident(),
|
||||
version: None,
|
||||
params: phc_params,
|
||||
salt: Some(salt),
|
||||
hash: Some(output),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
#![cfg(all(feature = "password-hash", hmac, kdf_pbkdf2))]
|
||||
|
||||
mod common;
|
||||
|
||||
use password_hash::phc::PasswordHash;
|
||||
use password_hash::{CustomizedPasswordHasher, PasswordHasher, PasswordVerifier};
|
||||
use wolfssl_wolfcrypt::pbkdf2_password_hash::*;
|
||||
|
||||
#[test]
|
||||
fn test_hash_and_verify() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef"; // 16 bytes
|
||||
let password = b"hunter2";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing failed");
|
||||
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha256.ident());
|
||||
assert!(hash.salt.is_some());
|
||||
assert!(hash.hash.is_some());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 32);
|
||||
|
||||
// Verify correct password succeeds
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("verification of correct password failed");
|
||||
|
||||
// Verify wrong password fails
|
||||
let result = hasher.verify_password(b"wrong_password", &hash);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_roundtrip_phc_string() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing failed");
|
||||
|
||||
// Serialize to PHC string and parse back
|
||||
let phc_string = hash.to_string();
|
||||
assert!(phc_string.starts_with("$pbkdf2-sha256$"));
|
||||
|
||||
let parsed = PasswordHash::new(&phc_string).expect("parsing PHC string failed");
|
||||
|
||||
// Verify with the parsed hash
|
||||
hasher
|
||||
.verify_password(password, &parsed)
|
||||
.expect("verification of parsed hash failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_params() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
assert_eq!(hasher.algorithm, Algorithm::Pbkdf2Sha256);
|
||||
assert_eq!(hasher.params.rounds, DEFAULT_ROUNDS);
|
||||
assert_eq!(hasher.params.output_len, DEFAULT_OUTPUT_LEN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha384_algorithm() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha384,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 48,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing with SHA-384 failed");
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha384.ident());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 48);
|
||||
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("SHA-384 verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sha512_algorithm() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha512,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 64,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("hashing with SHA-512 failed");
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha512.ident());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 64);
|
||||
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("SHA-512 verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_customized_hash() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
let custom_params = Params {
|
||||
rounds: 8192,
|
||||
output_len: 48,
|
||||
};
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_with_params(password, salt, custom_params)
|
||||
.expect("customized hashing failed");
|
||||
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 48);
|
||||
assert_eq!(hash.params.get_decimal("i"), Some(8192));
|
||||
|
||||
hasher
|
||||
.verify_password(password, &hash)
|
||||
.expect("customized hash verification failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_customized_hash_with_algorithm_override() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
let params = Params {
|
||||
rounds: 4096,
|
||||
output_len: 64,
|
||||
};
|
||||
|
||||
let hash = hasher
|
||||
.hash_password_customized(password, salt, Some("pbkdf2-sha512"), None, params)
|
||||
.expect("algorithm override failed");
|
||||
|
||||
assert_eq!(hash.algorithm, Algorithm::Pbkdf2Sha512.ident());
|
||||
assert_eq!(hash.hash.as_ref().unwrap().len(), 64);
|
||||
|
||||
// Verify with a Pbkdf2 instance using the matching algorithm
|
||||
let verifier = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha512,
|
||||
..Pbkdf2::default()
|
||||
};
|
||||
verifier
|
||||
.verify_password(password, &hash)
|
||||
.expect("verification with algorithm override failed");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version_rejected() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
let salt = b"0123456789abcdef";
|
||||
|
||||
let result =
|
||||
hasher.hash_password_customized(b"password", salt, None, Some(1), Params::default());
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unknown_algorithm_rejected() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2::default();
|
||||
let salt = b"0123456789abcdef";
|
||||
|
||||
let result = hasher.hash_password_customized(
|
||||
b"password",
|
||||
salt,
|
||||
Some("argon2id"),
|
||||
None,
|
||||
Params::default(),
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deterministic_output() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let salt = b"0123456789abcdef";
|
||||
let password = b"password";
|
||||
|
||||
let hash1 = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("first hash failed");
|
||||
let hash2 = hasher
|
||||
.hash_password_with_salt(password, salt)
|
||||
.expect("second hash failed");
|
||||
|
||||
assert_eq!(hash1.hash, hash2.hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_salts_produce_different_hashes() {
|
||||
common::setup();
|
||||
|
||||
let hasher = Pbkdf2 {
|
||||
algorithm: Algorithm::Pbkdf2Sha256,
|
||||
params: Params {
|
||||
rounds: 4096,
|
||||
output_len: 32,
|
||||
},
|
||||
};
|
||||
|
||||
let password = b"password";
|
||||
|
||||
let hash1 = hasher
|
||||
.hash_password_with_salt(password, b"salt_aaaaaaaaaa01")
|
||||
.expect("first hash failed");
|
||||
let hash2 = hasher
|
||||
.hash_password_with_salt(password, b"salt_aaaaaaaaaa02")
|
||||
.expect("second hash failed");
|
||||
|
||||
assert_ne!(hash1.hash, hash2.hash);
|
||||
}
|
||||
Reference in New Issue
Block a user