| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Key Derivation Functions (KDFs). |
| //! |
| //! KDFs are low-level primitives often used to construct higher-level |
| //! protocols. Unless you're sure that this is what you need, you should |
| //! probably be using something else. In particular: |
| //! - If you need password verification, see the [`password`] module. |
| //! |
| //! *This module is available if Mundane is built with the `kdf` feature.* |
| //! |
| //! [`password`]: ::password |
| |
| use std::num::NonZeroU32; |
| |
| use boringssl; |
| use hash::Hasher; |
| |
| /// The PBKDF2 Key Derivation Function. |
| /// |
| /// `pbkdf2` computes `iter` iterations of PBKDF2 of `password` and `salt`, |
| /// using an HMAC based on the hash function `H`. It stores the result in |
| /// `out_key`. Note that PBKDF2 can produce variable-length output, so it will |
| /// always fill the entirety of `out_key` regardless of its length. |
| /// |
| /// PBKDF2 is defined in RSA Security LLC's Public Key Cryptography Standards #5 |
| /// (PKCS #5) v2.0. For details, see [RFC 2898 Section 5.2]. |
| /// |
| /// # Security |
| /// |
| /// While PBKDF2 can produce any amount of key output, the entropy of its output |
| /// is bounded by the internal state. Be careful that the output key has enough |
| /// entropy for your needs. See [RFC 2898 Appendix B.1] for a discussion on |
| /// calculating the effective entropy of PBKDF2. Also remember that new attacks |
| /// are sometimes discovered, and it is your responsibility to keep up with the |
| /// latest attacks; RFC 2898's analysis may not be valid forever! |
| /// |
| /// [RFC 2898 Section 5.2]: https://tools.ietf.org/html/rfc2898#section-5.2 |
| /// [RFC 2898 Appendix B.1]: https://tools.ietf.org/html/rfc2898#appendix-B.1 |
| pub fn pbkdf2<H: Hasher>(password: &[u8], salt: &[u8], iters: NonZeroU32, out_key: &mut [u8]) { |
| // PKCS5_PBKDF2_HMAC can only fail on OOM or if iters is 0. |
| boringssl::pkcs5_pbkdf2_hmac(password, salt, iters.get(), &H::evp_md(), out_key).unwrap(); |
| } |
| |
| #[cfg(feature = "insecure")] |
| pub(crate) mod insecure_pbkdf2_hmac_sha1 { |
| use std::num::NonZeroU32; |
| |
| #[allow(deprecated)] |
| use hash::InsecureSha1; |
| use kdf::pbkdf2; |
| |
| /// INSECURE: The PBKDF2 Key Derivation Function over HMAC-SHA1. |
| /// |
| /// # Security |
| /// |
| /// PBKDF2-HMAC-SHA1 is considered insecure, and should only be used for |
| /// compatibility with legacy applications. |
| /// |
| /// # Behavior |
| /// |
| /// `pbkdf2_hmac_sha1` computes `iter` iterations of PBKDF2-HMAC-SHA1 of |
| /// `password` and `salt`. It stores the result in `out_key`. |
| /// |
| /// PBKDF2 is defined in RSA Security LLC's Public Key Cryptography |
| /// Standards #5 (PKCS #5) v2.0. For details, see [RFC 2898 Section 5.2]. |
| /// |
| /// # Further Security Considerations |
| /// |
| /// While PBKDF2 can produce any amount of key output, the entropy of its |
| /// output is bounded by the internal state. Be careful that the output key |
| /// has enough entropy for your needs. See [RFC 2898 Appendix B.1] for a |
| /// discussion on calculating the effective entropy of PBKDF2, but keep in |
| /// mind that SHA-1's insecurities may affect this analysis! Also remember |
| /// that new attacks are sometimes discovered, and it is your responsibility |
| /// to keep up with the latest attacks; RFC 2898's analysis may not be valid |
| /// forever! |
| /// |
| /// [RFC 2898 Section 5.2]: https://tools.ietf.org/html/rfc2898#section-5.2 |
| /// [RFC 2898 Appendix B.1]: https://tools.ietf.org/html/rfc2898#appendix-B.1 |
| #[deprecated(note = "PBKDF2-HMAC-SHA1 is considered insecure")] |
| pub fn insecure_pbkdf2_hmac_sha1( |
| password: &[u8], |
| salt: &[u8], |
| iters: NonZeroU32, |
| out_key: &mut [u8], |
| ) { |
| #[allow(deprecated)] |
| pbkdf2::<InsecureSha1>(password, salt, iters, out_key) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use hash::*; |
| |
| #[test] |
| fn test_smoke() { |
| for password_len in 0..8 { |
| for salt_len in 0..8 { |
| for iters in 1..8 { |
| for out_key_len in 0..8 { |
| fn test<H: Hasher>( |
| password_len: usize, |
| salt_len: usize, |
| iters: u32, |
| out_key_len: usize, |
| ) { |
| let password = [0, 1, 2, 3, 4, 5, 6, 7]; |
| let salt = [0, 1, 2, 3, 4, 5, 6, 7]; |
| let mut out_key_0 = [0; 8]; |
| let mut out_key_1 = [0; 8]; |
| |
| pbkdf2::<H>( |
| &password[..password_len], |
| &salt[..salt_len], |
| NonZeroU32::new(iters).unwrap(), |
| &mut out_key_0[..out_key_len], |
| ); |
| pbkdf2::<H>( |
| &password[..password_len], |
| &salt[..salt_len], |
| NonZeroU32::new(iters).unwrap(), |
| &mut out_key_1[..out_key_len], |
| ); |
| assert_eq!(&out_key_0[..out_key_len], &out_key_1[..out_key_len]); |
| } |
| |
| test::<Sha256>(password_len, salt_len, iters, out_key_len); |
| test::<Sha384>(password_len, salt_len, iters, out_key_len); |
| test::<Sha512>(password_len, salt_len, iters, out_key_len); |
| } |
| } |
| } |
| } |
| } |
| } |