use crate::crypto::chacha20poly1305::ChaCha20Poly1305;
use crate::types::{EncryptionMetadata, PlaintextBlob, Storable};
use ::prost::Message;
use std::borrow::Borrow;
use std::io;
use std::io::{Error, ErrorKind};
pub struct StorableBuilder<T: EntropySource> {
data_encryption_key: [u8; 32],
entropy_source: T,
}
impl<T: EntropySource> StorableBuilder<T> {
pub fn new(data_encryption_key: [u8; 32], entropy_source: T) -> StorableBuilder<T> {
Self { data_encryption_key, entropy_source }
}
}
pub trait EntropySource {
fn fill_bytes(&self, buffer: &mut [u8]);
}
const CHACHA20_CIPHER_NAME: &'static str = "ChaCha20Poly1305";
impl<T: EntropySource> StorableBuilder<T> {
pub fn build(&self, input: Vec<u8>, version: i64) -> Storable {
let mut nonce = vec![0u8; 12];
self.entropy_source.fill_bytes(&mut nonce[4..]);
let mut data_blob = PlaintextBlob { value: input, version }.encode_to_vec();
let mut cipher = ChaCha20Poly1305::new(&self.data_encryption_key, &nonce, &[]);
let mut tag = vec![0u8; 16];
cipher.encrypt_inplace(&mut data_blob, &mut tag);
Storable {
data: data_blob,
encryption_metadata: Some(EncryptionMetadata {
nonce,
tag,
cipher_format: CHACHA20_CIPHER_NAME.to_string(),
}),
}
}
pub fn deconstruct(&self, mut storable: Storable) -> io::Result<(Vec<u8>, i64)> {
let encryption_metadata = storable.encryption_metadata.unwrap();
let mut cipher =
ChaCha20Poly1305::new(&self.data_encryption_key, &encryption_metadata.nonce, &[]);
cipher
.decrypt_inplace(&mut storable.data, encryption_metadata.tag.borrow())
.map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid Tag"))?;
let data_blob = PlaintextBlob::decode(&storable.data[..])
.map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
Ok((data_blob.value, data_blob.version))
}
}
#[cfg(test)]
mod tests {
use super::*;
pub struct TestEntropyProvider;
impl EntropySource for TestEntropyProvider {
fn fill_bytes(&self, buffer: &mut [u8]) {
for (i, byte) in buffer.iter_mut().enumerate() {
*byte = (i % 256) as u8;
}
}
}
#[test]
fn encrypt_decrypt() {
let test_entropy_provider = TestEntropyProvider;
let mut data_key = [0u8; 32];
test_entropy_provider.fill_bytes(&mut data_key);
let storable_builder = StorableBuilder {
data_encryption_key: data_key,
entropy_source: test_entropy_provider,
};
let expected_data = b"secret".to_vec();
let expected_version = 8;
let storable = storable_builder.build(expected_data.clone(), expected_version);
let (actual_data, actual_version) = storable_builder.deconstruct(storable).unwrap();
assert_eq!(actual_data, expected_data);
assert_eq!(actual_version, expected_version);
}
}