Skip to main content

devolutions_crypto/key_derivation/
key_derivation_v1.rs

1//! Key Derivation V1: PBKDF2-HMAC-SHA256
2use std::convert::TryFrom;
3use std::io::{Cursor, Read, Write};
4
5use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
6use zeroize::Zeroizing;
7
8use rand::TryRng;
9
10use crate::key::{secret_key_from_raw, SecretKey};
11use crate::utils::derive_key_pbkdf2;
12use crate::{Error, Header, KeyDerivationVersion, Result, DEFAULT_PBKDF2_ITERATIONS};
13
14use super::{DerivationParameters, DerivationParametersPayload};
15
16pub const KEY_LENGTH: usize = 32;
17
18#[derive(Clone, Debug)]
19pub struct KeyDerivationV1 {
20    pub iterations: u32,
21    pub salt: Vec<u8>,
22}
23
24impl KeyDerivationV1 {
25    pub fn derive(&self, key: &[u8]) -> Zeroizing<Vec<u8>> {
26        Zeroizing::new(derive_key_pbkdf2(
27            key,
28            &self.salt,
29            self.iterations,
30            KEY_LENGTH,
31        ))
32    }
33}
34
35impl From<&KeyDerivationV1> for Vec<u8> {
36    fn from(params: &KeyDerivationV1) -> Self {
37        let mut data = Vec::with_capacity(8 + params.salt.len());
38        data.write_u32::<LittleEndian>(params.iterations).unwrap();
39        data.write_u32::<LittleEndian>(params.salt.len() as u32)
40            .unwrap();
41        data.write_all(&params.salt).unwrap();
42        data
43    }
44}
45
46impl TryFrom<&[u8]> for KeyDerivationV1 {
47    type Error = Error;
48
49    fn try_from(data: &[u8]) -> Result<Self> {
50        let mut cursor = Cursor::new(data);
51        let iterations = cursor.read_u32::<LittleEndian>()?;
52        let salt_len = cursor.read_u32::<LittleEndian>()? as usize;
53        let remaining = data.len() - (cursor.position() as usize);
54
55        if remaining < salt_len {
56            return Err(Error::InvalidLength);
57        }
58
59        let mut salt = vec![0u8; salt_len];
60        cursor.read_exact(&mut salt)?;
61        Ok(KeyDerivationV1 { iterations, salt })
62    }
63}
64
65// ── Pbkdf2 ───────────────────────────────────────────────────────────────────
66
67/// Derives a key using PBKDF2-HMAC-SHA256 (V1).
68pub struct Pbkdf2 {
69    iterations: u32,
70}
71
72impl Pbkdf2 {
73    /// Creates a `Pbkdf2` key derivation object with default parameters (600,000 iterations).
74    pub fn new() -> Self {
75        Self {
76            iterations: DEFAULT_PBKDF2_ITERATIONS,
77        }
78    }
79
80    /// Creates a `Pbkdf2` key derivation object with a custom iteration count.
81    /// The output key length is always 32 bytes to match `SecretKey`'s contract.
82    pub fn with_params(iterations: u32) -> Self {
83        Self { iterations }
84    }
85
86    /// Derives the key using a randomly generated salt.
87    pub fn derive(&self, key: &[u8]) -> Result<(SecretKey, DerivationParameters)> {
88        let mut salt = vec![0u8; 16];
89        rand::rngs::SysRng
90            .try_fill_bytes(&mut salt)
91            .map_err(|_| Error::RandomError)?;
92        self.derive_with_salt(key, &salt)
93    }
94
95    /// Derives the key using the provided salt.
96    pub fn derive_with_salt(
97        &self,
98        key: &[u8],
99        salt: &[u8],
100    ) -> Result<(SecretKey, DerivationParameters)> {
101        let params = KeyDerivationV1 {
102            iterations: self.iterations,
103            salt: salt.to_vec(),
104        };
105        let raw = params.derive(key);
106        let secret_key = secret_key_from_raw(raw)?;
107
108        let mut header: Header<DerivationParameters> = Header::default();
109        header.version = KeyDerivationVersion::V1;
110
111        let derivation_params = DerivationParameters {
112            header,
113            payload: DerivationParametersPayload::V1(params),
114        };
115
116        Ok((secret_key, derivation_params))
117    }
118
119    /// Returns a `DerivationParameters` capturing the current PBKDF2 settings with a freshly
120    /// generated random salt, without performing any derivation.
121    /// Useful for passing custom parameters to [`crate::password_hash::hash_password_with_parameters`].
122    pub fn parameters(self) -> Result<DerivationParameters> {
123        let mut salt = vec![0u8; 16];
124        rand::rngs::SysRng
125            .try_fill_bytes(&mut salt)
126            .map_err(|_| Error::RandomError)?;
127        let v1 = KeyDerivationV1 {
128            iterations: self.iterations,
129            salt,
130        };
131        let mut header: Header<DerivationParameters> = Header::default();
132        header.version = KeyDerivationVersion::V1;
133        Ok(DerivationParameters {
134            header,
135            payload: DerivationParametersPayload::V1(v1),
136        })
137    }
138}
139
140impl Default for Pbkdf2 {
141    fn default() -> Self {
142        Self::new()
143    }
144}