pbkdf2/
lib.rs

1//! This crate implements the PBKDF2 key derivation function as specified
2//! in [RFC 2898](https://tools.ietf.org/html/rfc2898).
3//!
4//! # Examples
5//!
6//! PBKDF2 is defined in terms of a keyed pseudo-random function (PRF). Most
7//! commonly HMAC is used as this PRF. In such cases you can use [`pbkdf2_hmac`]
8//! and [`pbkdf2_hmac_array`] functions. The former accepts a byte slice which
9//! gets filled with generated key, while the former returns an array with
10//! generated key of requested length.
11//!
12//! ```
13//! # #[cfg(feature = "hmac")] {
14//! use hex_literal::hex;
15//! use pbkdf2::{pbkdf2_hmac, pbkdf2_hmac_array};
16//! use sha2::Sha256;
17//!
18//! let password = b"password";
19//! let salt = b"salt";
20//! // number of iterations
21//! let n = 600_000;
22//! // Expected value of generated key
23//! let expected = hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637");
24//!
25//! let mut key1 = [0u8; 20];
26//! pbkdf2_hmac::<Sha256>(password, salt, n, &mut key1);
27//! assert_eq!(key1, expected);
28//!
29//! let key2 = pbkdf2_hmac_array::<Sha256, 20>(password, salt, n);
30//! assert_eq!(key2, expected);
31//! # }
32//! ```
33//!
34//! If you want to use a different PRF, then you can use [`pbkdf2`][crate::pbkdf2]
35//! and [`pbkdf2_array`] functions.
36//!
37//! This crates also provides the high-level password-hashing API through
38//! the [`Pbkdf2`] struct and traits defined in the
39//! [`password-hash`][password_hash] crate.
40//!
41//! Add the following to your crate's `Cargo.toml` to import it:
42//!
43//! ```toml
44//! [dependencies]
45//! pbkdf2 = { version = "0.12", features = ["simple"] }
46//! rand_core = { version = "0.6", features = ["std"] }
47//! ```
48//!
49//! The following example demonstrates the high-level password hashing API:
50//!
51//! ```
52//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
53//! # #[cfg(all(feature = "simple", feature = "std"))]
54//! # {
55//! use pbkdf2::{
56//!     password_hash::{
57//!         rand_core::OsRng,
58//!         PasswordHash, PasswordHasher, PasswordVerifier, SaltString
59//!     },
60//!     Pbkdf2
61//! };
62//!
63//! let password = b"hunter42"; // Bad password; don't actually use!
64//! let salt = SaltString::generate(&mut OsRng);
65//!
66//! // Hash password to PHC string ($pbkdf2-sha256$...)
67//! let password_hash = Pbkdf2.hash_password(password, &salt)?.to_string();
68//!
69//! // Verify password against PHC string
70//! let parsed_hash = PasswordHash::new(&password_hash)?;
71//! assert!(Pbkdf2.verify_password(password, &parsed_hash).is_ok());
72//! # }
73//! # Ok(())
74//! # }
75//! ```
76
77#![no_std]
78#![cfg_attr(docsrs, feature(doc_cfg))]
79#![doc(
80    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
81    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
82)]
83
84#[cfg(feature = "std")]
85extern crate std;
86
87#[cfg(feature = "simple")]
88extern crate alloc;
89
90#[cfg(feature = "simple")]
91#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
92pub use password_hash;
93
94#[cfg(feature = "simple")]
95mod simple;
96
97#[cfg(feature = "hmac")]
98pub use hmac;
99
100#[cfg(feature = "simple")]
101pub use crate::simple::{Algorithm, Params, Pbkdf2};
102
103#[cfg(feature = "parallel")]
104use rayon::prelude::*;
105
106use digest::{generic_array::typenum::Unsigned, FixedOutput, InvalidLength, KeyInit, Update};
107
108#[cfg(feature = "hmac")]
109use digest::{
110    block_buffer::Eager,
111    core_api::{BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore},
112    generic_array::typenum::{IsLess, Le, NonZero, U256},
113    HashMarker,
114};
115
116#[inline(always)]
117fn xor(res: &mut [u8], salt: &[u8]) {
118    debug_assert!(salt.len() >= res.len(), "length mismatch in xor");
119    res.iter_mut().zip(salt.iter()).for_each(|(a, b)| *a ^= b);
120}
121
122#[inline(always)]
123fn pbkdf2_body<PRF>(i: u32, chunk: &mut [u8], prf: &PRF, salt: &[u8], rounds: u32)
124where
125    PRF: Update + FixedOutput + Clone,
126{
127    for v in chunk.iter_mut() {
128        *v = 0;
129    }
130
131    let mut salt = {
132        let mut prfc = prf.clone();
133        prfc.update(salt);
134        prfc.update(&(i + 1).to_be_bytes());
135
136        let salt = prfc.finalize_fixed();
137        xor(chunk, &salt);
138        salt
139    };
140
141    for _ in 1..rounds {
142        let mut prfc = prf.clone();
143        prfc.update(&salt);
144        salt = prfc.finalize_fixed();
145
146        xor(chunk, &salt);
147    }
148}
149
150/// Generic implementation of PBKDF2 algorithm which accepts an arbitrary keyed PRF.
151///
152/// ```
153/// use hex_literal::hex;
154/// use pbkdf2::pbkdf2;
155/// use hmac::Hmac;
156/// use sha2::Sha256;
157///
158/// let mut buf = [0u8; 20];
159/// pbkdf2::<Hmac<Sha256>>(b"password", b"salt", 600_000, &mut buf)
160///     .expect("HMAC can be initialized with any key length");
161/// assert_eq!(buf, hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"));
162/// ```
163#[inline]
164pub fn pbkdf2<PRF>(
165    password: &[u8],
166    salt: &[u8],
167    rounds: u32,
168    res: &mut [u8],
169) -> Result<(), InvalidLength>
170where
171    PRF: KeyInit + Update + FixedOutput + Clone + Sync,
172{
173    let n = PRF::OutputSize::to_usize();
174    // note: HMAC can be initialized with keys of any size,
175    // so this panic never happens with it
176    let prf = PRF::new_from_slice(password)?;
177
178    #[cfg(not(feature = "parallel"))]
179    {
180        for (i, chunk) in res.chunks_mut(n).enumerate() {
181            pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
182        }
183    }
184    #[cfg(feature = "parallel")]
185    {
186        res.par_chunks_mut(n).enumerate().for_each(|(i, chunk)| {
187            pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
188        });
189    }
190
191    Ok(())
192}
193
194/// A variant of the [`pbkdf2`][crate::pbkdf2] function which returns an array
195/// instead of filling an input slice.
196///
197/// ```
198/// use hex_literal::hex;
199/// use pbkdf2::pbkdf2_array;
200/// use hmac::Hmac;
201/// use sha2::Sha256;
202///
203/// let res = pbkdf2_array::<Hmac<Sha256>, 20>(b"password", b"salt", 600_000)
204///     .expect("HMAC can be initialized with any key length");
205/// assert_eq!(res, hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"));
206/// ```
207#[inline]
208pub fn pbkdf2_array<PRF, const N: usize>(
209    password: &[u8],
210    salt: &[u8],
211    rounds: u32,
212) -> Result<[u8; N], InvalidLength>
213where
214    PRF: KeyInit + Update + FixedOutput + Clone + Sync,
215{
216    let mut buf = [0u8; N];
217    pbkdf2::<PRF>(password, salt, rounds, &mut buf).map(|()| buf)
218}
219
220/// A variant of the [`pbkdf2`][crate::pbkdf2] function which uses HMAC for PRF.
221/// It's generic over (eager) hash functions.
222///
223/// ```
224/// use hex_literal::hex;
225/// use pbkdf2::pbkdf2_hmac;
226/// use sha2::Sha256;
227///
228/// let mut buf = [0u8; 20];
229/// pbkdf2_hmac::<Sha256>(b"password", b"salt", 600_000, &mut buf);
230/// assert_eq!(buf, hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"));
231/// ```
232#[cfg(feature = "hmac")]
233#[cfg_attr(docsrs, doc(cfg(feature = "hmac")))]
234pub fn pbkdf2_hmac<D>(password: &[u8], salt: &[u8], rounds: u32, res: &mut [u8])
235where
236    D: CoreProxy,
237    D::Core: Sync
238        + HashMarker
239        + UpdateCore
240        + FixedOutputCore
241        + BufferKindUser<BufferKind = Eager>
242        + Default
243        + Clone,
244    <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
245    Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
246{
247    crate::pbkdf2::<hmac::Hmac<D>>(password, salt, rounds, res)
248        .expect("HMAC can be initialized with any key length");
249}
250
251/// A variant of the [`pbkdf2_hmac`] function which returns an array
252/// instead of filling an input slice.
253///
254/// ```
255/// use hex_literal::hex;
256/// use pbkdf2::pbkdf2_hmac_array;
257/// use sha2::Sha256;
258///
259/// assert_eq!(
260///     pbkdf2_hmac_array::<Sha256, 20>(b"password", b"salt", 600_000),
261///     hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"),
262/// );
263/// ```
264#[cfg(feature = "hmac")]
265#[cfg_attr(docsrs, doc(cfg(feature = "hmac")))]
266pub fn pbkdf2_hmac_array<D, const N: usize>(password: &[u8], salt: &[u8], rounds: u32) -> [u8; N]
267where
268    D: CoreProxy,
269    D::Core: Sync
270        + HashMarker
271        + UpdateCore
272        + FixedOutputCore
273        + BufferKindUser<BufferKind = Eager>
274        + Default
275        + Clone,
276    <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
277    Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
278{
279    let mut buf = [0u8; N];
280    pbkdf2_hmac::<D>(password, salt, rounds, &mut buf);
281    buf
282}