mundane/
kdf.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Key Derivation Functions (KDFs).
6//!
7//! KDFs are low-level primitives often used to construct higher-level
8//! protocols. Unless you're sure that this is what you need, you should
9//! probably be using something else. In particular:
10//! - If you need password verification, see the [`password`] module.
11//!
12//! *This module is available if Mundane is built with the `kdf` feature.*
13//!
14//! [`password`]: ::password
15
16use std::num::NonZeroU32;
17
18use boringssl;
19use hash::Hasher;
20
21/// The PBKDF2 Key Derivation Function.
22///
23/// `pbkdf2` computes `iter` iterations of PBKDF2 of `password` and `salt`,
24/// using an HMAC based on the hash function `H`. It stores the result in
25/// `out_key`. Note that PBKDF2 can produce variable-length output, so it will
26/// always fill the entirety of `out_key` regardless of its length.
27///
28/// PBKDF2 is defined in RSA Security LLC's Public Key Cryptography Standards #5
29/// (PKCS #5) v2.0. For details, see [RFC 2898 Section 5.2].
30///
31/// # Security
32///
33/// While PBKDF2 can produce any amount of key output, the entropy of its output
34/// is bounded by the internal state. Be careful that the output key has enough
35/// entropy for your needs. See [RFC 2898 Appendix B.1] for a discussion on
36/// calculating the effective entropy of PBKDF2. Also remember that new attacks
37/// are sometimes discovered, and it is your responsibility to keep up with the
38/// latest attacks; RFC 2898's analysis may not be valid forever!
39///
40/// [RFC 2898 Section 5.2]: https://tools.ietf.org/html/rfc2898#section-5.2
41/// [RFC 2898 Appendix B.1]: https://tools.ietf.org/html/rfc2898#appendix-B.1
42pub fn pbkdf2<H: Hasher>(password: &[u8], salt: &[u8], iters: NonZeroU32, out_key: &mut [u8]) {
43    // PKCS5_PBKDF2_HMAC can only fail on OOM or if iters is 0.
44    boringssl::pkcs5_pbkdf2_hmac(password, salt, iters.get(), &H::evp_md(), out_key).unwrap();
45}
46
47#[cfg(feature = "insecure")]
48pub(crate) mod insecure_pbkdf2_hmac_sha1 {
49    use std::num::NonZeroU32;
50
51    #[allow(deprecated)]
52    use hash::InsecureSha1;
53    use kdf::pbkdf2;
54
55    /// INSECURE: The PBKDF2 Key Derivation Function over HMAC-SHA1.
56    ///
57    /// # Security
58    ///
59    /// PBKDF2-HMAC-SHA1 is considered insecure, and should only be used for
60    /// compatibility with legacy applications.
61    ///
62    /// # Behavior
63    ///
64    /// `pbkdf2_hmac_sha1` computes `iter` iterations of PBKDF2-HMAC-SHA1 of
65    /// `password` and `salt`. It stores the result in `out_key`.
66    ///
67    /// PBKDF2 is defined in RSA Security LLC's Public Key Cryptography
68    /// Standards #5 (PKCS #5) v2.0. For details, see [RFC 2898 Section 5.2].
69    ///
70    /// # Further Security Considerations
71    ///
72    /// While PBKDF2 can produce any amount of key output, the entropy of its
73    /// output is bounded by the internal state. Be careful that the output key
74    /// has enough entropy for your needs. See [RFC 2898 Appendix B.1] for a
75    /// discussion on calculating the effective entropy of PBKDF2, but keep in
76    /// mind that SHA-1's insecurities may affect this analysis! Also remember
77    /// that new attacks are sometimes discovered, and it is your responsibility
78    /// to keep up with the latest attacks; RFC 2898's analysis may not be valid
79    /// forever!
80    ///
81    /// [RFC 2898 Section 5.2]: https://tools.ietf.org/html/rfc2898#section-5.2
82    /// [RFC 2898 Appendix B.1]: https://tools.ietf.org/html/rfc2898#appendix-B.1
83    #[deprecated(note = "PBKDF2-HMAC-SHA1 is considered insecure")]
84    pub fn insecure_pbkdf2_hmac_sha1(
85        password: &[u8],
86        salt: &[u8],
87        iters: NonZeroU32,
88        out_key: &mut [u8],
89    ) {
90        #[allow(deprecated)]
91        pbkdf2::<InsecureSha1>(password, salt, iters, out_key)
92    }
93}
94
95#[cfg(test)]
96mod tests {
97    use super::*;
98    use hash::*;
99
100    #[test]
101    fn test_smoke() {
102        for password_len in 0..8 {
103            for salt_len in 0..8 {
104                for iters in 1..8 {
105                    for out_key_len in 0..8 {
106                        fn test<H: Hasher>(
107                            password_len: usize,
108                            salt_len: usize,
109                            iters: u32,
110                            out_key_len: usize,
111                        ) {
112                            let password = [0, 1, 2, 3, 4, 5, 6, 7];
113                            let salt = [0, 1, 2, 3, 4, 5, 6, 7];
114                            let mut out_key_0 = [0; 8];
115                            let mut out_key_1 = [0; 8];
116
117                            pbkdf2::<H>(
118                                &password[..password_len],
119                                &salt[..salt_len],
120                                NonZeroU32::new(iters).unwrap(),
121                                &mut out_key_0[..out_key_len],
122                            );
123                            pbkdf2::<H>(
124                                &password[..password_len],
125                                &salt[..salt_len],
126                                NonZeroU32::new(iters).unwrap(),
127                                &mut out_key_1[..out_key_len],
128                            );
129                            assert_eq!(&out_key_0[..out_key_len], &out_key_1[..out_key_len]);
130                        }
131
132                        test::<Sha256>(password_len, salt_len, iters, out_key_len);
133                        test::<Sha384>(password_len, salt_len, iters, out_key_len);
134                        test::<Sha512>(password_len, salt_len, iters, out_key_len);
135                    }
136                }
137            }
138        }
139    }
140}