use core::fmt;
pub use crate::traits::Aead;
use crate::traits::VerificationError;
#[cfg(feature = "aegis256")]
mod aegis256;
#[cfg(any(
feature = "aes-gcm",
feature = "aes-gcm-siv",
all(feature = "aegis256", target_arch = "riscv64"),
all(feature = "aegis256", test),
))]
mod aes;
#[cfg(feature = "aes-gcm")]
mod aes256gcm;
#[cfg(feature = "aes-gcm-siv")]
mod aes256gcmsiv;
#[cfg(any(
feature = "aegis256",
all(target_arch = "riscv64", any(feature = "aes-gcm", feature = "aes-gcm-siv"))
))]
mod aes_round;
#[cfg(feature = "ascon-aead")]
mod ascon128;
#[cfg(any(feature = "chacha20poly1305", feature = "xchacha20poly1305"))]
mod chacha20;
#[cfg(feature = "chacha20poly1305")]
mod chacha20poly1305;
#[cfg(feature = "aes-gcm")]
mod ghash;
#[cfg(feature = "diag")]
pub mod introspect;
#[cfg(feature = "aes-gcm")]
mod nonce_counter;
#[cfg(any(feature = "chacha20poly1305", feature = "xchacha20poly1305"))]
mod poly1305;
#[cfg(any(feature = "aes-gcm", feature = "aes-gcm-siv"))]
mod polyval;
mod targets;
#[cfg(feature = "xchacha20poly1305")]
mod xchacha20poly1305;
#[cfg(feature = "aegis256")]
pub use aegis256::{Aegis256, Aegis256Key, Aegis256Tag};
#[cfg(feature = "aes-gcm")]
pub use aes256gcm::{Aes256Gcm, Aes256GcmKey, Aes256GcmTag};
#[cfg(feature = "aes-gcm-siv")]
pub use aes256gcmsiv::{Aes256GcmSiv, Aes256GcmSivKey, Aes256GcmSivTag};
#[cfg(feature = "ascon-aead")]
pub use ascon128::{AsconAead128, AsconAead128Key, AsconAead128Tag};
#[cfg(all(
feature = "diag",
target_arch = "aarch64",
any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
))]
pub use chacha20::diag_chacha20_xor_keystream_aarch64_neon;
#[cfg(all(feature = "diag", any(feature = "chacha20poly1305", feature = "xchacha20poly1305")))]
pub use chacha20::diag_chacha20_xor_keystream_portable;
#[cfg(all(
feature = "diag",
target_arch = "powerpc64",
target_endian = "little",
any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
))]
pub use chacha20::diag_chacha20_xor_keystream_power_vsx;
#[cfg(all(
feature = "diag",
target_arch = "riscv64",
any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
))]
pub use chacha20::diag_chacha20_xor_keystream_riscv64_vector;
#[cfg(all(
feature = "diag",
target_arch = "s390x",
any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
))]
pub use chacha20::diag_chacha20_xor_keystream_s390x_vector;
#[cfg(all(
feature = "diag",
target_arch = "wasm32",
any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
))]
pub use chacha20::diag_chacha20_xor_keystream_wasm_simd128;
#[cfg(all(
feature = "diag",
target_arch = "x86_64",
any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
))]
pub use chacha20::{diag_chacha20_xor_keystream_x86_avx2, diag_chacha20_xor_keystream_x86_avx512};
#[cfg(feature = "chacha20poly1305")]
pub use chacha20poly1305::{ChaCha20Poly1305, ChaCha20Poly1305Key, ChaCha20Poly1305Tag};
#[cfg(feature = "aes-gcm")]
pub use nonce_counter::{NonceCounter, NonceCounterExhausted, NonceCounterSealError};
#[cfg(feature = "xchacha20poly1305")]
pub use xchacha20poly1305::{XChaCha20Poly1305, XChaCha20Poly1305Key, XChaCha20Poly1305Tag};
macro_rules! define_nonce_type {
($name:ident, $len:expr, $doc:literal) => {
#[doc = $doc]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name([u8; Self::LENGTH]);
impl $name {
pub const LENGTH: usize = $len;
#[inline]
#[must_use]
pub const fn from_bytes(bytes: [u8; Self::LENGTH]) -> Self {
Self(bytes)
}
#[inline]
#[must_use]
pub const fn to_bytes(self) -> [u8; Self::LENGTH] {
self.0
}
#[inline]
#[must_use]
pub const fn as_bytes(&self) -> &[u8; Self::LENGTH] {
&self.0
}
}
impl AsRef<[u8]> for $name {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}(", stringify!($name))?;
crate::hex::fmt_hex_lower(&self.0, f)?;
write!(f, ")")
}
}
impl crate::traits::ConstantTimeEq for $name {
#[inline]
fn ct_eq(&self, other: &Self) -> bool {
crate::traits::ct::constant_time_eq(&self.0, &other.0)
}
}
impl $name {
#[doc = concat!(
"Construct a nonce by filling bytes from the provided closure.\n\n",
"```rust\n",
"use rscrypto::aead::",
stringify!($name),
";\n\n",
"let nonce = ",
stringify!($name),
"::generate(|buf| buf.fill(0xA5));\n",
"assert_eq!(nonce.as_bytes(), &[0xA5; ",
stringify!($name),
"::LENGTH]);\n",
"```"
)]
#[inline]
#[must_use]
pub fn generate(fill: impl FnOnce(&mut [u8; $len])) -> Self {
let mut bytes = [0u8; $len];
fill(&mut bytes);
Self(bytes)
}
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
#[inline]
#[must_use]
pub fn random() -> Self {
match Self::try_random() {
Ok(value) => value,
Err(e) => panic!("getrandom failed: {e}"),
}
}
#[cfg(feature = "getrandom")]
#[cfg_attr(docsrs, doc(cfg(feature = "getrandom")))]
#[inline]
pub fn try_random() -> Result<Self, getrandom::Error> {
let mut bytes = [0u8; $len];
getrandom::fill(&mut bytes).map(|()| Self(bytes))
}
}
};
}
define_nonce_type!(
Nonce96,
12,
"Explicit 96-bit nonce wrapper.
Pairs with `Aes256Gcm`, `Aes256GcmSiv`, and `ChaCha20Poly1305`."
);
define_nonce_type!(
Nonce128,
16,
"Explicit 128-bit nonce wrapper.
Pairs with `AsconAead128`."
);
define_nonce_type!(
Nonce192,
24,
"Explicit 192-bit nonce wrapper.
Pairs with `XChaCha20Poly1305`."
);
define_nonce_type!(
Nonce256,
32,
"Explicit 256-bit nonce wrapper.
Pairs with `Aegis256`."
);
impl_hex_fmt!(Nonce96);
impl_hex_fmt!(Nonce128);
impl_hex_fmt!(Nonce192);
impl_hex_fmt!(Nonce256);
impl_serde_bytes!(Nonce96);
impl_serde_bytes!(Nonce128);
impl_serde_bytes!(Nonce192);
impl_serde_bytes!(Nonce256);
define_unit_error! {
pub struct AeadBufferError;
"buffer length mismatch"
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SealError {
Buffer(AeadBufferError),
TooLarge,
}
impl SealError {
#[inline]
#[must_use]
pub const fn buffer() -> Self {
Self::Buffer(AeadBufferError::new())
}
#[inline]
#[must_use]
pub const fn too_large() -> Self {
Self::TooLarge
}
}
impl Default for SealError {
#[inline]
fn default() -> Self {
Self::buffer()
}
}
impl fmt::Display for SealError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Buffer(err) => err.fmt(f),
Self::TooLarge => f.write_str("input exceeds the algorithm maximum length"),
}
}
}
impl core::error::Error for SealError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Buffer(err) => Some(err),
Self::TooLarge => None,
}
}
}
impl From<AeadBufferError> for SealError {
#[inline]
fn from(value: AeadBufferError) -> Self {
Self::Buffer(value)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum OpenError {
Buffer(AeadBufferError),
TooLarge,
Verification(VerificationError),
}
impl OpenError {
#[inline]
#[must_use]
pub const fn buffer() -> Self {
Self::Buffer(AeadBufferError::new())
}
#[inline]
#[must_use]
pub const fn too_large() -> Self {
Self::TooLarge
}
#[inline]
#[must_use]
pub const fn verification() -> Self {
Self::Verification(VerificationError::new())
}
}
impl Default for OpenError {
#[inline]
fn default() -> Self {
Self::buffer()
}
}
impl fmt::Display for OpenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Buffer(err) => err.fmt(f),
Self::TooLarge => f.write_str("input exceeds the algorithm maximum length"),
Self::Verification(err) => err.fmt(f),
}
}
}
impl core::error::Error for OpenError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self {
Self::Buffer(err) => Some(err),
Self::TooLarge => None,
Self::Verification(err) => Some(err),
}
}
}
impl From<AeadBufferError> for OpenError {
#[inline]
fn from(value: AeadBufferError) -> Self {
Self::Buffer(value)
}
}
impl From<VerificationError> for OpenError {
#[inline]
fn from(value: VerificationError) -> Self {
Self::Verification(value)
}
}
#[cfg_attr(
not(any(
feature = "aes-gcm",
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305"
)),
allow(dead_code)
)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct LengthOverflow;
const _: () = assert!(usize::BITS <= u64::BITS);
#[cfg_attr(
not(any(
feature = "aes-gcm",
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305"
)),
allow(dead_code)
)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct AeadByteLengths {
aad: u64,
text: u64,
}
impl AeadByteLengths {
#[inline]
pub(crate) const fn from_usize(aad_len: usize, text_len: usize) -> Self {
Self {
aad: aad_len as u64,
text: text_len as u64,
}
}
#[cfg_attr(
not(any(feature = "chacha20poly1305", feature = "xchacha20poly1305")),
allow(dead_code)
)]
#[inline]
pub(crate) fn try_new(aad_len: usize, text_len: usize) -> Result<Self, LengthOverflow> {
Ok(Self::from_usize(aad_len, text_len))
}
#[cfg_attr(
not(any(feature = "aes-gcm", feature = "aes-gcm-siv", feature = "aegis256")),
allow(dead_code)
)]
#[inline]
pub(crate) fn try_new_bit_lengths(aad_len: usize, text_len: usize) -> Result<Self, LengthOverflow> {
const MAX_BIT_ENCODED_BYTES: u64 = u64::MAX / 8;
Ok(Self {
aad: try_bounded_length_as_u64(aad_len, MAX_BIT_ENCODED_BYTES)?,
text: try_bounded_length_as_u64(text_len, MAX_BIT_ENCODED_BYTES)?,
})
}
#[cfg_attr(
not(any(feature = "chacha20poly1305", feature = "xchacha20poly1305")),
allow(dead_code)
)]
#[inline]
pub(crate) fn to_le_bytes_block(self) -> [u8; 16] {
let mut block = [0u8; 16];
block[0..8].copy_from_slice(&self.aad.to_le_bytes());
block[8..16].copy_from_slice(&self.text.to_le_bytes());
block
}
#[cfg_attr(not(feature = "aes-gcm-siv"), allow(dead_code))]
#[inline]
pub(crate) fn to_le_bits_block(self) -> [u8; 16] {
let aad_bits = self.aad.strict_mul(8);
let text_bits = self.text.strict_mul(8);
let mut block = [0u8; 16];
block[0..8].copy_from_slice(&aad_bits.to_le_bytes());
block[8..16].copy_from_slice(&text_bits.to_le_bytes());
block
}
#[cfg_attr(not(feature = "aes-gcm"), allow(dead_code))]
#[inline]
pub(crate) fn to_be_bits_block(self) -> [u8; 16] {
let aad_bits = self.aad.strict_mul(8);
let text_bits = self.text.strict_mul(8);
let mut block = [0u8; 16];
block[0..8].copy_from_slice(&aad_bits.to_be_bytes());
block[8..16].copy_from_slice(&text_bits.to_be_bytes());
block
}
#[cfg_attr(
not(all(
target_arch = "x86_64",
any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
)),
allow(dead_code)
)]
#[inline]
pub(crate) const fn total_at_least(self, minimum: u64) -> bool {
if self.aad >= minimum {
return true;
}
self.text >= minimum - self.aad
}
}
#[cfg_attr(
not(any(
feature = "aes-gcm",
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305"
)),
allow(dead_code)
)]
#[inline]
pub(crate) fn try_length_as_u64(len: usize) -> Result<u64, LengthOverflow> {
u64::try_from(len).map_err(|_| LengthOverflow)
}
#[cfg_attr(
not(any(
feature = "aes-gcm",
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305"
)),
allow(dead_code)
)]
#[inline]
pub(crate) fn try_bounded_length_as_u64(len: usize, max: u64) -> Result<u64, LengthOverflow> {
let len = try_length_as_u64(len)?;
if len > max {
return Err(LengthOverflow);
}
Ok(len)
}
#[cfg_attr(
not(any(
feature = "aes-gcm",
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305"
)),
allow(dead_code)
)]
#[inline]
pub(crate) fn seal_bounded_length_as_u64(len: usize, max: u64) -> Result<u64, SealError> {
try_bounded_length_as_u64(len, max).map_err(|_| SealError::too_large())
}
#[cfg_attr(
not(any(feature = "aes-gcm", feature = "aes-gcm-siv", feature = "aegis256")),
allow(dead_code)
)]
#[inline]
pub(crate) fn seal_bit_lengths(aad_len: usize, text_len: usize) -> Result<AeadByteLengths, SealError> {
AeadByteLengths::try_new_bit_lengths(aad_len, text_len).map_err(|_| SealError::too_large())
}
#[cfg_attr(
not(any(
feature = "aes-gcm",
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305"
)),
allow(dead_code)
)]
#[inline]
pub(crate) fn open_bounded_length_as_u64(len: usize, max: u64) -> Result<u64, OpenError> {
try_bounded_length_as_u64(len, max).map_err(|_| OpenError::too_large())
}
#[cfg_attr(
not(any(feature = "aes-gcm", feature = "aes-gcm-siv", feature = "aegis256")),
allow(dead_code)
)]
#[inline]
pub(crate) fn open_bit_lengths(aad_len: usize, text_len: usize) -> Result<AeadByteLengths, OpenError> {
AeadByteLengths::try_new_bit_lengths(aad_len, text_len).map_err(|_| OpenError::too_large())
}
#[cfg(test)]
mod tests {
use alloc::string::ToString;
use super::{AeadBufferError, AeadByteLengths, Nonce96, Nonce128, Nonce192, Nonce256, OpenError, SealError};
use crate::traits::VerificationError;
#[test]
fn nonce_wrappers_round_trip() {
let nonce96 = Nonce96::from_bytes([0x11; Nonce96::LENGTH]);
let nonce128 = Nonce128::from_bytes([0x22; Nonce128::LENGTH]);
let nonce192 = Nonce192::from_bytes([0x33; Nonce192::LENGTH]);
assert_eq!(nonce96.to_bytes(), [0x11; Nonce96::LENGTH]);
assert_eq!(nonce128.to_bytes(), [0x22; Nonce128::LENGTH]);
assert_eq!(nonce192.to_bytes(), [0x33; Nonce192::LENGTH]);
let nonce256 = Nonce256::from_bytes([0x44; Nonce256::LENGTH]);
assert_eq!(nonce256.to_bytes(), [0x44; Nonce256::LENGTH]);
}
#[test]
fn open_error_conversions_preserve_variant() {
assert_eq!(OpenError::from(AeadBufferError::new()), OpenError::buffer());
assert_eq!(OpenError::from(VerificationError::new()), OpenError::verification());
}
#[test]
fn aead_length_error_conventions_remain_stable() {
assert_eq!(SealError::default(), SealError::buffer());
assert_eq!(
SealError::too_large().to_string(),
"input exceeds the algorithm maximum length"
);
assert_eq!(
OpenError::too_large().to_string(),
"input exceeds the algorithm maximum length"
);
}
#[test]
fn aead_byte_lengths_total_threshold_is_overflow_safe() {
assert!(!AeadByteLengths::from_usize(31, 32).total_at_least(64));
assert!(AeadByteLengths::from_usize(32, 32).total_at_least(64));
assert!(AeadByteLengths { aad: u64::MAX, text: 1 }.total_at_least(u64::MAX));
}
#[test]
#[cfg(target_pointer_width = "64")]
fn bit_length_blocks_reject_unencodable_lengths() {
let too_large = (u64::MAX / 8).strict_add(1) as usize;
assert!(AeadByteLengths::try_new_bit_lengths(too_large, 0).is_err());
assert!(AeadByteLengths::try_new_bit_lengths(0, too_large).is_err());
}
}