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