use std::io;
use std::io::Read;
use std::fs::File;
use std::path::Path;
use rust_crypto::aessafe::AesSafe128Decryptor;
use rust_crypto::buffer::BufferResult;
use rust_crypto::buffer::ReadBuffer;
use rust_crypto::buffer::RefReadBuffer;
use rust_crypto::buffer::RefWriteBuffer;
use rust_crypto::buffer::WriteBuffer;
use rust_crypto::blockmodes::CbcDecryptor;
use rust_crypto::blockmodes::DecPadding;
use rust_crypto::blockmodes::PkcsPadding;
use rust_crypto::symmetriccipher::Decryptor;
use zbackup::data::*;
type DecryptorType =
CbcDecryptor <
AesSafe128Decryptor,
DecPadding <PkcsPadding>,
>;
pub struct CryptoReader {
input: File,
decryptor: DecryptorType,
ciphertext_buffer: [u8; BUFFER_SIZE],
ciphertext_start: usize,
ciphertext_end: usize,
ciphertext_eof: bool,
plaintext_buffer: [u8; BUFFER_SIZE],
plaintext_start: usize,
plaintext_end: usize,
plaintext_eof: bool,
}
impl CryptoReader {
#[ inline ]
pub fn open <PathRef: AsRef <Path>> (
path: PathRef,
encryption_key: [u8; KEY_SIZE],
) -> io::Result <CryptoReader> {
Self::open_impl (
path.as_ref (),
encryption_key,
)
}
pub fn open_impl (
path: & Path,
encryption_key: [u8; KEY_SIZE],
) -> io::Result <CryptoReader> {
let file =
File::open (
path,
) ?;
let decryptor =
CbcDecryptor::new (
AesSafe128Decryptor::new (
& encryption_key),
PkcsPadding,
[0u8; KEY_SIZE].to_vec ());
Ok (CryptoReader {
input: file,
decryptor: decryptor,
ciphertext_buffer: [0u8; BUFFER_SIZE],
ciphertext_start: 0,
ciphertext_end: 0,
ciphertext_eof: false,
plaintext_buffer: [0u8; BUFFER_SIZE],
plaintext_start: 0,
plaintext_end: 0,
plaintext_eof: false,
})
}
fn read_and_decrypt (
& mut self,
) -> io::Result <()> {
assert! (
! self.plaintext_eof);
assert! (
self.plaintext_start == self.plaintext_end);
self.plaintext_start = 0;
self.plaintext_end = 0;
loop {
if ! self.ciphertext_eof
&& self.ciphertext_start == self.ciphertext_end {
self.ciphertext_start = 0;
self.ciphertext_end =
self.input.read (
& mut self.ciphertext_buffer,
) ?;
if self.ciphertext_end == 0 {
self.ciphertext_eof = true;
}
}
let mut read_buffer =
RefReadBuffer::new (
& mut self.ciphertext_buffer [
self.ciphertext_start
..
self.ciphertext_end
]);
let mut write_buffer =
RefWriteBuffer::new (
& mut self.plaintext_buffer [
self.plaintext_end .. ]);
let decrypt_result =
self.decryptor.decrypt (
& mut read_buffer,
& mut write_buffer,
self.ciphertext_eof,
).map_err (
|_|
io::Error::new (
io::ErrorKind::InvalidData,
"Decryption failed")
) ?;
self.ciphertext_start +=
read_buffer.position ();
self.plaintext_end +=
write_buffer.position ();
if write_buffer.position () == 0 {
break;
}
match decrypt_result {
BufferResult::BufferUnderflow =>
continue,
BufferResult::BufferOverflow =>
break,
};
}
if self.plaintext_start == self.plaintext_end {
self.plaintext_eof = true;
}
Ok (())
}
}
impl Read for CryptoReader {
fn read (
& mut self,
buffer: & mut [u8],
) -> io::Result <usize> {
let mut total_bytes_read: usize =
0;
while (
! (
self.plaintext_eof
&& self.plaintext_start == self.plaintext_end
)
&& total_bytes_read < buffer.len ()
) {
if self.plaintext_start == self.plaintext_end {
self.read_and_decrypt () ?;
}
let buffer_remaining =
& mut buffer [total_bytes_read .. ];
let available_bytes =
self.plaintext_end - self.plaintext_start;
if available_bytes == 0 {
return Ok (total_bytes_read);
}
if buffer_remaining.len () <= available_bytes {
let buffer_remaining_len =
buffer_remaining.len ();
buffer_remaining.copy_from_slice (
& self.plaintext_buffer [
self.plaintext_start ..
self.plaintext_start + buffer_remaining_len]);
self.plaintext_start +=
buffer_remaining_len;
total_bytes_read +=
buffer_remaining_len;
} else {
buffer_remaining [
0 .. available_bytes
].copy_from_slice (
& self.plaintext_buffer [
self.plaintext_start ..
self.plaintext_start + available_bytes]
);
self.plaintext_start +=
available_bytes;
total_bytes_read +=
available_bytes;
}
}
Ok (total_bytes_read)
}
}