use chacha20poly1305::{
XChaCha20Poly1305, XNonce,
aead::{Aead, KeyInit as AeadKeyInit, Payload},
};
use zeroize::Zeroizing;
use crate::CryptoError;
use crate::crypto::keys::{FILE_KEY_SIZE, FileKey};
pub const WRAP_NONCE_SIZE: usize = 24;
pub const TAG_SIZE: usize = 16;
pub const WRAPPED_FILE_KEY_SIZE: usize = FILE_KEY_SIZE + TAG_SIZE;
pub(crate) fn seal_file_key(
wrap_key: &[u8; 32],
wrap_nonce: &[u8; WRAP_NONCE_SIZE],
file_key: &FileKey,
) -> Result<[u8; WRAPPED_FILE_KEY_SIZE], CryptoError> {
let cipher = XChaCha20Poly1305::new(wrap_key.into());
let nonce = XNonce::from_slice(wrap_nonce);
let ciphertext = cipher
.encrypt(nonce, file_key.expose().as_ref())
.map_err(|_| CryptoError::InternalCryptoFailure("Internal error: envelope seal failed"))?;
ciphertext.as_slice().try_into().map_err(|_| {
CryptoError::InternalInvariant("Internal error: envelope ciphertext size mismatch")
})
}
pub(crate) fn open_file_key(
wrap_key: &[u8; 32],
wrap_nonce: &[u8; WRAP_NONCE_SIZE],
wrapped: &[u8; WRAPPED_FILE_KEY_SIZE],
on_fail: impl FnOnce() -> CryptoError,
) -> Result<FileKey, CryptoError> {
let cipher = XChaCha20Poly1305::new(wrap_key.into());
let nonce = XNonce::from_slice(wrap_nonce);
let plaintext = cipher
.decrypt(nonce, wrapped.as_ref())
.map_err(|_| on_fail())?;
let mut out = Zeroizing::new([0u8; FILE_KEY_SIZE]);
if plaintext.len() != FILE_KEY_SIZE {
return Err(CryptoError::InternalInvariant(
"Internal error: unwrapped file key size mismatch",
));
}
out.copy_from_slice(&plaintext);
Ok(FileKey::from_zeroizing(out))
}
pub(crate) fn seal_with_aad(
wrap_key: &[u8; 32],
wrap_nonce: &[u8; WRAP_NONCE_SIZE],
plaintext: &[u8],
aad: &[u8],
on_fail: impl FnOnce() -> CryptoError,
) -> Result<Vec<u8>, CryptoError> {
let cipher = XChaCha20Poly1305::new(wrap_key.into());
let nonce = XNonce::from_slice(wrap_nonce);
cipher
.encrypt(
nonce,
Payload {
msg: plaintext,
aad,
},
)
.map_err(|_| on_fail())
}
pub(crate) fn open_with_aad(
wrap_key: &[u8; 32],
wrap_nonce: &[u8; WRAP_NONCE_SIZE],
ciphertext: &[u8],
aad: &[u8],
on_fail: impl FnOnce() -> CryptoError,
) -> Result<Zeroizing<Vec<u8>>, CryptoError> {
let cipher = XChaCha20Poly1305::new(wrap_key.into());
let nonce = XNonce::from_slice(wrap_nonce);
Ok(Zeroizing::new(
cipher
.decrypt(
nonce,
Payload {
msg: ciphertext,
aad,
},
)
.map_err(|_| on_fail())?,
))
}