use std::io;
pub trait Aead {
fn encrypt(&self, key: &[u8; 32], nonce: &[u8], plaintext: &[u8]) -> io::Result<Vec<u8>>;
fn decrypt(&self, key: &[u8; 32], nonce: &[u8], ciphertext: &[u8]) -> io::Result<Vec<u8>>;
fn nonce_size(&self) -> usize;
fn tag_size(&self) -> usize;
}
pub struct ChaCha20Poly1305Aead;
impl Aead for ChaCha20Poly1305Aead {
fn encrypt(&self, key: &[u8; 32], nonce: &[u8], plaintext: &[u8]) -> io::Result<Vec<u8>> {
use chacha20poly1305::{
aead::{Aead as AeadTrait, KeyInit, Payload},
ChaCha20Poly1305, Nonce,
};
let cipher = ChaCha20Poly1305::new(key.into());
let nonce = Nonce::from_slice(nonce);
cipher
.encrypt(
nonce,
Payload {
msg: plaintext,
aad: b"",
},
)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "encryption failed"))
}
fn decrypt(&self, key: &[u8; 32], nonce: &[u8], ciphertext: &[u8]) -> io::Result<Vec<u8>> {
use chacha20poly1305::{
aead::{Aead as AeadTrait, KeyInit, Payload},
ChaCha20Poly1305, Nonce,
};
let cipher = ChaCha20Poly1305::new(key.into());
let nonce = Nonce::from_slice(nonce);
cipher
.decrypt(
nonce,
Payload {
msg: ciphertext,
aad: b"",
},
)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "decryption failed"))
}
fn nonce_size(&self) -> usize {
12 }
fn tag_size(&self) -> usize {
16 }
}
pub struct Aes256GcmSivAead;
impl Aead for Aes256GcmSivAead {
fn encrypt(&self, key: &[u8; 32], nonce: &[u8], plaintext: &[u8]) -> io::Result<Vec<u8>> {
use aes_gcm_siv::{
aead::{Aead as AeadTrait, KeyInit, Payload},
Aes256GcmSiv, Nonce,
};
let cipher = Aes256GcmSiv::new(key.into());
let nonce = Nonce::from_slice(nonce);
cipher
.encrypt(
nonce,
Payload {
msg: plaintext,
aad: b"",
},
)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "encryption failed"))
}
fn decrypt(&self, key: &[u8; 32], nonce: &[u8], ciphertext: &[u8]) -> io::Result<Vec<u8>> {
use aes_gcm_siv::{
aead::{Aead as AeadTrait, KeyInit, Payload},
Aes256GcmSiv, Nonce,
};
let cipher = Aes256GcmSiv::new(key.into());
let nonce = Nonce::from_slice(nonce);
cipher
.decrypt(
nonce,
Payload {
msg: ciphertext,
aad: b"",
},
)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "decryption failed"))
}
fn nonce_size(&self) -> usize {
12 }
fn tag_size(&self) -> usize {
16 }
}
pub fn default_aead() -> Box<dyn Aead> {
Box::new(Aes256GcmSivAead)
}
pub fn aead_name() -> &'static str {
"AES-256-GCM-SIV"
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_chacha20poly1305_round_trip() {
let aead = ChaCha20Poly1305Aead;
let key = [0u8; 32];
let nonce = [0u8; 12];
let plaintext = b"Hello, post-quantum world!";
let ciphertext = aead.encrypt(&key, &nonce, plaintext).unwrap();
assert_ne!(ciphertext.as_slice(), plaintext);
assert_eq!(ciphertext.len(), plaintext.len() + aead.tag_size());
let decrypted = aead.decrypt(&key, &nonce, &ciphertext).unwrap();
assert_eq!(decrypted.as_slice(), plaintext);
}
#[test]
fn test_aes256gcmsiv_round_trip() {
let aead = Aes256GcmSivAead;
let key = [0u8; 32];
let nonce = [0u8; 12];
let plaintext = b"FIPS 140-3 compliant encryption!";
let ciphertext = aead.encrypt(&key, &nonce, plaintext).unwrap();
assert_ne!(ciphertext.as_slice(), plaintext);
assert_eq!(ciphertext.len(), plaintext.len() + aead.tag_size());
let decrypted = aead.decrypt(&key, &nonce, &ciphertext).unwrap();
assert_eq!(decrypted.as_slice(), plaintext);
}
#[test]
fn test_default_aead() {
let aead = default_aead();
assert_eq!(aead.nonce_size(), 12);
assert_eq!(aead.tag_size(), 16);
}
#[test]
fn test_aead_name() {
assert_eq!(aead_name(), "AES-256-GCM-SIV");
}
}