use crate::crypto::aesgcm::{AesGcm256, ConstantTimeEq, Tag, TAG_LENGTH};
use crate::crypto::ecc::{retrieve_key, store_key_for_multi_recipients, MultiRecipientPersistent};
use crate::layers::traits::{LayerFailSafeReader, LayerReader, LayerWriter};
use crate::Error;
use std::io;
use std::io::{BufReader, Cursor, Read, Seek, SeekFrom, Write};
use crate::config::{ArchiveReaderConfig, ArchiveWriterConfig};
use crate::errors::ConfigError;
use rand::{Rng, SeedableRng};
use rand_chacha::ChaChaRng;
use x25519_dalek::{PublicKey, StaticSecret};
use serde::{Deserialize, Serialize};
const CIPHER_BUF_SIZE: u64 = 4096;
const KEY_SIZE: usize = 32;
const NONCE_SIZE: usize = 8;
const CHUNK_SIZE: u64 = 128 * 1024;
const NONCE_AES_SIZE: usize = 96 / 8;
type Nonce = [u8; NONCE_AES_SIZE];
fn build_nonce(nonce_prefix: [u8; NONCE_SIZE], current_ctr: u32) -> Nonce {
let mut nonce = [0u8; NONCE_AES_SIZE];
nonce[..NONCE_SIZE].copy_from_slice(&nonce_prefix);
nonce[NONCE_SIZE..].copy_from_slice(¤t_ctr.to_be_bytes());
nonce
}
#[derive(Serialize, Deserialize)]
pub struct EncryptionPersistentConfig {
pub multi_recipient: MultiRecipientPersistent,
nonce: [u8; NONCE_SIZE],
}
pub struct EncryptionConfig {
ecc_keys: Vec<PublicKey>,
key: [u8; KEY_SIZE],
nonce: [u8; NONCE_SIZE],
}
impl std::default::Default for EncryptionConfig {
fn default() -> Self {
let mut csprng = ChaChaRng::from_entropy();
let key = csprng.gen::<[u8; KEY_SIZE]>();
let nonce = csprng.gen::<[u8; NONCE_SIZE]>();
EncryptionConfig {
ecc_keys: Vec::new(),
key,
nonce,
}
}
}
impl EncryptionConfig {
pub fn check(&self) -> Result<(), ConfigError> {
if self.ecc_keys.is_empty() {
Err(ConfigError::EncryptionKeyIsMissing)
} else {
Ok(())
}
}
pub fn to_persistent(&self) -> Result<EncryptionPersistentConfig, ConfigError> {
let mut rng = ChaChaRng::from_entropy();
if let Ok(multi_recipient) =
store_key_for_multi_recipients(&self.ecc_keys, &self.key, &mut rng)
{
Ok(EncryptionPersistentConfig {
multi_recipient,
nonce: self.nonce,
})
} else {
Err(ConfigError::ECIESComputationError)
}
}
}
impl ArchiveWriterConfig {
pub fn add_public_keys(&mut self, keys: &[PublicKey]) -> &mut ArchiveWriterConfig {
self.encrypt.ecc_keys.extend_from_slice(keys);
self
}
pub fn encryption_key(&self) -> &[u8; KEY_SIZE] {
&self.encrypt.key
}
pub fn encryption_nonce(&self) -> &[u8; NONCE_SIZE] {
&self.encrypt.nonce
}
}
pub struct EncryptionReaderConfig {
private_keys: Vec<StaticSecret>,
encrypt_parameters: Option<([u8; KEY_SIZE], [u8; NONCE_SIZE])>,
}
impl std::default::Default for EncryptionReaderConfig {
fn default() -> Self {
Self {
private_keys: Vec::new(),
encrypt_parameters: None,
}
}
}
impl EncryptionReaderConfig {
pub fn load_persistent(
&mut self,
config: EncryptionPersistentConfig,
) -> Result<(), ConfigError> {
if self.private_keys.is_empty() {
return Err(ConfigError::PrivateKeyNotSet);
}
for private_key in &self.private_keys {
match retrieve_key(&config.multi_recipient, private_key) {
Ok(Some(key)) => {
self.encrypt_parameters = Some((key, config.nonce));
break;
}
_ => {
continue;
}
};
}
if self.encrypt_parameters.is_none() {
return Err(ConfigError::PrivateKeyNotFound);
}
Ok(())
}
}
impl ArchiveReaderConfig {
pub fn add_private_keys(&mut self, keys: &[StaticSecret]) -> &mut ArchiveReaderConfig {
self.encrypt.private_keys.extend_from_slice(keys);
self
}
pub fn get_encrypt_parameters(&self) -> Option<([u8; KEY_SIZE], [u8; NONCE_SIZE])> {
self.encrypt.encrypt_parameters
}
}
pub struct EncryptionLayerWriter<'a, W: 'a + Write> {
inner: Box<dyn 'a + LayerWriter<'a, W>>,
cipher: AesGcm256,
key: [u8; KEY_SIZE],
nonce_prefix: [u8; NONCE_SIZE],
current_chunk_offset: u64,
current_ctr: u32,
}
impl<'a, W: 'a + Write> EncryptionLayerWriter<'a, W> {
pub fn new(
inner: Box<dyn 'a + LayerWriter<'a, W>>,
config: &EncryptionConfig,
) -> Result<Self, Error> {
Ok(Self {
inner,
key: config.key,
nonce_prefix: config.nonce,
cipher: AesGcm256::new(&config.key, &build_nonce(config.nonce, 0), b"")?,
current_chunk_offset: 0,
current_ctr: 0,
})
}
fn renew_cipher(&mut self) -> Result<Tag, Error> {
self.current_ctr += 1;
self.current_chunk_offset = 0;
let cipher = AesGcm256::new(
&self.key,
&build_nonce(self.nonce_prefix, self.current_ctr),
b"",
)?;
let old_cipher = std::mem::replace(&mut self.cipher, cipher);
Ok(old_cipher.into_tag())
}
}
impl<'a, W: 'a + Write> LayerWriter<'a, W> for EncryptionLayerWriter<'a, W> {
fn into_inner(self) -> Option<Box<dyn 'a + LayerWriter<'a, W>>> {
Some(self.inner)
}
fn into_raw(self: Box<Self>) -> W {
self.inner.into_raw()
}
fn finalize(&mut self) -> Result<(), Error> {
let tag = self.renew_cipher()?;
self.inner.write_all(&tag)?;
self.inner.finalize()
}
}
impl<'a, W: Write> Write for EncryptionLayerWriter<'a, W> {
#[allow(clippy::comparison_chain)]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if self.current_chunk_offset > CHUNK_SIZE {
return Err(
Error::WrongWriterState("[EncryptWriter] Chunk too big".to_string()).into(),
);
} else if self.current_chunk_offset == CHUNK_SIZE {
let tag = self.renew_cipher()?;
self.inner.write_all(&tag)?;
}
let size = std::cmp::min(
std::cmp::min(CIPHER_BUF_SIZE, buf.len() as u64),
CHUNK_SIZE - self.current_chunk_offset,
);
let mut buf_tmp = Vec::with_capacity(size as usize);
let buf_src = BufReader::new(buf);
io::copy(&mut buf_src.take(size), &mut buf_tmp)?;
self.cipher.encrypt(&mut buf_tmp);
self.inner.write_all(&buf_tmp)?;
self.current_chunk_offset += size;
Ok(size as usize)
}
fn flush(&mut self) -> io::Result<()> {
self.inner.flush()
}
}
pub struct EncryptionLayerReader<'a, R: Read + Seek> {
inner: Box<dyn 'a + LayerReader<'a, R>>,
cipher: AesGcm256,
key: [u8; KEY_SIZE],
nonce: [u8; NONCE_SIZE],
chunk_cache: Cursor<Vec<u8>>,
current_chunk_number: u32,
}
impl<'a, R: 'a + Read + Seek> EncryptionLayerReader<'a, R> {
pub fn new(
inner: Box<dyn 'a + LayerReader<'a, R>>,
config: &EncryptionReaderConfig,
) -> Result<Self, Error> {
match config.encrypt_parameters {
Some((key, nonce)) => Ok(Self {
inner,
cipher: AesGcm256::new(&key, &build_nonce(nonce, 0), b"")?,
key,
nonce,
chunk_cache: Cursor::new(Vec::with_capacity(CHUNK_SIZE as usize)),
current_chunk_number: 0,
}),
None => Err(Error::PrivateKeyNeeded),
}
}
fn load_in_cache(&mut self) -> Result<Option<()>, Error> {
self.cipher = AesGcm256::new(
&self.key,
&build_nonce(self.nonce, self.current_chunk_number),
b"",
)?;
self.chunk_cache.get_mut().clear();
let mut data_and_tag = Vec::with_capacity(CHUNK_SIZE as usize + TAG_LENGTH);
let data_and_tag_read = (&mut self.inner)
.take(CHUNK_SIZE + TAG_LENGTH as u64)
.read_to_end(&mut data_and_tag)?;
if data_and_tag_read == 0 {
return Ok(None);
}
let mut tag = [0u8; TAG_LENGTH];
tag.copy_from_slice(&data_and_tag[data_and_tag_read - TAG_LENGTH..]);
data_and_tag.resize(data_and_tag_read - TAG_LENGTH, 0);
let mut data = data_and_tag;
let expected_tag = self.cipher.decrypt(data.as_mut_slice());
if expected_tag.ct_eq(&tag).unwrap_u8() != 1 {
Err(Error::AuthenticatedDecryptionWrongTag)
} else {
self.chunk_cache = Cursor::new(data);
Ok(Some(()))
}
}
}
impl<'a, R: 'a + Read + Seek> LayerReader<'a, R> for EncryptionLayerReader<'a, R> {
fn into_inner(self) -> Option<Box<dyn 'a + LayerReader<'a, R>>> {
Some(self.inner)
}
fn into_raw(self: Box<Self>) -> R {
self.inner.into_raw()
}
fn initialize(&mut self) -> Result<(), Error> {
self.inner.initialize()?;
self.seek(SeekFrom::Start(0))?;
Ok(())
}
}
impl<'a, R: 'a + Read + Seek> Read for EncryptionLayerReader<'a, R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let cache_to_consume = CHUNK_SIZE - self.chunk_cache.position();
if cache_to_consume == 0 {
self.current_chunk_number += 1;
if self.load_in_cache()?.is_none() {
return Ok(0);
}
return self.read(buf);
}
let size = std::cmp::min(cache_to_consume as usize, buf.len());
self.chunk_cache.read(&mut buf[..size])
}
}
const CHUNK_TAG_SIZE: u64 = CHUNK_SIZE + TAG_LENGTH as u64;
fn no_tag_position_to_tag_position(position: u64) -> u64 {
let cur_chunk = position / CHUNK_SIZE;
let cur_chunk_pos = position % CHUNK_SIZE;
cur_chunk * CHUNK_TAG_SIZE + cur_chunk_pos
}
fn tag_position_to_no_tag_position(position: u64) -> u64 {
let cur_chunk = position / CHUNK_TAG_SIZE;
let cur_chunk_pos = position % CHUNK_TAG_SIZE;
cur_chunk * CHUNK_SIZE + std::cmp::min(cur_chunk_pos, CHUNK_SIZE)
}
impl<'a, R: 'a + Read + Seek> Seek for EncryptionLayerReader<'a, R> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match pos {
SeekFrom::Start(pos) => {
let tag_position = no_tag_position_to_tag_position(pos);
let chunk_number = tag_position / CHUNK_TAG_SIZE;
let pos_chunk_start = chunk_number * CHUNK_TAG_SIZE;
let pos_in_chunk = tag_position % CHUNK_TAG_SIZE;
self.inner.seek(SeekFrom::Start(pos_chunk_start))?;
self.current_chunk_number = chunk_number as u32;
self.load_in_cache()?;
self.chunk_cache.seek(SeekFrom::Start(pos_in_chunk))?;
Ok(pos)
}
SeekFrom::Current(value) => {
let current_inner = tag_position_to_no_tag_position(self.inner.seek(pos)?);
let current_inner_chunk = {
let chunk_nb = current_inner / CHUNK_SIZE;
if chunk_nb == 0 {
0
} else {
chunk_nb - 1
}
};
let current = current_inner_chunk * CHUNK_SIZE + self.chunk_cache.position();
if value == 0 {
Ok(current)
} else {
self.seek(SeekFrom::Start((current as i64 + value) as u64))
}
}
SeekFrom::End(pos) => {
if pos > 0 {
return Err(Error::EndOfStream.into());
}
let end_inner_pos = self.inner.seek(SeekFrom::End(0))?;
let cur_chunk = end_inner_pos / CHUNK_TAG_SIZE;
let cur_chunk_pos = end_inner_pos % CHUNK_TAG_SIZE;
let end_pos = cur_chunk * CHUNK_SIZE + cur_chunk_pos - TAG_LENGTH as u64;
self.seek(SeekFrom::Start((pos + end_pos as i64) as u64))
}
}
}
}
pub struct EncryptionLayerFailSafeReader<'a, R: Read> {
inner: Box<dyn 'a + LayerFailSafeReader<'a, R>>,
cipher: AesGcm256,
key: [u8; KEY_SIZE],
nonce: [u8; NONCE_SIZE],
current_chunk_number: u32,
current_chunk_offset: u64,
}
impl<'a, R: 'a + Read> EncryptionLayerFailSafeReader<'a, R> {
pub fn new(
inner: Box<dyn 'a + LayerFailSafeReader<'a, R>>,
config: &EncryptionReaderConfig,
) -> Result<Self, Error> {
match config.encrypt_parameters {
Some((key, nonce)) => Ok(Self {
inner,
cipher: AesGcm256::new(&key, &build_nonce(nonce, 0), b"")?,
key,
nonce,
current_chunk_number: 0,
current_chunk_offset: 0,
}),
None => Err(Error::PrivateKeyNeeded),
}
}
}
impl<'a, R: 'a + Read> LayerFailSafeReader<'a, R> for EncryptionLayerFailSafeReader<'a, R> {
fn into_inner(self) -> Option<Box<dyn 'a + LayerFailSafeReader<'a, R>>> {
Some(self.inner)
}
fn into_raw(self: Box<Self>) -> R {
self.inner.into_raw()
}
}
impl<'a, R: Read> Read for EncryptionLayerFailSafeReader<'a, R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.current_chunk_offset == CHUNK_SIZE {
io::copy(
&mut (&mut self.inner).take(TAG_LENGTH as u64),
&mut io::sink(),
)?;
self.current_chunk_number += 1;
self.current_chunk_offset = 0;
self.cipher = AesGcm256::new(
&self.key,
&build_nonce(self.nonce, self.current_chunk_number),
b"",
)?;
return self.read(buf);
}
let mut buf_tmp = [0u8; CIPHER_BUF_SIZE as usize];
let size = std::cmp::min(CIPHER_BUF_SIZE as usize, buf.len());
let size = std::cmp::min((CHUNK_SIZE - self.current_chunk_offset) as usize, size);
let len = self.inner.read(&mut buf_tmp[..size])?;
self.current_chunk_offset += len as u64;
self.cipher.decrypt_unauthenticated(&mut buf_tmp[..len]);
(&buf_tmp[..len]).read_exact(&mut buf[..len])?;
Ok(len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::distributions::{Alphanumeric, Distribution};
use rand::rngs::StdRng;
use rand::SeedableRng;
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
use crate::layers::raw::{RawLayerFailSafeReader, RawLayerReader, RawLayerWriter};
static FAKE_FILE: [u8; 26] = *b"abcdefghijklmnopqrstuvwxyz";
static KEY: [u8; KEY_SIZE] = [2u8; KEY_SIZE];
static NONCE: [u8; NONCE_SIZE] = [3u8; NONCE_SIZE];
fn encrypt_write(file: Vec<u8>) -> Vec<u8> {
let mut encrypt_w = Box::new(
EncryptionLayerWriter::new(
Box::new(RawLayerWriter::new(file)),
&EncryptionConfig {
ecc_keys: Vec::new(),
key: KEY,
nonce: NONCE,
},
)
.unwrap(),
);
encrypt_w.write_all(&FAKE_FILE[..21]).unwrap();
encrypt_w.write_all(&FAKE_FILE[21..]).unwrap();
encrypt_w.finalize().unwrap();
let out = encrypt_w.into_raw();
assert_eq!(out.len(), FAKE_FILE.len() + TAG_LENGTH);
assert_ne!(out[..FAKE_FILE.len()], FAKE_FILE);
out
}
#[test]
fn encrypt_layer() {
let file = Vec::new();
let out = encrypt_write(file);
let buf = Cursor::new(out.as_slice());
let config = EncryptionReaderConfig {
private_keys: Vec::new(),
encrypt_parameters: Some((KEY, NONCE)),
};
let mut encrypt_r =
EncryptionLayerReader::new(Box::new(RawLayerReader::new(buf)), &config).unwrap();
encrypt_r.initialize().unwrap();
let mut output = Vec::new();
encrypt_r.read_to_end(&mut output).unwrap();
assert_eq!(output, FAKE_FILE);
}
#[test]
fn encrypt_failsafe_layer() {
let file = Vec::new();
let out = encrypt_write(file);
let config = EncryptionReaderConfig {
private_keys: Vec::new(),
encrypt_parameters: Some((KEY, NONCE)),
};
let mut encrypt_r = EncryptionLayerFailSafeReader::new(
Box::new(RawLayerFailSafeReader::new(out.as_slice())),
&config,
)
.unwrap();
let mut output = Vec::new();
encrypt_r.read_to_end(&mut output).unwrap();
assert_eq!(output[..FAKE_FILE.len()], FAKE_FILE);
}
#[test]
fn encrypt_failsafe_truncated() {
let file = Vec::new();
let out = encrypt_write(file);
let stop = out.len() / 2;
let config = EncryptionReaderConfig {
private_keys: Vec::new(),
encrypt_parameters: Some((KEY, NONCE)),
};
let mut encrypt_r = EncryptionLayerFailSafeReader::new(
Box::new(RawLayerFailSafeReader::new(&out[..stop])),
&config,
)
.unwrap();
let mut output = Vec::new();
encrypt_r.read_to_end(&mut output).unwrap();
assert_eq!(output.as_slice(), &FAKE_FILE[..stop]);
}
#[test]
fn seek_encrypt() {
let file = Vec::new();
let out = encrypt_write(file);
let buf = Cursor::new(out.as_slice());
let config = EncryptionReaderConfig {
private_keys: Vec::new(),
encrypt_parameters: Some((KEY, NONCE)),
};
let mut encrypt_r =
EncryptionLayerReader::new(Box::new(RawLayerReader::new(buf)), &config).unwrap();
encrypt_r.initialize().unwrap();
let mut output = Vec::new();
encrypt_r.read_to_end(&mut output).unwrap();
assert_eq!(output, FAKE_FILE);
let pos = encrypt_r.seek(SeekFrom::Current(0)).unwrap();
assert_eq!(pos, tag_position_to_no_tag_position(FAKE_FILE.len() as u64));
let pos = encrypt_r.seek(SeekFrom::Start(5)).unwrap();
assert_eq!(pos, 5);
let mut output = Vec::new();
encrypt_r.read_to_end(&mut output).unwrap();
println!("{:?}", output);
assert_eq!(output.as_slice(), &FAKE_FILE[5..]);
}
#[test]
fn encrypt_op_chunk_size() {
let file = Vec::new();
let mut encrypt_w = Box::new(
EncryptionLayerWriter::new(
Box::new(RawLayerWriter::new(file)),
&EncryptionConfig {
ecc_keys: Vec::new(),
key: KEY,
nonce: NONCE,
},
)
.unwrap(),
);
let length = (CHUNK_SIZE * 2) as usize;
let mut rng: StdRng = SeedableRng::from_seed([0u8; 32]);
let data: Vec<u8> = Alphanumeric
.sample_iter(&mut rng)
.take(length)
.map(|c| c as u8)
.collect();
encrypt_w.write_all(&data).unwrap();
encrypt_w.finalize().unwrap();
let out = encrypt_w.into_raw();
assert_eq!(out.len(), length + 2 * TAG_LENGTH);
assert_ne!(&out[..length], data.as_slice());
let buf = Cursor::new(out.as_slice());
let config = EncryptionReaderConfig {
private_keys: Vec::new(),
encrypt_parameters: Some((KEY, NONCE)),
};
let mut encrypt_r =
EncryptionLayerReader::new(Box::new(RawLayerReader::new(buf)), &config).unwrap();
encrypt_r.initialize().unwrap();
let mut output = Vec::new();
encrypt_r.read_to_end(&mut output).unwrap();
assert_eq!(output, data);
let pos = encrypt_r.seek(SeekFrom::Start(CHUNK_SIZE)).unwrap();
assert_eq!(pos, CHUNK_SIZE);
let mut output = Vec::new();
encrypt_r.read_to_end(&mut output).unwrap();
assert_eq!(output.as_slice(), &data[CHUNK_SIZE as usize..]);
}
}