#![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(
all(feature = "alloc", feature = "getrandom", feature = "phc"),
doc = "```"
)]
#![cfg_attr(
not(all(feature = "alloc", feature = "getrandom", feature = "phc")),
doc = "```ignore"
)]
#[macro_use]
extern crate alloc;
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
mod block_mix;
pub mod errors;
mod params;
mod romix;
#[cfg(feature = "mcf")]
pub mod mcf;
#[cfg(feature = "phc")]
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;
#[cfg(all(doc, feature = "password-hash"))]
use password_hash::{CustomizedPasswordHasher, PasswordHasher, PasswordVerifier};
pub fn scrypt(
password: &[u8],
salt: &[u8],
params: &Params,
output: &mut [u8],
) -> Result<(), errors::InvalidOutputLen> {
if output.is_empty() || output.len() / 32 > 0xffff_ffff {
return Err(errors::InvalidOutputLen);
}
let n = 1 << params.log_n;
let r128 = (params.r as usize) * 128;
let pr128 = (params.p as usize) * r128;
let nr128 = n * r128;
let mut b = vec![0u8; pr128];
pbkdf2_hmac::<Sha256>(password, salt, 1, &mut b);
#[cfg(not(feature = "parallel"))]
romix_sequential(nr128, r128, n, &mut b);
#[cfg(feature = "parallel")]
romix_parallel(nr128, r128, n, &mut b);
pbkdf2_hmac::<Sha256>(password, &b, 1, output);
Ok(())
}
#[cfg(not(feature = "parallel"))]
fn romix_sequential(nr128: usize, r128: usize, n: usize, b: &mut [u8]) {
let mut v = vec![0u8; nr128];
let mut t = vec![0u8; r128];
b.chunks_mut(r128).for_each(|chunk| {
romix::scrypt_ro_mix(chunk, &mut v, &mut t, n);
});
}
#[cfg(feature = "parallel")]
fn romix_parallel(nr128: usize, r128: usize, n: usize, b: &mut [u8]) {
use rayon::{iter::ParallelIterator as _, slice::ParallelSliceMut as _};
b.par_chunks_mut(r128).for_each(|chunk| {
let mut v = vec![0u8; nr128];
let mut t = vec![0u8; r128];
romix::scrypt_ro_mix(chunk, &mut v, &mut t, n);
});
}
#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct Scrypt {
params: Params,
}
#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
impl Scrypt {
#[must_use]
pub const fn new() -> Self {
Self {
params: Params::RECOMMENDED,
}
}
#[must_use]
pub const fn new_with_params(params: Params) -> Self {
Self { params }
}
}
#[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 {}