#![no_std]
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
#![cfg_attr(feature = "sha2", doc = "```")]
#![cfg_attr(not(feature = "sha2"), doc = "```ignore")]
#![cfg_attr(all(feature = "getrandom", feature = "phc"), doc = "```")]
#![cfg_attr(not(all(feature = "getrandom", feature = "phc")), doc = "```ignore")]
#[cfg(feature = "mcf")]
pub mod mcf;
#[cfg(feature = "phc")]
pub mod phc;
#[cfg(feature = "sha2")]
mod algorithm;
#[cfg(feature = "sha2")]
mod params;
#[cfg(feature = "sha2")]
pub use crate::{algorithm::Algorithm, params::Params};
#[cfg(feature = "hmac")]
pub use hmac;
#[cfg(any(feature = "mcf", feature = "phc"))]
pub use password_hash;
#[cfg(any(feature = "mcf", feature = "phc"))]
pub use password_hash::{PasswordHasher, PasswordVerifier};
#[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]) {
debug_assert!(salt.len() >= res.len(), "length mismatch in xor");
res.iter_mut().zip(salt.iter()).for_each(|(a, b)| *a ^= b);
}
#[inline(always)]
fn pbkdf2_body<PRF>(i: u32, chunk: &mut [u8], prf: &PRF, salt: &[u8], rounds: u32)
where
PRF: Update + FixedOutput + Clone,
{
for v in chunk.iter_mut() {
*v = 0;
}
let mut salt = {
let mut prfc = prf.clone();
prfc.update(salt);
prfc.update(&(i + 1).to_be_bytes());
let salt = prfc.finalize_fixed();
xor(chunk, &salt);
salt
};
for _ in 1..rounds {
let mut prfc = prf.clone();
prfc.update(&salt);
salt = prfc.finalize_fixed();
xor(chunk, &salt);
}
}
#[cfg_attr(feature = "sha2", doc = "```")]
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
#[inline]
pub fn pbkdf2<PRF>(
password: &[u8],
salt: &[u8],
rounds: u32,
res: &mut [u8],
) -> Result<(), InvalidLength>
where
PRF: KeyInit + Update + FixedOutput + Clone,
{
let n = PRF::OutputSize::to_usize();
let prf = PRF::new_from_slice(password)?;
for (i, chunk) in res.chunks_mut(n).enumerate() {
#[allow(clippy::cast_possible_truncation, reason = "TODO")]
pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
}
Ok(())
}
#[cfg_attr(feature = "sha2", doc = "```")]
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
#[inline]
pub fn pbkdf2_array<PRF, const N: usize>(
password: &[u8],
salt: &[u8],
rounds: u32,
) -> Result<[u8; N], InvalidLength>
where
PRF: KeyInit + Update + FixedOutput + Clone,
{
let mut buf = [0u8; N];
pbkdf2::<PRF>(password, salt, rounds, &mut buf).map(|()| buf)
}
#[cfg_attr(feature = "sha2", doc = "```")]
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
#[cfg(feature = "hmac")]
#[allow(clippy::missing_panics_doc, reason = "condition should not occur")]
pub fn pbkdf2_hmac<D: EagerHash>(password: &[u8], salt: &[u8], rounds: u32, res: &mut [u8]) {
pbkdf2::<hmac::Hmac<D>>(password, salt, rounds, res)
.expect("HMAC can be initialized with any key length");
}
#[cfg_attr(feature = "sha2", doc = "```")]
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
#[cfg(feature = "hmac")]
#[must_use]
pub fn pbkdf2_hmac_array<D: EagerHash, const N: usize>(
password: &[u8],
salt: &[u8],
rounds: u32,
) -> [u8; N] {
let mut buf = [0u8; N];
pbkdf2_hmac::<D>(password, salt, rounds, &mut buf);
buf
}
#[cfg_attr(feature = "sha2", doc = "```")]
#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
#[cfg(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 = "sha2")]
Algorithm::Pbkdf2Sha256 => pbkdf2_hmac::<sha2::Sha256>,
#[cfg(feature = "sha2")]
Algorithm::Pbkdf2Sha512 => pbkdf2_hmac::<sha2::Sha512>,
};
f(password, salt, params.rounds(), out);
}
#[cfg(feature = "sha2")]
#[cfg_attr(feature = "sha2", derive(Default))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Pbkdf2 {
algorithm: Algorithm,
params: Params,
}
#[cfg(feature = "sha2")]
impl Pbkdf2 {
pub const SHA256: Self = Self::new(
Algorithm::Pbkdf2Sha256,
Params::recommended_for(Algorithm::Pbkdf2Sha256),
);
pub const SHA512: Self = Self::new(
Algorithm::Pbkdf2Sha512,
Params::recommended_for(Algorithm::Pbkdf2Sha512),
);
}
#[cfg(feature = "sha2")]
impl Pbkdf2 {
#[must_use]
pub const fn new(algorithm: Algorithm, params: Params) -> Self {
Self { algorithm, 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(feature = "sha2")]
impl From<Algorithm> for Pbkdf2 {
fn from(algorithm: Algorithm) -> Self {
Self {
algorithm,
params: Params::recommended_for(algorithm),
}
}
}
#[cfg(feature = "sha2")]
impl From<Params> for Pbkdf2 {
fn from(params: Params) -> Self {
Self {
algorithm: Algorithm::default(),
params,
}
}
}
#[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 {}