devolutions_crypto/key_derivation/
mod.rs1mod key_derivation_v1;
25mod key_derivation_v2;
26
27pub use key_derivation_v1::Pbkdf2;
28pub use key_derivation_v2::Argon2;
29
30use key_derivation_v1::KeyDerivationV1;
31use key_derivation_v2::KeyDerivationV2;
32
33use std::borrow::Borrow;
34use std::convert::TryFrom;
35
36#[cfg(feature = "fuzz")]
37use arbitrary::Arbitrary;
38
39#[cfg(feature = "wbindgen")]
40use wasm_bindgen::prelude::*;
41
42use zeroize::Zeroizing;
43
44use crate::key::{secret_key_from_raw, SecretKey};
45
46#[cfg(feature = "fuzz")]
47use crate::Argon2Parameters;
48use crate::{DataType, Error, Header, HeaderType, KeyDerivationVersion, Result};
49
50use super::enums::KeyDerivationSubtype;
51
52#[derive(Clone, Debug)]
57#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
58#[cfg_attr(feature = "wbindgen", wasm_bindgen(inspectable))]
59pub struct DerivationParameters {
60 pub(crate) header: Header<DerivationParameters>,
61 pub(super) payload: DerivationParametersPayload,
62}
63
64impl HeaderType for DerivationParameters {
65 type Version = KeyDerivationVersion;
66 type Subtype = KeyDerivationSubtype;
67
68 fn data_type() -> DataType {
69 DataType::KeyDerivation
70 }
71}
72
73#[derive(Clone, Debug)]
74#[cfg_attr(feature = "fuzz", derive(Arbitrary))]
75pub(super) enum DerivationParametersPayload {
76 V1(KeyDerivationV1),
77 V2(KeyDerivationV2),
78}
79
80#[cfg(feature = "fuzz")]
81impl<'a> Arbitrary<'a> for KeyDerivationV1 {
82 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
83 Ok(KeyDerivationV1 {
84 iterations: u32::arbitrary(u)?,
85 salt: Vec::<u8>::arbitrary(u)?,
86 })
87 }
88}
89
90#[cfg(feature = "fuzz")]
91impl<'a> Arbitrary<'a> for KeyDerivationV2 {
92 fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
93 Ok(KeyDerivationV2 {
94 params: Argon2Parameters::default(),
95 })
96 }
97}
98
99impl From<DerivationParameters> for Vec<u8> {
100 fn from(data: DerivationParameters) -> Self {
101 let mut header: Self = data.header.borrow().into();
102 let mut payload: Self = data.payload.into();
103 header.append(&mut payload);
104 header
105 }
106}
107
108impl TryFrom<&[u8]> for DerivationParameters {
109 type Error = Error;
110
111 fn try_from(data: &[u8]) -> Result<Self> {
112 if data.len() < Header::len() {
113 return Err(Error::InvalidLength);
114 }
115
116 let header = Header::try_from(&data[0..Header::len()])?;
117
118 let payload = match header.version {
119 KeyDerivationVersion::V1 => {
120 DerivationParametersPayload::V1(KeyDerivationV1::try_from(&data[Header::len()..])?)
121 }
122 KeyDerivationVersion::V2 => {
123 DerivationParametersPayload::V2(KeyDerivationV2::try_from(&data[Header::len()..])?)
124 }
125 KeyDerivationVersion::Latest => return Err(Error::UnknownVersion),
126 };
127
128 Ok(Self { header, payload })
129 }
130}
131
132impl From<DerivationParametersPayload> for Vec<u8> {
133 fn from(payload: DerivationParametersPayload) -> Self {
134 match payload {
135 DerivationParametersPayload::V1(v1) => Vec::from(&v1),
136 DerivationParametersPayload::V2(v2) => Vec::from(&v2),
137 }
138 }
139}
140
141impl DerivationParameters {
142 pub fn derive(&self, password: &[u8]) -> Result<SecretKey> {
144 let raw = match &self.payload {
145 DerivationParametersPayload::V1(v1) => v1.derive(password),
146 DerivationParametersPayload::V2(v2) => v2.derive(password)?,
147 };
148
149 secret_key_from_raw(raw)
150 }
151
152 pub fn compute(&self, password: &[u8]) -> Result<Zeroizing<Vec<u8>>> {
154 match &self.payload {
155 DerivationParametersPayload::V1(v1) => Ok(v1.derive(password)),
156 DerivationParametersPayload::V2(v2) => v2.derive(password),
157 }
158 }
159
160 pub fn output_length(&self) -> usize {
162 match &self.payload {
163 DerivationParametersPayload::V1(_) => key_derivation_v1::KEY_LENGTH,
164 DerivationParametersPayload::V2(v2) => v2.params.length as usize,
165 }
166 }
167}
168
169pub fn derive_key(
188 password: &[u8],
189 version: KeyDerivationVersion,
190) -> Result<(SecretKey, DerivationParameters)> {
191 match version {
192 KeyDerivationVersion::V1 => Pbkdf2::new().derive(password),
193 KeyDerivationVersion::V2 | KeyDerivationVersion::Latest => Argon2::new().derive(password),
194 }
195}
196
197#[cfg(test)]
200mod tests {
201 use std::convert::TryFrom;
202
203 use crate::key::secret_key_from_raw;
204 use crate::Argon2Parameters;
205
206 use super::*;
207
208 #[test]
211 fn pbkdf2_derive_same_input_same_salt_produces_same_key() {
212 let pbkdf2 = Pbkdf2::with_params(10);
213 let salt = b"fixed_salt_value";
214 let (key1, _) = pbkdf2.derive_with_salt(b"password", salt).unwrap();
215 let (key2, _) = pbkdf2.derive_with_salt(b"password", salt).unwrap();
216 assert_eq!(key1.as_bytes(), key2.as_bytes());
217 }
218
219 #[test]
220 fn pbkdf2_derive_different_password_produces_different_key() {
221 let pbkdf2 = Pbkdf2::with_params(10);
222 let salt = b"fixed_salt_value";
223 let (key1, _) = pbkdf2.derive_with_salt(b"password1", salt).unwrap();
224 let (key2, _) = pbkdf2.derive_with_salt(b"password2", salt).unwrap();
225 assert_ne!(key1.as_bytes(), key2.as_bytes());
226 }
227
228 #[test]
229 fn pbkdf2_derive_different_salt_produces_different_key() {
230 let pbkdf2 = Pbkdf2::with_params(10);
231 let (key1, _) = pbkdf2.derive_with_salt(b"password", b"salt_one").unwrap();
232 let (key2, _) = pbkdf2.derive_with_salt(b"password", b"salt_two").unwrap();
233 assert_ne!(key1.as_bytes(), key2.as_bytes());
234 }
235
236 #[test]
237 fn pbkdf2_derive_generates_random_salt() {
238 let pbkdf2 = Pbkdf2::with_params(10);
239 let (_, params1) = pbkdf2.derive(b"password").unwrap();
240 let (_, params2) = pbkdf2.derive(b"password").unwrap();
241 let bytes1: Vec<u8> = params1.into();
242 let bytes2: Vec<u8> = params2.into();
243 assert_ne!(bytes1, bytes2);
244 }
245
246 #[test]
247 fn pbkdf2_derive_with_salt_roundtrip() {
248 let pbkdf2 = Pbkdf2::with_params(10);
249 let salt = b"roundtrip_salt!!";
250 let (key1, params) = pbkdf2.derive_with_salt(b"password", salt).unwrap();
251
252 let params_bytes: Vec<u8> = params.into();
254 let params2 = DerivationParameters::try_from(params_bytes.as_slice()).unwrap();
255
256 let payload_bytes: Vec<u8> = params2.payload.into();
258 let v1 = KeyDerivationV1::try_from(payload_bytes.as_slice()).unwrap();
259 let raw = v1.derive(b"password");
260 let key2 = secret_key_from_raw(raw).unwrap();
261
262 assert_eq!(key1.as_bytes(), key2.as_bytes());
263 }
264
265 #[test]
266 fn pbkdf2_derivation_parameters_serialize_roundtrip() {
267 let pbkdf2 = Pbkdf2::with_params(12345);
268 let (_, params) = pbkdf2
269 .derive_with_salt(b"password", b"some_salt_here!!")
270 .unwrap();
271 let bytes: Vec<u8> = params.into();
272 let params2 = DerivationParameters::try_from(bytes.as_slice()).unwrap();
273 assert_eq!(params2.header.version, KeyDerivationVersion::V1);
274 }
275
276 #[test]
279 fn argon2_derive_same_params_same_input_produces_same_key() {
280 let mut argon2_params = Argon2Parameters::default();
281 argon2_params.iterations = 2;
282 argon2_params.memory = 64;
283 argon2_params.set_salt(b"fixed_salt_16byt".to_vec());
285
286 let argon2 = Argon2::with_params(argon2_params.clone());
287 let (key1, _) = argon2.derive(b"password").unwrap();
288
289 let argon2 = Argon2::with_params(argon2_params);
290 let (key2, _) = argon2.derive(b"password").unwrap();
291
292 assert_eq!(key1.as_bytes(), key2.as_bytes());
293 }
294
295 #[test]
296 fn argon2_derive_different_salt_produces_different_key() {
297 let (key1, _) = Argon2::new().derive(b"password").unwrap();
299 let (key2, _) = Argon2::new().derive(b"password").unwrap();
300 assert_ne!(key1.as_bytes(), key2.as_bytes());
301 }
302
303 #[test]
304 fn argon2_derivation_parameters_serialize_roundtrip() {
305 let mut argon2_params = Argon2Parameters::default();
306 argon2_params.iterations = 2;
307 argon2_params.memory = 64;
308 let (_, params) = Argon2::with_params(argon2_params)
309 .derive(b"password")
310 .unwrap();
311 let bytes: Vec<u8> = params.into();
312 let params2 = DerivationParameters::try_from(bytes.as_slice()).unwrap();
313 assert_eq!(params2.header.version, KeyDerivationVersion::V2);
314 }
315
316 #[test]
319 fn validate_header_accepts_key_derivation() {
320 use crate::utils::validate_header;
321 let (_, params) = Pbkdf2::with_params(10)
322 .derive_with_salt(b"pw", b"salt_16bytes!!!")
323 .unwrap();
324 let bytes: Vec<u8> = params.into();
325 assert!(validate_header(&bytes, DataType::KeyDerivation));
326 }
327
328 #[test]
329 fn validate_header_rejects_wrong_type() {
330 use crate::utils::validate_header;
331 use crate::DataType;
332 let (_, params) = Pbkdf2::with_params(10)
333 .derive_with_salt(b"pw", b"salt_16bytes!!!")
334 .unwrap();
335 let bytes: Vec<u8> = params.into();
336 assert!(!validate_header(&bytes, DataType::Ciphertext));
337 assert!(!validate_header(&bytes, DataType::Key));
338 assert!(!validate_header(&bytes, DataType::PasswordHash));
339 }
340}