1use std::convert::TryInto;
2use std::io;
3use std::io::Cursor;
4use std::collections::HashMap;
5
6use log::info;
7use uuid::Uuid;
8use openssl::symm::{Cipher, Crypter, Mode};
9use ring::digest::{Context, SHA256};
10use byteorder::{LittleEndian, ReadBytesExt};
11
12use crate::{KDF_AES_KDBX3, MapValue};
13
14pub use self::argon2::*;
15
16mod argon2;
17
18#[cfg(test)]
19mod tests;
20
21pub const KDF_PARAM_UUID: &str = "$UUID"; pub const KDF_PARAM_SALT: &str = "S"; pub const KDF_PARAM_ROUNDS: &str = "R"; pub const KDF_PARAM_PARALLELISM: &str = "P"; pub const KDF_PARAM_MEMORY: &str = "M"; pub const KDF_PARAM_ITERATIONS: &str = "I"; pub const KDF_PARAM_VERSION: &str = "V"; const _KDF_PARAM_SECRET_KEY: &str = "K"; const _KDF_PARAM_ASSOC_DATA: &str = "A"; pub trait Kdf {
32 fn uuid(&self) -> Uuid;
33 fn randomize(&mut self);
34 fn transform_key(&self, composite_key: &[u8]) -> io::Result<Vec<u8>>;
35 fn save(&self, custom_data: &mut HashMap<String, MapValue>);
36}
37
38pub struct AesKdf {
39 salt: [u8; 32],
40 rounds: u64,
41}
42
43impl AesKdf {
44 pub fn load(custom_data: &HashMap<String, MapValue>) -> io::Result<Self> {
45 match (&custom_data[KDF_PARAM_SALT], &custom_data[KDF_PARAM_ROUNDS]) {
48 (MapValue::ByteArray(ref salt), MapValue::UInt64(rounds)) => Ok(AesKdf {
49 salt: salt.clone().try_into().unwrap(), rounds: *rounds,
51 }),
52 _ => Err(io::Error::new(io::ErrorKind::Unsupported, "Bad rounds")),
53 }
54 }
55}
56
57impl Kdf for AesKdf {
58 fn uuid(&self) -> Uuid {
59 KDF_AES_KDBX3
60 }
61
62 fn randomize(&mut self) {
63 unimplemented!("Can't randomize yet")
64 }
65
66 fn save(&self, custom_data: &mut HashMap<String, MapValue>) {
67 custom_data.insert(KDF_PARAM_ROUNDS.to_string(), MapValue::UInt64(self.rounds));
68 custom_data.insert(
69 KDF_PARAM_SALT.to_string(),
70 MapValue::ByteArray(self.salt.into()),
71 );
72 }
73
74 fn transform_key(&self, composite_key: &[u8]) -> io::Result<Vec<u8>> {
75 info!("Found AES KDF");
76 println!("Calculating transformed key ({})", self.rounds);
77
78 let mut transform_key = composite_key.to_owned();
79 let cipher = Cipher::aes_256_ecb();
80 let mut c = Crypter::new(cipher, Mode::Encrypt, &self.salt, None)?;
81 for _ in 0..cipher.block_size() {
82 transform_key.push(0);
83 }
84 let mut out = vec![0; 16 + 16 + cipher.block_size()];
85 c.pad(false);
86 for _ in 0..self.rounds {
87 c.update(&transform_key[0..32], &mut out)?;
88 let temp = transform_key;
89 transform_key = out;
90 out = temp;
91 }
92 transform_key.truncate(32);
93 let mut context = Context::new(&SHA256);
94 context.update(&transform_key);
95 Ok(context.finish().as_ref().to_owned())
96 }
97}
98
99impl Default for AesKdf {
100 fn default() -> Self {
101 Self {
102 salt: [0; 32],
103 rounds: 60000,
104 }
105 }
106}
107
108pub fn transform_aes_kdf(
109 composite_key: &[u8],
110 custom_data: &HashMap<String, Vec<u8>>,
111) -> io::Result<Vec<u8>> {
112 let transform_seed = &custom_data[KDF_PARAM_SALT];
113 let mut c = Cursor::new(&custom_data[KDF_PARAM_ROUNDS]);
114 let transform_round = c.read_u64::<LittleEndian>()?;
115
116 info!("Found AES KDF");
117 println!("Calculating transformed key ({})", transform_round);
118
119 let mut transform_key = composite_key.to_owned();
120 let cipher = Cipher::aes_256_ecb();
121 let mut c = Crypter::new(cipher, Mode::Encrypt, transform_seed, None)?;
122 for _ in 0..cipher.block_size() {
123 transform_key.push(0);
124 }
125 let mut out = vec![0; 16 + 16 + cipher.block_size()];
126 c.pad(false);
127 for _ in 0..transform_round {
128 c.update(&transform_key[0..32], &mut out)?;
129 let temp = transform_key;
130 transform_key = out;
131 out = temp;
132 }
133 transform_key.truncate(32);
134 let mut context = Context::new(&SHA256);
135 context.update(&transform_key);
136 Ok(context.finish().as_ref().to_owned())
137}