| 1 | #[cfg (not(any(boringssl, awslc)))] |
| 2 | use libc::c_int; |
| 3 | use std::convert::TryInto; |
| 4 | #[cfg (not(any(boringssl, awslc)))] |
| 5 | use std::ptr; |
| 6 | |
| 7 | use crate::cvt; |
| 8 | use crate::error::ErrorStack; |
| 9 | use crate::hash::MessageDigest; |
| 10 | #[cfg (not(any(boringssl, awslc)))] |
| 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(any(boringssl, awslc)))] |
| 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 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, awslc), 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(any(boringssl, awslc)))] |
| 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(any(boringssl, awslc)))] |
| 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, awslc))] |
| 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 | |