use std::io::{self, Read, Write};
use std::ops::{BitXorAssign, BitXor};
use blowfish::cipher::{BlockEncrypt, BlockDecrypt, Block};
use blowfish::Blowfish;
pub const BLOCK_SIZE: usize = 8;
pub struct BlowfishReader<'a, R: Read> {
inner: R,
blowfish: &'a Blowfish,
cur_block: BlowfishBlock,
cur_pos: usize,
last_block: BlowfishBlock,
}
impl<'a, R: Read> BlowfishReader<'a, R> {
#[inline]
pub fn new(inner: R, blowfish: &'a Blowfish) -> Self {
Self {
inner,
blowfish,
cur_block: BlowfishBlock::new(),
cur_pos: BLOCK_SIZE,
last_block: BlowfishBlock::new(),
}
}
}
impl<'a, R: Read> Read for BlowfishReader<'a, R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if buf.is_empty() {
return Ok(0)
}
debug_assert!(self.cur_pos <= BLOCK_SIZE);
if self.cur_pos == BLOCK_SIZE {
match self.inner.read_exact(self.cur_block.slice_mut()) {
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(0),
Err(e) => return Err(e),
_ => ()
}
self.blowfish.decrypt_block(Block::<Blowfish>::from_mut_slice(self.cur_block.slice_mut()));
self.cur_pos = 0;
self.cur_block ^= self.last_block;
self.last_block = self.cur_block;
}
let len = buf.len().min(BLOCK_SIZE - self.cur_pos);
buf[..len].copy_from_slice(&self.cur_block.slice()[self.cur_pos..]);
self.cur_pos += len;
Ok(len)
}
}
pub struct BlowfishWriter<'a, W: Write> {
inner: W,
blowfish: &'a Blowfish,
tmp_block: BlowfishBlock,
tmp_pos: usize,
xor_block: BlowfishBlock,
}
impl<'a, W: Write> BlowfishWriter<'a, W> {
#[inline]
pub fn new(inner: W, blowfish: &'a Blowfish) -> Self {
Self {
inner,
blowfish,
tmp_block: BlowfishBlock::new(),
tmp_pos: 0,
xor_block: BlowfishBlock::new(),
}
}
}
impl<'a, W: Write> Write for BlowfishWriter<'a, W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let len = buf.len().min(BLOCK_SIZE - self.tmp_pos);
self.tmp_block.slice_mut()[self.tmp_pos..][..len].copy_from_slice(&buf[..len]);
self.tmp_pos += len;
debug_assert!(self.tmp_pos <= BLOCK_SIZE);
if self.tmp_pos == BLOCK_SIZE {
self.flush()?;
}
Ok(len)
}
fn flush(&mut self) -> io::Result<()> {
if self.tmp_pos == 0 {
return Ok(())
}
self.xor_block ^= self.tmp_block;
let saved = self.tmp_block;
let src = Block::<Blowfish>::from_slice(self.xor_block.slice());
let dst = Block::<Blowfish>::from_mut_slice(self.tmp_block.slice_mut());
self.blowfish.encrypt_block_b2b(src, dst);
self.inner.write_all(self.tmp_block.slice())?;
self.xor_block = saved;
self.tmp_block.clear();
self.tmp_pos = 0;
Ok(())
}
}
impl<'a, W: Write> Drop for BlowfishWriter<'a, W> {
fn drop(&mut self) {
let _ = self.flush();
}
}
#[repr(C)]
#[derive(Clone, Copy)]
union BlowfishBlock {
full: u64,
slice: [u8; 8],
}
impl BlowfishBlock {
#[inline]
fn new() -> Self {
Self { full: 0 }
}
#[inline]
fn clear(&mut self) {
self.full = 0;
}
#[inline]
fn full(&self) -> &u64 {
unsafe { &self.full }
}
#[inline]
fn full_mut(&mut self) -> &mut u64 {
unsafe { &mut self.full }
}
#[inline]
fn slice(&self) -> &[u8; 8] {
unsafe { &self.slice }
}
#[inline]
fn slice_mut(&mut self) -> &mut [u8; 8] {
unsafe { &mut self.slice }
}
}
impl BitXorAssign for BlowfishBlock {
fn bitxor_assign(&mut self, rhs: Self) {
*self.full_mut() ^= *rhs.full();
}
}
impl BitXor for BlowfishBlock {
type Output = BlowfishBlock;
fn bitxor(mut self, rhs: Self) -> Self::Output {
self ^= rhs;
self
}
}