use aes::{Aes128, Aes192, Aes256};
use blowfish::Blowfish;
use bytes::{Buf, Bytes, BytesMut};
use camellia::{Camellia128, Camellia192, Camellia256};
use cast5::Cast5;
use cfb_mode::{cipher::KeyIvInit, BufEncryptor};
use cipher::{BlockCipher, BlockDecrypt, BlockEncryptMut, BlockSizeUser};
use des::TdesEde3;
use idea::Idea;
use log::debug;
use rand::{CryptoRng, Rng};
use sha1::{Digest, Sha1};
use twofish::Twofish;
use crate::pgp::{
crypto::sym::SymmetricKeyAlgorithm,
errors::{bail, unsupported_err, Result},
util::fill_buffer,
};
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum StreamEncryptor<R>
where
R: std::io::Read,
{
Idea(StreamEncryptorInner<Idea, R>),
TripleDes(StreamEncryptorInner<TdesEde3, R>),
Cast5(StreamEncryptorInner<Cast5, R>),
Blowfish(StreamEncryptorInner<Blowfish, R>),
Aes128(StreamEncryptorInner<Aes128, R>),
Aes192(StreamEncryptorInner<Aes192, R>),
Aes256(StreamEncryptorInner<Aes256, R>),
Twofish(StreamEncryptorInner<Twofish, R>),
Camellia128(StreamEncryptorInner<Camellia128, R>),
Camellia192(StreamEncryptorInner<Camellia192, R>),
Camellia256(StreamEncryptorInner<Camellia256, R>),
}
impl<R: std::io::Read> StreamEncryptor<R> {
pub fn new<B: Rng + CryptoRng>(
rng: B,
alg: SymmetricKeyAlgorithm,
key: &[u8],
plaintext: R,
) -> Result<Self> {
match alg {
SymmetricKeyAlgorithm::Plaintext => {
bail!("'Plaintext' is not a legal cipher for encrypted data")
}
SymmetricKeyAlgorithm::IDEA => Ok(StreamEncryptor::Idea(StreamEncryptorInner::new(
rng, plaintext, key,
)?)),
SymmetricKeyAlgorithm::TripleDES => Ok(StreamEncryptor::TripleDes(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::CAST5 => Ok(StreamEncryptor::Cast5(StreamEncryptorInner::new(
rng, plaintext, key,
)?)),
SymmetricKeyAlgorithm::Blowfish => Ok(StreamEncryptor::Blowfish(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::AES128 => Ok(StreamEncryptor::Aes128(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::AES192 => Ok(StreamEncryptor::Aes192(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::AES256 => Ok(StreamEncryptor::Aes256(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::Twofish => Ok(StreamEncryptor::Twofish(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::Camellia128 => Ok(StreamEncryptor::Camellia128(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::Camellia192 => Ok(StreamEncryptor::Camellia192(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::Camellia256 => Ok(StreamEncryptor::Camellia256(
StreamEncryptorInner::new(rng, plaintext, key)?,
)),
SymmetricKeyAlgorithm::Private10 | SymmetricKeyAlgorithm::Other(_) => {
unsupported_err!("SymmetricKeyAlgorithm {:?}")
}
}
}
}
impl<R> std::io::Read for StreamEncryptor<R>
where
R: std::io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
Self::Idea(ref mut i) => i.read(buf),
Self::TripleDes(ref mut i) => i.read(buf),
Self::Cast5(ref mut i) => i.read(buf),
Self::Blowfish(ref mut i) => i.read(buf),
Self::Aes128(ref mut i) => i.read(buf),
Self::Aes192(ref mut i) => i.read(buf),
Self::Aes256(ref mut i) => i.read(buf),
Self::Twofish(ref mut i) => i.read(buf),
Self::Camellia128(ref mut i) => i.read(buf),
Self::Camellia192(ref mut i) => i.read(buf),
Self::Camellia256(ref mut i) => i.read(buf),
}
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
match self {
Self::Idea(ref mut i) => i.read_to_end(buf),
Self::TripleDes(ref mut i) => i.read_to_end(buf),
Self::Cast5(ref mut i) => i.read_to_end(buf),
Self::Blowfish(ref mut i) => i.read_to_end(buf),
Self::Aes128(ref mut i) => i.read_to_end(buf),
Self::Aes192(ref mut i) => i.read_to_end(buf),
Self::Aes256(ref mut i) => i.read_to_end(buf),
Self::Twofish(ref mut i) => i.read_to_end(buf),
Self::Camellia128(ref mut i) => i.read_to_end(buf),
Self::Camellia192(ref mut i) => i.read_to_end(buf),
Self::Camellia256(ref mut i) => i.read_to_end(buf),
}
}
}
#[derive(derive_more::Debug)]
pub enum StreamEncryptorInner<M, R>
where
M: BlockDecrypt + BlockEncryptMut + BlockCipher,
BufEncryptor<M>: KeyIvInit,
R: std::io::Read,
{
Prefix {
hasher: Sha1,
#[debug("BufEncryptor")]
encryptor: BufEncryptor<M>,
prefix: Bytes,
source: R,
},
Data {
hasher: Sha1,
#[debug("BufEncryptor")]
encryptor: BufEncryptor<M>,
buffer: BytesMut,
source: Option<R>,
},
Mdc {
mdc: Bytes,
},
Done,
Unknown,
}
impl<M, R> StreamEncryptorInner<M, R>
where
M: BlockDecrypt + BlockEncryptMut + BlockCipher,
BufEncryptor<M>: KeyIvInit,
R: std::io::Read,
{
fn new<RAND>(mut rng: RAND, source: R, key: &[u8]) -> Result<Self>
where
RAND: Rng + CryptoRng,
{
debug!("protected encrypt stream");
let bs = <M as BlockSizeUser>::block_size();
let mut prefix = vec![0u8; bs + 2];
rng.fill_bytes(&mut prefix[..bs]);
prefix[bs] = prefix[bs - 2];
prefix[bs + 1] = prefix[bs - 1];
let mut hasher = Sha1::default();
let iv_vec = vec![0u8; bs];
let mut encryptor = BufEncryptor::<M>::new_from_slices(key, &iv_vec)?;
hasher.update(&prefix);
encryptor.encrypt(&mut prefix);
Ok(Self::Prefix {
hasher,
encryptor,
prefix: prefix.into(),
source,
})
}
fn buffer_size() -> usize {
8 * 1024
}
fn fill_inner(&mut self) -> std::io::Result<()> {
match self {
Self::Prefix { prefix, .. } => {
if prefix.has_remaining() {
return Ok(());
}
}
Self::Data {
hasher,
encryptor,
buffer,
source,
} => {
if buffer.has_remaining() {
return Ok(());
}
let mdc = if let Some(source) = source {
buffer.resize(Self::buffer_size(), 0);
let read = fill_buffer(source, buffer, None)?;
if read < buffer.len() {
buffer.truncate(read);
}
if buffer.is_empty() {
true
} else {
hasher.update(&buffer);
encryptor.encrypt(buffer);
false
}
} else {
true
};
if !mdc {
return Ok(());
}
}
Self::Mdc { mdc } => {
if mdc.has_remaining() {
return Ok(());
}
}
Self::Done => {
return Ok(());
}
Self::Unknown => {
panic!("encryption panicked");
}
};
match std::mem::replace(self, Self::Unknown) {
Self::Prefix {
mut hasher,
mut encryptor,
mut source,
..
} => {
let mut buffer = BytesMut::zeroed(Self::buffer_size());
let read = fill_buffer(&mut source, &mut buffer, None)?;
let source = if read < buffer.len() {
buffer.truncate(read);
None
} else {
Some(source)
};
hasher.update(&buffer);
encryptor.encrypt(&mut buffer);
*self = Self::Data {
hasher,
encryptor,
buffer,
source,
};
}
Self::Data {
mut hasher,
mut encryptor,
..
} => {
let mdc_header = [0xD3, 0x14];
hasher.update(mdc_header);
let mut mdc = BytesMut::zeroed(22);
mdc[..2].copy_from_slice(&mdc_header);
let checksum = &hasher.finalize()[..20];
mdc[2..22].copy_from_slice(checksum);
encryptor.encrypt(&mut mdc[..]);
*self = Self::Mdc { mdc: mdc.freeze() };
}
Self::Mdc { .. } => {
*self = Self::Done;
}
Self::Done => {}
Self::Unknown => {
panic!("encryption panicked");
}
}
Ok(())
}
}
impl<M, R> std::io::Read for StreamEncryptorInner<M, R>
where
M: BlockDecrypt + BlockEncryptMut + BlockCipher,
BufEncryptor<M>: KeyIvInit,
R: std::io::Read,
{
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.fill_inner()?;
match self {
Self::Prefix { prefix, .. } => {
let to_write = buf.len().min(prefix.remaining());
prefix.copy_to_slice(&mut buf[..to_write]);
Ok(to_write)
}
Self::Data { buffer, .. } => {
let to_write = buf.len().min(buffer.remaining());
buffer.copy_to_slice(&mut buf[..to_write]);
Ok(to_write)
}
Self::Mdc { mdc } => {
let to_write = buf.len().min(mdc.remaining());
mdc.copy_to_slice(&mut buf[..to_write]);
Ok(to_write)
}
Self::Done => Ok(0),
Self::Unknown => unreachable!("error state"),
}
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
let mut read = 0;
loop {
self.fill_inner()?;
match self {
Self::Prefix { prefix, .. } => {
let to_write = prefix.remaining();
buf.extend_from_slice(&prefix[..to_write]);
prefix.advance(to_write);
read += to_write;
}
Self::Data { buffer, .. } => {
let to_write = buffer.remaining();
buf.extend_from_slice(&buffer[..to_write]);
buffer.advance(to_write);
read += to_write;
}
Self::Mdc { mdc } => {
let to_write = mdc.remaining();
buf.extend_from_slice(&mdc[..to_write]);
mdc.advance(to_write);
read += to_write;
}
Self::Done => {
break;
}
Self::Unknown => unreachable!("error state"),
}
}
Ok(read)
}
}