use alloc::vec::Vec;
use crate::HpkeError;
use crate::sealed::Sealed;
pub trait Aead: Sealed {
const ID: u16;
const KEY_LEN: usize;
const NONCE_LEN: usize;
const TAG_LEN: usize;
type Cipher;
fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError>;
}
pub trait SealingAead: Aead {
fn seal(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, HpkeError>;
fn open(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, HpkeError>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ChaCha20Poly1305;
impl Sealed for ChaCha20Poly1305 {}
impl Aead for ChaCha20Poly1305 {
const ID: u16 = 0x0003;
const KEY_LEN: usize = 32;
const NONCE_LEN: usize = 12;
const TAG_LEN: usize = 16;
type Cipher = chacha20poly1305::ChaCha20Poly1305;
fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError> {
use chacha20poly1305::KeyInit;
chacha20poly1305::ChaCha20Poly1305::new_from_slice(key)
.map_err(|_| HpkeError::AeadInitError)
}
}
impl SealingAead for ChaCha20Poly1305 {
fn seal(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, HpkeError> {
use chacha20poly1305::{
Nonce,
aead::{Aead as _, Payload},
};
cipher
.encrypt(Nonce::from_slice(nonce), Payload { msg: pt, aad })
.map_err(|_| HpkeError::SealError)
}
fn open(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, HpkeError> {
use chacha20poly1305::{
Nonce,
aead::{Aead as _, Payload},
};
cipher
.decrypt(Nonce::from_slice(nonce), Payload { msg: ct, aad })
.map_err(|_| HpkeError::OpenError)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Aes128Gcm;
impl Sealed for Aes128Gcm {}
impl Aead for Aes128Gcm {
const ID: u16 = 0x0001;
const KEY_LEN: usize = 16;
const NONCE_LEN: usize = 12;
const TAG_LEN: usize = 16;
type Cipher = aes_gcm::Aes128Gcm;
fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError> {
use aes_gcm::KeyInit;
aes_gcm::Aes128Gcm::new_from_slice(key).map_err(|_| HpkeError::AeadInitError)
}
}
impl SealingAead for Aes128Gcm {
fn seal(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, HpkeError> {
use aes_gcm::aead::Aead as _;
cipher
.encrypt(
aes_gcm::Nonce::from_slice(nonce),
aead::Payload { msg: pt, aad },
)
.map_err(|_| HpkeError::SealError)
}
fn open(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, HpkeError> {
use aes_gcm::aead::Aead as _;
cipher
.decrypt(
aes_gcm::Nonce::from_slice(nonce),
aead::Payload { msg: ct, aad },
)
.map_err(|_| HpkeError::OpenError)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Aes256Gcm;
impl Sealed for Aes256Gcm {}
impl Aead for Aes256Gcm {
const ID: u16 = 0x0002;
const KEY_LEN: usize = 32;
const NONCE_LEN: usize = 12;
const TAG_LEN: usize = 16;
type Cipher = aes_gcm::Aes256Gcm;
fn init(key: &[u8]) -> Result<Self::Cipher, HpkeError> {
use aes_gcm::KeyInit;
aes_gcm::Aes256Gcm::new_from_slice(key).map_err(|_| HpkeError::AeadInitError)
}
}
impl SealingAead for Aes256Gcm {
fn seal(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
pt: &[u8],
) -> Result<Vec<u8>, HpkeError> {
use aes_gcm::aead::Aead as _;
cipher
.encrypt(
aes_gcm::Nonce::from_slice(nonce),
aead::Payload { msg: pt, aad },
)
.map_err(|_| HpkeError::SealError)
}
fn open(
cipher: &Self::Cipher,
nonce: &[u8],
aad: &[u8],
ct: &[u8],
) -> Result<Vec<u8>, HpkeError> {
use aes_gcm::aead::Aead as _;
cipher
.decrypt(
aes_gcm::Nonce::from_slice(nonce),
aead::Payload { msg: ct, aad },
)
.map_err(|_| HpkeError::OpenError)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ExportOnly;
impl Sealed for ExportOnly {}
impl Aead for ExportOnly {
const ID: u16 = 0xFFFF;
const KEY_LEN: usize = 0;
const NONCE_LEN: usize = 0;
const TAG_LEN: usize = 0;
type Cipher = ();
fn init(_key: &[u8]) -> Result<Self::Cipher, HpkeError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use hex::FromHex;
#[test]
fn rfc8439_chacha20poly1305_test_vector() {
let key = Vec::from_hex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
.unwrap();
let nonce = Vec::from_hex("070000004041424344454647").unwrap();
let aad = Vec::from_hex("50515253c0c1c2c3c4c5c6c7").unwrap();
let pt = b"Ladies and Gentlemen of the class of '99: \
If I could offer you only one tip for the future, sunscreen would be it.";
let cipher = ChaCha20Poly1305::init(&key).unwrap();
let ct = ChaCha20Poly1305::seal(&cipher, &nonce, &aad, pt).unwrap();
let recovered = ChaCha20Poly1305::open(&cipher, &nonce, &aad, &ct).unwrap();
assert_eq!(recovered, pt);
let mut bad = ct.clone();
bad[0] ^= 1;
assert_eq!(
ChaCha20Poly1305::open(&cipher, &nonce, &aad, &bad),
Err(HpkeError::OpenError)
);
}
#[test]
fn aes128gcm_roundtrip_short() {
let key = [0u8; 16];
let nonce = [0u8; 12];
let aad = b"";
let pt = b"hello";
let cipher = Aes128Gcm::init(&key).unwrap();
let ct = Aes128Gcm::seal(&cipher, &nonce, aad, pt).unwrap();
assert_eq!(ct.len(), pt.len() + 16);
assert_eq!(Aes128Gcm::open(&cipher, &nonce, aad, &ct).unwrap(), pt);
}
#[test]
fn aes256gcm_roundtrip_short() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let pt = b"world";
let cipher = Aes256Gcm::init(&key).unwrap();
let ct = Aes256Gcm::seal(&cipher, &nonce, b"aad", pt).unwrap();
assert_eq!(Aes256Gcm::open(&cipher, &nonce, b"aad", &ct).unwrap(), pt);
}
#[test]
fn aes128gcm_rejects_bad_key_len() {
let r = Aes128Gcm::init(&[0u8; 15]);
assert_eq!(r.err(), Some(HpkeError::AeadInitError));
}
#[test]
fn export_only_implements_aead_only() {
fn assert_aead<A: Aead>() {}
assert_aead::<ExportOnly>();
assert_eq!(ExportOnly::ID, 0xFFFF);
assert_eq!(ExportOnly::KEY_LEN, 0);
assert_eq!(ExportOnly::NONCE_LEN, 0);
assert_eq!(ExportOnly::TAG_LEN, 0);
assert!(ExportOnly::init(&[]).is_ok());
}
}