#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use core::fmt::Debug;
#[cfg(feature = "getrandom")]
use crate::aead::RandomSealError;
use crate::aead::{AeadBufferError, AeadNonce, OpenError, SealError};
#[doc(hidden)]
pub struct SealToken(());
impl SealToken {
const fn new() -> Self {
Self(())
}
}
pub trait Aead {
const KEY_SIZE: usize;
const NONCE_SIZE: usize;
const TAG_SIZE: usize;
type Key;
type Nonce: AeadNonce;
type Tag: Copy + Debug + AsRef<[u8]>;
#[must_use]
fn new(key: &Self::Key) -> Self;
fn tag_from_slice(bytes: &[u8]) -> Result<Self::Tag, AeadBufferError>;
#[doc(hidden)]
fn __encrypt_in_place_with_nonce(
&self,
nonce: &Self::Nonce,
aad: &[u8],
buffer: &mut [u8],
token: SealToken,
) -> Result<Self::Tag, SealError>;
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
#[inline]
fn seal_random(&self, aad: &[u8], plaintext: &[u8], out: &mut [u8]) -> Result<Self::Nonce, RandomSealError> {
let nonce = Self::Nonce::try_random()?;
AeadWithNonce::encrypt(self, &nonce, aad, plaintext, out)?;
Ok(nonce)
}
#[cfg(all(feature = "alloc", feature = "getrandom"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "getrandom"))))]
#[inline]
fn seal_random_to_vec(&self, aad: &[u8], plaintext: &[u8]) -> Result<(Self::Nonce, Vec<u8>), RandomSealError> {
let len = Self::ciphertext_len(plaintext.len()).map_err(SealError::from)?;
let mut out = vec![0u8; len];
match self.seal_random(aad, plaintext, &mut out) {
Ok(nonce) => Ok((nonce, out)),
Err(err) => {
super::ct::zeroize(&mut out);
Err(err)
}
}
}
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
#[inline]
fn seal_random_in_place(&self, aad: &[u8], buffer: &mut [u8]) -> Result<(Self::Nonce, Self::Tag), RandomSealError> {
let nonce = Self::Nonce::try_random()?;
let tag = self.__encrypt_in_place_with_nonce(&nonce, aad, buffer, SealToken::new())?;
Ok((nonce, tag))
}
fn decrypt_in_place(
&self,
nonce: &Self::Nonce,
aad: &[u8],
buffer: &mut [u8],
tag: &Self::Tag,
) -> Result<(), OpenError>;
#[inline]
fn decrypt_in_place_detached(
&self,
nonce: &Self::Nonce,
aad: &[u8],
buffer: &mut [u8],
tag: &Self::Tag,
) -> Result<(), OpenError> {
self.decrypt_in_place(nonce, aad, buffer, tag)
}
#[inline]
fn ciphertext_len(plaintext_len: usize) -> Result<usize, AeadBufferError> {
plaintext_len
.checked_add(Self::TAG_SIZE)
.ok_or_else(AeadBufferError::new)
}
#[inline]
fn plaintext_len(ciphertext_and_tag_len: usize) -> Result<usize, AeadBufferError> {
if ciphertext_and_tag_len < Self::TAG_SIZE {
return Err(AeadBufferError::new());
}
Ok(ciphertext_and_tag_len.strict_sub(Self::TAG_SIZE))
}
#[inline]
fn decrypt(
&self,
nonce: &Self::Nonce,
aad: &[u8],
ciphertext_and_tag: &[u8],
out: &mut [u8],
) -> Result<(), OpenError> {
let plaintext_len = Self::plaintext_len(ciphertext_and_tag.len())?;
if out.len() != plaintext_len {
return Err(OpenError::buffer());
}
let (ciphertext, tag_bytes) = ciphertext_and_tag.split_at(plaintext_len);
out.copy_from_slice(ciphertext);
let tag = Self::tag_from_slice(tag_bytes)?;
if let Err(e) = self.decrypt_in_place(nonce, aad, out, &tag) {
super::ct::zeroize(out);
return Err(e);
}
Ok(())
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[inline]
fn decrypt_to_vec(&self, nonce: &Self::Nonce, aad: &[u8], ciphertext_and_tag: &[u8]) -> Result<Vec<u8>, OpenError> {
let len = Self::plaintext_len(ciphertext_and_tag.len())?;
let mut out = vec![0u8; len];
match self.decrypt(nonce, aad, ciphertext_and_tag, &mut out) {
Ok(()) => Ok(out),
Err(err) => {
super::ct::zeroize(&mut out);
Err(err)
}
}
}
}
pub trait AeadWithNonce: Aead {
#[inline]
fn encrypt_in_place(&self, nonce: &Self::Nonce, aad: &[u8], buffer: &mut [u8]) -> Result<Self::Tag, SealError> {
self.__encrypt_in_place_with_nonce(nonce, aad, buffer, SealToken::new())
}
#[inline]
fn encrypt_in_place_detached(
&self,
nonce: &Self::Nonce,
aad: &[u8],
buffer: &mut [u8],
) -> Result<Self::Tag, SealError> {
self.encrypt_in_place(nonce, aad, buffer)
}
#[inline]
fn encrypt(&self, nonce: &Self::Nonce, aad: &[u8], plaintext: &[u8], out: &mut [u8]) -> Result<(), SealError> {
let expected = Self::ciphertext_len(plaintext.len()).map_err(SealError::from)?;
if out.len() != expected {
return Err(SealError::buffer());
}
let (ciphertext, tag_out) = out.split_at_mut(plaintext.len());
ciphertext.copy_from_slice(plaintext);
let tag = match self.encrypt_in_place(nonce, aad, ciphertext) {
Ok(tag) => tag,
Err(err) => {
super::ct::zeroize(out);
return Err(err);
}
};
tag_out.copy_from_slice(tag.as_ref());
Ok(())
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
#[inline]
fn encrypt_to_vec(&self, nonce: &Self::Nonce, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, SealError> {
let len = Self::ciphertext_len(plaintext.len()).map_err(SealError::from)?;
let mut out = vec![0u8; len];
match self.encrypt(nonce, aad, plaintext, &mut out) {
Ok(()) => Ok(out),
Err(err) => {
super::ct::zeroize(&mut out);
Err(err)
}
}
}
}
impl<T: Aead + ?Sized> AeadWithNonce for T {}