use block_modes::block_padding::ZeroPadding;
use block_modes::{BlockMode, BlockModeIv, Cbc, Ecb};
use byteorder::{LittleEndian, WriteBytesExt};
use hmac::{Hmac, Mac};
use key::hash_password;
use rand::os::OsRng;
use rand::RngCore;
use sha2::{Digest, Sha256};
use std::cmp::min;
use std::io;
use std::io::{Cursor, Write};
use std::result::Result;
use twofish::block_cipher_trait::generic_array::GenericArray;
use twofish::Twofish;
type TwofishEcb = Ecb<Twofish, ZeroPadding>;
type TwofishCbc = Cbc<Twofish, ZeroPadding>;
type HmacSha256 = Hmac<Sha256>;
pub struct PwsafeWriter<W> {
inner: W,
cipher: TwofishCbc,
hmac: HmacSha256,
}
impl<W: Write> PwsafeWriter<W> {
pub fn new(mut inner: W, iter: u32, password: &[u8]) -> Result<Self, io::Error> {
inner.write_all(b"PWS3")?;
let mut salt = [0u8; 32];
let mut r = OsRng::new().unwrap();
r.fill_bytes(&mut salt);
inner.write_all(&salt)?;
inner.write_u32::<LittleEndian>(iter)?;
let key = hash_password(&salt, iter, password);
let mut hasher = Sha256::default();
hasher.input(&key);
let hash = hasher.result();
inner.write_all(&hash)?;
let mut k = [0u8; 32];
let mut l = [0u8; 32];
let mut iv = [0u8; 16];
r.fill_bytes(&mut k);
r.fill_bytes(&mut l);
r.fill_bytes(&mut iv);
let iv = GenericArray::from_slice(&iv);
let cbc_cipher = TwofishCbc::new_varkey(&k, &iv).unwrap();
let sha256_hmac = HmacSha256::new_varkey(&l).unwrap();
let mut ecb_cipher = TwofishEcb::new_varkey(&key).unwrap();
ecb_cipher.encrypt_nopad(&mut k).unwrap();
ecb_cipher.encrypt_nopad(&mut l).unwrap();
inner.write_all(&k)?;
inner.write_all(&l)?;
inner.write_all(&iv)?;
let w = PwsafeWriter {
inner,
cipher: cbc_cipher,
hmac: sha256_hmac,
};
Ok(w)
}
pub fn write_field(&mut self, field_type: u8, data: &[u8]) -> Result<(), io::Error> {
let mut i: usize = 0;
let mut block = [0u8; 16];
let mut cur = Cursor::new(Vec::new());
cur.write_u32::<LittleEndian>(data.len() as u32)?;
cur.write_u8(field_type)?;
let mut r = OsRng::new().unwrap();
self.hmac.input(&data);
loop {
let l = min(16 - cur.get_ref().len(), data.len() - i);
cur.write_all(&data[i..i + l])?;
if l == 0 {
i += 16
} else {
i += l;
}
let v = cur.into_inner();
let vlen = v.len();
block[0..vlen].copy_from_slice(&v);
r.fill_bytes(&mut block[vlen..16]); self.cipher.encrypt_nopad(&mut block).unwrap();
self.inner.write_all(&block)?;
cur = Cursor::new(Vec::new());
if i >= data.len() {
break;
}
}
Ok(())
}
pub fn finish(&mut self) -> Result<(), io::Error> {
self.inner.write_all(b"PWS3-EOFPWS3-EOF")?;
self.inner.write_all(&self.hmac.result().code())?;
Ok(())
}
}