use std::cell::Cell;
use crate::{Cipher, CryptoError, CryptoErrorKind};
use crate::cipher_mode::{Padding, InitialVec, EncryptStream, Pond, DecryptStream};
use std::marker::PhantomData;
pub struct CBC<C, P, IV> {
buf: Cell<Vec<u8>>,
cur_iv: Vec<u8>,
cipher: C,
padding: P,
iv: IV,
phd: PhantomData<*const u8>,
}
impl<C, P, IV> CBC<C, P, IV>
where C: Cipher, P: 'static + Padding, IV: InitialVec<C> {
pub fn new(c: C, p: P, iv: IV) -> Result<Self, CryptoError> {
let mut iv = iv;
let block_len = c.block_size().unwrap_or(1);
let mut cur_iv = Vec::with_capacity(block_len);
if let Err(e) = iv.initial_vec(&mut cur_iv) {
return Err(e);
} else if c.block_size().is_some() && cur_iv.len() != block_len {
return Err(CryptoError::new(CryptoErrorKind::InnerErr,
format!("Wrong IV len: {}, The IV len must be the {} in bytes", cur_iv.len(), block_len)));
}
Ok(Self {
buf: Cell::new(Vec::with_capacity(block_len)),
cur_iv,
cipher: c,
padding: p,
iv,
phd: PhantomData,
})
}
pub fn update_iv(&mut self) -> Result<&Vec<u8>, CryptoError> {
let block_len = self.cipher.block_size().unwrap_or(1);
match self.iv.initial_vec(&mut self.cur_iv) {
Ok(_) => {
if self.cipher.block_size().is_some() && block_len != self.cur_iv.len() {
Err(CryptoError::new(CryptoErrorKind::InnerErr,
format!("Wrong IV len: {}, The IV len must be the {} in bytes", self.cur_iv.len(), block_len)))
} else {
Ok(&self.cur_iv)
}
},
Err(e) => Err(e),
}
}
pub fn cur_iv(&self) -> Vec<u8> {
self.cur_iv.clone()
}
pub fn set_iv(&mut self, iv: Vec<u8>) -> Result<(), CryptoError> {
let block_len = self.cipher.block_size().unwrap_or(1);
if self.cipher.block_size().is_some() && iv.len() != block_len {
Err(CryptoError::new(CryptoErrorKind::InvalidParameter,
format!("Wrong IV len: {}, the IV len must be the {} in bytes", self.cur_iv.len(), block_len)))
} else {
let mut iv = iv;
self.cur_iv.clear();
self.cur_iv.append(&mut iv);
Ok(())
}
}
pub fn encrypt_stream(self) -> CBCEncrypt<C, P, IV> {
CBCEncrypt {
pond: Vec::with_capacity(self.cipher.block_size().unwrap_or(1)),
data: Vec::with_capacity(self.cipher.block_size().unwrap_or(1)),
ij: self.cur_iv.clone(),
cbc: self,
}
}
pub fn decrypt_stream(self) -> CBCDecrypt<C, P, IV> {
CBCDecrypt {
pond: Vec::with_capacity(self.cipher.block_size().unwrap_or(1)),
data: Vec::with_capacity(self.cipher.block_size().unwrap_or(1)),
ij: self.cur_iv.clone(),
cbc: self,
}
}
#[inline]
fn get_buf(&self) -> &mut Vec<u8> {
unsafe {
&mut (*self.buf.as_ptr())
}
}
#[inline]
fn xor_iv(block: &[u8], cur_iv: &mut Vec<u8>) {
cur_iv.iter_mut().zip(block.iter()).for_each(|(a, &b)| {
*a = (*a) ^ b;
});
}
}
impl<C, P, IV> Cipher for CBC<C, P, IV>
where C: Cipher, P: 'static + Padding, IV: InitialVec<C> {
type Output = usize;
fn block_size(&self) -> Option<usize> {
self.cipher.block_size()
}
fn encrypt(&self, dst: &mut Vec<u8>, plaintext_block: &[u8]) -> Result<usize, CryptoError> {
let block_len = self.cipher.block_size().unwrap_or(1);
let mut data = plaintext_block;
let mut cur_iv = self.cur_iv.to_vec();
let txt = self.get_buf();
dst.clear();
while data.len() >= block_len {
let tmp = &data[..block_len];
Self::xor_iv(tmp, &mut cur_iv);
match self.cipher.encrypt(txt, cur_iv.as_slice()) {
Ok(_) => {
cur_iv.clear();
cur_iv.extend_from_slice(txt.as_slice());
dst.append(txt);
data = &data[block_len..];
},
Err(e) => {
return Err(e);
}
}
}
let mut tmp = data.to_vec();
self.padding.padding(&mut tmp);
let mut data = tmp.as_slice();
while !data.is_empty() {
let len = std::cmp::min(block_len, data.len());
let tmp = &data[..len];
match self.cipher.encrypt(txt, tmp) {
Ok(_) => {
dst.append(txt);
data = &data[len..];
},
Err(e) => {
return Err(e);
},
}
}
Ok(dst.len())
}
fn decrypt(&self, dst: &mut Vec<u8>, cipher_block: &[u8]) -> Result<usize, CryptoError> {
let block_size = self.cipher.block_size().unwrap_or(1);
if (cipher_block.len() % block_size) != 0 {
return Err(CryptoError::new(CryptoErrorKind::InvalidParameter,
format!("Wrong ciphertext length: {}, the ciphertext block length(in bytes) only can be {}",
cipher_block.len(), block_size)));
}
let mut data = cipher_block;
let txt = self.get_buf();
let mut curiv = self.cur_iv.as_slice();
dst.clear();
while !data.is_empty() {
let len = std::cmp::min(block_size, data.len());
let tmp = &data[..len];
match self.cipher.decrypt(txt, tmp) {
Ok(_) => {
curiv.iter().zip(txt.iter()).for_each(|(&a, &b)| {
dst.push(a ^ b);
});
curiv = tmp;
data = &data[len..];
},
Err(e) => {
return Err(e);
}
}
}
self.padding.unpadding(dst)
}
}
impl<C, P, IV> Clone for CBC<C, P, IV>
where C: Cipher + Clone, P: 'static + Padding + Clone, IV: InitialVec<C> + Clone {
fn clone(&self) -> Self {
CBC {
buf: Cell::new(Vec::with_capacity(self.cipher.block_size().unwrap_or(1))),
cur_iv: self.cur_iv.clone(),
cipher: self.cipher.clone(),
padding: self.padding.clone(),
iv: self.iv.clone(),
phd: PhantomData,
}
}
}
pub struct CBCEncrypt<C, P, IV> {
cbc: CBC<C, P, IV>,
data: Vec<u8>,
pond: Vec<u8>,
ij: Vec<u8>,
}
pub struct CBCDecrypt<C, P, IV> {
cbc: CBC<C, P, IV>,
data: Vec<u8>,
pond: Vec<u8>,
ij: Vec<u8>,
}
impl_cipher_iv!(CBCEncrypt, cbc);
impl_fn_reset_iv!(CBCEncrypt, cbc);
impl_cipher_iv!(CBCDecrypt, cbc);
impl_fn_reset_iv!(CBCDecrypt, cbc);
impl<C, P, IV> CBCEncrypt<C, P, IV>
where C: Cipher, P: 'static + Padding, IV: InitialVec<C> {
fn xor_iv(cur: &mut Vec<u8>, block: &[u8], prev: &Vec<u8>) {
cur.clear();
block.iter().zip(prev.iter()).for_each(|(&a, &b)| {
cur.push(a ^ b);
});
}
}
impl<C, P, IV> EncryptStream for CBCEncrypt<C, P, IV>
where C: Cipher, P: 'static + Padding, IV: InitialVec<C> {
fn write(&mut self, data: &[u8]) -> Result<Pond, CryptoError> {
let block_len = self.cbc.cipher.block_size().unwrap_or(1);
let mut data = data;
if data.is_empty() {
return Ok(Pond::new(&mut self.pond, false));
} else {
let len = std::cmp::min(block_len - self.data.len(), data.len());
self.data.extend(data.iter().take(len));
data = &data[len..];
}
let txt = self.cbc.get_buf();
if self.data.len() == block_len {
Self::xor_iv(txt, self.data.as_slice(), &self.ij);
match self.cbc.cipher.encrypt(&mut self.ij, txt.as_slice()) {
Ok(_) => {
self.pond.extend(self.ij.iter());
self.data.clear();
},
Err(e) => {
return Err(e);
}
}
}
while data.len() >= block_len {
let tmp = &data[..block_len];
Self::xor_iv(txt, tmp, &self.ij);
match self.cbc.cipher.encrypt(&mut self.ij, txt.as_slice()) {
Ok(_) => {
self.pond.extend(self.ij.iter());
data = &data[block_len..];
},
Err(e) => {
return Err(e);
}
}
}
if !data.is_empty() {self.data.extend_from_slice(data);}
Ok(Pond::new(&mut self.pond, false))
}
fn finish(&mut self) -> Result<Pond, CryptoError> {
self.cbc.padding.padding(&mut self.data);
let block_len = self.cbc.cipher.block_size().unwrap_or(1);
let txt = self.cbc.get_buf();
let mut data = self.data.as_slice();
while !data.is_empty() {
let len = std::cmp::min(block_len, data.len());
let tmp = &data[..len];
Self::xor_iv(txt, tmp, &self.ij);
match self.cbc.cipher.encrypt(&mut self.ij, txt.as_slice()) {
Ok(_) => {
self.pond.append(&mut self.ij);
self.ij.extend(self.cbc.cur_iv.iter());
data = &data[len..];
},
Err(e) => {
return Err(e);
}
}
}
self.data.clear();
Ok(Pond::new(&mut self.pond, true))
}
}
impl<C, P, IV> DecryptStream for CBCDecrypt<C, P, IV>
where C: Cipher, P: 'static + Padding, IV: InitialVec<C> {
fn write(&mut self, data: &[u8]) -> Result<Pond, CryptoError> {
let block_len = self.cbc.cipher.block_size().unwrap_or(1);
if data.is_empty() {
return Ok(Pond::new(&mut self.pond, false));
} else {
self.data.extend_from_slice(data);
}
let txt = self.cbc.get_buf();
let mut data = self.data.as_slice();
while data.len() > block_len {
let tmp = &data[..block_len];
match self.cbc.cipher.decrypt(txt, tmp) {
Ok(_) => {
txt.iter_mut().zip(self.ij.iter_mut().zip(tmp.iter())).for_each(|(a, (b, &c))|
{
*a ^= *b;
*b = c;
});
self.pond.append(txt);
data = &data[block_len..];
},
Err(e) => {
return Err(e);
}
}
}
let mut data = data.to_vec();
self.data.clear();
self.data.append(&mut data);
Ok(Pond::new(&mut self.pond, false))
}
fn finish(&mut self) -> Result<Pond, CryptoError> {
let txt = self.cbc.get_buf();
match self.cbc.cipher.decrypt(txt, self.data.as_slice()) {
Ok(_) => {
txt.iter_mut().zip(self.ij.iter()).for_each(|(a, &b)| {
*a ^= b;
});
if let Err(e) = self.cbc.padding.unpadding(txt) {
Err(e)
} else {
self.data.clear();
self.pond.append(txt);
self.ij.clear();
self.ij.extend(self.cbc.cur_iv.iter());
Ok(Pond::new(&mut self.pond, true))
}
},
Err(e) => Err(e),
}
}
}