#![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"
)]
#![deny(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms)]
#![cfg_attr(feature = "getrandom", doc = "```")]
#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
mod errors;
mod params;
#[cfg(feature = "password-hash")]
mod algorithm;
#[cfg(feature = "password-hash")]
mod mcf;
pub use crate::{
errors::{Error, Result},
params::Params,
};
#[cfg(feature = "password-hash")]
pub use {
crate::{
algorithm::Algorithm,
mcf::{PasswordHashRef, ShaCrypt},
},
password_hash::{self, CustomizedPasswordHasher, PasswordHasher, PasswordVerifier},
};
#[cfg(all(feature = "password-hash", feature = "alloc"))]
pub use ::mcf::PasswordHash;
use sha2::{Digest, Sha256, Sha512};
pub const BLOCK_SIZE_SHA256: usize = 32;
pub const BLOCK_SIZE_SHA512: usize = 64;
#[must_use]
pub fn sha256_crypt(password: &[u8], salt: &[u8], params: Params) -> [u8; BLOCK_SIZE_SHA256] {
let pw_len = password.len();
let salt_len = salt.len();
let salt = match salt_len {
0..=15 => &salt[0..salt_len],
_ => &salt[0..16],
};
let salt_len = salt.len();
let digest_a = sha256_crypt_intermediate(password, salt);
let mut hasher_alt = Sha256::default();
for _ in 0..pw_len {
hasher_alt.update(password);
}
let dp = hasher_alt.finalize();
hasher_alt = Sha256::default();
for _ in 0..(16 + digest_a[0] as usize) {
hasher_alt.update(salt);
}
let ds = hasher_alt.finalize();
let mut digest_c = digest_a;
for i in 0..params.rounds {
let mut hasher = Sha256::default();
if (i & 1) != 0 {
hash_byte_seq(pw_len, &dp, &mut hasher);
} else {
hasher.update(digest_c);
}
if i % 3 != 0 {
hash_byte_seq(salt_len, &ds, &mut hasher);
}
if i % 7 != 0 {
hash_byte_seq(pw_len, &dp, &mut hasher);
}
if (i & 1) != 0 {
hasher.update(digest_c);
} else {
hash_byte_seq(pw_len, &dp, &mut hasher);
}
digest_c.clone_from_slice(&hasher.finalize());
}
digest_c
}
#[must_use]
pub fn sha512_crypt(password: &[u8], salt: &[u8], params: Params) -> [u8; BLOCK_SIZE_SHA512] {
let pw_len = password.len();
let salt_len = salt.len();
let salt = match salt_len {
0..=15 => &salt[0..salt_len],
_ => &salt[0..16],
};
let salt_len = salt.len();
let digest_a = sha512_crypt_intermediate(password, salt);
let mut hasher_alt = Sha512::default();
for _ in 0..pw_len {
hasher_alt.update(password);
}
let dp = hasher_alt.finalize();
hasher_alt = Sha512::default();
for _ in 0..(16 + digest_a[0] as usize) {
hasher_alt.update(salt);
}
let ds = hasher_alt.finalize();
let mut digest_c = digest_a;
for i in 0..params.rounds {
let mut hasher = Sha512::default();
if (i & 1) != 0 {
hash_byte_seq(pw_len, &dp, &mut hasher);
} else {
hasher.update(digest_c);
}
if i % 3 != 0 {
hash_byte_seq(salt_len, &ds, &mut hasher);
}
if i % 7 != 0 {
hash_byte_seq(pw_len, &dp, &mut hasher);
}
if (i & 1) != 0 {
hasher.update(digest_c);
} else {
hash_byte_seq(pw_len, &dp, &mut hasher);
}
digest_c.clone_from_slice(&hasher.finalize());
}
digest_c
}
fn sha256_crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA256] {
let pw_len = password.len();
let mut hasher = Sha256::default();
hasher.update(password);
hasher.update(salt);
let mut hasher_alt = Sha256::default();
hasher_alt.update(password);
hasher_alt.update(salt);
hasher_alt.update(password);
let digest_b = hasher_alt.finalize();
for _ in 0..(pw_len / BLOCK_SIZE_SHA256) {
hasher.update(digest_b);
}
hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA256)]);
let mut n = pw_len;
for _ in 0..pw_len {
if n == 0 {
break;
}
if (n & 1) != 0 {
hasher.update(digest_b);
} else {
hasher.update(password);
}
n >>= 1;
}
hasher.finalize().into()
}
fn sha512_crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA512] {
let pw_len = password.len();
let mut hasher = Sha512::default();
hasher.update(password);
hasher.update(salt);
let mut hasher_alt = Sha512::default();
hasher_alt.update(password);
hasher_alt.update(salt);
hasher_alt.update(password);
let digest_b = hasher_alt.finalize();
for _ in 0..(pw_len / BLOCK_SIZE_SHA512) {
hasher.update(digest_b);
}
hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA512)]);
let mut n = pw_len;
for _ in 0..pw_len {
if n == 0 {
break;
}
if (n & 1) != 0 {
hasher.update(digest_b);
} else {
hasher.update(password);
}
n >>= 1;
}
hasher.finalize().into()
}
fn hash_byte_seq<D: Digest>(len: usize, fill_from: &[u8], digest: &mut D) {
let bs = fill_from.len();
for _ in 0..(len / bs) {
digest.update(fill_from);
}
digest.update(&fill_from[..(len % bs)]);
}