use std::num::NonZeroU32;
use ring::aead;
use crate::{DataOffset, LogFsError, journal::NextEntryOffset};
#[derive(Clone)]
pub struct CryptoConfig {
pub key: zeroize::Zeroizing<String>,
pub salt: zeroize::Zeroizing<Vec<u8>>,
pub iterations: NonZeroU32,
}
impl std::fmt::Debug for CryptoConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CryptoConfig")
.field("key", &"*****")
.field("seed", &"*****")
.field("iterations", &"*****")
.finish()
}
}
pub struct Crypto {
key: aead::LessSafeKey,
}
impl Crypto {
pub const EXTRA_PAYLOAD_LEN: usize = 16;
pub fn new(config: CryptoConfig) -> Self {
let mut derived_key = [0u8; ring::digest::SHA256_OUTPUT_LEN];
ring::pbkdf2::derive(
ring::pbkdf2::PBKDF2_HMAC_SHA512,
config.iterations,
config.salt.as_slice(),
config.key.as_bytes(),
&mut derived_key,
);
let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &derived_key)
.expect("Internal error: invalid key");
let aead_key = aead::LessSafeKey::new(unbound_key);
Self { key: aead_key }
}
fn build_entry_nonce(sequence: u64) -> aead::Nonce {
let mut nonce_bytes = [0u8; 12];
nonce_bytes[0..8].copy_from_slice(&sequence.to_le_bytes());
aead::Nonce::assume_unique_for_key(nonce_bytes)
}
fn build_data_nonce(sequence: u64, chunk_index: u32) -> Result<aead::Nonce, LogFsError> {
let mut nonce_bytes = [0u8; 12];
nonce_bytes[0..8].copy_from_slice(&sequence.to_le_bytes());
nonce_bytes[8..12].copy_from_slice(&chunk_index.to_le_bytes());
let nonce = aead::Nonce::assume_unique_for_key(nonce_bytes);
Ok(nonce)
}
pub fn decrypt_entry<'a>(
&self,
sequence: u64,
header_data: &[u8],
buffer: &'a mut [u8],
) -> Result<(&'a [u8], NextEntryOffset), LogFsError> {
let nonce = Self::build_entry_nonce(sequence);
let aad = aead::Aad::from(header_data);
let data = self
.key
.open_in_place(nonce, aad, buffer)
.map(|x| &*x)
.map_err(|_| LogFsError::new_internal("Could not decrypt journal entry"))?;
Ok((data, self.extra_payload_len() as usize))
}
pub fn extra_payload_len(&self) -> DataOffset {
aead::CHACHA20_POLY1305.tag_len() as u64
}
pub fn encrypt_entry(
&self,
sequence: u64,
header_data: &[u8],
data: &mut Vec<u8>,
) -> Result<(), LogFsError> {
let nonce = Self::build_entry_nonce(sequence);
let aad = aead::Aad::from(header_data);
self.key
.seal_in_place_append_tag(nonce, aad, data)
.map_err(|_| LogFsError::new_internal("Could not encrypt journal entry"))?;
Ok(())
}
pub fn encrypt_data(
&self,
sequence: u64,
chunk_index: u32,
data: &mut Vec<u8>,
) -> Result<(), LogFsError> {
let data_nonce = Self::build_data_nonce(sequence, chunk_index)?;
let aad = aead::Aad::from(&[]);
self.key
.seal_in_place_append_tag(data_nonce, aad, data)
.map_err(|_| LogFsError::new_internal("Could not encrypt journal entry"))
}
pub fn decrypt_data_ref<'a>(
&self,
sequence: u64,
chunk_index: u32,
data: &'a mut [u8],
) -> Result<&'a [u8], LogFsError> {
let nonce = Self::build_data_nonce(sequence, chunk_index)?;
let slice = self
.key
.open_in_place(nonce, aead::Aad::from(&[]), data)
.map_err(|_| LogFsError::new_internal("Could not decrypt data"))?;
Ok(slice)
}
pub fn decrypt_data(
&self,
sequence: u64,
chunk_index: u32,
mut data: Vec<u8>,
) -> Result<Vec<u8>, LogFsError> {
let full_length = data.len();
let nonce = Self::build_data_nonce(sequence, chunk_index)?;
self.key
.open_in_place(nonce, aead::Aad::from(&[]), data.as_mut_slice())
.map_err(|_| LogFsError::new_internal("Could not decrypt data"))?;
data.truncate(full_length - self.extra_payload_len() as usize);
Ok(data)
}
}