#![no_std]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
)]
#![warn(missing_docs, rust_2018_idioms)]
pub use cipher;
use cipher::{
generic_array::{typenum::Unsigned, GenericArray},
AsyncStreamCipher, BlockCipher, BlockEncrypt, FromBlockCipher, ParBlocks,
};
use core::slice;
pub struct Cfb<C: BlockCipher + BlockEncrypt> {
cipher: C,
iv: GenericArray<u8, C::BlockSize>,
pos: usize,
}
impl<C: BlockCipher + BlockEncrypt> FromBlockCipher for Cfb<C> {
type BlockCipher = C;
type NonceSize = C::BlockSize;
fn from_block_cipher(cipher: C, iv: &GenericArray<u8, Self::NonceSize>) -> Self {
let mut iv = iv.clone();
cipher.encrypt_block(&mut iv);
Self { cipher, iv, pos: 0 }
}
}
impl<C: BlockCipher + BlockEncrypt> AsyncStreamCipher for Cfb<C> {
fn encrypt(&mut self, mut data: &mut [u8]) {
let bs = C::BlockSize::USIZE;
let n = data.len();
if n < bs - self.pos {
xor_set1(data, &mut self.iv[self.pos..self.pos + n]);
self.pos += n;
return;
}
let (left, right) = { data }.split_at_mut(bs - self.pos);
data = right;
let mut iv = self.iv.clone();
xor_set1(left, &mut iv[self.pos..]);
self.cipher.encrypt_block(&mut iv);
let mut chunks = data.chunks_exact_mut(bs);
for chunk in &mut chunks {
xor_set1(chunk, iv.as_mut_slice());
self.cipher.encrypt_block(&mut iv);
}
let rem = chunks.into_remainder();
xor_set1(rem, iv.as_mut_slice());
self.pos = rem.len();
self.iv = iv;
}
fn decrypt(&mut self, mut data: &mut [u8]) {
let bs = C::BlockSize::to_usize();
let pb = C::ParBlocks::to_usize();
let n = data.len();
if n < bs - self.pos {
xor_set2(data, &mut self.iv[self.pos..self.pos + n]);
self.pos += n;
return;
}
let (left, right) = { data }.split_at_mut(bs - self.pos);
data = right;
let mut iv = self.iv.clone();
xor_set2(left, &mut iv[self.pos..]);
self.cipher.encrypt_block(&mut iv);
let bss = bs * pb;
if pb != 1 && data.len() >= bss {
let mut iv_blocks: ParBlocks<C> =
unsafe { (&*(data.as_ptr() as *const ParBlocks<C>)).clone() };
self.cipher.encrypt_blocks(&mut iv_blocks);
let (block, r) = { data }.split_at_mut(bs);
data = r;
xor(block, iv.as_slice());
while data.len() >= 2 * bss - bs {
let (blocks, r) = { data }.split_at_mut(bss);
data = r;
let mut next_iv_blocks: ParBlocks<C> = unsafe {
let ptr = data.as_ptr().offset(-(bs as isize));
(&*(ptr as *const ParBlocks<C>)).clone()
};
self.cipher.encrypt_blocks(&mut next_iv_blocks);
xor(blocks, unsafe {
let ptr = iv_blocks.as_mut_ptr() as *mut u8;
slice::from_raw_parts(ptr, bss)
});
iv_blocks = next_iv_blocks;
}
let n = pb - 1;
let (blocks, r) = { data }.split_at_mut(n * bs);
data = r;
let chunks = blocks.chunks_mut(bs);
for (iv, block) in iv_blocks[..n].iter().zip(chunks) {
xor(block, iv.as_slice())
}
iv = iv_blocks[n].clone();
}
let mut chunks = data.chunks_exact_mut(bs);
for chunk in &mut chunks {
xor_set2(chunk, iv.as_mut_slice());
self.cipher.encrypt_block(&mut iv);
}
let rem = chunks.into_remainder();
xor_set2(rem, iv.as_mut_slice());
self.pos = rem.len();
self.iv = iv;
}
}
#[inline(always)]
fn xor(buf1: &mut [u8], buf2: &[u8]) {
for (a, b) in buf1.iter_mut().zip(buf2) {
*a ^= *b;
}
}
#[inline(always)]
fn xor_set1(buf1: &mut [u8], buf2: &mut [u8]) {
for (a, b) in buf1.iter_mut().zip(buf2) {
let t = *a ^ *b;
*a = t;
*b = t;
}
}
#[inline(always)]
fn xor_set2(buf1: &mut [u8], buf2: &mut [u8]) {
for (a, b) in buf1.iter_mut().zip(buf2) {
let t = *a;
*a ^= *b;
*b = t;
}
}