mirror of
https://github.com/RustCrypto/password-hashes.git
synced 2026-01-25 04:06:23 +00:00
Add impls for kdf::{Kdf, Pbkdf} (#823)
For the password hash algorithms that already have a `struct` where we can impl traits (i.e. any with a `password-hash`/`phc`/`mcf` feature) adds feature-gated impls of the traits from the new `kdf` crate. The `Kdf` trait provides a generic API, and `Pbkdf` is a marker trait for password-based KDFs where a password can be used as a secret input.
This commit is contained in:
3
.github/workflows/argon2.yml
vendored
3
.github/workflows/argon2.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
targets: ${{ matrix.target }}
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features password-hash
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features kdf
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features password-hash
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features zeroize
|
||||
|
||||
@@ -73,6 +73,7 @@ jobs:
|
||||
targets: ${{ matrix.target }}
|
||||
- run: ${{ matrix.deps }}
|
||||
- run: cargo test --no-default-features
|
||||
- run: cargo test --no-default-features --features kdf
|
||||
- run: cargo test --no-default-features --features password-hash
|
||||
- run: cargo test
|
||||
- run: cargo test --all-features
|
||||
|
||||
11
.github/workflows/balloon-hash.yml
vendored
11
.github/workflows/balloon-hash.yml
vendored
@@ -35,6 +35,7 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
targets: ${{ matrix.target }}
|
||||
- run: cargo build --target ${{ matrix.target }} --release --no-default-features
|
||||
- run: cargo build --target ${{ matrix.target }} --release --no-default-features --features kdf
|
||||
- run: cargo build --target ${{ matrix.target }} --release --no-default-features --features password-hash
|
||||
|
||||
minimal-versions:
|
||||
@@ -56,7 +57,9 @@ jobs:
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
- run: cargo test --release
|
||||
- run: cargo test --release --no-default-features --features alloc
|
||||
- run: cargo test --release --no-default-features --features alloc,zeroize
|
||||
- run: cargo test --release --all-features
|
||||
- run: cargo test
|
||||
- run: cargo test --no-default-features --features alloc
|
||||
- run: cargo test --no-default-features --features alloc,zeroize
|
||||
- run: cargo test --no-default-features --features kdf
|
||||
- run: cargo test --all-features
|
||||
- run: cargo test --all-features --release
|
||||
|
||||
8
.github/workflows/pbkdf2.yml
vendored
8
.github/workflows/pbkdf2.yml
vendored
@@ -56,5 +56,11 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
- uses: RustCrypto/actions/cargo-hack-install@master
|
||||
- run: cargo hack test --feature-powerset
|
||||
- run: cargo hack check --feature-powerset --no-dev-deps
|
||||
- run: cargo hack test --feature-powerset --exclude-features default,getrandom,hmac,kdf,password-hash,rand_core
|
||||
- run: cargo test --no-default-features --features getrandom
|
||||
- run: cargo test --no-default-features --features hmac
|
||||
- run: cargo test --no-default-features --features kdf
|
||||
- run: cargo test --no-default-features --features password-hash
|
||||
- run: cargo test --no-default-features --features rand_core
|
||||
- run: cargo test --all-features --release
|
||||
|
||||
10
.github/workflows/scrypt.yml
vendored
10
.github/workflows/scrypt.yml
vendored
@@ -35,6 +35,7 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
targets: ${{ matrix.target }}
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features kdf
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features password-hash
|
||||
|
||||
minimal-versions:
|
||||
@@ -42,7 +43,7 @@ jobs:
|
||||
if: false
|
||||
uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master
|
||||
with:
|
||||
working-directory: ${{ github.workflow }}
|
||||
working-directory: ${{ github.workflow }}
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -59,5 +60,8 @@ jobs:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
- run: cargo test --no-default-features
|
||||
- run: cargo test
|
||||
- run: cargo test --all-features
|
||||
- run: cargo doc --no-default-features
|
||||
- run: cargo test --no-default-features --features kdf
|
||||
- run: cargo test --no-default-features --features mcf
|
||||
- run: cargo test --no-default-features --features phc
|
||||
- run: cargo test --all-features --release
|
||||
- run: cargo test --all-features --release
|
||||
|
||||
21
.github/workflows/yescrypt.yml
vendored
21
.github/workflows/yescrypt.yml
vendored
@@ -24,6 +24,27 @@ jobs:
|
||||
with:
|
||||
working-directory: ${{ github.workflow }}
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- 1.85.0 # MSRV
|
||||
- stable
|
||||
target:
|
||||
- thumbv7em-none-eabi
|
||||
- wasm32-unknown-unknown
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: RustCrypto/actions/cargo-cache@master
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
targets: ${{ matrix.target }}
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features kdf
|
||||
- run: cargo build --target ${{ matrix.target }} --no-default-features --features password-hash
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
|
||||
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -10,6 +10,7 @@ dependencies = [
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"hex-literal",
|
||||
"kdf",
|
||||
"password-hash",
|
||||
"rayon",
|
||||
"zeroize",
|
||||
@@ -28,6 +29,7 @@ dependencies = [
|
||||
"crypto-bigint",
|
||||
"digest",
|
||||
"hex-literal",
|
||||
"kdf",
|
||||
"password-hash",
|
||||
"rayon",
|
||||
"sha2",
|
||||
@@ -274,6 +276,12 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kdf"
|
||||
version = "0.1.0-pre.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4852c654c9650d06a4293146ead04bedcf29861dc0de1115ca1492b7a27c50aa"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.179"
|
||||
@@ -334,6 +342,7 @@ dependencies = [
|
||||
"digest",
|
||||
"hex-literal",
|
||||
"hmac",
|
||||
"kdf",
|
||||
"mcf",
|
||||
"password-hash",
|
||||
"sha1",
|
||||
@@ -434,6 +443,7 @@ name = "scrypt"
|
||||
version = "0.12.0-rc.8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"kdf",
|
||||
"mcf",
|
||||
"password-hash",
|
||||
"pbkdf2",
|
||||
@@ -580,6 +590,7 @@ version = "0.1.0-rc.3"
|
||||
dependencies = [
|
||||
"hex-literal",
|
||||
"hmac",
|
||||
"kdf",
|
||||
"mcf",
|
||||
"password-hash",
|
||||
"pbkdf2",
|
||||
|
||||
@@ -21,6 +21,7 @@ base64ct = "1.7"
|
||||
blake2 = { version = "0.11.0-rc.3", default-features = false }
|
||||
|
||||
# optional dependencies
|
||||
kdf = { version = "0.1.0-pre.1", optional = true }
|
||||
rayon = { version = "1.7", optional = true }
|
||||
password-hash = { version = "0.6.0-rc.9", optional = true, features = ["phc"] }
|
||||
zeroize = { version = "1", default-features = false, optional = true }
|
||||
@@ -35,6 +36,7 @@ hex-literal = "1"
|
||||
default = ["alloc", "getrandom", "password-hash"]
|
||||
alloc = ["password-hash?/alloc"]
|
||||
|
||||
kdf = ["alloc", "dep:kdf"]
|
||||
getrandom = ["password-hash/getrandom"]
|
||||
parallel = ["dep:rayon"]
|
||||
password-hash = ["dep:password-hash"]
|
||||
|
||||
@@ -60,6 +60,15 @@ pub enum Error {
|
||||
OutOfMemory,
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::B64Encoding(err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
@@ -90,6 +99,13 @@ impl From<base64ct::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl From<Error> for kdf::Error {
|
||||
fn from(_err: Error) -> kdf::Error {
|
||||
kdf::Error
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "password-hash")]
|
||||
impl From<Error> for password_hash::Error {
|
||||
fn from(err: Error) -> password_hash::Error {
|
||||
@@ -114,12 +130,3 @@ impl From<Error> for password_hash::Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::B64Encoding(err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,8 @@ pub use crate::{
|
||||
version::Version,
|
||||
};
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
pub use kdf::{self, Kdf, Pbkdf};
|
||||
#[cfg(feature = "password-hash")]
|
||||
pub use {
|
||||
crate::algorithm::{ARGON2D_IDENT, ARGON2I_IDENT, ARGON2ID_IDENT},
|
||||
@@ -608,6 +610,17 @@ impl<'key> Argon2<'key> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Kdf for Argon2<'_> {
|
||||
fn derive_key(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> kdf::Result<()> {
|
||||
self.hash_password_into(password, &salt, out)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Pbkdf for Argon2<'_> {}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "password-hash"))]
|
||||
impl CustomizedPasswordHasher<PasswordHash> for Argon2<'_> {
|
||||
type Params = Params;
|
||||
|
||||
@@ -18,6 +18,7 @@ digest = { version = "0.11.0-rc.4", default-features = false }
|
||||
crypto-bigint = { version = "0.7.0-rc.9", default-features = false, features = ["hybrid-array"] }
|
||||
|
||||
# optional dependencies
|
||||
kdf = { version = "0.1.0-pre.1", optional = true }
|
||||
password-hash = { version = "0.6.0-rc.9", optional = true, default-features = false, features = ["phc"] }
|
||||
rayon = { version = "1.7", optional = true }
|
||||
zeroize = { version = "1", default-features = false, optional = true }
|
||||
@@ -30,6 +31,7 @@ sha2 = "0.11.0-rc.3"
|
||||
default = ["alloc", "getrandom", "password-hash"]
|
||||
alloc = ["password-hash/alloc"]
|
||||
|
||||
kdf = ["alloc", "dep:kdf"]
|
||||
getrandom = ["password-hash/getrandom"]
|
||||
parallel = ["dep:rayon"]
|
||||
password-hash = ["dep:password-hash"]
|
||||
|
||||
@@ -28,6 +28,8 @@ pub enum Error {
|
||||
},
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
@@ -43,6 +45,13 @@ impl fmt::Display for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl From<Error> for kdf::Error {
|
||||
fn from(_err: Error) -> kdf::Error {
|
||||
kdf::Error
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "password-hash")]
|
||||
impl From<Error> for password_hash::Error {
|
||||
fn from(err: Error) -> password_hash::Error {
|
||||
@@ -56,5 +65,3 @@ impl From<Error> for password_hash::Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::error::Error for Error {}
|
||||
|
||||
@@ -65,6 +65,8 @@ use digest::array::Array;
|
||||
use digest::typenum::Unsigned;
|
||||
use digest::{Digest, FixedOutputReset};
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
pub use kdf::{self, Kdf, Pbkdf};
|
||||
#[cfg(all(feature = "alloc", feature = "password-hash"))]
|
||||
pub use password_hash::phc::Salt;
|
||||
|
||||
@@ -95,8 +97,9 @@ where
|
||||
pub secret: Option<&'key [u8]>,
|
||||
}
|
||||
|
||||
impl<'key, D: Digest + FixedOutputReset> Balloon<'key, D>
|
||||
impl<'key, D> Balloon<'key, D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
Array<u8, D::OutputSize>: ArrayDecoding,
|
||||
{
|
||||
/// Create a new Balloon context.
|
||||
@@ -111,10 +114,13 @@ where
|
||||
|
||||
/// Hash a password and associated parameters.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn hash(&self, pwd: &[u8], salt: &[u8]) -> Result<Array<u8, D::OutputSize>> {
|
||||
pub fn hash_password_to_array(
|
||||
&self,
|
||||
pwd: &[u8],
|
||||
salt: &[u8],
|
||||
) -> Result<Array<u8, D::OutputSize>> {
|
||||
let mut output = Array::default();
|
||||
self.hash_into(pwd, salt, &mut output)?;
|
||||
|
||||
self.hash_password_into(pwd, salt, &mut output)?;
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
@@ -122,13 +128,13 @@ where
|
||||
///
|
||||
/// The `output` has to have the same size as the hash output size: `D::OutputSize`.
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn hash_into(&self, pwd: &[u8], salt: &[u8], output: &mut [u8]) -> Result<()> {
|
||||
pub fn hash_password_into(&self, pwd: &[u8], salt: &[u8], output: &mut [u8]) -> Result<()> {
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
let mut memory = alloc::vec![Array::default(); self.params.s_cost.get() as usize];
|
||||
#[cfg(feature = "parallel")]
|
||||
let mut memory = alloc::vec![Array::default(); (self.params.s_cost.get() * self.params.p_cost.get()) as usize];
|
||||
|
||||
self.hash_into_with_memory(pwd, salt, &mut memory, output)?;
|
||||
self.hash_password_into_with_memory(pwd, salt, &mut memory, output)?;
|
||||
#[cfg(feature = "zeroize")]
|
||||
memory.iter_mut().for_each(|block| block.zeroize());
|
||||
Ok(())
|
||||
@@ -139,19 +145,19 @@ where
|
||||
/// This method takes an explicit `memory_blocks` parameter which allows
|
||||
/// the caller to provide the backing storage for the algorithm's state:
|
||||
///
|
||||
/// - Users with the `alloc` feature enabled can use [`Balloon::hash`]
|
||||
/// - Users with the `alloc` feature enabled can use [`Balloon::hash_password`]
|
||||
/// to have it allocated for them.
|
||||
/// - `no_std` users on "heapless" targets can use an array of the [`Array`] type
|
||||
/// to stack allocate this buffer. It needs a minimum size of `s_cost` or `s_cost * p_cost`
|
||||
/// with the `parallel` crate feature enabled.
|
||||
pub fn hash_with_memory(
|
||||
pub fn hash_password_with_memory(
|
||||
&self,
|
||||
pwd: &[u8],
|
||||
salt: &[u8],
|
||||
memory_blocks: &mut [Array<u8, D::OutputSize>],
|
||||
) -> Result<Array<u8, D::OutputSize>> {
|
||||
let mut output = Array::default();
|
||||
self.hash_into_with_memory(pwd, salt, memory_blocks, &mut output)?;
|
||||
self.hash_password_into_with_memory(pwd, salt, memory_blocks, &mut output)?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
@@ -160,8 +166,8 @@ where
|
||||
///
|
||||
/// The `output` has to have the same size as the hash output size: `D::OutputSize`.
|
||||
///
|
||||
/// See [`Balloon::hash_with_memory`] for more details.
|
||||
pub fn hash_into_with_memory(
|
||||
/// See [`Balloon::hash_password_with_memory`] for more details.
|
||||
pub fn hash_password_into_with_memory(
|
||||
&self,
|
||||
pwd: &[u8],
|
||||
salt: &[u8],
|
||||
@@ -192,6 +198,26 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl<D> Kdf for Balloon<'_, D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
Array<u8, D::OutputSize>: ArrayDecoding,
|
||||
{
|
||||
fn derive_key(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> kdf::Result<()> {
|
||||
self.hash_password_into(password, &salt, out)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl<D> Pbkdf for Balloon<'_, D>
|
||||
where
|
||||
D: Digest + FixedOutputReset,
|
||||
Array<u8, D::OutputSize>: ArrayDecoding,
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "alloc", feature = "password-hash"))]
|
||||
impl<D> CustomizedPasswordHasher<PasswordHash> for Balloon<'_, D>
|
||||
where
|
||||
@@ -235,7 +261,7 @@ where
|
||||
salt: &[u8],
|
||||
) -> password_hash::Result<PasswordHash> {
|
||||
let salt = Salt::new(salt)?;
|
||||
let hash = self.hash(password, &salt)?;
|
||||
let hash = self.hash_password_to_array(password, &salt)?;
|
||||
let output = Output::new(&hash)?;
|
||||
|
||||
Ok(PasswordHash {
|
||||
|
||||
@@ -64,7 +64,7 @@ fn test_vectors() {
|
||||
|
||||
assert_eq!(
|
||||
balloon
|
||||
.hash_with_memory(test_vector.password, test_vector.salt, &mut memory)
|
||||
.hash_password_with_memory(test_vector.password, test_vector.salt, &mut memory)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
test_vector.output,
|
||||
|
||||
@@ -100,7 +100,7 @@ fn test_vectors() {
|
||||
|
||||
assert_eq!(
|
||||
balloon
|
||||
.hash_with_memory(test_vector.password, test_vector.salt, &mut memory)
|
||||
.hash_password_with_memory(test_vector.password, test_vector.salt, &mut memory)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
test_vector.output,
|
||||
|
||||
@@ -18,6 +18,7 @@ digest = { version = "0.11.0-rc.4", features = ["mac"] }
|
||||
|
||||
# optional dependencies
|
||||
hmac = { version = "0.13.0-rc.3", optional = true, default-features = false }
|
||||
kdf = { version = "0.1.0-pre.1", optional = true }
|
||||
mcf = { version = "0.6.0-rc.3", optional = true, default-features = false, features = ["base64"] }
|
||||
password-hash = { version = "0.6.0-rc.9", default-features = false, optional = true }
|
||||
sha1 = { version = "0.11.0-rc.3", default-features = false, optional = true }
|
||||
@@ -34,10 +35,14 @@ belt-hash = "0.2.0-rc.3"
|
||||
[features]
|
||||
default = ["hmac"]
|
||||
alloc = ["mcf?/alloc", "password-hash?/alloc"]
|
||||
|
||||
kdf = ["sha2", "dep:kdf"]
|
||||
getrandom = ["password-hash/getrandom"]
|
||||
mcf = ["hmac", "sha2", "dep:password-hash", "dep:mcf"]
|
||||
phc = ["hmac", "password-hash/phc", "sha2"]
|
||||
mcf = ["sha2", "password-hash", "dep:mcf"]
|
||||
phc = ["password-hash/phc", "sha2"]
|
||||
rand_core = ["password-hash/rand_core"]
|
||||
sha1 = ["hmac", "dep:sha1"]
|
||||
sha2 = ["hmac", "dep:sha2"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use core::{
|
||||
fmt::{self, Display},
|
||||
str::FromStr,
|
||||
};
|
||||
use password_hash::Error;
|
||||
use core::fmt::{self, Display};
|
||||
|
||||
#[cfg(feature = "phc")]
|
||||
use password_hash::phc::Ident;
|
||||
#[cfg(feature = "password-hash")]
|
||||
use {core::str::FromStr, password_hash::Error};
|
||||
|
||||
/// PBKDF2 variants.
|
||||
///
|
||||
@@ -13,14 +11,16 @@ use password_hash::phc::Ident;
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
#[non_exhaustive]
|
||||
pub enum Algorithm {
|
||||
/// PBKDF2 SHA1
|
||||
/// PBKDF2-HMAC-SHA1 a.k.a. `$pbkdf2`
|
||||
#[cfg(feature = "sha1")]
|
||||
Pbkdf2Sha1,
|
||||
|
||||
/// PBKDF2 SHA-256
|
||||
/// PBKDF2-HMAC-SHA-256 a.k.a. `$pbkdf2-sha256`
|
||||
#[cfg(feature = "sha2")]
|
||||
Pbkdf2Sha256,
|
||||
|
||||
/// PBKDF2 SHA-512
|
||||
/// PBKDF2-HMAC-SHA-512 a.k.a. `$pbkdf2-sha512`
|
||||
#[cfg(feature = "sha2")]
|
||||
Pbkdf2Sha512,
|
||||
}
|
||||
|
||||
@@ -30,9 +30,11 @@ impl Algorithm {
|
||||
pub const PBKDF2_SHA1_ID: &'static str = "pbkdf2";
|
||||
|
||||
/// PBKDF2 (SHA-256) algorithm identifier
|
||||
#[cfg(feature = "sha2")]
|
||||
pub const PBKDF2_SHA256_ID: &'static str = "pbkdf2-sha256";
|
||||
|
||||
/// PBKDF2 (SHA-512) algorithm identifier
|
||||
#[cfg(feature = "sha2")]
|
||||
pub const PBKDF2_SHA512_ID: &'static str = "pbkdf2-sha512";
|
||||
|
||||
/// PBKDF2 (SHA-1) algorithm identifier
|
||||
@@ -53,9 +55,11 @@ impl Algorithm {
|
||||
/// > internal hash function of HMAC-SHA-256.
|
||||
///
|
||||
/// [OWASP cheat sheet]: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
|
||||
#[cfg(feature = "sha2")]
|
||||
pub const RECOMMENDED: Self = Self::Pbkdf2Sha256;
|
||||
|
||||
/// Parse an [`Algorithm`] from the provided string.
|
||||
#[cfg(feature = "password-hash")]
|
||||
pub fn new(id: impl AsRef<str>) -> password_hash::Result<Self> {
|
||||
id.as_ref().parse()
|
||||
}
|
||||
@@ -65,7 +69,9 @@ impl Algorithm {
|
||||
match self {
|
||||
#[cfg(feature = "sha1")]
|
||||
Algorithm::Pbkdf2Sha1 => Self::PBKDF2_SHA1_ID,
|
||||
#[cfg(feature = "sha2")]
|
||||
Algorithm::Pbkdf2Sha256 => Self::PBKDF2_SHA256_ID,
|
||||
#[cfg(feature = "sha2")]
|
||||
Algorithm::Pbkdf2Sha512 => Self::PBKDF2_SHA512_ID,
|
||||
}
|
||||
}
|
||||
@@ -77,6 +83,7 @@ impl AsRef<str> for Algorithm {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "sha2")]
|
||||
impl Default for Algorithm {
|
||||
fn default() -> Self {
|
||||
Self::RECOMMENDED
|
||||
@@ -89,6 +96,7 @@ impl Display for Algorithm {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "password-hash")]
|
||||
impl FromStr for Algorithm {
|
||||
type Err = Error;
|
||||
|
||||
@@ -109,6 +117,7 @@ impl From<Algorithm> for Ident {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "password-hash")]
|
||||
impl<'a> TryFrom<&'a str> for Algorithm {
|
||||
type Error = Error;
|
||||
|
||||
@@ -116,7 +125,9 @@ impl<'a> TryFrom<&'a str> for Algorithm {
|
||||
match name {
|
||||
#[cfg(feature = "sha1")]
|
||||
Self::PBKDF2_SHA1_ID => Ok(Algorithm::Pbkdf2Sha1),
|
||||
#[cfg(feature = "sha2")]
|
||||
Self::PBKDF2_SHA256_ID => Ok(Algorithm::Pbkdf2Sha256),
|
||||
#[cfg(feature = "sha2")]
|
||||
Self::PBKDF2_SHA512_ID => Ok(Algorithm::Pbkdf2Sha512),
|
||||
_ => Err(Error::Algorithm),
|
||||
}
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
//!
|
||||
//! This API operates directly on byte slices:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[cfg(feature = "hmac")] {
|
||||
#![cfg_attr(feature = "sha2", doc = "```")]
|
||||
#![cfg_attr(not(feature = "sha2"), doc = "```ignore")]
|
||||
//! // NOTE: example requires `getrandom` feature is enabled
|
||||
//!
|
||||
//! use hex_literal::hex;
|
||||
//! use pbkdf2::{pbkdf2_hmac, pbkdf2_hmac_array};
|
||||
//! use sha2::Sha256;
|
||||
//! use pbkdf2::{pbkdf2_hmac, pbkdf2_hmac_array, sha2::Sha256};
|
||||
//!
|
||||
//! let password = b"password";
|
||||
//! let salt = b"salt";
|
||||
@@ -44,27 +45,14 @@
|
||||
//!
|
||||
//! let key2 = pbkdf2_hmac_array::<Sha256, 20>(password, salt, n);
|
||||
//! assert_eq!(key2, expected);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! If you want to use a different PRF, then you can use [`pbkdf2`] and [`pbkdf2_array`] functions.
|
||||
//!
|
||||
//! This crates also provides the high-level password-hashing API through
|
||||
//! the [`Pbkdf2`] struct and traits defined in the
|
||||
//! [`password-hash`][password_hash] crate.
|
||||
//!
|
||||
//! Add the following to your crate's `Cargo.toml` to import it:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! pbkdf2 = { version = "0.12", features = ["password-hash"] }
|
||||
//! rand_core = { version = "0.6", features = ["std"] }
|
||||
//! ```
|
||||
//!
|
||||
//! ## PHC string API
|
||||
//!
|
||||
//! This crate can produce and verify password hash strings encoded in the Password Hashing
|
||||
//! Competition (PHC) string format.
|
||||
//! Competition (PHC) string format using the [`Pbkdf2`] struct.
|
||||
//!
|
||||
//! The following example demonstrates the high-level password hashing API:
|
||||
//!
|
||||
@@ -98,12 +86,12 @@ pub mod mcf;
|
||||
#[cfg(feature = "phc")]
|
||||
pub mod phc;
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "sha1", feature = "sha2"))]
|
||||
mod algorithm;
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "sha1", feature = "sha2"))]
|
||||
mod params;
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "sha1", feature = "sha2"))]
|
||||
pub use crate::{algorithm::Algorithm, params::Params};
|
||||
#[cfg(feature = "hmac")]
|
||||
pub use hmac;
|
||||
@@ -111,11 +99,17 @@ pub use hmac;
|
||||
pub use password_hash;
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
pub use password_hash::{PasswordHasher, PasswordVerifier};
|
||||
#[cfg(feature = "sha1")]
|
||||
pub use sha1;
|
||||
#[cfg(feature = "sha2")]
|
||||
pub use sha2;
|
||||
|
||||
use digest::{FixedOutput, InvalidLength, KeyInit, Update, typenum::Unsigned};
|
||||
|
||||
#[cfg(feature = "hmac")]
|
||||
use hmac::EagerHash;
|
||||
#[cfg(feature = "kdf")]
|
||||
use kdf::{Kdf, Pbkdf};
|
||||
|
||||
#[inline(always)]
|
||||
fn xor(res: &mut [u8], salt: &[u8]) {
|
||||
@@ -153,11 +147,10 @@ where
|
||||
|
||||
/// Generic implementation of PBKDF2 algorithm which accepts an arbitrary keyed PRF.
|
||||
///
|
||||
/// ```
|
||||
#[cfg_attr(feature = "sha2", doc = "```")]
|
||||
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
|
||||
/// use hex_literal::hex;
|
||||
/// use pbkdf2::pbkdf2;
|
||||
/// use hmac::Hmac;
|
||||
/// use sha2::Sha256;
|
||||
/// use pbkdf2::{pbkdf2, hmac::Hmac, sha2::Sha256};
|
||||
///
|
||||
/// let mut buf = [0u8; 20];
|
||||
/// pbkdf2::<Hmac<Sha256>>(b"password", b"salt", 600_000, &mut buf)
|
||||
@@ -186,11 +179,10 @@ where
|
||||
|
||||
/// A variant of the [`pbkdf2`] function which returns an array instead of filling an input slice.
|
||||
///
|
||||
/// ```
|
||||
#[cfg_attr(feature = "sha2", doc = "```")]
|
||||
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
|
||||
/// use hex_literal::hex;
|
||||
/// use pbkdf2::pbkdf2_array;
|
||||
/// use hmac::Hmac;
|
||||
/// use sha2::Sha256;
|
||||
/// use pbkdf2::{pbkdf2_array, hmac::Hmac, sha2::Sha256};
|
||||
///
|
||||
/// let res = pbkdf2_array::<Hmac<Sha256>, 20>(b"password", b"salt", 600_000)
|
||||
/// .expect("HMAC can be initialized with any key length");
|
||||
@@ -213,10 +205,10 @@ where
|
||||
///
|
||||
/// It's generic over (eager) hash functions.
|
||||
///
|
||||
/// ```
|
||||
#[cfg_attr(feature = "sha2", doc = "```")]
|
||||
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
|
||||
/// use hex_literal::hex;
|
||||
/// use pbkdf2::pbkdf2_hmac;
|
||||
/// use sha2::Sha256;
|
||||
/// use pbkdf2::{pbkdf2_hmac, sha2::Sha256};
|
||||
///
|
||||
/// let mut buf = [0u8; 20];
|
||||
/// pbkdf2_hmac::<Sha256>(b"password", b"salt", 600_000, &mut buf);
|
||||
@@ -227,17 +219,17 @@ pub fn pbkdf2_hmac<D>(password: &[u8], salt: &[u8], rounds: u32, res: &mut [u8])
|
||||
where
|
||||
D: EagerHash<Core: Sync>,
|
||||
{
|
||||
crate::pbkdf2::<hmac::Hmac<D>>(password, salt, rounds, res)
|
||||
pbkdf2::<hmac::Hmac<D>>(password, salt, rounds, res)
|
||||
.expect("HMAC can be initialized with any key length");
|
||||
}
|
||||
|
||||
/// A variant of the [`pbkdf2_hmac`] function which returns an array
|
||||
/// instead of filling an input slice.
|
||||
///
|
||||
/// ```
|
||||
#[cfg_attr(feature = "sha2", doc = "```")]
|
||||
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
|
||||
/// use hex_literal::hex;
|
||||
/// use pbkdf2::pbkdf2_hmac_array;
|
||||
/// use sha2::Sha256;
|
||||
/// use pbkdf2::{pbkdf2_hmac_array, sha2::Sha256};
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// pbkdf2_hmac_array::<Sha256, 20>(b"password", b"salt", 600_000),
|
||||
@@ -254,14 +246,50 @@ where
|
||||
buf
|
||||
}
|
||||
|
||||
/// API for using [`pbkdf2_hmac`] which supports the [`Algorithm`] and [`Params`] types and with
|
||||
/// it runtime selection of which algorithm to use.
|
||||
///
|
||||
#[cfg_attr(feature = "sha2", doc = "```")]
|
||||
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
|
||||
/// use hex_literal::hex;
|
||||
/// use pbkdf2::pbkdf2_hmac_with_params;
|
||||
///
|
||||
/// let algorithm = pbkdf2::Algorithm::Pbkdf2Sha256;
|
||||
/// let params = pbkdf2::Params::default();
|
||||
///
|
||||
/// let mut buf = [0u8; 32];
|
||||
/// pbkdf2_hmac_with_params(b"password", b"salt", algorithm, params, &mut buf);
|
||||
/// assert_eq!(buf, hex!("669cfe52482116fda1aa2cbe409b2f56c8e4563752b7a28f6eaab614ee005178"));
|
||||
/// ```
|
||||
#[cfg(any(feature = "sha1", feature = "sha2"))]
|
||||
pub fn pbkdf2_hmac_with_params(
|
||||
password: &[u8],
|
||||
salt: &[u8],
|
||||
algorithm: Algorithm,
|
||||
params: Params,
|
||||
out: &mut [u8],
|
||||
) {
|
||||
let f = match algorithm {
|
||||
#[cfg(feature = "sha1")]
|
||||
Algorithm::Pbkdf2Sha1 => pbkdf2_hmac::<sha1::Sha1>,
|
||||
#[cfg(feature = "sha2")]
|
||||
Algorithm::Pbkdf2Sha256 => pbkdf2_hmac::<sha2::Sha256>,
|
||||
#[cfg(feature = "sha2")]
|
||||
Algorithm::Pbkdf2Sha512 => pbkdf2_hmac::<sha2::Sha512>,
|
||||
};
|
||||
|
||||
f(password, salt, params.rounds(), out);
|
||||
}
|
||||
|
||||
/// PBKDF2 type for use with the [`PasswordHasher`] and [`PasswordVerifier`] traits, which
|
||||
/// implements support for password hash strings.
|
||||
///
|
||||
/// Supports the following password hash string formats, gated under the following crate features:
|
||||
/// - `mcf`: support for the Modular Crypt Format
|
||||
/// - `phc`: support for the Password Hashing Competition string format
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
#[cfg(any(feature = "sha1", feature = "sha2"))]
|
||||
#[cfg_attr(feature = "sha2", derive(Default))]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Pbkdf2 {
|
||||
/// Algorithm to use
|
||||
algorithm: Algorithm,
|
||||
@@ -270,7 +298,7 @@ pub struct Pbkdf2 {
|
||||
params: Params,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(feature = "sha2")]
|
||||
impl Pbkdf2 {
|
||||
/// PBKDF2 configured with SHA-256 as the default.
|
||||
pub const SHA256: Self = Self::new(Algorithm::Pbkdf2Sha256, Params::RECOMMENDED);
|
||||
@@ -279,15 +307,20 @@ impl Pbkdf2 {
|
||||
pub const SHA512: Self = Self::new(Algorithm::Pbkdf2Sha512, Params::RECOMMENDED);
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "sha1", feature = "sha2"))]
|
||||
impl Pbkdf2 {
|
||||
/// Initialize [`Pbkdf2`] with default parameters.
|
||||
pub const fn new(algorithm: Algorithm, params: Params) -> Self {
|
||||
Self { algorithm, params }
|
||||
}
|
||||
|
||||
/// Hash password into the given output buffer using the configured params.
|
||||
pub fn hash_password_into(&self, password: &[u8], salt: &[u8], out: &mut [u8]) {
|
||||
pbkdf2_hmac_with_params(password, salt, self.algorithm, self.params, out);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "sha1", feature = "sha2"))]
|
||||
impl From<Algorithm> for Pbkdf2 {
|
||||
fn from(algorithm: Algorithm) -> Self {
|
||||
Self {
|
||||
@@ -297,7 +330,7 @@ impl From<Algorithm> for Pbkdf2 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(feature = "sha2")]
|
||||
impl From<Params> for Pbkdf2 {
|
||||
fn from(params: Params) -> Self {
|
||||
Self {
|
||||
@@ -306,3 +339,14 @@ impl From<Params> for Pbkdf2 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Kdf for Pbkdf2 {
|
||||
fn derive_key(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> kdf::Result<()> {
|
||||
self.hash_password_into(password, salt, out);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Pbkdf for Pbkdf2 {}
|
||||
|
||||
@@ -12,15 +12,12 @@ pub use mcf::PasswordHashRef;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use mcf::PasswordHash;
|
||||
|
||||
use crate::{Algorithm, Params, Pbkdf2, pbkdf2_hmac};
|
||||
use crate::{Algorithm, Params, Pbkdf2, pbkdf2_hmac_with_params};
|
||||
use mcf::Base64;
|
||||
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;
|
||||
|
||||
@@ -50,14 +47,7 @@ impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
.get_mut(..params.output_len())
|
||||
.ok_or(Error::OutputSize)?;
|
||||
|
||||
let f = match algorithm {
|
||||
#[cfg(feature = "sha1")]
|
||||
Algorithm::Pbkdf2Sha1 => pbkdf2_hmac::<Sha1>,
|
||||
Algorithm::Pbkdf2Sha256 => pbkdf2_hmac::<Sha256>,
|
||||
Algorithm::Pbkdf2Sha512 => pbkdf2_hmac::<Sha512>,
|
||||
};
|
||||
|
||||
f(password, salt, params.rounds(), out);
|
||||
pbkdf2_hmac_with_params(password, salt, algorithm, params, out);
|
||||
|
||||
let mut mcf_hash = PasswordHash::from_id(algorithm.to_str()).expect("should have valid ID");
|
||||
mcf_hash
|
||||
@@ -118,14 +108,7 @@ impl PasswordVerifier<PasswordHashRef> for Pbkdf2 {
|
||||
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")]
|
||||
Algorithm::Pbkdf2Sha1 => pbkdf2_hmac::<Sha1>,
|
||||
Algorithm::Pbkdf2Sha256 => pbkdf2_hmac::<Sha256>,
|
||||
Algorithm::Pbkdf2Sha512 => pbkdf2_hmac::<Sha512>,
|
||||
};
|
||||
|
||||
f(password, &salt, params.rounds(), out);
|
||||
pbkdf2_hmac_with_params(password, salt, algorithm, params, out);
|
||||
|
||||
// TODO(tarcieri): use `subtle` or `ctutils` for comparison
|
||||
if out
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use core::{
|
||||
fmt::{self, Display},
|
||||
str::FromStr,
|
||||
};
|
||||
use password_hash::{Error, Result};
|
||||
use core::fmt::{self, Display};
|
||||
|
||||
#[cfg(feature = "phc")]
|
||||
use password_hash::phc::{self, Decimal, ParamsString};
|
||||
#[cfg(feature = "password-hash")]
|
||||
use {
|
||||
core::str::FromStr,
|
||||
password_hash::{Error, Result},
|
||||
};
|
||||
|
||||
/// PBKDF2 params
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
@@ -49,11 +50,13 @@ impl Params {
|
||||
};
|
||||
|
||||
/// Create new params with the given number of rounds.
|
||||
#[cfg(feature = "password-hash")]
|
||||
pub const fn new(rounds: u32) -> Result<Self> {
|
||||
Self::new_with_output_len(rounds, Self::RECOMMENDED_OUTPUT_LENGTH)
|
||||
}
|
||||
|
||||
/// Create new params with a customized output length.
|
||||
#[cfg(feature = "password-hash")]
|
||||
pub const fn new_with_output_len(rounds: u32, output_len: usize) -> Result<Self> {
|
||||
if rounds < Self::MIN_ROUNDS
|
||||
|| output_len < Self::MIN_OUTPUT_LENGTH
|
||||
@@ -88,6 +91,7 @@ impl Display for Params {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "password-hash")]
|
||||
impl FromStr for Params {
|
||||
type Err = Error;
|
||||
|
||||
@@ -98,6 +102,7 @@ impl FromStr for Params {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "password-hash")]
|
||||
impl TryFrom<u32> for Params {
|
||||
type Error = Error;
|
||||
|
||||
|
||||
@@ -2,15 +2,11 @@
|
||||
|
||||
pub use password_hash::phc::PasswordHash;
|
||||
|
||||
use crate::{Algorithm, Params, Pbkdf2, pbkdf2_hmac};
|
||||
use crate::{Algorithm, Params, Pbkdf2, pbkdf2_hmac_with_params};
|
||||
use password_hash::{
|
||||
CustomizedPasswordHasher, Error, PasswordHasher, Result,
|
||||
phc::{Output, Salt},
|
||||
};
|
||||
use sha2::{Sha256, Sha512};
|
||||
|
||||
#[cfg(feature = "sha1")]
|
||||
use sha1::Sha1;
|
||||
|
||||
impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
type Params = Params;
|
||||
@@ -40,14 +36,7 @@ impl CustomizedPasswordHasher<PasswordHash> for Pbkdf2 {
|
||||
.get_mut(..params.output_len())
|
||||
.ok_or(Error::OutputSize)?;
|
||||
|
||||
let f = match algorithm {
|
||||
#[cfg(feature = "sha1")]
|
||||
Algorithm::Pbkdf2Sha1 => pbkdf2_hmac::<Sha1>,
|
||||
Algorithm::Pbkdf2Sha256 => pbkdf2_hmac::<Sha256>,
|
||||
Algorithm::Pbkdf2Sha512 => pbkdf2_hmac::<Sha512>,
|
||||
};
|
||||
|
||||
f(password, &salt, params.rounds(), out);
|
||||
pbkdf2_hmac_with_params(password, salt.as_ref(), algorithm, params, out);
|
||||
let output = Output::new(out)?;
|
||||
|
||||
Ok(PasswordHash {
|
||||
@@ -84,8 +73,8 @@ mod tests {
|
||||
/// Input:
|
||||
/// - P = "passwordPASSWORDpassword" (24 octets)
|
||||
/// - S = "saltSALTsaltSALTsaltSALTsaltSALTsalt" (36 octets)
|
||||
/// c = 4096
|
||||
/// dkLen = 40
|
||||
/// - c = 4096
|
||||
/// - dkLen = 40
|
||||
#[test]
|
||||
fn hash_with_default_algorithm() {
|
||||
let params = Params::new_with_output_len(4096, 40).unwrap();
|
||||
|
||||
@@ -21,6 +21,7 @@ sha2 = { version = "0.11.0-rc.3", default-features = false }
|
||||
rayon = { version = "1.11", optional = true }
|
||||
|
||||
# optional dependencies
|
||||
kdf = { version = "0.1.0-pre.1", optional = true }
|
||||
mcf = { version = "0.6.0-rc.2", optional = true }
|
||||
password-hash = { version = "0.6.0-rc.9", optional = true, default-features = false }
|
||||
subtle = { version = "2", optional = true, default-features = false }
|
||||
@@ -29,7 +30,8 @@ subtle = { version = "2", optional = true, default-features = false }
|
||||
alloc = ["password-hash?/alloc"]
|
||||
|
||||
getrandom = ["password-hash", "password-hash/getrandom"]
|
||||
mcf = ["alloc", "password-hash", "dep:mcf", "dep:subtle"]
|
||||
kdf = ["alloc", "dep:kdf"]
|
||||
mcf = ["alloc", "phc", "dep:mcf", "dep:subtle"]
|
||||
phc = ["password-hash/phc"]
|
||||
rand_core = ["password-hash/rand_core"]
|
||||
parallel = ["dep:rayon"]
|
||||
|
||||
@@ -16,6 +16,13 @@ impl fmt::Display for InvalidOutputLen {
|
||||
|
||||
impl core::error::Error for InvalidOutputLen {}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl From<InvalidOutputLen> for kdf::Error {
|
||||
fn from(_err: InvalidOutputLen) -> kdf::Error {
|
||||
kdf::Error
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidParams {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("invalid scrypt parameters")
|
||||
|
||||
@@ -62,6 +62,8 @@ pub mod phc;
|
||||
|
||||
pub use crate::params::Params;
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
pub use kdf::{self, Kdf, Pbkdf};
|
||||
#[cfg(feature = "password-hash")]
|
||||
pub use password_hash;
|
||||
|
||||
@@ -146,14 +148,14 @@ fn romix_parallel(nr128: usize, r128: usize, n: usize, b: &mut [u8]) {
|
||||
/// This type holds the default parameters to use when computing password hashes.
|
||||
///
|
||||
/// See the toplevel documentation for a code example.
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct Scrypt {
|
||||
/// Default parameters to use.
|
||||
params: Params,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
|
||||
impl Scrypt {
|
||||
/// Initialize [`Scrypt`] with default parameters.
|
||||
pub const fn new() -> Self {
|
||||
@@ -168,9 +170,20 @@ impl Scrypt {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "mcf", feature = "phc"))]
|
||||
#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
|
||||
impl From<Params> for Scrypt {
|
||||
fn from(params: Params) -> Self {
|
||||
Self::new_with_params(params)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Kdf for Scrypt {
|
||||
fn derive_key(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> kdf::Result<()> {
|
||||
scrypt(password, salt, &self.params, out)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Pbkdf for Scrypt {}
|
||||
|
||||
@@ -6,10 +6,7 @@ use {
|
||||
fmt::{self, Display},
|
||||
str::FromStr,
|
||||
},
|
||||
password_hash::{
|
||||
Error,
|
||||
phc::{Decimal, Output, ParamsString, PasswordHash},
|
||||
},
|
||||
password_hash::{Error, phc},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "phc", doc))]
|
||||
@@ -125,7 +122,7 @@ impl Params {
|
||||
p: u32,
|
||||
len: usize,
|
||||
) -> Result<Params, InvalidParams> {
|
||||
if !(Output::MIN_LENGTH..=Output::MAX_LENGTH).contains(&len) {
|
||||
if !(phc::Output::MIN_LENGTH..=phc::Output::MAX_LENGTH).contains(&len) {
|
||||
return Err(InvalidParams);
|
||||
}
|
||||
|
||||
@@ -179,7 +176,9 @@ impl Default for Params {
|
||||
#[cfg(feature = "phc")]
|
||||
impl Display for Params {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
ParamsString::try_from(self).map_err(|_| fmt::Error)?.fmt(f)
|
||||
phc::ParamsString::try_from(self)
|
||||
.map_err(|_| fmt::Error)?
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,16 +187,16 @@ impl FromStr for Params {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> password_hash::Result<Self> {
|
||||
let params_string = ParamsString::from_str(s).map_err(|_| Error::ParamsInvalid)?;
|
||||
let params_string = phc::ParamsString::from_str(s).map_err(|_| Error::ParamsInvalid)?;
|
||||
Self::try_from(¶ms_string)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "phc")]
|
||||
impl TryFrom<&ParamsString> for Params {
|
||||
impl TryFrom<&phc::ParamsString> for Params {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(params: &ParamsString) -> password_hash::Result<Self> {
|
||||
fn try_from(params: &phc::ParamsString) -> password_hash::Result<Self> {
|
||||
let mut log_n = Self::RECOMMENDED_LOG_N;
|
||||
let mut r = Self::RECOMMENDED_R;
|
||||
let mut p = Self::RECOMMENDED_P;
|
||||
@@ -230,10 +229,10 @@ impl TryFrom<&ParamsString> for Params {
|
||||
}
|
||||
|
||||
#[cfg(feature = "phc")]
|
||||
impl TryFrom<&PasswordHash> for Params {
|
||||
impl TryFrom<&phc::PasswordHash> for Params {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(hash: &PasswordHash) -> password_hash::Result<Self> {
|
||||
fn try_from(hash: &phc::PasswordHash) -> password_hash::Result<Self> {
|
||||
if hash.version.is_some() {
|
||||
return Err(Error::Version);
|
||||
}
|
||||
@@ -251,23 +250,23 @@ impl TryFrom<&PasswordHash> for Params {
|
||||
}
|
||||
|
||||
#[cfg(feature = "phc")]
|
||||
impl TryFrom<Params> for ParamsString {
|
||||
impl TryFrom<Params> for phc::ParamsString {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(params: Params) -> Result<ParamsString, Error> {
|
||||
fn try_from(params: Params) -> Result<phc::ParamsString, Error> {
|
||||
Self::try_from(¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "phc")]
|
||||
impl TryFrom<&Params> for ParamsString {
|
||||
impl TryFrom<&Params> for phc::ParamsString {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(input: &Params) -> Result<ParamsString, Error> {
|
||||
let mut output = ParamsString::new();
|
||||
fn try_from(input: &Params) -> Result<phc::ParamsString, Error> {
|
||||
let mut output = phc::ParamsString::new();
|
||||
|
||||
for (name, value) in [
|
||||
("ln", input.log_n as Decimal),
|
||||
("ln", input.log_n as phc::Decimal),
|
||||
("r", input.r),
|
||||
("p", input.p),
|
||||
] {
|
||||
|
||||
@@ -21,6 +21,7 @@ sha2 = { version = "0.11.0-rc.3", default-features = false }
|
||||
subtle = { version = "2", default-features = false }
|
||||
|
||||
# optional dependencies
|
||||
kdf = { version = "0.1.0-pre.1", optional = true }
|
||||
mcf = { version = "0.6.0-rc.2", optional = true, default-features = false, features = ["alloc", "base64"] }
|
||||
password-hash = { version = "0.6.0-rc.9", optional = true, default-features = false }
|
||||
|
||||
@@ -30,6 +31,7 @@ hex-literal = "1"
|
||||
[features]
|
||||
default = ["getrandom"]
|
||||
getrandom = ["password-hash", "password-hash/getrandom"]
|
||||
kdf = ["dep:kdf"]
|
||||
rand_core = ["password-hash/rand_core"]
|
||||
password-hash = ["dep:mcf", "dep:password-hash"]
|
||||
|
||||
|
||||
@@ -37,6 +37,13 @@ impl From<TryFromIntError> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl From<Error> for kdf::Error {
|
||||
fn from(_: Error) -> Self {
|
||||
kdf::Error
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "password-hash")]
|
||||
impl From<Error> for password_hash::Error {
|
||||
fn from(err: Error) -> Self {
|
||||
|
||||
@@ -81,9 +81,11 @@ pub use crate::{
|
||||
params::Params,
|
||||
};
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
pub use kdf::{self, Kdf, Pbkdf};
|
||||
#[cfg(feature = "password-hash")]
|
||||
pub use {
|
||||
crate::mcf::{PasswordHash, PasswordHashRef, Yescrypt},
|
||||
crate::mcf::{PasswordHash, PasswordHashRef},
|
||||
password_hash::{self, CustomizedPasswordHasher, PasswordHasher, PasswordVerifier},
|
||||
};
|
||||
|
||||
@@ -217,3 +219,41 @@ fn yescrypt_body(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// yescrypt password hashing type which can produce and verify strings in Modular Crypt Format
|
||||
/// (MCF) which begin with `$y$`
|
||||
///
|
||||
/// This type impls traits from the [`password-hash`][`password_hash`] crate, notably the
|
||||
/// [`PasswordHasher`], [`PasswordVerifier`], and [`CustomizedPasswordHasher`] traits.
|
||||
///
|
||||
/// See the toplevel documentation for a code example.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Yescrypt {
|
||||
/// Default parameters to use when hashing passwords.
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl Yescrypt {
|
||||
/// Hash password into the given output buffer using the configured params.
|
||||
pub fn hash_password_into(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> Result<()> {
|
||||
yescrypt(password, salt, &self.params, out)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Params> for Yescrypt {
|
||||
fn from(params: Params) -> Self {
|
||||
Self { params }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Kdf for Yescrypt {
|
||||
fn derive_key(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> kdf::Result<()> {
|
||||
self.hash_password_into(password, salt, out)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kdf")]
|
||||
impl Pbkdf for Yescrypt {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
pub use mcf::{PasswordHash, PasswordHashRef};
|
||||
|
||||
use crate::{Params, yescrypt};
|
||||
use crate::{Params, Yescrypt, yescrypt};
|
||||
use alloc::vec;
|
||||
use mcf::Base64;
|
||||
use password_hash::{
|
||||
@@ -16,25 +16,6 @@ const YESCRYPT_MCF_ID: &str = "y";
|
||||
/// Base64 variant used by yescrypt.
|
||||
const YESCRYPT_BASE64: Base64 = Base64::Crypt;
|
||||
|
||||
/// yescrypt password hashing type which can produce and verify strings in Modular Crypt Format
|
||||
/// (MCF) which begin with `$y$`
|
||||
///
|
||||
/// This type impls traits from the [`password-hash`][`password_hash`] crate, notably the
|
||||
/// [`PasswordHasher`], [`PasswordVerifier`], and [`CustomizedPasswordHasher`] traits.
|
||||
///
|
||||
/// See the toplevel documentation for a code example.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct Yescrypt {
|
||||
/// Default parameters to use when hashing passwords.
|
||||
params: Params,
|
||||
}
|
||||
|
||||
impl From<Params> for Yescrypt {
|
||||
fn from(params: Params) -> Self {
|
||||
Self { params }
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomizedPasswordHasher<PasswordHash> for Yescrypt {
|
||||
type Params = Params;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user