use oxicrypto::{blake3, new_rng, AesGcmSiv256, XChaCha20Poly1305};
use crate::error::EncryptError;
pub fn derive_cell_id(key_bytes: &[u8]) -> [u8; 32] {
blake3(key_bytes)
}
pub trait Aead: Send + Sync {
fn nonce_len(&self) -> usize;
fn tag_len(&self) -> usize;
fn seal(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, EncryptError>;
fn open(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, EncryptError>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct XChaCha20Poly1305Aead;
impl Aead for XChaCha20Poly1305Aead {
fn nonce_len(&self) -> usize {
24
}
fn tag_len(&self) -> usize {
16
}
fn seal(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, EncryptError> {
let tag_len = self.tag_len();
let out_len = pt
.len()
.checked_add(tag_len)
.ok_or(EncryptError::RngFailed)?;
let mut out = vec![0u8; out_len];
let nonce24: &[u8; 24] =
nonce
.try_into()
.map_err(|_| EncryptError::CiphertextTooShort {
min_expected: 24,
got: nonce.len(),
})?;
let cipher = XChaCha20Poly1305;
cipher
.seal(key, nonce24, aad, pt, &mut out)
.map_err(|_| EncryptError::AuthenticationFailed)?;
Ok(out)
}
fn open(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, EncryptError> {
let tag_len = self.tag_len();
if ct.len() < tag_len {
return Err(EncryptError::CiphertextTooShort {
min_expected: tag_len,
got: ct.len(),
});
}
let pt_len = ct.len() - tag_len;
let mut pt = vec![0u8; pt_len];
let nonce24: &[u8; 24] =
nonce
.try_into()
.map_err(|_| EncryptError::CiphertextTooShort {
min_expected: 24,
got: nonce.len(),
})?;
let cipher = XChaCha20Poly1305;
cipher
.open(key, nonce24, aad, ct, &mut pt)
.map_err(|_| EncryptError::AuthenticationFailed)?;
Ok(pt)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AesGcmSiv256Aead;
impl Aead for AesGcmSiv256Aead {
fn nonce_len(&self) -> usize {
12
}
fn tag_len(&self) -> usize {
16
}
fn seal(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, EncryptError> {
let tag_len = self.tag_len();
let out_len = pt
.len()
.checked_add(tag_len)
.ok_or(EncryptError::RngFailed)?;
let mut out = vec![0u8; out_len];
let nonce12: &[u8; 12] =
nonce
.try_into()
.map_err(|_| EncryptError::CiphertextTooShort {
min_expected: 12,
got: nonce.len(),
})?;
let cipher = AesGcmSiv256;
cipher
.seal(key, nonce12, aad, pt, &mut out)
.map_err(|_| EncryptError::AuthenticationFailed)?;
Ok(out)
}
fn open(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, EncryptError> {
let tag_len = self.tag_len();
if ct.len() < tag_len {
return Err(EncryptError::CiphertextTooShort {
min_expected: tag_len,
got: ct.len(),
});
}
let pt_len = ct.len() - tag_len;
let mut pt = vec![0u8; pt_len];
let nonce12: &[u8; 12] =
nonce
.try_into()
.map_err(|_| EncryptError::CiphertextTooShort {
min_expected: 12,
got: nonce.len(),
})?;
let cipher = AesGcmSiv256;
cipher
.open(key, nonce12, aad, ct, &mut pt)
.map_err(|_| EncryptError::AuthenticationFailed)?;
Ok(pt)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub enum AeadKind {
#[default]
XChaCha20Poly1305,
AesGcmSiv256,
}
impl Aead for AeadKind {
fn nonce_len(&self) -> usize {
match self {
AeadKind::XChaCha20Poly1305 => XChaCha20Poly1305Aead.nonce_len(),
AeadKind::AesGcmSiv256 => AesGcmSiv256Aead.nonce_len(),
}
}
fn tag_len(&self) -> usize {
match self {
AeadKind::XChaCha20Poly1305 => XChaCha20Poly1305Aead.tag_len(),
AeadKind::AesGcmSiv256 => AesGcmSiv256Aead.tag_len(),
}
}
fn seal(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, EncryptError> {
match self {
AeadKind::XChaCha20Poly1305 => XChaCha20Poly1305Aead.seal(key, nonce, aad, pt),
AeadKind::AesGcmSiv256 => AesGcmSiv256Aead.seal(key, nonce, aad, pt),
}
}
fn open(
&self,
key: &[u8; 32],
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, EncryptError> {
match self {
AeadKind::XChaCha20Poly1305 => XChaCha20Poly1305Aead.open(key, nonce, aad, ct),
AeadKind::AesGcmSiv256 => AesGcmSiv256Aead.open(key, nonce, aad, ct),
}
}
}
pub fn encrypt_with_aead<A: Aead>(
aead: &A,
key: &[u8; 32],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, EncryptError> {
let nonce_len = aead.nonce_len();
let mut nonce = vec![0u8; nonce_len];
let mut rng = new_rng().map_err(|_| EncryptError::RngFailed)?;
rng.fill(&mut nonce).map_err(|_| EncryptError::RngFailed)?;
let ct_with_tag = aead.seal(key, &nonce, aad, pt)?;
let mut out = Vec::with_capacity(nonce_len + ct_with_tag.len());
out.extend_from_slice(&nonce);
out.extend_from_slice(&ct_with_tag);
Ok(out)
}
pub fn decrypt_with_aead<A: Aead>(
aead: &A,
key: &[u8; 32],
aad: &[u8],
wire: &[u8],
) -> Result<Vec<u8>, EncryptError> {
let nonce_len = aead.nonce_len();
let min_len = nonce_len + aead.tag_len();
if wire.len() < min_len {
return Err(EncryptError::CiphertextTooShort {
min_expected: min_len,
got: wire.len(),
});
}
let nonce = &wire[..nonce_len];
let ct_with_tag = &wire[nonce_len..];
aead.open(key, nonce, aad, ct_with_tag)
}