1 | #[cfg (not(boringssl))] |
2 | use libc::c_int; |
3 | use std::convert::TryInto; |
4 | #[cfg (not(boringssl))] |
5 | use std::ptr; |
6 | |
7 | use crate::cvt; |
8 | use crate::error::ErrorStack; |
9 | use crate::hash::MessageDigest; |
10 | #[cfg (not(boringssl))] |
11 | use crate::symm::Cipher; |
12 | use openssl_macros::corresponds; |
13 | |
14 | #[derive (Clone, Eq, PartialEq, Hash, Debug)] |
15 | pub struct KeyIvPair { |
16 | pub key: Vec<u8>, |
17 | pub iv: Option<Vec<u8>>, |
18 | } |
19 | |
20 | /// Derives a key and an IV from various parameters. |
21 | /// |
22 | /// If specified, `salt` must be 8 bytes in length. |
23 | /// |
24 | /// If the total key and IV length is less than 16 bytes and MD5 is used then |
25 | /// the algorithm is compatible with the key derivation algorithm from PKCS#5 |
26 | /// v1.5 or PBKDF1 from PKCS#5 v2.0. |
27 | /// |
28 | /// New applications should not use this and instead use |
29 | /// `pbkdf2_hmac` or another more modern key derivation algorithm. |
30 | #[corresponds (EVP_BytesToKey)] |
31 | #[allow (clippy::useless_conversion)] |
32 | #[cfg (not(boringssl))] |
33 | pub fn bytes_to_key( |
34 | cipher: Cipher, |
35 | digest: MessageDigest, |
36 | data: &[u8], |
37 | salt: Option<&[u8]>, |
38 | count: i32, |
39 | ) -> Result<KeyIvPair, ErrorStack> { |
40 | unsafe { |
41 | assert!(data.len() <= c_int::max_value() as usize); |
42 | let salt_ptr = match salt { |
43 | Some(salt) => { |
44 | assert_eq!(salt.len(), ffi::PKCS5_SALT_LEN as usize); |
45 | salt.as_ptr() |
46 | } |
47 | None => ptr::null(), |
48 | }; |
49 | |
50 | ffi::init(); |
51 | |
52 | let mut iv = cipher.iv_len().map(|l| vec![0; l]); |
53 | |
54 | let cipher = cipher.as_ptr(); |
55 | let digest = digest.as_ptr(); |
56 | |
57 | let len = cvt(ffi::EVP_BytesToKey( |
58 | cipher, |
59 | digest, |
60 | salt_ptr, |
61 | ptr::null(), |
62 | data.len() as c_int, |
63 | count.into(), |
64 | ptr::null_mut(), |
65 | ptr::null_mut(), |
66 | ))?; |
67 | |
68 | let mut key = vec![0; len as usize]; |
69 | let iv_ptr = iv |
70 | .as_mut() |
71 | .map(|v| v.as_mut_ptr()) |
72 | .unwrap_or(ptr::null_mut()); |
73 | |
74 | cvt(ffi::EVP_BytesToKey( |
75 | cipher, |
76 | digest, |
77 | salt_ptr, |
78 | data.as_ptr(), |
79 | data.len() as c_int, |
80 | count as c_int, |
81 | key.as_mut_ptr(), |
82 | iv_ptr, |
83 | ))?; |
84 | |
85 | Ok(KeyIvPair { key, iv }) |
86 | } |
87 | } |
88 | |
89 | /// Derives a key from a password and salt using the PBKDF2-HMAC algorithm with a digest function. |
90 | #[corresponds (PKCS5_PBKDF2_HMAC)] |
91 | pub fn pbkdf2_hmac( |
92 | pass: &[u8], |
93 | salt: &[u8], |
94 | iter: usize, |
95 | hash: MessageDigest, |
96 | key: &mut [u8], |
97 | ) -> Result<(), ErrorStack> { |
98 | unsafe { |
99 | ffi::init(); |
100 | cvt(ffi::PKCS5_PBKDF2_HMAC( |
101 | pass.as_ptr() as *const _, |
102 | pass.len().try_into().unwrap(), |
103 | salt.as_ptr(), |
104 | salt.len().try_into().unwrap(), |
105 | iter.try_into().unwrap(), |
106 | hash.as_ptr(), |
107 | key.len().try_into().unwrap(), |
108 | key.as_mut_ptr(), |
109 | )) |
110 | .map(|_| ()) |
111 | } |
112 | } |
113 | |
114 | /// Derives a key from a password and salt using the scrypt algorithm. |
115 | /// |
116 | /// Requires OpenSSL 1.1.0 or newer. |
117 | #[corresponds (EVP_PBE_scrypt)] |
118 | #[cfg (all(any(ossl110, boringssl), not(osslconf = "OPENSSL_NO_SCRYPT" )))] |
119 | #[allow (clippy::useless_conversion)] |
120 | pub fn scrypt( |
121 | pass: &[u8], |
122 | salt: &[u8], |
123 | n: u64, |
124 | r: u64, |
125 | p: u64, |
126 | maxmem: u64, |
127 | key: &mut [u8], |
128 | ) -> Result<(), ErrorStack> { |
129 | unsafe { |
130 | ffi::init(); |
131 | cvt(ffi::EVP_PBE_scrypt( |
132 | pass.as_ptr() as *const _, |
133 | pass.len(), |
134 | salt.as_ptr() as *const _, |
135 | salt.len(), |
136 | n, |
137 | r, |
138 | p, |
139 | maxmem.try_into().unwrap(), |
140 | key.as_mut_ptr() as *mut _, |
141 | key.len(), |
142 | )) |
143 | .map(|_| ()) |
144 | } |
145 | } |
146 | |
147 | #[cfg (test)] |
148 | mod tests { |
149 | use crate::hash::MessageDigest; |
150 | #[cfg (not(boringssl))] |
151 | use crate::symm::Cipher; |
152 | |
153 | // Test vectors from |
154 | // https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.1.1_release_20150424/testsuite/pbkdf2-test.c |
155 | #[test ] |
156 | fn pbkdf2_hmac_sha256() { |
157 | let mut buf = [0; 16]; |
158 | |
159 | super::pbkdf2_hmac(b"passwd" , b"salt" , 1, MessageDigest::sha256(), &mut buf).unwrap(); |
160 | assert_eq!( |
161 | buf, |
162 | &[ |
163 | 0x55_u8, 0xac_u8, 0x04_u8, 0x6e_u8, 0x56_u8, 0xe3_u8, 0x08_u8, 0x9f_u8, 0xec_u8, |
164 | 0x16_u8, 0x91_u8, 0xc2_u8, 0x25_u8, 0x44_u8, 0xb6_u8, 0x05_u8, |
165 | ][..] |
166 | ); |
167 | |
168 | super::pbkdf2_hmac( |
169 | b"Password" , |
170 | b"NaCl" , |
171 | 80000, |
172 | MessageDigest::sha256(), |
173 | &mut buf, |
174 | ) |
175 | .unwrap(); |
176 | assert_eq!( |
177 | buf, |
178 | &[ |
179 | 0x4d_u8, 0xdc_u8, 0xd8_u8, 0xf6_u8, 0x0b_u8, 0x98_u8, 0xbe_u8, 0x21_u8, 0x83_u8, |
180 | 0x0c_u8, 0xee_u8, 0x5e_u8, 0xf2_u8, 0x27_u8, 0x01_u8, 0xf9_u8, |
181 | ][..] |
182 | ); |
183 | } |
184 | |
185 | // Test vectors from |
186 | // https://git.lysator.liu.se/nettle/nettle/blob/nettle_3.1.1_release_20150424/testsuite/pbkdf2-test.c |
187 | #[test ] |
188 | fn pbkdf2_hmac_sha512() { |
189 | let mut buf = [0; 64]; |
190 | |
191 | super::pbkdf2_hmac(b"password" , b"NaCL" , 1, MessageDigest::sha512(), &mut buf).unwrap(); |
192 | assert_eq!( |
193 | &buf[..], |
194 | &[ |
195 | 0x73_u8, 0xde_u8, 0xcf_u8, 0xa5_u8, 0x8a_u8, 0xa2_u8, 0xe8_u8, 0x4f_u8, 0x94_u8, |
196 | 0x77_u8, 0x1a_u8, 0x75_u8, 0x73_u8, 0x6b_u8, 0xb8_u8, 0x8b_u8, 0xd3_u8, 0xc7_u8, |
197 | 0xb3_u8, 0x82_u8, 0x70_u8, 0xcf_u8, 0xb5_u8, 0x0c_u8, 0xb3_u8, 0x90_u8, 0xed_u8, |
198 | 0x78_u8, 0xb3_u8, 0x05_u8, 0x65_u8, 0x6a_u8, 0xf8_u8, 0x14_u8, 0x8e_u8, 0x52_u8, |
199 | 0x45_u8, 0x2b_u8, 0x22_u8, 0x16_u8, 0xb2_u8, 0xb8_u8, 0x09_u8, 0x8b_u8, 0x76_u8, |
200 | 0x1f_u8, 0xc6_u8, 0x33_u8, 0x60_u8, 0x60_u8, 0xa0_u8, 0x9f_u8, 0x76_u8, 0x41_u8, |
201 | 0x5e_u8, 0x9f_u8, 0x71_u8, 0xea_u8, 0x47_u8, 0xf9_u8, 0xe9_u8, 0x06_u8, 0x43_u8, |
202 | 0x06_u8, |
203 | ][..] |
204 | ); |
205 | |
206 | super::pbkdf2_hmac( |
207 | b"pass \0word" , |
208 | b"sa \0lt" , |
209 | 1, |
210 | MessageDigest::sha512(), |
211 | &mut buf, |
212 | ) |
213 | .unwrap(); |
214 | assert_eq!( |
215 | &buf[..], |
216 | &[ |
217 | 0x71_u8, 0xa0_u8, 0xec_u8, 0x84_u8, 0x2a_u8, 0xbd_u8, 0x5c_u8, 0x67_u8, 0x8b_u8, |
218 | 0xcf_u8, 0xd1_u8, 0x45_u8, 0xf0_u8, 0x9d_u8, 0x83_u8, 0x52_u8, 0x2f_u8, 0x93_u8, |
219 | 0x36_u8, 0x15_u8, 0x60_u8, 0x56_u8, 0x3c_u8, 0x4d_u8, 0x0d_u8, 0x63_u8, 0xb8_u8, |
220 | 0x83_u8, 0x29_u8, 0x87_u8, 0x10_u8, 0x90_u8, 0xe7_u8, 0x66_u8, 0x04_u8, 0xa4_u8, |
221 | 0x9a_u8, 0xf0_u8, 0x8f_u8, 0xe7_u8, 0xc9_u8, 0xf5_u8, 0x71_u8, 0x56_u8, 0xc8_u8, |
222 | 0x79_u8, 0x09_u8, 0x96_u8, 0xb2_u8, 0x0f_u8, 0x06_u8, 0xbc_u8, 0x53_u8, 0x5e_u8, |
223 | 0x5a_u8, 0xb5_u8, 0x44_u8, 0x0d_u8, 0xf7_u8, 0xe8_u8, 0x78_u8, 0x29_u8, 0x6f_u8, |
224 | 0xa7_u8, |
225 | ][..] |
226 | ); |
227 | |
228 | super::pbkdf2_hmac( |
229 | b"passwordPASSWORDpassword" , |
230 | b"salt \0\0\0" , |
231 | 50, |
232 | MessageDigest::sha512(), |
233 | &mut buf, |
234 | ) |
235 | .unwrap(); |
236 | assert_eq!( |
237 | &buf[..], |
238 | &[ |
239 | 0x01_u8, 0x68_u8, 0x71_u8, 0xa4_u8, 0xc4_u8, 0xb7_u8, 0x5f_u8, 0x96_u8, 0x85_u8, |
240 | 0x7f_u8, 0xd2_u8, 0xb9_u8, 0xf8_u8, 0xca_u8, 0x28_u8, 0x02_u8, 0x3b_u8, 0x30_u8, |
241 | 0xee_u8, 0x2a_u8, 0x39_u8, 0xf5_u8, 0xad_u8, 0xca_u8, 0xc8_u8, 0xc9_u8, 0x37_u8, |
242 | 0x5f_u8, 0x9b_u8, 0xda_u8, 0x1c_u8, 0xcd_u8, 0x1b_u8, 0x6f_u8, 0x0b_u8, 0x2f_u8, |
243 | 0xc3_u8, 0xad_u8, 0xda_u8, 0x50_u8, 0x54_u8, 0x12_u8, 0xe7_u8, 0x9d_u8, 0x89_u8, |
244 | 0x00_u8, 0x56_u8, 0xc6_u8, 0x2e_u8, 0x52_u8, 0x4c_u8, 0x7d_u8, 0x51_u8, 0x15_u8, |
245 | 0x4b_u8, 0x1a_u8, 0x85_u8, 0x34_u8, 0x57_u8, 0x5b_u8, 0xd0_u8, 0x2d_u8, 0xee_u8, |
246 | 0x39_u8, |
247 | ][..] |
248 | ); |
249 | } |
250 | |
251 | #[test ] |
252 | #[cfg (not(boringssl))] |
253 | fn bytes_to_key() { |
254 | let salt = [16_u8, 34_u8, 19_u8, 23_u8, 141_u8, 4_u8, 207_u8, 221_u8]; |
255 | |
256 | let data = [ |
257 | 143_u8, 210_u8, 75_u8, 63_u8, 214_u8, 179_u8, 155_u8, 241_u8, 242_u8, 31_u8, 154_u8, |
258 | 56_u8, 198_u8, 145_u8, 192_u8, 64_u8, 2_u8, 245_u8, 167_u8, 220_u8, 55_u8, 119_u8, |
259 | 233_u8, 136_u8, 139_u8, 27_u8, 71_u8, 242_u8, 119_u8, 175_u8, 65_u8, 207_u8, |
260 | ]; |
261 | |
262 | let expected_key = vec![ |
263 | 249_u8, 115_u8, 114_u8, 97_u8, 32_u8, 213_u8, 165_u8, 146_u8, 58_u8, 87_u8, 234_u8, |
264 | 3_u8, 43_u8, 250_u8, 97_u8, 114_u8, 26_u8, 98_u8, 245_u8, 246_u8, 238_u8, 177_u8, |
265 | 229_u8, 161_u8, 183_u8, 224_u8, 174_u8, 3_u8, 6_u8, 244_u8, 236_u8, 255_u8, |
266 | ]; |
267 | let expected_iv = vec![ |
268 | 4_u8, 223_u8, 153_u8, 219_u8, 28_u8, 142_u8, 234_u8, 68_u8, 227_u8, 69_u8, 98_u8, |
269 | 107_u8, 208_u8, 14_u8, 236_u8, 60_u8, |
270 | ]; |
271 | |
272 | assert_eq!( |
273 | super::bytes_to_key( |
274 | Cipher::aes_256_cbc(), |
275 | MessageDigest::sha1(), |
276 | &data, |
277 | Some(&salt), |
278 | 1, |
279 | ) |
280 | .unwrap(), |
281 | super::KeyIvPair { |
282 | key: expected_key, |
283 | iv: Some(expected_iv), |
284 | } |
285 | ); |
286 | } |
287 | |
288 | #[test ] |
289 | #[cfg (any(ossl110, boringssl))] |
290 | fn scrypt() { |
291 | let pass = "pleaseletmein" ; |
292 | let salt = "SodiumChloride" ; |
293 | let expected = |
294 | "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613\ |
295 | f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887" ; |
296 | |
297 | let mut actual = [0; 64]; |
298 | super::scrypt( |
299 | pass.as_bytes(), |
300 | salt.as_bytes(), |
301 | 16384, |
302 | 8, |
303 | 1, |
304 | 0, |
305 | &mut actual, |
306 | ) |
307 | .unwrap(); |
308 | assert_eq!(hex::encode(&actual[..]), expected); |
309 | } |
310 | } |
311 | |