Coverage Report

Created: 2025-07-12 06:15

/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/src/pbkdf2.rs
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2015 Brian Smith.
2
//
3
// Permission to use, copy, modify, and/or distribute this software for any
4
// purpose with or without fee is hereby granted, provided that the above
5
// copyright notice and this permission notice appear in all copies.
6
//
7
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15
//! PBKDF2 derivation and verification.
16
//!
17
//! Use `derive` to derive PBKDF2 outputs. Use `verify` to verify secret
18
//! against previously-derived outputs.
19
//!
20
//! PBKDF2 is specified in [RFC 2898 Section 5.2] with test vectors given in
21
//! [RFC 6070]. See also [NIST Special Publication 800-132].
22
//!
23
//! [RFC 2898 Section 5.2]: https://tools.ietf.org/html/rfc2898#section-5.2
24
//! [RFC 6070]: https://tools.ietf.org/html/rfc6070
25
//! [NIST Special Publication 800-132]:
26
//!    http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf
27
//!
28
//! # Examples
29
//!
30
//! ## Password Database Example
31
//!
32
//! ```
33
//! use ring::{digest, pbkdf2};
34
//! use std::{collections::HashMap, num::NonZeroU32};
35
//!
36
//! static PBKDF2_ALG: pbkdf2::Algorithm = pbkdf2::PBKDF2_HMAC_SHA256;
37
//! const CREDENTIAL_LEN: usize = digest::SHA256_OUTPUT_LEN;
38
//! pub type Credential = [u8; CREDENTIAL_LEN];
39
//!
40
//! enum Error {
41
//!     WrongUsernameOrPassword
42
//! }
43
//!
44
//! struct PasswordDatabase {
45
//!     pbkdf2_iterations: NonZeroU32,
46
//!     db_salt_component: [u8; 16],
47
//!
48
//!     // Normally this would be a persistent database.
49
//!     storage: HashMap<String, Credential>,
50
//! }
51
//!
52
//! impl PasswordDatabase {
53
//!     pub fn store_password(&mut self, username: &str, password: &str) {
54
//!         let salt = self.salt(username);
55
//!         let mut to_store: Credential = [0u8; CREDENTIAL_LEN];
56
//!         pbkdf2::derive(PBKDF2_ALG, self.pbkdf2_iterations, &salt,
57
//!                        password.as_bytes(), &mut to_store);
58
//!         self.storage.insert(String::from(username), to_store);
59
//!     }
60
//!
61
//!     pub fn verify_password(&self, username: &str, attempted_password: &str)
62
//!                            -> Result<(), Error> {
63
//!         match self.storage.get(username) {
64
//!            Some(actual_password) => {
65
//!                let salt = self.salt(username);
66
//!                pbkdf2::verify(PBKDF2_ALG, self.pbkdf2_iterations, &salt,
67
//!                               attempted_password.as_bytes(),
68
//!                               actual_password)
69
//!                     .map_err(|_| Error::WrongUsernameOrPassword)
70
//!            },
71
//!
72
//!            None => Err(Error::WrongUsernameOrPassword)
73
//!         }
74
//!     }
75
//!
76
//!     // The salt should have a user-specific component so that an attacker
77
//!     // cannot crack one password for multiple users in the database. It
78
//!     // should have a database-unique component so that an attacker cannot
79
//!     // crack the same user's password across databases in the unfortunate
80
//!     // but common case that the user has used the same password for
81
//!     // multiple systems.
82
//!     fn salt(&self, username: &str) -> Vec<u8> {
83
//!         let mut salt = Vec::with_capacity(self.db_salt_component.len() +
84
//!                                           username.as_bytes().len());
85
//!         salt.extend(self.db_salt_component.as_ref());
86
//!         salt.extend(username.as_bytes());
87
//!         salt
88
//!     }
89
//! }
90
//!
91
//! fn main() {
92
//!     // Normally these parameters would be loaded from a configuration file.
93
//!     let mut db = PasswordDatabase {
94
//!         pbkdf2_iterations: NonZeroU32::new(100_000).unwrap(),
95
//!         db_salt_component: [
96
//!             // This value was generated from a secure PRNG.
97
//!             0xd6, 0x26, 0x98, 0xda, 0xf4, 0xdc, 0x50, 0x52,
98
//!             0x24, 0xf2, 0x27, 0xd1, 0xfe, 0x39, 0x01, 0x8a
99
//!         ],
100
//!         storage: HashMap::new(),
101
//!     };
102
//!
103
//!     db.store_password("alice", "@74d7]404j|W}6u");
104
//!
105
//!     // An attempt to log in with the wrong password fails.
106
//!     assert!(db.verify_password("alice", "wrong password").is_err());
107
//!
108
//!     // Normally there should be an expoentially-increasing delay between
109
//!     // attempts to further protect against online attacks.
110
//!
111
//!     // An attempt to log in with the right password succeeds.
112
//!     assert!(db.verify_password("alice", "@74d7]404j|W}6u").is_ok());
113
//! }
114
115
use self::{derive_error::DeriveError, verify_error::VerifyError};
116
use crate::{
117
    bb, cpu, digest,
118
    error::{self, TooMuchOutputRequestedError},
119
    hmac::{self, InputTooLongError},
120
};
121
use core::num::NonZeroU32;
122
123
/// A PBKDF2 algorithm.
124
#[derive(Clone, Copy, PartialEq, Eq)]
125
pub struct Algorithm(hmac::Algorithm);
126
127
/// PBKDF2 using HMAC-SHA1.
128
pub static PBKDF2_HMAC_SHA1: Algorithm = Algorithm(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY);
129
130
/// PBKDF2 using HMAC-SHA256.
131
pub static PBKDF2_HMAC_SHA256: Algorithm = Algorithm(hmac::HMAC_SHA256);
132
133
/// PBKDF2 using HMAC-SHA384.
134
pub static PBKDF2_HMAC_SHA384: Algorithm = Algorithm(hmac::HMAC_SHA384);
135
136
/// PBKDF2 using HMAC-SHA512.
137
pub static PBKDF2_HMAC_SHA512: Algorithm = Algorithm(hmac::HMAC_SHA512);
138
139
/// Fills `out` with the key derived using PBKDF2 with the given inputs.
140
///
141
/// Do not use `derive` as part of verifying a secret; use `verify` instead, to
142
/// minimize the effectiveness of timing attacks.
143
///
144
/// `out.len()` must be no larger than the digest length * (2**32 - 1), per the
145
/// PBKDF2 specification.
146
///
147
/// | Parameter   | RFC 2898 Section 5.2 Term
148
/// |-------------|-------------------------------------------
149
/// | digest_alg  | PRF (HMAC with the given digest algorithm)
150
/// | iterations  | c (iteration count)
151
/// | salt        | S (salt)
152
/// | secret      | P (password)
153
/// | out         | dk (derived key)
154
/// | out.len()   | dkLen (derived key length)
155
///
156
/// # Panics
157
///
158
/// Panics if `out.len() > u32::MAX * digest_alg.output_len()`, where
159
/// `digest_alg` is the underlying HMAC/digest algorithm.
160
///
161
/// Panics if `salt` is so astronomically gigantic that it isn't a valid input
162
/// to the underlying digest function.
163
///
164
/// Panics if `secret` is so astronomically gigantic that it isn't a valid
165
/// input to the underlying digest function.
166
0
pub fn derive(
167
0
    algorithm: Algorithm,
168
0
    iterations: NonZeroU32,
169
0
    salt: &[u8],
170
0
    secret: &[u8],
171
0
    out: &mut [u8],
172
0
) {
173
0
    let cpu = cpu::features();
174
0
    try_derive(algorithm, iterations, salt, secret, out, cpu)
175
0
        .map_err(error::erase::<DeriveError>)
176
0
        .unwrap()
177
0
}
178
179
0
fn try_derive(
180
0
    algorithm: Algorithm,
181
0
    iterations: NonZeroU32,
182
0
    salt: &[u8],
183
0
    secret: &[u8],
184
0
    out: &mut [u8],
185
0
    cpu: cpu::Features,
186
0
) -> Result<(), DeriveError> {
187
0
    let digest_alg = algorithm.0.digest_algorithm();
188
0
    let output_len = digest_alg.output_len();
189
190
    // This implementation's performance is asymptotically optimal as described
191
    // in https://jbp.io/2015/08/11/pbkdf2-performance-matters/. However, it
192
    // hasn't been optimized to the same extent as fastpbkdf2. In particular,
193
    // this implementation is probably doing a lot of unnecessary copying.
194
195
0
    let secret =
196
0
        hmac::Key::try_new(algorithm.0, secret, cpu).map_err(DeriveError::secret_too_long)?;
197
198
    // Clear |out|.
199
0
    out.fill(0);
200
0
201
0
    let mut idx: u32 = 0;
202
0
203
0
    let out_len = out.len();
204
0
    for chunk in out.chunks_mut(output_len) {
205
0
        idx = idx.checked_add(1).ok_or_else(|| {
206
0
            DeriveError::too_much_output_requested(TooMuchOutputRequestedError::new(out_len))
207
0
        })?;
208
        // If the salt is too long, then we'll detect this on the first
209
        // iteration before we've written any output.
210
0
        derive_block(&secret, iterations, salt, idx, chunk, cpu)
211
0
            .map_err(DeriveError::salt_too_long)?;
212
    }
213
0
    Ok(())
214
0
}
215
216
0
fn derive_block(
217
0
    secret: &hmac::Key,
218
0
    iterations: NonZeroU32,
219
0
    salt: &[u8],
220
0
    idx: u32,
221
0
    out: &mut [u8],
222
0
    cpu: cpu::Features,
223
0
) -> Result<(), InputTooLongError> {
224
0
    let mut ctx = hmac::Context::with_key(secret);
225
0
    ctx.update(salt);
226
0
    ctx.update(&u32::to_be_bytes(idx));
227
228
0
    let mut u = ctx.try_sign(cpu)?;
229
230
0
    let mut remaining: u32 = iterations.into();
231
    loop {
232
0
        bb::xor_assign_at_start(&mut out[..], u.as_ref());
233
0
234
0
        if remaining == 1 {
235
0
            break;
236
0
        }
237
0
        remaining -= 1;
238
0
239
0
        // This will not fail, because the output of HMAC is never too long to
240
0
        // be an input for the same algorithm, but we can't prove that with
241
0
        // only locally-available information.
242
0
        u = secret.sign(u.as_ref(), cpu)?
243
    }
244
0
    Ok(())
245
0
}
246
247
cold_exhaustive_error! {
248
    enum derive_error::DeriveError {
249
        secret_too_long => SecretTooLong(InputTooLongError),
250
        salt_too_long => SaltTooLong(InputTooLongError),
251
        too_much_output_requested => TooMuchOutputRequested(TooMuchOutputRequestedError),
252
    }
253
}
254
255
cold_exhaustive_error! {
256
    enum verify_error::VerifyError {
257
        mismatch => Mismatch(()),
258
        secret_too_long => SecretTooLong(InputTooLongError),
259
        salt_too_long => SaltTooLong(InputTooLongError),
260
        previously_derived_empty => PreviouslyDerivedEmpty(usize),
261
    }
262
}
263
264
/// Verifies that a previously-derived (e.g., using `derive`) PBKDF2 value
265
/// matches the PBKDF2 value derived from the other inputs.
266
///
267
/// The comparison is done in constant time to prevent timing attacks. The
268
/// comparison will fail if `previously_derived` is empty (has a length of
269
/// zero).
270
///
271
/// | Parameter                  | RFC 2898 Section 5.2 Term
272
/// |----------------------------|--------------------------------------------
273
/// | digest_alg                 | PRF (HMAC with the given digest algorithm).
274
/// | `iterations`               | c (iteration count)
275
/// | `salt`                     | S (salt)
276
/// | `secret`                   | P (password)
277
/// | `previously_derived`       | dk (derived key)
278
/// | `previously_derived.len()` | dkLen (derived key length)
279
0
pub fn verify(
280
0
    algorithm: Algorithm,
281
0
    iterations: NonZeroU32,
282
0
    salt: &[u8],
283
0
    secret: &[u8],
284
0
    previously_derived: &[u8],
285
0
) -> Result<(), error::Unspecified> {
286
0
    let cpu = cpu::features();
287
0
    try_verify(algorithm, iterations, salt, secret, previously_derived, cpu)
288
0
        .map_err(error::erase::<VerifyError>)
289
0
}
290
291
0
fn try_verify(
292
0
    algorithm: Algorithm,
293
0
    iterations: NonZeroU32,
294
0
    salt: &[u8],
295
0
    secret: &[u8],
296
0
    previously_derived: &[u8],
297
0
    cpu: cpu::Features,
298
0
) -> Result<(), VerifyError> {
299
0
    let digest_alg = algorithm.0.digest_algorithm();
300
0
301
0
    if previously_derived.is_empty() {
302
0
        return Err(VerifyError::previously_derived_empty(0));
303
0
    }
304
0
305
0
    let mut derived_buf = [0u8; digest::MAX_OUTPUT_LEN];
306
0
307
0
    let output_len = digest_alg.output_len();
308
0
    let secret =
309
0
        hmac::Key::try_new(algorithm.0, secret, cpu).map_err(VerifyError::secret_too_long)?;
310
0
    let mut idx: u32 = 0;
311
0
312
0
    let mut matches = 1;
313
314
0
    for previously_derived_chunk in previously_derived.chunks(output_len) {
315
0
        idx = idx.checked_add(1).ok_or_else(|| {
316
0
            // `previously_derived` is so gigantic that PBKDF2 couldn't
317
0
            // have been used to compute it.
318
0
            VerifyError::mismatch(())
319
0
        })?;
320
321
0
        let derived_chunk = &mut derived_buf[..previously_derived_chunk.len()];
322
0
        derived_chunk.fill(0);
323
0
324
0
        derive_block(&secret, iterations, salt, idx, derived_chunk, cpu)
325
0
            .map_err(VerifyError::salt_too_long)?;
326
327
        // XXX: This isn't fully constant-time-safe. TODO: Fix that.
328
        #[allow(clippy::bool_to_int_with_if)]
329
0
        let current_block_matches =
330
0
            if bb::verify_slices_are_equal(derived_chunk, previously_derived_chunk).is_ok() {
331
0
                1
332
            } else {
333
0
                0
334
            };
335
336
0
        matches &= current_block_matches;
337
    }
338
339
0
    if matches == 0 {
340
0
        return Err(VerifyError::mismatch(()));
341
0
    }
342
0
343
0
    Ok(())
344
0
}