use oxicrypto::{new_rng, XChaCha20Poly1305};
use crate::error::EncryptError;
use crate::keys::KeyProvider;
pub const MIN_CIPHERTEXT_LEN: usize = NONCE_LEN + TAG_LEN;
const NONCE_LEN: usize = 24;
const TAG_LEN: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CellId {
pub table_id: u64,
pub row_id: u64,
pub col_id: u32,
}
impl CellId {
pub fn to_aad_bytes(self) -> [u8; 20] {
let mut buf = [0u8; 20];
buf[0..8].copy_from_slice(&self.table_id.to_le_bytes());
buf[8..16].copy_from_slice(&self.row_id.to_le_bytes());
buf[16..20].copy_from_slice(&self.col_id.to_le_bytes());
buf
}
}
pub fn encrypt_cell<K: KeyProvider>(
key_provider: &K,
cell_id: CellId,
plaintext: &[u8],
) -> Result<Vec<u8>, EncryptError> {
let key32 = key_provider.key32()?;
let mut rng = new_rng().map_err(|_| EncryptError::RngFailed)?;
let mut nonce = [0u8; NONCE_LEN];
rng.fill(&mut nonce).map_err(|_| EncryptError::RngFailed)?;
let aad = cell_id.to_aad_bytes();
let ct_len = plaintext
.len()
.checked_add(TAG_LEN)
.ok_or(EncryptError::RngFailed)?; let mut output = vec![0u8; NONCE_LEN + ct_len];
output[..NONCE_LEN].copy_from_slice(&nonce);
let cipher = XChaCha20Poly1305;
let written = cipher
.seal(key32, &nonce, &aad, plaintext, &mut output[NONCE_LEN..])
.map_err(|_| EncryptError::AuthenticationFailed)?;
output.truncate(NONCE_LEN + written);
Ok(output)
}
pub fn decrypt_cell<K: KeyProvider>(
key_provider: &K,
cell_id: CellId,
ciphertext_with_nonce: &[u8],
) -> Result<Vec<u8>, EncryptError> {
if ciphertext_with_nonce.len() < MIN_CIPHERTEXT_LEN {
return Err(EncryptError::CiphertextTooShort {
min_expected: MIN_CIPHERTEXT_LEN,
got: ciphertext_with_nonce.len(),
});
}
let key32 = key_provider.key32()?;
let nonce: &[u8; NONCE_LEN] = ciphertext_with_nonce[..NONCE_LEN].try_into().map_err(|_| {
EncryptError::CiphertextTooShort {
min_expected: MIN_CIPHERTEXT_LEN,
got: ciphertext_with_nonce.len(),
}
})?;
let ct = &ciphertext_with_nonce[NONCE_LEN..];
let aad = cell_id.to_aad_bytes();
let pt_len = ct.len().saturating_sub(TAG_LEN);
let mut plaintext = vec![0u8; pt_len];
let cipher = XChaCha20Poly1305;
cipher
.open(key32, nonce, &aad, ct, &mut plaintext)
.map_err(|_| EncryptError::AuthenticationFailed)?;
Ok(plaintext)
}