Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

pbkdf2/
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//! # Examples
10//!
11//! PBKDF2 is defined in terms of a keyed pseudo-random function (PRF).
12//! The most commonly used PRF for this purpose is HMAC. In such cases
13//! you can use [`pbkdf2_hmac`] and [`pbkdf2_hmac_array`] functions.
14//! The former accepts a byte slice which gets filled with generated key,
15//! while the latter returns an array with generated key of requested length.
16//!
17//! Note that it is not recommended to generate keys using PBKDF2 that exceed
18//! the output size of the PRF (equal to the hash size in the case of HMAC).
19//! If you need to generate a large amount of cryptographic material,
20//! consider using a separate [key derivation function][KDF].
21//!
22//! [KDF]: https://github.com/RustCrypto/KDFs
23//!
24//! ## Low-level API
25//!
26//! This API operates directly on byte slices:
27//!
28#![cfg_attr(feature = "sha2", doc = "```")]
29#![cfg_attr(not(feature = "sha2"), doc = "```ignore")]
30//! // NOTE: example requires `getrandom` feature is enabled
31//!
32//! use hex_literal::hex;
33//! use pbkdf2::{pbkdf2_hmac, pbkdf2_hmac_array, sha2::Sha256};
34//!
35//! let password = b"password";
36//! let salt = b"salt";
37//! // number of iterations
38//! let n = 600_000;
39//! // Expected value of generated key
40//! let expected = hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637");
41//!
42//! let mut key1 = [0u8; 20];
43//! pbkdf2_hmac::<Sha256>(password, salt, n, &mut key1);
44//! assert_eq!(key1, expected);
45//!
46//! let key2 = pbkdf2_hmac_array::<Sha256, 20>(password, salt, n);
47//! assert_eq!(key2, expected);
48//! ```
49//!
50//! If you want to use a different PRF, then you can use [`pbkdf2`] and [`pbkdf2_array`] functions.
51//!
52//! ## PHC string API
53//!
54//! This crate can produce and verify password hash strings encoded in the Password Hashing
55//! Competition (PHC) string format using the [`Pbkdf2`] struct.
56//!
57//! The following example demonstrates the high-level password hashing API:
58//!
59#![cfg_attr(all(feature = "getrandom", feature = "phc"), doc = "```")]
60#![cfg_attr(not(all(feature = "getrandom", feature = "phc")), doc = "```ignore")]
61//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
62//! // NOTE: example requires `getrandom` feature is enabled
63//!
64//! use pbkdf2::{
65//!     password_hash::{PasswordHasher, PasswordVerifier},
66//!     phc::PasswordHash,
67//!     Pbkdf2
68//! };
69//!
70//! let pbkdf2 = Pbkdf2::default(); // Uses `Algorithm::default()` and `Params::RECOMMENDED`
71//! let password = b"hunter2"; // Bad password; don't actually use!
72//!
73//! // Hash password to PHC string ($pbkdf2-sha256$...)
74//! let pwhash: PasswordHash = pbkdf2.hash_password(password)?;
75//! let pwhash_string = pwhash.to_string();
76//!
77//! // Verify password against PHC string
78//! let parsed_hash = PasswordHash::new(&pwhash_string)?;
79//! pbkdf2.verify_password(password, &parsed_hash)?;
80//! # Ok(())
81//! # }
82//! ```
83
84#[cfg(feature = "mcf")]
85pub mod mcf;
86#[cfg(feature = "phc")]
87pub mod phc;
88
89#[cfg(feature = "sha2")]
90mod algorithm;
91#[cfg(feature = "sha2")]
92mod params;
93
94#[cfg(feature = "sha2")]
95pub use crate::{algorithm::Algorithm, params::Params};
96#[cfg(feature = "hmac")]
97pub use hmac;
98#[cfg(any(feature = "mcf", feature = "phc"))]
99pub use password_hash;
100#[cfg(any(feature = "mcf", feature = "phc"))]
101pub use password_hash::{PasswordHasher, PasswordVerifier};
102#[cfg(feature = "sha2")]
103pub use sha2;
104
105use digest::{FixedOutput, InvalidLength, KeyInit, Update, typenum::Unsigned};
106
107#[cfg(feature = "hmac")]
108use hmac::EagerHash;
109#[cfg(feature = "kdf")]
110use kdf::{Kdf, Pbkdf};
111
112#[inline(always)]
113fn xor(res: &mut [u8], salt: &[u8]) {
114    debug_assert!(salt.len() >= res.len(), "length mismatch in xor");
115    res.iter_mut().zip(salt.iter()).for_each(|(a, b)| *a ^= b);
116}
117
118#[inline(always)]
119fn pbkdf2_body<PRF>(i: u32, chunk: &mut [u8], prf: &PRF, salt: &[u8], rounds: u32)
120where
121    PRF: Update + FixedOutput + Clone,
122{
123    for v in chunk.iter_mut() {
124        *v = 0;
125    }
126
127    let mut salt = {
128        let mut prfc = prf.clone();
129        prfc.update(salt);
130        prfc.update(&(i + 1).to_be_bytes());
131
132        let salt = prfc.finalize_fixed();
133        xor(chunk, &salt);
134        salt
135    };
136
137    for _ in 1..rounds {
138        let mut prfc = prf.clone();
139        prfc.update(&salt);
140        salt = prfc.finalize_fixed();
141
142        xor(chunk, &salt);
143    }
144}
145
146/// Generic implementation of PBKDF2 algorithm which accepts an arbitrary keyed PRF.
147///
148#[cfg_attr(feature = "sha2", doc = "```")]
149#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
150/// use hex_literal::hex;
151/// use pbkdf2::{pbkdf2, hmac::Hmac, sha2::Sha256};
152///
153/// let mut buf = [0u8; 20];
154/// pbkdf2::<Hmac<Sha256>>(b"password", b"salt", 600_000, &mut buf)
155///     .expect("HMAC can be initialized with any key length");
156/// assert_eq!(buf, hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"));
157/// ```
158///
159/// # Errors
160/// Returns `InvalidLength` if the length of `password` is unsupported by `PRF`.
161#[inline]
162pub fn pbkdf2<PRF>(
163    password: &[u8],
164    salt: &[u8],
165    rounds: u32,
166    res: &mut [u8],
167) -> Result<(), InvalidLength>
168where
169    PRF: KeyInit + Update + FixedOutput + Clone,
170{
171    let n = PRF::OutputSize::to_usize();
172    let prf = PRF::new_from_slice(password)?;
173
174    for (i, chunk) in res.chunks_mut(n).enumerate() {
175        #[allow(clippy::cast_possible_truncation, reason = "TODO")]
176        pbkdf2_body(i as u32, chunk, &prf, salt, rounds);
177    }
178
179    Ok(())
180}
181
182/// A variant of the [`pbkdf2`] function which returns an array instead of filling an input slice.
183///
184#[cfg_attr(feature = "sha2", doc = "```")]
185#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
186/// use hex_literal::hex;
187/// use pbkdf2::{pbkdf2_array, hmac::Hmac, sha2::Sha256};
188///
189/// let res = pbkdf2_array::<Hmac<Sha256>, 20>(b"password", b"salt", 600_000)
190///     .expect("HMAC can be initialized with any key length");
191/// assert_eq!(res, hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"));
192/// ```
193///
194/// # Errors
195/// Returns `InvalidLength` if the length of `password` is unsupported by `PRF`.
196#[inline]
197pub fn pbkdf2_array<PRF, const N: usize>(
198    password: &[u8],
199    salt: &[u8],
200    rounds: u32,
201) -> Result<[u8; N], InvalidLength>
202where
203    PRF: KeyInit + Update + FixedOutput + Clone,
204{
205    let mut buf = [0u8; N];
206    pbkdf2::<PRF>(password, salt, rounds, &mut buf).map(|()| buf)
207}
208
209/// A variant of the [`pbkdf2`] function which uses HMAC for PRF.
210///
211/// It's generic over (eager) hash functions.
212///
213#[cfg_attr(feature = "sha2", doc = "```")]
214#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
215/// use hex_literal::hex;
216/// use pbkdf2::{pbkdf2_hmac, sha2::Sha256};
217///
218/// let mut buf = [0u8; 20];
219/// pbkdf2_hmac::<Sha256>(b"password", b"salt", 600_000, &mut buf);
220/// assert_eq!(buf, hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"));
221/// ```
222#[cfg(feature = "hmac")]
223#[allow(clippy::missing_panics_doc, reason = "condition should not occur")]
224pub fn pbkdf2_hmac<D: EagerHash>(password: &[u8], salt: &[u8], rounds: u32, res: &mut [u8]) {
225    pbkdf2::<hmac::Hmac<D>>(password, salt, rounds, res)
226        .expect("HMAC can be initialized with any key length");
227}
228
229/// A variant of the [`pbkdf2_hmac`] function which returns an array
230/// instead of filling an input slice.
231///
232#[cfg_attr(feature = "sha2", doc = "```")]
233#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
234/// use hex_literal::hex;
235/// use pbkdf2::{pbkdf2_hmac_array, sha2::Sha256};
236///
237/// assert_eq!(
238///     pbkdf2_hmac_array::<Sha256, 20>(b"password", b"salt", 600_000),
239///     hex!("669cfe52482116fda1aa2cbe409b2f56c8e45637"),
240/// );
241/// ```
242#[cfg(feature = "hmac")]
243#[must_use]
244pub fn pbkdf2_hmac_array<D: EagerHash, const N: usize>(
245    password: &[u8],
246    salt: &[u8],
247    rounds: u32,
248) -> [u8; N] {
249    let mut buf = [0u8; N];
250    pbkdf2_hmac::<D>(password, salt, rounds, &mut buf);
251    buf
252}
253
254/// API for using [`pbkdf2_hmac`] which supports the [`Algorithm`] and [`Params`] types and with
255/// it runtime selection of which algorithm to use.
256///
257#[cfg_attr(feature = "sha2", doc = "```")]
258#[cfg_attr(not(feature = "sha2"), doc = "```ignore")]
259/// use hex_literal::hex;
260/// use pbkdf2::pbkdf2_hmac_with_params;
261///
262/// let algorithm = pbkdf2::Algorithm::Pbkdf2Sha256;
263/// let params = pbkdf2::Params::default();
264///
265/// let mut buf = [0u8; 32];
266/// pbkdf2_hmac_with_params(b"password", b"salt", algorithm, params, &mut buf);
267/// assert_eq!(buf, hex!("669cfe52482116fda1aa2cbe409b2f56c8e4563752b7a28f6eaab614ee005178"));
268/// ```
269#[cfg(feature = "sha2")]
270pub fn pbkdf2_hmac_with_params(
271    password: &[u8],
272    salt: &[u8],
273    algorithm: Algorithm,
274    params: Params,
275    out: &mut [u8],
276) {
277    let f = match algorithm {
278        #[cfg(feature = "sha2")]
279        Algorithm::Pbkdf2Sha256 => pbkdf2_hmac::<sha2::Sha256>,
280        #[cfg(feature = "sha2")]
281        Algorithm::Pbkdf2Sha512 => pbkdf2_hmac::<sha2::Sha512>,
282    };
283
284    f(password, salt, params.rounds(), out);
285}
286
287/// PBKDF2 type for use with the [`PasswordHasher`] and [`PasswordVerifier`] traits, which
288/// implements support for password hash strings.
289///
290/// Supports the following password hash string formats, gated under the following crate features:
291/// - `mcf`: support for the Modular Crypt Format
292/// - `phc`: support for the Password Hashing Competition string format
293#[cfg(feature = "sha2")]
294#[cfg_attr(feature = "sha2", derive(Default))]
295#[derive(Copy, Clone, Debug, Eq, PartialEq)]
296pub struct Pbkdf2 {
297    /// Algorithm to use
298    algorithm: Algorithm,
299
300    /// Default parameters to use.
301    params: Params,
302}
303
304#[cfg(feature = "sha2")]
305impl Pbkdf2 {
306    /// PBKDF2 configured with SHA-256 as the default.
307    pub const SHA256: Self = Self::new(
308        Algorithm::Pbkdf2Sha256,
309        Params::recommended_for(Algorithm::Pbkdf2Sha256),
310    );
311
312    /// PBKDF2 configured with SHA-512 as the default.
313    pub const SHA512: Self = Self::new(
314        Algorithm::Pbkdf2Sha512,
315        Params::recommended_for(Algorithm::Pbkdf2Sha512),
316    );
317}
318
319#[cfg(feature = "sha2")]
320impl Pbkdf2 {
321    /// Initialize [`Pbkdf2`] with default parameters.
322    #[must_use]
323    pub const fn new(algorithm: Algorithm, params: Params) -> Self {
324        Self { algorithm, params }
325    }
326
327    /// Hash password into the given output buffer using the configured params.
328    pub fn hash_password_into(&self, password: &[u8], salt: &[u8], out: &mut [u8]) {
329        pbkdf2_hmac_with_params(password, salt, self.algorithm, self.params, out);
330    }
331}
332
333#[cfg(feature = "sha2")]
334impl From<Algorithm> for Pbkdf2 {
335    fn from(algorithm: Algorithm) -> Self {
336        Self {
337            algorithm,
338            params: Params::recommended_for(algorithm),
339        }
340    }
341}
342
343#[cfg(feature = "sha2")]
344impl From<Params> for Pbkdf2 {
345    fn from(params: Params) -> Self {
346        Self {
347            algorithm: Algorithm::default(),
348            params,
349        }
350    }
351}
352
353#[cfg(feature = "kdf")]
354impl Kdf for Pbkdf2 {
355    fn derive_key(&self, password: &[u8], salt: &[u8], out: &mut [u8]) -> kdf::Result<()> {
356        self.hash_password_into(password, salt, out);
357        Ok(())
358    }
359}
360
361#[cfg(feature = "kdf")]
362impl Pbkdf for Pbkdf2 {}