#[cfg(feature = "aes-gcm")]
mod aes_gcm;
#[cfg(feature = "crypto-aws-lc-rs")]
mod aws_lc_rs;
#[cfg(feature = "chacha20poly1305")]
mod chacha20poly1305;
pub(crate) mod global;
#[cfg(feature = "crypto-graviola")]
mod graviola;
#[cfg(feature = "crypto-ring")]
mod ring;
use crate::{
codec::{
Base64Alphabet, base64_decode, base64_decoded_len_ub, base64_encode, base64_encoded_len,
},
collection::{ExpansionTy, Vector},
crypto::CryptoError,
misc::SensitiveBytes,
rng::CryptoRng,
};
use core::marker::PhantomData;
const NONCE_LEN: usize = 12;
const TAG_LEN: usize = 16;
pub trait Aead {
type Secret;
#[inline]
fn decrypt_base64_to_buffer<'buffer>(
associated_data: &[u8],
buffer: &'buffer mut Vector<u8>,
encrypted_data: &[u8],
secret: &Self::Secret,
) -> crate::Result<&'buffer mut [u8]> {
let additional = base64_decoded_len_ub(encrypted_data.len());
let begin = buffer.len();
buffer.expand(ExpansionTy::Additional(additional), 0)?;
let buffer_slice = buffer.get_mut(begin..).unwrap_or_default();
let len = base64_decode(Base64Alphabet::UrlNoPad, encrypted_data, buffer_slice)?.len();
buffer.truncate(begin.wrapping_add(len));
Self::decrypt_in_place(associated_data, buffer.get_mut(begin..).unwrap_or_default(), secret)
}
fn decrypt_in_place<'encrypted>(
associated_data: &[u8],
data: &'encrypted mut [u8],
secret: &Self::Secret,
) -> crate::Result<&'encrypted mut [u8]>;
#[inline]
fn encrypt_in_place<RNG>(
associated_data: &[u8],
data: &mut [u8],
rng: &mut RNG,
secret: &Self::Secret,
) -> crate::Result<()>
where
RNG: CryptoRng,
{
#[rustfmt::skip]
let [
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
plaintext @ ..,
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15
] = data
else {
return Err(CryptoError::InvalidAesData.into());
};
let nonce = [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11];
let tag = [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15];
Self::encrypt_parts(associated_data, nonce, plaintext, rng, secret, tag)
}
#[inline]
fn encrypt_in_place_detached<RNG>(
associated_data: &[u8],
plaintext: &mut [u8],
rng: &mut RNG,
secret: &Self::Secret,
) -> crate::Result<([u8; NONCE_LEN], [u8; TAG_LEN])>
where
RNG: CryptoRng,
{
let mut nonce = [0u8; NONCE_LEN];
let mut tag = [0u8; TAG_LEN];
let [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11] = &mut nonce;
let [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15] = &mut tag;
Self::encrypt_parts(
associated_data,
[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11],
plaintext,
rng,
secret,
[b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15],
)?;
Ok((nonce, tag))
}
fn encrypt_parts<RNG>(
associated_data: &[u8],
nonce: [&mut u8; NONCE_LEN],
plaintext: &mut [u8],
rng: &mut RNG,
secret: &Self::Secret,
tag: [&mut u8; TAG_LEN],
) -> crate::Result<()>
where
RNG: CryptoRng;
#[inline]
fn encrypt_to_buffer<'buffer, RNG>(
associated_data: &[u8],
buffer: &'buffer mut Vector<u8>,
plaintext: &[u8],
rng: &mut RNG,
secret: &Self::Secret,
) -> crate::Result<&'buffer mut [u8]>
where
RNG: CryptoRng,
{
let start = buffer.len();
let _ = buffer.extend_from_copyable_slices([
[0; NONCE_LEN].as_slice(),
plaintext,
[0; TAG_LEN].as_slice(),
])?;
Self::encrypt_in_place(
associated_data,
buffer.get_mut(start..).unwrap_or_default(),
rng,
secret,
)?;
Ok(buffer.get_mut(start..).unwrap_or_default())
}
#[inline]
fn encrypt_to_buffer_base64<'buffer, RNG>(
associated_data: &[u8],
buffer: &'buffer mut Vector<u8>,
plaintext: &[u8],
rng: &mut RNG,
secret: &Self::Secret,
) -> crate::Result<&'buffer str>
where
RNG: CryptoRng,
{
let begin = buffer.len();
let data_len = NONCE_LEN.wrapping_add(plaintext.len()).wrapping_add(TAG_LEN);
let base64_len = base64_encoded_len(data_len, true).unwrap_or(usize::MAX);
buffer.expand(ExpansionTy::Additional(base64_len), 0)?;
let _ = buffer.extend_from_copyable_slices([
[0; NONCE_LEN].as_slice(),
plaintext,
[0; TAG_LEN].as_slice(),
])?;
Self::encrypt_in_place(
associated_data,
buffer.get_mut(begin.wrapping_add(base64_len)..).unwrap_or_default(),
rng,
secret,
)?;
let slice_mut = buffer.get_mut(begin..).and_then(|el| el.split_at_mut_checked(base64_len));
let Some((base64, content)) = slice_mut else {
return Ok("");
};
let base64_idx = base64_encode(Base64Alphabet::UrlNoPad, content, base64)?.len();
drop(SensitiveBytes::new_unlocked(content));
buffer.truncate(begin.wrapping_add(base64_idx));
let bytes = buffer.get_mut(begin..).unwrap_or_default();
Ok(unsafe { core::str::from_utf8_unchecked(bytes) })
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct AeadStub<S>(PhantomData<S>);
impl<S> Aead for AeadStub<S> {
type Secret = S;
#[inline]
fn decrypt_in_place<'encrypted>(
_: &[u8],
_: &'encrypted mut [u8],
_: &Self::Secret,
) -> crate::Result<&'encrypted mut [u8]> {
Ok(&mut [])
}
#[inline]
fn encrypt_parts<RNG>(
_: &[u8],
_: [&mut u8; NONCE_LEN],
_: &mut [u8],
_: &mut RNG,
_: &Self::Secret,
_: [&mut u8; TAG_LEN],
) -> crate::Result<()>
where
RNG: CryptoRng,
{
Ok(())
}
}
#[cfg(any(
feature = "aes-gcm",
feature = "chacha20poly1305",
feature = "crypto-aws-lc-rs",
feature = "crypto-graviola",
feature = "crypto-ring",
))]
fn generate_nonce<RNG: CryptoRng>(nonce: [&mut u8; NONCE_LEN], rng: &mut RNG) -> [u8; NONCE_LEN] {
let [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11] = nonce;
let [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, _, _, _, _] = rng.u8_16();
*a0 = b0;
*a1 = b1;
*a2 = b2;
*a3 = b3;
*a4 = b4;
*a5 = b5;
*a6 = b6;
*a7 = b7;
*a8 = b8;
*a9 = b9;
*a10 = b10;
*a11 = b11;
[*a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a10, *a11]
}
#[cfg(any(feature = "crypto-aws-lc-rs", feature = "crypto-graviola", feature = "crypto-ring"))]
fn split_nonce_content(
data: &mut [u8],
error: CryptoError,
) -> crate::Result<([u8; NONCE_LEN], &mut [u8])> {
let [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, content @ ..] = data else {
return Err(error.into());
};
let nonce = [*a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a10, *a11];
Ok((nonce, content))
}
#[cfg(any(feature = "aes-gcm", feature = "chacha20poly1305"))]
fn split_nonce_content_tag(
data: &mut [u8],
error: CryptoError,
) -> crate::Result<([u8; NONCE_LEN], &mut [u8], [u8; TAG_LEN])> {
#[rustfmt::skip]
let [
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11,
content @ ..,
b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15
] = data
else {
return Err(error.into());
};
let nonce = [*a0, *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8, *a9, *a10, *a11];
let tag = [*b0, *b1, *b2, *b3, *b4, *b5, *b6, *b7, *b8, *b9, *b10, *b11, *b12, *b13, *b14, *b15];
Ok((nonce, content, tag))
}
#[cfg(any(
feature = "aes-gcm",
feature = "chacha20poly1305",
feature = "crypto-aws-lc-rs",
feature = "crypto-graviola",
feature = "crypto-ring"
))]
fn write_tag(from: [u8; TAG_LEN], to: [&mut u8; TAG_LEN]) {
for (dest, src) in to.into_iter().zip(from) {
*dest = src;
}
}