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