#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![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"
)]
#![deny(unsafe_code)]
#![cfg_attr(feature = "getrandom", doc = "```")]
#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
extern crate alloc;
mod error;
mod mode;
mod params;
mod pwxform;
mod salsa20;
mod smix;
mod util;
#[cfg(feature = "password-hash")]
mod mcf;
pub use crate::{
error::{Error, Result},
mode::Mode,
params::Params,
};
#[cfg(feature = "kdf")]
pub use kdf::{self, Kdf, Pbkdf};
#[cfg(feature = "password-hash")]
pub use {
crate::mcf::{PasswordHash, PasswordHashRef},
password_hash::{self, CustomizedPasswordHasher, PasswordHasher, PasswordVerifier},
};
use alloc::vec;
use sha2::{Digest, Sha256};
pub fn yescrypt(passwd: &[u8], salt: &[u8], params: &Params, out: &mut [u8]) -> Result<()> {
let mut passwd = passwd;
let mut dk = [0u8; 32];
if params.mode.is_rw()
&& params.p >= 1
&& params.n / u64::from(params.p) >= 0x100
&& params.n / u64::from(params.p) * u64::from(params.r) >= 0x20000
{
let mut prehash_params = *params;
prehash_params.n >>= 6;
prehash_params.t = 0;
yescrypt_body(passwd, salt, &prehash_params, true, &mut dk)?;
passwd = &dk;
}
yescrypt_body(passwd, salt, params, false, out)
}
fn yescrypt_body(
passwd: &[u8],
salt: &[u8],
params: &Params,
prehash: bool,
out: &mut [u8],
) -> Result<()> {
let mode = params.mode;
let n = params.n;
let r = params.r;
let p = params.p;
let t = params.t;
if !((out.len() as u64 <= u64::from(u32::MAX) * 32)
&& (u64::from(r) * u64::from(p) < (1 << 30) as u64)
&& !(n & (n - 1) != 0 || n <= 1 || r < 1 || p < 1)
&& !(u64::from(r) > u64::MAX / 128 / u64::from(p) || n > u64::MAX / 128 / u64::from(r))
&& (n <= u64::MAX / (u64::from(t) + 1)))
{
return Err(Error::Params);
}
let mut v = vec![0; 32 * (r as usize) * usize::try_from(n)?];
let mut b = vec![0; 32 * (r as usize) * (p as usize)];
let mut xy = vec![0; 64 * (r as usize)];
let mut passwd = passwd;
let mut sha256 = [0u8; 32];
let key: &[u8] = if prehash {
b"yescrypt-prehash"
} else {
b"yescrypt"
};
if !mode.is_classic() {
sha256 = util::hmac_sha256(key, passwd)?;
passwd = &sha256;
}
pbkdf2::pbkdf2_hmac::<Sha256>(passwd, salt, 1, util::cast_slice_mut(&mut b)?);
if !mode.is_classic() {
sha256.copy_from_slice(util::cast_slice(&b[..8])?);
passwd = &sha256;
}
if mode.is_rw() {
smix::smix(&mut b, r, n, p, t, mode, &mut v, &mut xy, &mut sha256)?;
passwd = &sha256;
} else {
for i in 0..p {
smix::smix(
&mut b[(32 * (r as usize) * (i as usize))..],
r,
n,
1,
t,
mode,
&mut v,
&mut xy,
&mut [],
)?;
}
}
let mut dk = [0u8; 32];
if !mode.is_classic() && out.len() < 32 {
pbkdf2::pbkdf2_hmac::<Sha256>(passwd, util::cast_slice(&b)?, 1, &mut dk);
}
pbkdf2::pbkdf2_hmac::<Sha256>(passwd, util::cast_slice(&b)?, 1, out);
if !mode.is_classic() && !prehash {
let dkp = if !mode.is_classic() && out.len() < 32 {
&mut dk
} else {
&mut *out
};
sha256 = util::hmac_sha256(&dkp[..32], b"Client Key")?;
let clen = out.len().clamp(0, 32);
dk = Sha256::digest(sha256).into();
out[..clen].copy_from_slice(&dk[..clen]);
}
Ok(())
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct Yescrypt {
params: Params,
}
impl Yescrypt {
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 {}