Commit e4fc0c42 authored by Tony Finn's avatar Tony Finn

Twofish support

parent 1fb8b5ab
......@@ -23,6 +23,7 @@ aes = "0.3.2"
block-modes = "0.3.3"
hmac = "0.7.1"
sha2 = "0.8.1"
twofish = "0.2.0"
[[bin]]
name = "kdbx-decrypt"
......
......@@ -16,9 +16,9 @@ Library for reading and writing KDBX libraries from Rust
| AES KDF | Yes | Yes | Yes | Yes | Yes |
| Argon 2 KDF | Yes | Yes | Yes | No | Yes |
| AES Cipher | Yes | Yes | Yes | Yes | Yes |
| TwoFish Cipher | Yes | Yes | No | Yes | No |
| Chacha20 Cipher | No | Yes | Yes | No | No |
| Salsa20 Cipher | No | Yes | Yes | Yes | No |
| TwoFish Cipher | No | Yes | No | Yes | No |
| **Features** | | | | | |
| Memory protection| No | Yes | No | No | Yes |
| Keyfile auth | Yes | Yes | Yes | Yes | Yes |
......
......@@ -69,9 +69,7 @@ impl Unlocked {
crate::xml::write_xml(&mut encrypted_stream, &self.database)?;
let hmacw = encrypted_stream.finish()?;
let inner = hmacw.finish()?;
inner.flush()?;
encrypted_stream.finish()?;
Ok(encrypted_buf)
}
}
......
......@@ -3,10 +3,25 @@ use crate::crypto;
use std::io;
use aes::{Aes128, Aes256};
use aes::block_cipher_trait::BlockCipher;
use derive_more::From;
use twofish::Twofish;
use super::{BlockCipherReader, BlockCipherWriter, BlockCipherWriterExt, HMacReader, HmacWriter};
fn block_cipher_read_stream<C, R>(inner: R, key: crypto::CipherKey, iv: &[u8]) -> io::Result<BlockCipherReader<C, R>>
where C: BlockCipher,
R: io::Read
{
BlockCipherReader::<C, _>::wrap(inner, key, iv)
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid cipher params - Could not create CBC block mode".to_string(),
)
})
}
pub(crate) fn kdbx4_read_stream<'a, R: io::Read + 'a>(
inner: R,
hmac_key: crypto::HmacKey,
......@@ -18,27 +33,20 @@ pub(crate) fn kdbx4_read_stream<'a, R: io::Read + 'a>(
let buffered = io::BufReader::new(inner);
let verified = HMacReader::new(buffered, hmac_key);
let decrypted: Box<dyn io::Read> = match cipher {
binary::Cipher::Aes256 => BlockCipherReader::<Aes256, _>::wrap(verified, cipher_key, iv)
.map(|r| Box::new(r) as Box<dyn io::Read>)
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid cipher params - Could not create CBC block mode".to_string(),
)
}),
binary::Cipher::Aes128 => BlockCipherReader::<Aes128, _>::wrap(verified, cipher_key, iv)
.map(|r| Box::new(r) as Box<dyn io::Read>)
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid cipher params - Could not create CBC block mode".to_string(),
)
}),
_ => Err(io::Error::new(
binary::Cipher::Aes256 => {
Box::new(block_cipher_read_stream::<Aes256, _>(verified, cipher_key, iv)?)
},
binary::Cipher::Aes128 => {
Box::new(block_cipher_read_stream::<Aes128, _>(verified, cipher_key, iv)?)
},
binary::Cipher::TwoFish => {
Box::new(block_cipher_read_stream::<Twofish, _>(verified, cipher_key, iv)?)
},
_ => return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unsupported cipher setting {:?}", cipher),
)),
}?;
};
let decompressed: Box<dyn io::Read> = match compression {
binary::CompressionType::None => Box::new(decrypted),
binary::CompressionType::Gzip => Box::new(libflate::gzip::Decoder::new(decrypted)?),
......@@ -68,14 +76,17 @@ impl<'a, W> Kdbx4Write<'a, W>
where
W: 'a + io::Write,
{
pub(crate) fn finish(self) -> io::Result<HmacWriter<'a, W>> {
match self.0 {
Kdbx4WriteInner::Raw(mut inner) => inner.finish(),
pub(crate) fn finish(self) -> io::Result<W> {
let mut encryption = match self.0 {
Kdbx4WriteInner::Raw(inner) => Ok(inner),
Kdbx4WriteInner::Gzip(gz) => gz
.finish()
.into_result()
.and_then(|mut inner| inner.finish()),
}
.into_result(),
}?;
let hmacw = encryption.finish()?;
let mut inner = hmacw.finish()?;
inner.flush()?;
Ok(inner)
}
}
......@@ -97,6 +108,19 @@ where
}
}
pub(crate) fn block_cipher_write_stream<C, W>(inner: W, key: crypto::CipherKey, iv: &[u8]) -> io::Result<BlockCipherWriter<C, W>>
where W: io::Write,
C: BlockCipher
{
BlockCipherWriter::<C, _>::wrap(inner, key, iv)
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid cipher params - Could not create CBC block mode".to_string(),
)
})
}
pub(crate) fn kdbx4_write_stream<'a, W: 'a + io::Write>(
inner: W,
hmac_key: crypto::HmacKey,
......@@ -107,27 +131,14 @@ pub(crate) fn kdbx4_write_stream<'a, W: 'a + io::Write>(
) -> io::Result<Kdbx4Write<'a, W>> {
let verified = HmacWriter::new(inner, hmac_key);
let encrypted: Box<dyn BlockCipherWriterExt<HmacWriter<'a, W>> + 'a> = match cipher {
binary::Cipher::Aes256 => BlockCipherWriter::<Aes256, _>::wrap(verified, cipher_key, iv)
.map(|w| Box::new(w) as Box<dyn BlockCipherWriterExt<'a, _>>)
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid cipher params - Could not create CBC block mode".to_string(),
)
}),
binary::Cipher::Aes128 => BlockCipherWriter::<Aes128, _>::wrap(verified, cipher_key, iv)
.map(|w| Box::new(w) as Box<dyn BlockCipherWriterExt<'a, _>>)
.map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid cipher params - Could not create CBC block mode".to_string(),
)
}),
_ => Err(io::Error::new(
binary::Cipher::Aes256 => Box::new(block_cipher_write_stream::<Aes256, _>(verified, cipher_key, iv)?),
binary::Cipher::Aes128 => Box::new(block_cipher_write_stream::<Aes128, _>(verified, cipher_key, iv)?),
binary::Cipher::TwoFish => Box::new(block_cipher_write_stream::<Twofish, _>(verified, cipher_key, iv)?),
_ => return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unsupported cipher setting {:?}", cipher),
)),
}?;
};
Ok(match compression {
binary::CompressionType::None => Kdbx4WriteInner::Raw(encrypted).into(),
binary::CompressionType::Gzip => {
......
......@@ -35,3 +35,38 @@ fn kdbx4_parsing() -> Result<(), kdbx_rs::Error> {
Ok(())
}
#[test]
fn kdbx4_parsing_twofish() -> Result<(), kdbx_rs::Error> {
let mut file_path = PathBuf::new();
file_path.push(env!("CARGO_MANIFEST_DIR"));
file_path.push("res");
file_path.push("kdbx4-argon2-twofish.kdbx");
let file = fs::File::open(file_path).unwrap();
let db = kdbx_rs::from_reader(file).unwrap();
let key = kdbx_rs::CompositeKey::from_password("kdbxrs");
let db = db.unlock(&key).map_err(|e| e.0)?;
let xml = kdbx_rs::xml::parse_xml(db.raw_xml().unwrap())?;
assert_eq!(1, xml.groups.len());
assert_eq!("Root", xml.groups[0].name);
assert_eq!(
"cd4233f1-fac2-4272-b309-3c5e7df90097",
xml.groups[0].uuid.to_string()
);
assert_eq!(1, xml.groups[0].entries.len());
assert_eq!(
"d5870a13-f968-41c5-a233-69b7bc86a628",
xml.groups[0].entries[0].uuid.to_string()
);
assert_eq!(1, xml.groups[0].entries[0].history.len());
assert_eq!(
"d5870a13-f968-41c5-a233-69b7bc86a628",
xml.groups[0].entries[0].history[0].uuid.to_string()
);
Ok(())
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment