use std::convert::TryInto;
use std::io;
use std::io::Cursor;
use std::collections::HashMap;
use log::info;
use uuid::Uuid;
use openssl::symm::{Cipher, Crypter, Mode};
use ring::digest::{Context, SHA256};
use byteorder::{LittleEndian, ReadBytesExt};
use crate::{KDF_AES_KDBX3, MapValue};
pub use self::argon2::*;
mod argon2;
#[cfg(test)]
mod tests;
pub 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 {
fn uuid(&self) -> Uuid;
fn randomize(&mut self);
fn transform_key(&self, composite_key: &[u8]) -> io::Result<Vec<u8>>;
fn save(&self, custom_data: &mut HashMap<String, MapValue>);
}
pub struct AesKdf {
salt: [u8; 32],
rounds: u64,
}
impl AesKdf {
pub fn load(custom_data: &HashMap<String, MapValue>) -> io::Result<Self> {
match (&custom_data[KDF_PARAM_SALT], &custom_data[KDF_PARAM_ROUNDS]) {
(MapValue::ByteArray(ref salt), MapValue::UInt64(rounds)) => Ok(AesKdf {
salt: salt.clone().try_into().unwrap(),
rounds: *rounds,
}),
_ => Err(io::Error::new(io::ErrorKind::Unsupported, "Bad rounds")),
}
}
}
impl Kdf for AesKdf {
fn uuid(&self) -> Uuid {
KDF_AES_KDBX3
}
fn randomize(&mut self) {
unimplemented!("Can't randomize yet")
}
fn save(&self, custom_data: &mut HashMap<String, MapValue>) {
custom_data.insert(KDF_PARAM_ROUNDS.to_string(), MapValue::UInt64(self.rounds));
custom_data.insert(
KDF_PARAM_SALT.to_string(),
MapValue::ByteArray(self.salt.into()),
);
}
fn transform_key(&self, composite_key: &[u8]) -> io::Result<Vec<u8>> {
info!("Found AES KDF");
println!("Calculating transformed key ({})", self.rounds);
let mut transform_key = composite_key.to_owned();
let cipher = Cipher::aes_256_ecb();
let mut c = Crypter::new(cipher, Mode::Encrypt, &self.salt, None)?;
for _ in 0..cipher.block_size() {
transform_key.push(0);
}
let mut out = vec![0; 16 + 16 + cipher.block_size()];
c.pad(false);
for _ in 0..self.rounds {
c.update(&transform_key[0..32], &mut out)?;
let temp = transform_key;
transform_key = out;
out = temp;
}
transform_key.truncate(32);
let mut context = Context::new(&SHA256);
context.update(&transform_key);
Ok(context.finish().as_ref().to_owned())
}
}
impl Default for AesKdf {
fn default() -> Self {
Self {
salt: [0; 32],
rounds: 60000,
}
}
}
pub fn transform_aes_kdf(
composite_key: &[u8],
custom_data: &HashMap<String, Vec<u8>>,
) -> io::Result<Vec<u8>> {
let transform_seed = &custom_data[KDF_PARAM_SALT];
let mut c = Cursor::new(&custom_data[KDF_PARAM_ROUNDS]);
let transform_round = c.read_u64::<LittleEndian>()?;
info!("Found AES KDF");
println!("Calculating transformed key ({})", transform_round);
let mut transform_key = composite_key.to_owned();
let cipher = Cipher::aes_256_ecb();
let mut c = Crypter::new(cipher, Mode::Encrypt, transform_seed, None)?;
for _ in 0..cipher.block_size() {
transform_key.push(0);
}
let mut out = vec![0; 16 + 16 + cipher.block_size()];
c.pad(false);
for _ in 0..transform_round {
c.update(&transform_key[0..32], &mut out)?;
let temp = transform_key;
transform_key = out;
out = temp;
}
transform_key.truncate(32);
let mut context = Context::new(&SHA256);
context.update(&transform_key);
Ok(context.finish().as_ref().to_owned())
}