#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
missing_debug_implementations
)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "dev")]
pub mod dev;
pub use crypto_common::{
Key, KeyInit, KeySizeUser,
array::{self, typenum::consts},
};
#[cfg(feature = "arrayvec")]
pub use arrayvec;
#[cfg(feature = "bytes")]
pub use bytes;
#[cfg(feature = "rand_core")]
pub use crypto_common::rand_core;
#[cfg(feature = "heapless")]
pub use heapless;
pub use inout;
use core::fmt;
use crypto_common::array::{Array, ArraySize, typenum::Unsigned};
use inout::InOutBuf;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "bytes")]
use bytes::BytesMut;
#[cfg(feature = "os_rng")]
use crypto_common::rand_core::{OsError, OsRng, TryRngCore};
#[cfg(feature = "rand_core")]
use rand_core::{CryptoRng, TryCryptoRng};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Error;
pub type Result<T> = core::result::Result<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("aead::Error")
}
}
impl core::error::Error for Error {}
pub type Nonce<A> = Array<u8, <A as AeadCore>::NonceSize>;
pub type Tag<A> = Array<u8, <A as AeadCore>::TagSize>;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum TagPosition {
Postfix,
Prefix,
}
pub trait AeadCore {
type NonceSize: ArraySize;
type TagSize: ArraySize;
const TAG_POSITION: TagPosition;
#[cfg(feature = "os_rng")]
fn generate_nonce() -> core::result::Result<Nonce<Self>, OsError> {
let mut nonce = Nonce::<Self>::default();
OsRng.try_fill_bytes(&mut nonce)?;
Ok(nonce)
}
#[cfg(feature = "rand_core")]
fn generate_nonce_with_rng<R: CryptoRng + ?Sized>(rng: &mut R) -> Nonce<Self> {
let mut nonce = Nonce::<Self>::default();
rng.fill_bytes(&mut nonce);
nonce
}
#[cfg(feature = "rand_core")]
fn try_generate_nonce_with_rng<R: TryCryptoRng + ?Sized>(
rng: &mut R,
) -> core::result::Result<Nonce<Self>, R::Error> {
let mut nonce = Nonce::<Self>::default();
rng.try_fill_bytes(&mut nonce)?;
Ok(nonce)
}
}
#[cfg(feature = "alloc")]
pub trait Aead: AeadCore {
fn encrypt<'msg, 'aad>(
&self,
nonce: &Nonce<Self>,
plaintext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>>;
fn decrypt<'msg, 'aad>(
&self,
nonce: &Nonce<Self>,
ciphertext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>>;
}
#[cfg(feature = "alloc")]
impl<T: AeadInOut> Aead for T {
fn encrypt<'msg, 'aad>(
&self,
nonce: &Nonce<Self>,
plaintext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>> {
let payload = plaintext.into();
let mut buffer = Vec::with_capacity(payload.msg.len() + Self::TagSize::to_usize());
buffer.extend_from_slice(payload.msg);
self.encrypt_in_place(nonce, payload.aad, &mut buffer)?;
Ok(buffer)
}
fn decrypt<'msg, 'aad>(
&self,
nonce: &Nonce<Self>,
ciphertext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>> {
let payload = ciphertext.into();
let mut buffer = Vec::from(payload.msg);
self.decrypt_in_place(nonce, payload.aad, &mut buffer)?;
Ok(buffer)
}
}
pub trait AeadInOut: AeadCore {
fn encrypt_inout_detached(
&self,
nonce: &Nonce<Self>,
associated_data: &[u8],
buffer: InOutBuf<'_, '_, u8>,
) -> Result<Tag<Self>>;
fn decrypt_inout_detached(
&self,
nonce: &Nonce<Self>,
associated_data: &[u8],
buffer: InOutBuf<'_, '_, u8>,
tag: &Tag<Self>,
) -> Result<()>;
fn encrypt_in_place(
&self,
nonce: &Nonce<Self>,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
match Self::TAG_POSITION {
TagPosition::Prefix => {
let msg_len = buffer.len();
buffer.extend_from_slice(&Tag::<Self>::default())?;
let buffer = buffer.as_mut();
let tag_size = Self::TagSize::USIZE;
buffer.copy_within(..msg_len, tag_size);
let (tag_dst, msg) = buffer.split_at_mut(tag_size);
let tag = self.encrypt_inout_detached(nonce, associated_data, msg.into())?;
tag_dst.copy_from_slice(&tag);
}
TagPosition::Postfix => {
let tag =
self.encrypt_inout_detached(nonce, associated_data, buffer.as_mut().into())?;
buffer.extend_from_slice(tag.as_slice())?;
}
}
Ok(())
}
fn decrypt_in_place(
&self,
nonce: &Nonce<Self>,
associated_data: &[u8],
buffer: &mut dyn Buffer,
) -> Result<()> {
let tag_size = Self::TagSize::USIZE;
let tagless_len = buffer.len().checked_sub(tag_size).ok_or(Error)?;
match Self::TAG_POSITION {
TagPosition::Prefix => {
let (tag, msg) = buffer.as_mut().split_at_mut(tag_size);
let tag = Tag::<Self>::try_from(&*tag).expect("tag length mismatch");
self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?;
buffer.as_mut().copy_within(tag_size.., 0);
}
TagPosition::Postfix => {
let (msg, tag) = buffer.as_mut().split_at_mut(tagless_len);
let tag = Tag::<Self>::try_from(&*tag).expect("tag length mismatch");
self.decrypt_inout_detached(nonce, associated_data, msg.into(), &tag)?;
}
}
buffer.truncate(tagless_len);
Ok(())
}
}
#[derive(Debug)]
pub struct Payload<'msg, 'aad> {
pub msg: &'msg [u8],
pub aad: &'aad [u8],
}
impl<'msg> From<&'msg [u8]> for Payload<'msg, '_> {
fn from(msg: &'msg [u8]) -> Self {
Self { msg, aad: b"" }
}
}
pub trait Buffer: AsRef<[u8]> + AsMut<[u8]> {
fn len(&self) -> usize {
self.as_ref().len()
}
fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
fn extend_from_slice(&mut self, other: &[u8]) -> Result<()>;
fn truncate(&mut self, len: usize);
}
#[cfg(feature = "alloc")]
impl Buffer for Vec<u8> {
fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
Vec::extend_from_slice(self, other);
Ok(())
}
fn truncate(&mut self, len: usize) {
Vec::truncate(self, len);
}
}
#[cfg(feature = "bytes")]
impl Buffer for BytesMut {
fn len(&self) -> usize {
BytesMut::len(self)
}
fn is_empty(&self) -> bool {
BytesMut::is_empty(self)
}
fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
BytesMut::extend_from_slice(self, other);
Ok(())
}
fn truncate(&mut self, len: usize) {
BytesMut::truncate(self, len);
}
}
#[cfg(feature = "arrayvec")]
impl<const N: usize> Buffer for arrayvec::ArrayVec<u8, N> {
fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
arrayvec::ArrayVec::try_extend_from_slice(self, other).map_err(|_| Error)
}
fn truncate(&mut self, len: usize) {
arrayvec::ArrayVec::truncate(self, len);
}
}
#[cfg(feature = "heapless")]
impl<const N: usize> Buffer for heapless::Vec<u8, N> {
fn extend_from_slice(&mut self, other: &[u8]) -> Result<()> {
heapless::Vec::extend_from_slice(self, other).map_err(|_| Error)
}
fn truncate(&mut self, len: usize) {
heapless::Vec::truncate(self, len);
}
}
#[cfg(feature = "alloc")]
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
type DynAeadInPlace<N, T> = dyn Aead<NonceSize = N, TagSize = T>;
}