use std::marker::PhantomData;
use seckey::Bytes;
use ::stream::StreamCipher;
use ::auth::NonceMac;
use ::hash::GenericHash;
use super::{ AeadCipher, DecryptFail };
#[derive(Debug, Clone)]
pub struct RivGeneral<C, M, H> {
cipher: C,
mac: M,
hash: PhantomData<H>,
aad: Vec<u8>
}
impl<C, M, H> AeadCipher for RivGeneral<C, M, H>
where
C: StreamCipher,
M: NonceMac + Clone,
H: GenericHash
{
fn new(key: &[u8]) -> Self where Self: Sized {
assert_eq!(key.len(), Self::KEY_LENGTH);
let mac_key = H::default()
.with_size(M::KEY_LENGTH)
.hash::<Bytes>(key);
let mut mac = M::new(&mac_key);
mac.with_size(C::NONCE_LENGTH);
RivGeneral {
cipher: C::new(key),
mac: mac,
hash: PhantomData,
aad: Vec::new()
}
}
const KEY_LENGTH: usize = C::KEY_LENGTH;
const TAG_LENGTH: usize = C::NONCE_LENGTH;
const NONCE_LENGTH: usize = M::NONCE_LENGTH;
#[inline]
fn with_aad(&mut self, aad: &[u8]) -> &mut Self {
self.aad = aad.into();
self
}
fn encrypt(&self, nonce: &[u8], data: &[u8]) -> Vec<u8> {
assert_eq!(nonce.len(), Self::NONCE_LENGTH);
let mut mac = self.mac.clone();
mac.with_nonce(nonce);
let mut aad_and_data = Vec::with_capacity(self.aad.len() + data.len());
aad_and_data.extend_from_slice(&self.aad);
aad_and_data.extend_from_slice(data);
let mut tag = mac.result::<Vec<u8>>(&aad_and_data);
let mut output = self.cipher.process(&tag, data);
let xorkey = mac.result::<Bytes>(&output);
for (b, &x) in tag.iter_mut().zip(xorkey.iter()) {
*b ^= x;
}
output.append(&mut tag);
output
}
fn decrypt(&self, nonce: &[u8], data: &[u8]) -> Result<Vec<u8>, DecryptFail> {
assert_eq!(nonce.len(), Self::NONCE_LENGTH);
if data.len() < Self::TAG_LENGTH { Err(DecryptFail::LengthError)? };
let mut mac = self.mac.clone();
mac.with_nonce(nonce);
let (data, tag) = data.split_at(data.len() - Self::TAG_LENGTH);
let mut xorkey = mac.result::<Bytes>(data);
for (b, &x) in xorkey.iter_mut().zip(tag) {
*b ^= x;
}
let output = self.cipher.process(&xorkey, data);
let mut aad_and_data = Vec::with_capacity(self.aad.len() + output.len());
aad_and_data.extend_from_slice(&self.aad);
aad_and_data.extend_from_slice(&output);
if mac.verify(&aad_and_data, &xorkey) {
Ok(output)
} else {
Err(DecryptFail::AuthenticationFail)
}
}
}