/rust/git/checkouts/nss-rs-71e20fe79ef91440/9b94ca3/src/pbkdf2.rs
Line | Count | Source |
1 | | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
2 | | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
3 | | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
4 | | // option. This file may not be copied, modified, or distributed |
5 | | // except according to those terms. |
6 | | |
7 | | use std::{os::raw::c_int, ptr::null_mut}; |
8 | | |
9 | | use crate::{ |
10 | | Error, SECItemBorrowed, |
11 | | hmac::{HmacAlgorithm, hmac_alg_to_prf_oid}, |
12 | | p11::{ |
13 | | PK11_CreatePBEV2AlgorithmID, PK11_PBEKeyGen, PRBool, SECOID_DestroyAlgorithmID, SECOidTag, |
14 | | Slot, SymKey, |
15 | | }, |
16 | | }; |
17 | | |
18 | | /// Derive a key using PBKDF2. |
19 | | /// |
20 | | /// Returns the derived key bytes as a `Vec<u8>`. |
21 | | /// |
22 | | /// # Errors |
23 | | /// |
24 | | /// Returns an error if inputs have invalid lengths, or if NSS functions fail. |
25 | 0 | pub fn pbkdf2( |
26 | 0 | alg: &HmacAlgorithm, |
27 | 0 | password: &[u8], |
28 | 0 | salt: &[u8], |
29 | 0 | iterations: u32, |
30 | 0 | key_len: usize, |
31 | 0 | ) -> Result<Vec<u8>, Error> { |
32 | 0 | crate::init()?; |
33 | | |
34 | 0 | let iterations = c_int::try_from(iterations)?; |
35 | 0 | let key_len_int = c_int::try_from(key_len)?; |
36 | | |
37 | 0 | let mut salt_item = SECItemBorrowed::wrap(salt)?; |
38 | | |
39 | 0 | let slot = Slot::internal()?; |
40 | 0 | let mut pw_item = SECItemBorrowed::wrap(password)?; |
41 | | |
42 | 0 | let algid = unsafe { |
43 | 0 | PK11_CreatePBEV2AlgorithmID( |
44 | | SECOidTag::SEC_OID_PKCS5_PBKDF2, |
45 | 0 | hmac_alg_to_prf_oid(alg), |
46 | 0 | hmac_alg_to_prf_oid(alg), |
47 | 0 | key_len_int, |
48 | 0 | iterations, |
49 | 0 | salt_item.as_mut(), |
50 | | ) |
51 | | }; |
52 | 0 | if algid.is_null() { |
53 | 0 | return Err(Error::last_nss_error()); |
54 | 0 | } |
55 | | |
56 | 0 | let key_ptr = unsafe { |
57 | 0 | PK11_PBEKeyGen( |
58 | 0 | *slot, |
59 | 0 | algid, |
60 | 0 | pw_item.as_mut(), |
61 | 0 | PRBool::from(false), |
62 | 0 | null_mut(), |
63 | | ) |
64 | | }; |
65 | 0 | unsafe { |
66 | 0 | SECOID_DestroyAlgorithmID(algid, PRBool::from(true)); |
67 | 0 | } |
68 | | |
69 | 0 | let key = SymKey::from_ptr(key_ptr)?; |
70 | 0 | let data = key.key_data()?; |
71 | 0 | Ok(Vec::from(data)) |
72 | 0 | } |
73 | | |
74 | | #[cfg(test)] |
75 | | mod tests { |
76 | | use super::*; |
77 | | |
78 | | #[test] |
79 | | fn rfc_7914_vector_1() { |
80 | | // RFC 7914 ยง11 provides PBKDF2-HMAC-SHA256 vectors. Using a common one: |
81 | | // password="password", salt="salt", iter=1, dkLen=32. |
82 | | let dk = pbkdf2(&HmacAlgorithm::HMAC_SHA2_256, b"password", b"salt", 1, 32).unwrap(); |
83 | | let expected = [ |
84 | | 0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, |
85 | | 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, |
86 | | 0xb7, 0x0b, 0xe1, 0x7b, |
87 | | ]; |
88 | | assert_eq!(dk, expected); |
89 | | } |
90 | | |
91 | | #[test] |
92 | | fn rfc_7914_vector_iter_2() { |
93 | | let dk = pbkdf2(&HmacAlgorithm::HMAC_SHA2_256, b"password", b"salt", 2, 32).unwrap(); |
94 | | let expected = [ |
95 | | 0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3, 0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, |
96 | | 0x6d, 0xd0, 0x2a, 0x30, 0x3f, 0x8e, 0xf3, 0xc2, 0x51, 0xdf, 0xd6, 0xe2, 0xd8, 0x5a, |
97 | | 0x95, 0x47, 0x4c, 0x43, |
98 | | ]; |
99 | | assert_eq!(dk, expected); |
100 | | } |
101 | | |
102 | | #[test] |
103 | | fn pbkdf2_sha384_vector() { |
104 | | let dk = pbkdf2(&HmacAlgorithm::HMAC_SHA2_384, b"password", b"salt", 1, 20).unwrap(); |
105 | | let expected = [ |
106 | | 0xc0, 0xe1, 0x4f, 0x06, 0xe4, 0x9e, 0x32, 0xd7, 0x3f, 0x9f, 0x52, 0xdd, 0xf1, 0xd0, |
107 | | 0xc5, 0xc7, 0x19, 0x16, 0x09, 0x23, |
108 | | ]; |
109 | | assert_eq!(dk, expected); |
110 | | } |
111 | | |
112 | | #[test] |
113 | | fn pbkdf2_sha512_vector() { |
114 | | let dk = pbkdf2(&HmacAlgorithm::HMAC_SHA2_512, b"password", b"salt", 1, 20).unwrap(); |
115 | | let expected = [ |
116 | | 0x86, 0x7f, 0x70, 0xcf, 0x1a, 0xde, 0x02, 0xcf, 0xf3, 0x75, 0x25, 0x99, 0xa3, 0xa5, |
117 | | 0x3d, 0xc4, 0xaf, 0x34, 0xc7, 0xa6, |
118 | | ]; |
119 | | assert_eq!(dk, expected); |
120 | | } |
121 | | |
122 | | #[test] |
123 | | fn deterministic_across_calls() { |
124 | | let a = pbkdf2( |
125 | | &HmacAlgorithm::HMAC_SHA2_256, |
126 | | b"hello", |
127 | | b"saltysalt0000000", |
128 | | 10_000, |
129 | | 32, |
130 | | ) |
131 | | .unwrap(); |
132 | | let b = pbkdf2( |
133 | | &HmacAlgorithm::HMAC_SHA2_256, |
134 | | b"hello", |
135 | | b"saltysalt0000000", |
136 | | 10_000, |
137 | | 32, |
138 | | ) |
139 | | .unwrap(); |
140 | | assert_eq!(a, b); |
141 | | } |
142 | | |
143 | | #[test] |
144 | | fn different_salt_different_key() { |
145 | | let a = pbkdf2( |
146 | | &HmacAlgorithm::HMAC_SHA2_256, |
147 | | b"hello", |
148 | | b"saltysalt0000000", |
149 | | 10_000, |
150 | | 32, |
151 | | ) |
152 | | .unwrap(); |
153 | | let b = pbkdf2( |
154 | | &HmacAlgorithm::HMAC_SHA2_256, |
155 | | b"hello", |
156 | | b"saltysalt0000001", |
157 | | 10_000, |
158 | | 32, |
159 | | ) |
160 | | .unwrap(); |
161 | | assert_ne!(a, b); |
162 | | } |
163 | | } |