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 {}