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 crate::{constant_time, digest, error, hmac}; |
116 | use core::num::NonZeroU32; |
117 | |
118 | /// A PBKDF2 algorithm. |
119 | #[derive (Clone, Copy, PartialEq, Eq)] |
120 | pub struct Algorithm(hmac::Algorithm); |
121 | |
122 | /// PBKDF2 using HMAC-SHA1. |
123 | pub static PBKDF2_HMAC_SHA1: Algorithm = Algorithm(hmac::HMAC_SHA1_FOR_LEGACY_USE_ONLY); |
124 | |
125 | /// PBKDF2 using HMAC-SHA256. |
126 | pub static PBKDF2_HMAC_SHA256: Algorithm = Algorithm(hmac::HMAC_SHA256); |
127 | |
128 | /// PBKDF2 using HMAC-SHA384. |
129 | pub static PBKDF2_HMAC_SHA384: Algorithm = Algorithm(hmac::HMAC_SHA384); |
130 | |
131 | /// PBKDF2 using HMAC-SHA512. |
132 | pub static PBKDF2_HMAC_SHA512: Algorithm = Algorithm(hmac::HMAC_SHA512); |
133 | |
134 | /// Fills `out` with the key derived using PBKDF2 with the given inputs. |
135 | /// |
136 | /// Do not use `derive` as part of verifying a secret; use `verify` instead, to |
137 | /// minimize the effectiveness of timing attacks. |
138 | /// |
139 | /// `out.len()` must be no larger than the digest length * (2**32 - 1), per the |
140 | /// PBKDF2 specification. |
141 | /// |
142 | /// | Parameter | RFC 2898 Section 5.2 Term |
143 | /// |-------------|------------------------------------------- |
144 | /// | digest_alg | PRF (HMAC with the given digest algorithm) |
145 | /// | iterations | c (iteration count) |
146 | /// | salt | S (salt) |
147 | /// | secret | P (password) |
148 | /// | out | dk (derived key) |
149 | /// | out.len() | dkLen (derived key length) |
150 | /// |
151 | /// # Panics |
152 | /// |
153 | /// `derive` panics if `out.len()` is larger than (2**32 - 1) * the digest |
154 | /// algorithm's output length, per the PBKDF2 specification. |
155 | pub fn derive( |
156 | algorithm: Algorithm, |
157 | iterations: NonZeroU32, |
158 | salt: &[u8], |
159 | secret: &[u8], |
160 | out: &mut [u8], |
161 | ) { |
162 | let digest_alg: &Algorithm = algorithm.0.digest_algorithm(); |
163 | let output_len: usize = digest_alg.output_len(); |
164 | |
165 | // This implementation's performance is asymptotically optimal as described |
166 | // in https://jbp.io/2015/08/11/pbkdf2-performance-matters/. However, it |
167 | // hasn't been optimized to the same extent as fastpbkdf2. In particular, |
168 | // this implementation is probably doing a lot of unnecessary copying. |
169 | |
170 | let secret: Key = hmac::Key::new(algorithm:algorithm.0, key_value:secret); |
171 | |
172 | // Clear |out|. |
173 | out.fill(0); |
174 | |
175 | let mut idx: u32 = 0; |
176 | |
177 | for chunk: &mut [u8] in out.chunks_mut(chunk_size:output_len) { |
178 | idx = idx.checked_add(1).expect(msg:"derived key too long" ); |
179 | derive_block(&secret, iterations, salt, idx, out:chunk); |
180 | } |
181 | } |
182 | |
183 | fn derive_block(secret: &hmac::Key, iterations: NonZeroU32, salt: &[u8], idx: u32, out: &mut [u8]) { |
184 | let mut ctx: Context = hmac::Context::with_key(signing_key:secret); |
185 | ctx.update(data:salt); |
186 | ctx.update(&u32::to_be_bytes(self:idx)); |
187 | |
188 | let mut u: Tag = ctx.sign(); |
189 | |
190 | let mut remaining: u32 = iterations.into(); |
191 | loop { |
192 | out.iter_mut().zip(u.as_ref()).for_each(|(o: &mut u8, u: &u8)| *o ^= *u); |
193 | |
194 | if remaining == 1 { |
195 | break; |
196 | } |
197 | remaining -= 1; |
198 | |
199 | u = hmac::sign(key:secret, data:u.as_ref()); |
200 | } |
201 | } |
202 | |
203 | /// Verifies that a previously-derived (e.g., using `derive`) PBKDF2 value |
204 | /// matches the PBKDF2 value derived from the other inputs. |
205 | /// |
206 | /// The comparison is done in constant time to prevent timing attacks. The |
207 | /// comparison will fail if `previously_derived` is empty (has a length of |
208 | /// zero). |
209 | /// |
210 | /// | Parameter | RFC 2898 Section 5.2 Term |
211 | /// |----------------------------|-------------------------------------------- |
212 | /// | digest_alg | PRF (HMAC with the given digest algorithm). |
213 | /// | `iterations` | c (iteration count) |
214 | /// | `salt` | S (salt) |
215 | /// | `secret` | P (password) |
216 | /// | `previously_derived` | dk (derived key) |
217 | /// | `previously_derived.len()` | dkLen (derived key length) |
218 | /// |
219 | /// # Panics |
220 | /// |
221 | /// `verify` panics if `out.len()` is larger than (2**32 - 1) * the digest |
222 | /// algorithm's output length, per the PBKDF2 specification. |
223 | pub fn verify( |
224 | algorithm: Algorithm, |
225 | iterations: NonZeroU32, |
226 | salt: &[u8], |
227 | secret: &[u8], |
228 | previously_derived: &[u8], |
229 | ) -> Result<(), error::Unspecified> { |
230 | let digest_alg = algorithm.0.digest_algorithm(); |
231 | |
232 | if previously_derived.is_empty() { |
233 | return Err(error::Unspecified); |
234 | } |
235 | |
236 | let mut derived_buf = [0u8; digest::MAX_OUTPUT_LEN]; |
237 | |
238 | let output_len = digest_alg.output_len(); |
239 | let secret = hmac::Key::new(algorithm.0, secret); |
240 | let mut idx: u32 = 0; |
241 | |
242 | let mut matches = 1; |
243 | |
244 | for previously_derived_chunk in previously_derived.chunks(output_len) { |
245 | idx = idx.checked_add(1).expect("derived key too long" ); |
246 | |
247 | let derived_chunk = &mut derived_buf[..previously_derived_chunk.len()]; |
248 | derived_chunk.fill(0); |
249 | |
250 | derive_block(&secret, iterations, salt, idx, derived_chunk); |
251 | |
252 | // XXX: This isn't fully constant-time-safe. TODO: Fix that. |
253 | #[allow (clippy::bool_to_int_with_if)] |
254 | let current_block_matches = |
255 | if constant_time::verify_slices_are_equal(derived_chunk, previously_derived_chunk) |
256 | .is_ok() |
257 | { |
258 | 1 |
259 | } else { |
260 | 0 |
261 | }; |
262 | |
263 | matches &= current_block_matches; |
264 | } |
265 | |
266 | if matches == 0 { |
267 | return Err(error::Unspecified); |
268 | } |
269 | |
270 | Ok(()) |
271 | } |
272 | |