1#![no_std]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8
9#![cfg_attr(
21 all(feature = "alloc", feature = "getrandom", feature = "phc"),
22 doc = "```"
23)]
24#![cfg_attr(
25 not(all(feature = "alloc", feature = "getrandom", feature = "phc")),
26 doc = "```ignore"
27)]
28#[macro_use]
53extern crate alloc;
54
55use pbkdf2::pbkdf2_hmac;
56use sha2::Sha256;
57
58mod block_mix;
59pub mod errors;
61mod params;
62mod romix;
63
64#[cfg(feature = "mcf")]
65pub mod mcf;
66#[cfg(feature = "phc")]
67pub mod phc;
68
69pub use crate::params::Params;
70
71#[cfg(feature = "kdf")]
72pub use kdf::{self, Kdf, Pbkdf};
73#[cfg(feature = "password-hash")]
74pub use password_hash;
75
76#[cfg(all(doc, feature = "password-hash"))]
77use password_hash::{CustomizedPasswordHasher, PasswordHasher, PasswordVerifier};
78
79pub fn scrypt(
101 password: &[u8],
102 salt: &[u8],
103 params: &Params,
104 output: &mut [u8],
105) -> Result<(), errors::InvalidOutputLen> {
106 if output.is_empty() || output.len() / 32 > 0xffff_ffff {
109 return Err(errors::InvalidOutputLen);
110 }
111
112 let n = 1 << params.log_n;
115 let r128 = (params.r as usize) * 128;
116 let pr128 = (params.p as usize) * r128;
117 let nr128 = n * r128;
118
119 let mut b = vec![0u8; pr128];
120 pbkdf2_hmac::<Sha256>(password, salt, 1, &mut b);
121
122 #[cfg(not(feature = "parallel"))]
123 romix_sequential(nr128, r128, n, &mut b);
124 #[cfg(feature = "parallel")]
125 romix_parallel(nr128, r128, n, &mut b);
126
127 pbkdf2_hmac::<Sha256>(password, &b, 1, output);
128 Ok(())
129}
130
131#[cfg(not(feature = "parallel"))]
132fn romix_sequential(nr128: usize, r128: usize, n: usize, b: &mut [u8]) {
133 let mut v = vec![0u8; nr128];
134 let mut t = vec![0u8; r128];
135
136 b.chunks_mut(r128).for_each(|chunk| {
137 romix::scrypt_ro_mix(chunk, &mut v, &mut t, n);
138 });
139}
140
141#[cfg(feature = "parallel")]
142fn romix_parallel(nr128: usize, r128: usize, n: usize, b: &mut [u8]) {
143 use rayon::{iter::ParallelIterator as _, slice::ParallelSliceMut as _};
144
145 b.par_chunks_mut(r128).for_each(|chunk| {
146 let mut v = vec![0u8; nr128];
147 let mut t = vec![0u8; r128];
148 romix::scrypt_ro_mix(chunk, &mut v, &mut t, n);
149 });
150}
151
152#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
160#[derive(Copy, Clone, Debug, Default, PartialEq)]
161pub struct Scrypt {
162 params: Params,
164}
165
166#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
167impl Scrypt {
168 #[must_use]
170 pub const fn new() -> Self {
171 Self {
172 params: Params::RECOMMENDED,
173 }
174 }
175
176 #[must_use]
178 pub const fn new_with_params(params: Params) -> Self {
179 Self { params }
180 }
181}
182
183#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
184impl From<Params> for Scrypt {
185 fn from(params: Params) -> Self {
186 Self::new_with_params(params)
187 }
188}
189
190#[cfg(feature = "kdf")]
191impl Kdf for Scrypt {
192 fn derive_key(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> kdf::Result<()> {
193 scrypt(password, salt, &self.params, out)?;
194 Ok(())
195 }
196}
197
198#[cfg(feature = "kdf")]
199impl Pbkdf for Scrypt {}