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 AUTHORS DISCLAIM ALL WARRANTIES |
8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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 | constant_time, 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 | pub fn derive( |
167 | algorithm: Algorithm, |
168 | iterations: NonZeroU32, |
169 | salt: &[u8], |
170 | secret: &[u8], |
171 | out: &mut [u8], |
172 | ) { |
173 | let cpu: Features = cpu::features(); |
174 | try_deriveResult<(), Unspecified>(algorithm, iterations, salt, secret, out, cpu) |
175 | .map_err(op:error::erase::<DeriveError>) |
176 | .unwrap() |
177 | } |
178 | |
179 | fn try_derive( |
180 | algorithm: Algorithm, |
181 | iterations: NonZeroU32, |
182 | salt: &[u8], |
183 | secret: &[u8], |
184 | out: &mut [u8], |
185 | cpu: cpu::Features, |
186 | ) -> Result<(), DeriveError> { |
187 | let digest_alg = algorithm.0.digest_algorithm(); |
188 | 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 | let secret = |
196 | hmac::Key::try_new(algorithm.0, secret, cpu).map_err(DeriveError::secret_too_long)?; |
197 | |
198 | // Clear |out|. |
199 | out.fill(0); |
200 | |
201 | let mut idx: u32 = 0; |
202 | |
203 | let out_len = out.len(); |
204 | for chunk in out.chunks_mut(output_len) { |
205 | idx = idx.checked_add(1).ok_or_else(|| { |
206 | DeriveError::too_much_output_requested(TooMuchOutputRequestedError::new(out_len)) |
207 | })?; |
208 | // If the salt is too long, then we'll detect this on the first |
209 | // iteration before we've written any output. |
210 | derive_block(&secret, iterations, salt, idx, chunk, cpu) |
211 | .map_err(DeriveError::salt_too_long)?; |
212 | } |
213 | Ok(()) |
214 | } |
215 | |
216 | fn derive_block( |
217 | secret: &hmac::Key, |
218 | iterations: NonZeroU32, |
219 | salt: &[u8], |
220 | idx: u32, |
221 | out: &mut [u8], |
222 | cpu: cpu::Features, |
223 | ) -> Result<(), InputTooLongError> { |
224 | let mut ctx: Context = hmac::Context::with_key(signing_key:secret); |
225 | ctx.update(data:salt); |
226 | ctx.update(&u32::to_be_bytes(self:idx)); |
227 | |
228 | let mut u: Tag = ctx.try_sign(cpu)?; |
229 | |
230 | let mut remaining: u32 = iterations.into(); |
231 | loop { |
232 | constant_time::xor_assign_at_start(&mut out[..], b:u.as_ref()); |
233 | |
234 | if remaining == 1 { |
235 | break; |
236 | } |
237 | remaining -= 1; |
238 | |
239 | // This will not fail, because the output of HMAC is never too long to |
240 | // be an input for the same algorithm, but we can't prove that with |
241 | // only locally-available information. |
242 | u = secret.sign(data:u.as_ref(), cpu)? |
243 | } |
244 | Ok(()) |
245 | } |
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 | pub fn verify( |
280 | algorithm: Algorithm, |
281 | iterations: NonZeroU32, |
282 | salt: &[u8], |
283 | secret: &[u8], |
284 | previously_derived: &[u8], |
285 | ) -> Result<(), error::Unspecified> { |
286 | let cpu: Features = cpu::features(); |
287 | try_verify(algorithm, iterations, salt, secret, previously_derived, cpu) |
288 | .map_err(op:error::erase::<VerifyError>) |
289 | } |
290 | |
291 | fn try_verify( |
292 | algorithm: Algorithm, |
293 | iterations: NonZeroU32, |
294 | salt: &[u8], |
295 | secret: &[u8], |
296 | previously_derived: &[u8], |
297 | cpu: cpu::Features, |
298 | ) -> Result<(), VerifyError> { |
299 | let digest_alg = algorithm.0.digest_algorithm(); |
300 | |
301 | if previously_derived.is_empty() { |
302 | return Err(VerifyError::previously_derived_empty(0)); |
303 | } |
304 | |
305 | let mut derived_buf = [0u8; digest::MAX_OUTPUT_LEN]; |
306 | |
307 | let output_len = digest_alg.output_len(); |
308 | let secret = |
309 | hmac::Key::try_new(algorithm.0, secret, cpu).map_err(VerifyError::secret_too_long)?; |
310 | let mut idx: u32 = 0; |
311 | |
312 | let mut matches = 1; |
313 | |
314 | for previously_derived_chunk in previously_derived.chunks(output_len) { |
315 | idx = idx.checked_add(1).ok_or_else(|| { |
316 | // `previously_derived` is so gigantic that PBKDF2 couldn't |
317 | // have been used to compute it. |
318 | VerifyError::mismatch(()) |
319 | })?; |
320 | |
321 | let derived_chunk = &mut derived_buf[..previously_derived_chunk.len()]; |
322 | derived_chunk.fill(0); |
323 | |
324 | derive_block(&secret, iterations, salt, idx, derived_chunk, cpu) |
325 | .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 | let current_block_matches = |
330 | if constant_time::verify_slices_are_equal(derived_chunk, previously_derived_chunk) |
331 | .is_ok() |
332 | { |
333 | 1 |
334 | } else { |
335 | 0 |
336 | }; |
337 | |
338 | matches &= current_block_matches; |
339 | } |
340 | |
341 | if matches == 0 { |
342 | return Err(VerifyError::mismatch(())); |
343 | } |
344 | |
345 | Ok(()) |
346 | } |
347 | |