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