kdbx_rs/
crypto.rs

1use crate::binary;
2
3use aes::Aes256;
4use cipher::generic_array::GenericArray;
5use cipher::BlockEncryptMut;
6use hmac::digest::CtOutput;
7use hmac::{Hmac, Mac};
8use sha2::{Digest, Sha256, Sha512};
9use std::string::ToString;
10use thiserror::Error;
11
12type HmacSha256 = Hmac<Sha256>;
13
14/// Credentials needed to unlock the database
15///
16/// Currently it supports unlocking a database with a combination
17/// of password, keyfile or both.
18///
19/// For the compmon case of creating credentials from just a password,
20/// you can use
21///
22/// ```
23/// # use kdbx_rs::CompositeKey;
24/// CompositeKey::from_password("abcdef");
25/// ```
26///
27/// Otherwise you can use [`CompositeKey::new`] to provide other combinations
28pub struct CompositeKey {
29    pw: Option<String>,
30    keyfile: Option<Vec<u8>>,
31}
32
33impl CompositeKey {
34    /// Create a new composite key
35    pub fn new(pw: Option<String>, keyfile: Option<Vec<u8>>) -> CompositeKey {
36        CompositeKey { pw, keyfile }
37    }
38
39    /// Utility method for making a key with just a password
40    pub fn from_password(pw: &str) -> CompositeKey {
41        CompositeKey::new(Some(pw.into()), None)
42    }
43
44    pub(crate) fn composed(&self) -> ComposedKey {
45        let mut buffer = Vec::new();
46        if let Some(ref pw) = self.pw {
47            buffer.extend(Sha256::digest(pw.as_bytes()))
48            //buffer.extend(pw.as_bytes());
49        }
50        if let Some(ref keyfile) = self.keyfile {
51            buffer.extend(Sha256::digest(keyfile))
52        }
53
54        ComposedKey(Sha256::digest(&buffer).iter().cloned().collect())
55    }
56}
57
58#[derive(Debug)]
59/// Hashed combined input credentials used as KDF input
60pub struct ComposedKey(Vec<u8>);
61
62impl ComposedKey {
63    /// Generate a master key used to derive all other keys
64    pub fn master_key(
65        &self,
66        kdf_options: &binary::KdfParams,
67    ) -> Result<MasterKey, KeyGenerationError> {
68        match kdf_options {
69            binary::KdfParams::Argon2 {
70                variant,
71                memory_bytes,
72                version,
73                iterations,
74                lanes,
75                salt,
76            } => {
77                let config = argon2::Config {
78                    variant: *variant,
79                    version: argon2::Version::from_u32(*version)
80                        .map_err(|e| KeyGenerationError::KeyGeneration(e.to_string()))?,
81                    lanes: *lanes,
82                    mem_cost: (memory_bytes / 1024) as u32,
83                    time_cost: *iterations as u32,
84                    ..Default::default()
85                };
86                let hash = argon2::hash_raw(&self.0, salt, &config)
87                    .map_err(|e| KeyGenerationError::KeyGeneration(e.to_string()))?;
88
89                Ok(MasterKey(hash))
90            }
91            binary::KdfParams::Aes { rounds, salt } => {
92                use cipher::KeyInit;
93                let mut cipher = Aes256::new_from_slice(salt).unwrap();
94                let chunked: Vec<GenericArray<u8, _>> = self
95                    .0
96                    .chunks_exact(16)
97                    .map(|chunk| *GenericArray::from_slice(chunk))
98                    .collect();
99                let mut blocks = [chunked[0], chunked[1]];
100                for _ in 0..*rounds {
101                    cipher.encrypt_blocks_mut(&mut blocks);
102                }
103                let mut transformed_hasher = Sha256::new();
104                transformed_hasher.update(blocks[0]);
105                transformed_hasher.update(blocks[1]);
106                let transformed = transformed_hasher.finalize().to_vec();
107
108                Ok(MasterKey(transformed))
109            }
110            _ => Ok(MasterKey(Vec::new())),
111        }
112    }
113}
114
115/// Master key - this is generated from the user's composite key and is used to generate all other keys
116#[derive(Debug)]
117pub struct MasterKey(Vec<u8>);
118
119impl MasterKey {
120    /// Obtain a key to use for data integrity checks
121    pub(crate) fn hmac_key(&self, seed: &[u8]) -> HmacKey {
122        let mut data_to_hash = Vec::new();
123        data_to_hash.extend(seed.iter());
124        data_to_hash.extend(self.0.iter());
125        data_to_hash.push(1);
126
127        HmacKey(Sha512::digest(&data_to_hash).iter().cloned().collect())
128    }
129
130    /// Obtain a key to initialise a cipher
131    pub(crate) fn cipher_key(&self, seed: &[u8]) -> CipherKey {
132        let mut data_to_hash = Vec::new();
133        data_to_hash.extend(seed.iter());
134        data_to_hash.extend(self.0.iter());
135
136        CipherKey(Sha256::digest(&data_to_hash).iter().cloned().collect())
137    }
138}
139
140/// Used to initialise the encryption/decryption cipher
141pub(crate) struct CipherKey(pub(crate) Vec<u8>);
142
143/// Base key for all HMAC data integrity checks
144pub(crate) struct HmacKey(Vec<u8>);
145
146impl HmacKey {
147    /// Obtain a key to verify a single block
148    pub(crate) fn block_key(&self, block_idx: u64) -> HmacBlockKey {
149        let mut block_key_hash = Sha512::new();
150        block_key_hash.update(block_idx.to_le_bytes());
151        block_key_hash.update(&*self.0);
152        HmacBlockKey(block_idx, block_key_hash.finalize().to_vec())
153    }
154}
155
156/// Key to perform data integrity checks on a specific block
157pub(crate) struct HmacBlockKey(u64, Vec<u8>);
158
159impl HmacBlockKey {
160    /// Verify that a block in the data section is valid
161    pub(crate) fn verify_data_block(&self, hmac: &[u8], data: &[u8]) -> bool {
162        let mut calc_hmac = HmacSha256::new_from_slice(&self.1).unwrap();
163        calc_hmac.update(&self.0.to_le_bytes());
164        calc_hmac.update(&(data.len() as u32).to_le_bytes());
165        calc_hmac.update(data);
166        calc_hmac.verify_slice(hmac).is_ok()
167    }
168
169    /// Calculate a HMAC for a block in the data section
170    pub(crate) fn calculate_data_hmac(
171        &self,
172        data: &[u8],
173    ) -> Result<CtOutput<HmacSha256>, cipher::InvalidLength> {
174        let mut calc_hmac: HmacSha256 = HmacSha256::new_from_slice(&self.1).unwrap();
175        calc_hmac.update(&self.0.to_le_bytes());
176        calc_hmac.update(&(data.len() as u32).to_le_bytes());
177        calc_hmac.update(data);
178        Ok(calc_hmac.finalize())
179    }
180
181    /// Calculate a HMAC for a block in the header section
182    pub(crate) fn calculate_header_hmac(
183        &self,
184        data: &[u8],
185    ) -> Result<CtOutput<HmacSha256>, cipher::InvalidLength> {
186        let mut calc_hmac = HmacSha256::new_from_slice(&self.1)?;
187        calc_hmac.update(data);
188        Ok(calc_hmac.finalize())
189    }
190
191    /// Verify that the header block is valid
192    pub(crate) fn verify_header_block(&self, hmac: &[u8], data: &[u8]) -> bool {
193        let mut calc_hmac = HmacSha256::new_from_slice(&self.1).unwrap();
194        calc_hmac.update(data);
195        calc_hmac.verify_slice(hmac).is_ok()
196    }
197}
198
199/// Confirm the hash of a given block of data for data corruption detection
200pub(crate) fn verify_sha256(data: &[u8], expected_sha: &[u8]) -> bool {
201    expected_sha == &*Sha256::digest(data)
202}
203
204pub(crate) fn sha256(data: &[u8]) -> Vec<u8> {
205    Sha256::digest(data).as_slice().to_vec()
206}
207
208#[derive(Debug, Error)]
209/// Errors encountered generating crypto keys
210pub enum KeyGenerationError {
211    /// Unexpected error when generating a key
212    #[error("Could not generate key: {0}")]
213    KeyGeneration(String),
214    /// KDF Options are not supported by this library
215    #[error("Generation for KDF Options: {0:?} not implemented")]
216    UnimplementedKdfOptions(binary::KdfParams),
217}