Skip to main content

scrypt/
lib.rs

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//! If you are only using the low-level [`scrypt`] function instead of the
10//! higher-level [`Scrypt`] struct to produce/verify hash strings,
11//! it's recommended to disable default features in your `Cargo.toml`:
12//!
13//! ```toml
14//! [dependencies]
15//! scrypt = { version = "0.12", default-features = false }
16//! ```
17//!
18//! # Usage (simple PHC password hash usage with default params)
19//!
20#![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//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
29//! // NOTE: example requires `getrandom` feature is enabled
30//!
31//! use scrypt::{
32//!     password_hash::{
33//!         PasswordHasher, PasswordVerifier, phc::{PasswordHash, Salt}
34//!     },
35//!     Scrypt
36//! };
37//!
38//! let scrypt = Scrypt::default(); // Uses `Params::RECOMMENDED`
39//! let password = b"hunter42"; // Bad password; don't actually use!
40//!
41//! // Hash password to PHC string ($scrypt$...)
42//! let hash: PasswordHash = scrypt.hash_password(password)?;
43//! let hash_string = hash.to_string();
44//!
45//! // Verify password against PHC string
46//! let parsed_hash = PasswordHash::new(&hash_string)?;
47//! scrypt.verify_password(password, &parsed_hash)?;
48//! # Ok(())
49//! # }
50//! ```
51
52#[macro_use]
53extern crate alloc;
54
55use pbkdf2::pbkdf2_hmac;
56use sha2::Sha256;
57
58mod block_mix;
59/// Errors for `scrypt` operations.
60pub 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
79/// The scrypt key derivation function.
80///
81/// # Arguments
82/// - `password` - The password to process as a byte vector
83/// - `salt` - The salt value to use as a byte vector
84/// - `params` - The `ScryptParams` to use
85/// - `output` - The resulting derived key is returned in this byte vector.
86///   **WARNING: Make sure to compare this value in constant time!**
87///
88/// # Errors
89/// Returns [`errors::InvalidOutputLen`] if `output` does not satisfy the following condition:
90///
91/// ```text
92/// output.len() > 0 && output.len() <= (2^32 - 1) * 32
93/// ```
94///
95/// # Note about output lengths
96/// The output size is determined entirely by size of the `output` parameter.
97///
98/// If the length of the [`Params`] have been customized using the [`Params::new_with_output_len`]
99/// constructor, that length is ignored and the length of `output` is used instead.
100pub fn scrypt(
101    password: &[u8],
102    salt: &[u8],
103    params: &Params,
104    output: &mut [u8],
105) -> Result<(), errors::InvalidOutputLen> {
106    // This check required by Scrypt:
107    // check output.len() > 0 && output.len() <= (2^32 - 1) * 32
108    if output.is_empty() || output.len() / 32 > 0xffff_ffff {
109        return Err(errors::InvalidOutputLen);
110    }
111
112    // The checks in the ScryptParams constructor guarantee
113    // that the following is safe:
114    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/// scrypt password hashing type which can produce and verify strings in either the Password Hashing
153/// Competition (PHC) string format which begin with `$scrypt$`, or in Modular Crypt Format (MCF)
154/// which begin with `$7$`.
155///
156/// This type holds the default parameters to use when computing password hashes.
157///
158/// See the toplevel documentation for a code example.
159#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
160#[derive(Copy, Clone, Debug, Default, PartialEq)]
161pub struct Scrypt {
162    /// Default parameters to use.
163    params: Params,
164}
165
166#[cfg(any(feature = "kdf", feature = "mcf", feature = "phc"))]
167impl Scrypt {
168    /// Initialize [`Scrypt`] with default parameters.
169    #[must_use]
170    pub const fn new() -> Self {
171        Self {
172            params: Params::RECOMMENDED,
173        }
174    }
175
176    /// Initialize [`Scrypt`] with the provided parameters.
177    #[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 {}