#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::error::{validate, Error, Result};
use crate::types::{Nonce, SecretBytes};
use rand::{CryptoRng, RngCore};
pub mod aes;
pub mod modes;
pub use aes::{Aes128, Aes192, Aes256};
pub use modes::{cbc::Cbc, ctr::Ctr};
pub trait CipherAlgorithm {
const KEY_SIZE: usize;
const BLOCK_SIZE: usize;
fn name() -> &'static str;
}
pub trait AesVariant: CipherAlgorithm {
const ROUNDS: usize;
}
pub trait CipherMode {
fn name() -> &'static str;
const REQUIRES_IV: bool;
const IS_AUTHENTICATED: bool;
const IV_SIZE: usize;
const TAG_SIZE: Option<usize>;
}
pub trait BlockCipher {
type Algorithm: CipherAlgorithm;
type Key: AsRef<[u8]> + AsMut<[u8]> + Clone + Zeroize;
fn new(key: &Self::Key) -> Self;
fn encrypt_block(&self, block: &mut [u8]) -> Result<()>;
fn decrypt_block(&self, block: &mut [u8]) -> Result<()>;
fn key_size() -> usize {
Self::Algorithm::KEY_SIZE
}
fn block_size() -> usize {
Self::Algorithm::BLOCK_SIZE
}
fn name() -> &'static str {
Self::Algorithm::name()
}
fn generate_key<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Key;
}
pub trait BlockCipherMode<C: BlockCipher> {
type Mode: CipherMode;
type Nonce: AsRef<[u8]> + AsMut<[u8]> + Clone;
fn new(cipher: C, nonce: &Self::Nonce) -> Result<Self>
where
Self: Sized;
fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>>;
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>>;
fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Nonce;
fn mode_name() -> &'static str {
Self::Mode::name()
}
}
pub trait AuthenticatedCipherMode<C: BlockCipher>: BlockCipherMode<C> {
type Tag: AsRef<[u8]> + AsMut<[u8]> + Clone;
fn encrypt_with_aad(&self, plaintext: &[u8], aad: &[u8]) -> Result<Vec<u8>>;
fn decrypt_with_aad(&self, ciphertext: &[u8], aad: &[u8]) -> Result<Vec<u8>>;
fn tag_size() -> usize {
Self::Mode::TAG_SIZE.unwrap_or(0)
}
}
pub enum Aes128Algorithm {}
impl CipherAlgorithm for Aes128Algorithm {
const KEY_SIZE: usize = 16;
const BLOCK_SIZE: usize = 16;
fn name() -> &'static str {
"AES-128"
}
}
impl AesVariant for Aes128Algorithm {
const ROUNDS: usize = 10;
}
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct TypedAes128 {
inner: aes::Aes128,
}
impl CipherAlgorithm for TypedAes128 {
const KEY_SIZE: usize = 16;
const BLOCK_SIZE: usize = 16;
fn name() -> &'static str {
"AES-128"
}
}
impl BlockCipher for TypedAes128 {
type Algorithm = Aes128Algorithm;
type Key = SecretBytes<16>;
fn new(key: &Self::Key) -> Self {
Self {
inner: aes::Aes128::new(key),
}
}
fn encrypt_block(&self, block: &mut [u8]) -> Result<()> {
validate::length("AES-128 block", block.len(), Self::Algorithm::BLOCK_SIZE)?;
self.inner.encrypt_block(block)
}
fn decrypt_block(&self, block: &mut [u8]) -> Result<()> {
validate::length("AES-128 block", block.len(), Self::Algorithm::BLOCK_SIZE)?;
self.inner.decrypt_block(block)
}
fn generate_key<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Key {
let mut key = [0u8; 16];
rng.fill_bytes(&mut key);
SecretBytes::new(key)
}
}
pub enum CbcMode {}
impl CipherMode for CbcMode {
const REQUIRES_IV: bool = true;
const IS_AUTHENTICATED: bool = false;
const IV_SIZE: usize = 16; const TAG_SIZE: Option<usize> = None;
fn name() -> &'static str {
"CBC"
}
}
pub struct TypedCbc<C: BlockCipher + CipherAlgorithm + Zeroize + ZeroizeOnDrop> {
inner: modes::cbc::Cbc<C>,
_phantom: core::marker::PhantomData<C>,
}
impl<C: BlockCipher + CipherAlgorithm + Zeroize + ZeroizeOnDrop> BlockCipherMode<C>
for TypedCbc<C>
{
type Mode = CbcMode;
type Nonce = Nonce<16>;
fn new(cipher: C, nonce: &Self::Nonce) -> Result<Self> {
validate::length(
"CBC initialization vector",
nonce.as_ref().len(),
C::BLOCK_SIZE,
)?;
let inner = modes::cbc::Cbc::new(cipher, nonce)?;
Ok(Self {
inner,
_phantom: core::marker::PhantomData,
})
}
fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
if plaintext.len() % C::BLOCK_SIZE != 0 {
return Err(Error::Length {
context: "CBC plaintext",
expected: ((plaintext.len() / C::BLOCK_SIZE) + 1) * C::BLOCK_SIZE,
actual: plaintext.len(),
});
}
self.inner.encrypt(plaintext)
}
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
if ciphertext.len() % C::BLOCK_SIZE != 0 {
return Err(Error::Length {
context: "CBC ciphertext",
expected: ((ciphertext.len() / C::BLOCK_SIZE) + 1) * C::BLOCK_SIZE,
actual: ciphertext.len(),
});
}
self.inner.decrypt(ciphertext)
}
fn generate_nonce<R: RngCore + CryptoRng>(rng: &mut R) -> Self::Nonce {
let mut nonce = [0u8; 16];
rng.fill_bytes(&mut nonce);
Nonce::new(nonce)
}
}