Coverage Report

Created: 2026-02-14 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.15.4/src/pbkdf2.rs
Line
Count
Source
1
// Copyright 2015-2022 Brian Smith.
2
// SPDX-License-Identifier: ISC
3
// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4
// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6
//! PBKDF2 derivation and verification.
7
//!
8
//! Use `derive` to derive PBKDF2 outputs. Use `verify` to verify secret
9
//! against previously-derived outputs.
10
//!
11
//! PBKDF2 is specified in [RFC 2898 Section 5.2] with test vectors given in
12
//! [RFC 6070]. See also [NIST Special Publication 800-132].
13
//!
14
//! [RFC 2898 Section 5.2]: https://tools.ietf.org/html/rfc2898#section-5.2
15
//! [RFC 6070]: https://tools.ietf.org/html/rfc6070
16
//! [NIST Special Publication 800-132]:
17
//!    http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf
18
//!
19
//! # Examples
20
//!
21
//! ## Password Database Example
22
//!
23
//! ```
24
//! use aws_lc_rs::{digest, pbkdf2};
25
//! use std::{collections::HashMap, num::NonZeroU32};
26
//!
27
//! static PBKDF2_ALG: pbkdf2::Algorithm = pbkdf2::PBKDF2_HMAC_SHA256;
28
//! const CREDENTIAL_LEN: usize = digest::SHA256_OUTPUT_LEN;
29
//! pub type Credential = [u8; CREDENTIAL_LEN];
30
//!
31
//! enum Error {
32
//!     WrongUsernameOrPassword
33
//! }
34
//!
35
//! struct PasswordDatabase {
36
//!     pbkdf2_iterations: NonZeroU32,
37
//!     db_salt_component: [u8; 16],
38
//!
39
//!     // Normally this would be a persistent database.
40
//!     storage: HashMap<String, Credential>,
41
//! }
42
//!
43
//! impl PasswordDatabase {
44
//!     pub fn store_password(&mut self, username: &str, password: &str) {
45
//!         let salt = self.salt(username);
46
//!         let mut to_store: Credential = [0u8; CREDENTIAL_LEN];
47
//!         pbkdf2::derive(PBKDF2_ALG, self.pbkdf2_iterations, &salt,
48
//!                        password.as_bytes(), &mut to_store);
49
//!         self.storage.insert(String::from(username), to_store);
50
//!     }
51
//!
52
//!     pub fn verify_password(&self, username: &str, attempted_password: &str)
53
//!                            -> Result<(), Error> {
54
//!         match self.storage.get(username) {
55
//!            Some(actual_password) => {
56
//!                let salt = self.salt(username);
57
//!                pbkdf2::verify(PBKDF2_ALG, self.pbkdf2_iterations, &salt,
58
//!                               attempted_password.as_bytes(),
59
//!                               actual_password)
60
//!                     .map_err(|_| Error::WrongUsernameOrPassword)
61
//!            },
62
//!
63
//!            None => Err(Error::WrongUsernameOrPassword)
64
//!         }
65
//!     }
66
//!
67
//!     // The salt should have a user-specific component so that an attacker
68
//!     // cannot crack one password for multiple users in the database. It
69
//!     // should have a database-unique component so that an attacker cannot
70
//!     // crack the same user's password across databases in the unfortunate
71
//!     // but common case that the user has used the same password for
72
//!     // multiple systems.
73
//!     fn salt(&self, username: &str) -> Vec<u8> {
74
//!         let mut salt = Vec::with_capacity(self.db_salt_component.len() +
75
//!                                           username.as_bytes().len());
76
//!         salt.extend(self.db_salt_component.as_ref());
77
//!         salt.extend(username.as_bytes());
78
//!         salt
79
//!     }
80
//! }
81
//!
82
//! fn main() {
83
//!     // Normally these parameters would be loaded from a configuration file.
84
//!     let mut db = PasswordDatabase {
85
//!         pbkdf2_iterations: NonZeroU32::new(100_000).unwrap(),
86
//!         db_salt_component: [
87
//!             // This value was generated from a secure PRNG.
88
//!             0xd6, 0x26, 0x98, 0xda, 0xf4, 0xdc, 0x50, 0x52,
89
//!             0x24, 0xf2, 0x27, 0xd1, 0xfe, 0x39, 0x01, 0x8a
90
//!         ],
91
//!         storage: HashMap::new(),
92
//!     };
93
//!
94
//!     db.store_password("alice", "@74d7]404j|W}6u");
95
//!
96
//!     // An attempt to log in with the wrong password fails.
97
//!     assert!(db.verify_password("alice", "wrong password").is_err());
98
//!
99
//!     // Normally there should be an expoentially-increasing delay between
100
//!     // attempts to further protect against online attacks.
101
//!
102
//!     // An attempt to log in with the right password succeeds.
103
//!     assert!(db.verify_password("alice", "@74d7]404j|W}6u").is_ok());
104
//! }
105
106
use crate::aws_lc::PKCS5_PBKDF2_HMAC;
107
use crate::error::Unspecified;
108
use crate::fips::indicator_check;
109
use crate::{constant_time, digest, hmac};
110
use core::num::NonZeroU32;
111
use zeroize::Zeroize;
112
113
/// A PBKDF2 algorithm.
114
///
115
/// `max_output_len` is computed as u64 instead of usize to prevent overflowing on 32-bit machines.
116
#[derive(Clone, Copy, PartialEq, Eq)]
117
pub struct Algorithm {
118
    algorithm: hmac::Algorithm,
119
    max_output_len: u64,
120
}
121
122
/// PBKDF2 using HMAC-SHA1.
123
pub const PBKDF2_HMAC_SHA1: Algorithm = Algorithm {
124
    algorithm: hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY,
125
    max_output_len: MAX_USIZE32 * digest::SHA1_OUTPUT_LEN as u64,
126
};
127
128
/// PBKDF2 using HMAC-SHA256.
129
pub const PBKDF2_HMAC_SHA256: Algorithm = Algorithm {
130
    algorithm: hmac::HMAC_SHA256,
131
    max_output_len: MAX_USIZE32 * digest::SHA256_OUTPUT_LEN as u64,
132
};
133
134
/// PBKDF2 using HMAC-SHA384.
135
pub const PBKDF2_HMAC_SHA384: Algorithm = Algorithm {
136
    algorithm: hmac::HMAC_SHA384,
137
    max_output_len: MAX_USIZE32 * digest::SHA384_OUTPUT_LEN as u64,
138
};
139
140
/// PBKDF2 using HMAC-SHA512.
141
pub const PBKDF2_HMAC_SHA512: Algorithm = Algorithm {
142
    algorithm: hmac::HMAC_SHA512,
143
    max_output_len: MAX_USIZE32 * digest::SHA512_OUTPUT_LEN as u64,
144
};
145
146
const MAX_USIZE32: u64 = u32::MAX as u64;
147
148
/// Fills `out` with the key derived using PBKDF2 with the given inputs.
149
///
150
/// Do not use `derive` as part of verifying a secret; use `verify` instead, to
151
/// minimize the effectiveness of timing attacks.
152
///
153
/// `out.len()` must be no larger than the digest length * (2**32 - 1), per the
154
/// PBKDF2 specification.
155
///
156
/// | Parameter   | RFC 2898 Section 5.2 Term
157
/// |-------------|-------------------------------------------
158
/// | `digest_alg`  | PRF (HMAC with the given digest algorithm)
159
/// | `iterations`  | c (iteration count)
160
/// | `salt`        | S (salt)
161
/// | `secret`      | P (password)
162
/// | `out`         | dk (derived key)
163
/// | `out.len()`   | dkLen (derived key length)
164
///
165
/// # Panics
166
///
167
/// `derive` panics if `out.len()` is larger than (2**32 - 1) * the digest
168
/// algorithm's output length, per the PBKDF2 specification.
169
//
170
// # FIPS
171
// The following conditions must be met:
172
// * Algorithm is one of the following:
173
//   * `PBKDF2_HMAC_SHA1`
174
//   * `PBKDF2_HMAC_SHA256`
175
//   * `PBKDF2_HMAC_SHA384`
176
//   * `PBKDF2_HMAC_SHA512`
177
// * `salt.len()` >= 16
178
// * `sercet.len()` >= 14
179
// * `iterations` >= 1000
180
#[inline]
181
0
pub fn derive(
182
0
    algorithm: Algorithm,
183
0
    iterations: NonZeroU32,
184
0
    salt: &[u8],
185
0
    secret: &[u8],
186
0
    out: &mut [u8],
187
0
) {
188
0
    try_derive(algorithm, iterations, salt, secret, out).expect("pbkdf2 derive failed");
189
0
}
190
191
#[inline]
192
0
fn try_derive(
193
0
    algorithm: Algorithm,
194
0
    iterations: NonZeroU32,
195
0
    salt: &[u8],
196
0
    secret: &[u8],
197
0
    out: &mut [u8],
198
0
) -> Result<(), Unspecified> {
199
0
    assert!(
200
0
        out.len() as u64 <= algorithm.max_output_len,
201
0
        "derived key too long"
202
    );
203
204
0
    if 1 != indicator_check!(unsafe {
205
0
        PKCS5_PBKDF2_HMAC(
206
0
            secret.as_ptr().cast(),
207
0
            secret.len(),
208
0
            salt.as_ptr(),
209
0
            salt.len(),
210
0
            iterations.get(),
211
0
            digest::match_digest_type(&algorithm.algorithm.digest_algorithm().id).as_const_ptr(),
212
0
            out.len(),
213
0
            out.as_mut_ptr(),
214
0
        )
215
0
    }) {
216
0
        return Err(Unspecified);
217
0
    }
218
0
    Ok(())
219
0
}
220
221
/// Verifies that a previously-derived (e.g., using `derive`) PBKDF2 value
222
/// matches the PBKDF2 value derived from the other inputs.
223
///
224
/// The comparison is done in constant time to prevent timing attacks. The
225
/// comparison will fail if `previously_derived` is empty (has a length of
226
/// zero).
227
///
228
/// | Parameter                  | RFC 2898 Section 5.2 Term
229
/// |----------------------------|--------------------------------------------
230
/// | `digest_alg`                 | PRF (HMAC with the given digest algorithm).
231
/// | `iterations`               | c (iteration count)
232
/// | `salt`                     | S (salt)
233
/// | `secret`                   | P (password)
234
/// | `previously_derived`       | dk (derived key)
235
/// | `previously_derived.len()` | dkLen (derived key length)
236
///
237
/// # Errors
238
/// `error::Unspecified` is the inputs were not verified.
239
///
240
/// # Panics
241
///
242
/// `verify` panics if `previously_derived.len()` is larger than (2**32 - 1) * the digest
243
/// algorithm's output length, per the PBKDF2 specification.
244
//
245
// # FIPS
246
// The following conditions must be met:
247
// * Algorithm is one of the following:
248
//   * `PBKDF2_HMAC_SHA1`
249
//   * `PBKDF2_HMAC_SHA256`
250
//   * `PBKDF2_HMAC_SHA384`
251
//   * `PBKDF2_HMAC_SHA512`
252
// * `salt.len()` >= 16
253
// * `secret.len()` >= 14
254
// * `iterations` >= 1000
255
#[inline]
256
0
pub fn verify(
257
0
    algorithm: Algorithm,
258
0
    iterations: NonZeroU32,
259
0
    salt: &[u8],
260
0
    secret: &[u8],
261
0
    previously_derived: &[u8],
262
0
) -> Result<(), Unspecified> {
263
0
    if previously_derived.is_empty() {
264
0
        return Err(Unspecified);
265
0
    }
266
0
    assert!(
267
0
        previously_derived.len() as u64 <= algorithm.max_output_len,
268
0
        "derived key too long"
269
    );
270
271
    // Create a vector with the expected output length.
272
0
    let mut derived_buf = vec![0u8; previously_derived.len()];
273
274
0
    try_derive(algorithm, iterations, salt, secret, &mut derived_buf)?;
275
276
0
    let result = constant_time::verify_slices_are_equal(&derived_buf, previously_derived);
277
0
    derived_buf.zeroize();
278
0
    result
279
0
}
280
281
#[cfg(test)]
282
mod tests {
283
    use crate::pbkdf2;
284
    use core::num::NonZeroU32;
285
286
    #[cfg(feature = "fips")]
287
    mod fips;
288
289
    #[test]
290
    fn pbkdf2_coverage() {
291
        // Coverage sanity check.
292
        assert!(pbkdf2::PBKDF2_HMAC_SHA256 == pbkdf2::PBKDF2_HMAC_SHA256);
293
        assert!(pbkdf2::PBKDF2_HMAC_SHA256 != pbkdf2::PBKDF2_HMAC_SHA384);
294
295
        let iterations = NonZeroU32::new(100_u32).unwrap();
296
        for &alg in &[
297
            pbkdf2::PBKDF2_HMAC_SHA1,
298
            pbkdf2::PBKDF2_HMAC_SHA256,
299
            pbkdf2::PBKDF2_HMAC_SHA384,
300
            pbkdf2::PBKDF2_HMAC_SHA512,
301
        ] {
302
            let mut out = vec![0u8; 64];
303
            pbkdf2::derive(alg, iterations, b"salt", b"password", &mut out);
304
305
            let alg_clone = alg;
306
            let mut out2 = vec![0u8; 64];
307
            pbkdf2::derive(alg_clone, iterations, b"salt", b"password", &mut out2);
308
            assert_eq!(out, out2);
309
        }
310
    }
311
}