#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use alloc::vec::Vec;
use lib_q_core::{
Aead,
AeadDecryptSemantic,
AeadKey,
Algorithm,
DecryptSemanticOutcome,
Nonce,
Result,
};
use crate::metadata::{
AeadMetadata,
AeadWithMetadata,
};
pub struct SaturninAead {
metadata: &'static AeadMetadata,
inner: lib_q_saturnin::SaturninAead,
}
impl SaturninAead {
pub fn new() -> Self {
Self {
metadata: crate::metadata::get_metadata(Algorithm::Saturnin)
.expect("Saturnin metadata not found"),
inner: lib_q_saturnin::SaturninAead::new(),
}
}
}
impl Aead for SaturninAead {
fn encrypt(
&self,
key: &AeadKey,
nonce: &Nonce,
plaintext: &[u8],
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>> {
self.validate_key(key)?;
self.validate_nonce(nonce)?;
crate::security::validation::validate_plaintext(plaintext)?;
let associated_data = associated_data.unwrap_or(&[]);
crate::security::validation::validate_associated_data(associated_data)?;
#[cfg(feature = "saturnin")]
{
self.inner
.encrypt(key, nonce, plaintext, Some(associated_data))
}
#[cfg(not(feature = "saturnin"))]
{
Err(lib_q_core::Error::NotImplemented {
feature: "Saturnin AEAD implementation requires 'saturnin' feature",
})
}
}
fn decrypt(
&self,
key: &AeadKey,
nonce: &Nonce,
ciphertext: &[u8],
associated_data: Option<&[u8]>,
) -> Result<Vec<u8>> {
self.validate_key(key)?;
self.validate_nonce(nonce)?;
self.validate_ciphertext_size(ciphertext.len())?;
crate::security::validation::validate_ciphertext(ciphertext)?;
let associated_data = associated_data.unwrap_or(&[]);
crate::security::validation::validate_associated_data(associated_data)?;
#[cfg(feature = "saturnin")]
{
self.inner
.decrypt(key, nonce, ciphertext, Some(associated_data))
}
#[cfg(not(feature = "saturnin"))]
{
Err(lib_q_core::Error::NotImplemented {
feature: "Saturnin AEAD implementation requires 'saturnin' feature",
})
}
}
}
impl AeadDecryptSemantic for SaturninAead {
fn decrypt_semantic(
&self,
key: &AeadKey,
nonce: &Nonce,
ciphertext: &[u8],
associated_data: Option<&[u8]>,
) -> Result<DecryptSemanticOutcome> {
self.validate_key(key)?;
self.validate_nonce(nonce)?;
self.validate_ciphertext_size(ciphertext.len())?;
crate::security::validation::validate_ciphertext(ciphertext)?;
let associated_data = associated_data.unwrap_or(&[]);
crate::security::validation::validate_associated_data(associated_data)?;
#[cfg(feature = "saturnin")]
{
self.inner
.decrypt_semantic(key, nonce, ciphertext, Some(associated_data))
}
#[cfg(not(feature = "saturnin"))]
{
Err(lib_q_core::Error::NotImplemented {
feature: "Saturnin AEAD implementation requires 'saturnin' feature",
})
}
}
}
impl AeadWithMetadata for SaturninAead {
fn metadata(&self) -> &'static AeadMetadata {
self.metadata
}
}
impl Default for SaturninAead {
fn default() -> Self {
Self::new()
}
}
impl crate::plugin::AeadPlugin for SaturninAead {
fn algorithm(&self) -> Algorithm {
Algorithm::Saturnin
}
fn create(&self) -> Result<Box<dyn AeadWithMetadata>> {
Ok(Box::new(Self::new()))
}
fn metadata(&self) -> &'static AeadMetadata {
crate::metadata::get_metadata(Algorithm::Saturnin)
.expect("Metadata not found for algorithm")
}
fn name(&self) -> &'static str {
"Saturnin AEAD"
}
fn version(&self) -> &'static str {
"1.0.0"
}
fn description(&self) -> &'static str {
"Lightweight post-quantum symmetric algorithm suite for IoT and constrained devices"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_saturnin_creation() {
let aead = SaturninAead::new();
assert_eq!(aead.algorithm(), Algorithm::Saturnin);
assert_eq!(aead.key_size(), 32);
assert_eq!(aead.nonce_size(), 16);
assert_eq!(aead.tag_size(), 32);
assert_eq!(aead.security_level(), 1);
}
#[test]
fn test_saturnin_metadata() {
let aead = SaturninAead::new();
let metadata = aead.metadata();
assert_eq!(metadata.algorithm, Algorithm::Saturnin);
assert_eq!(metadata.name, "Saturnin");
assert_eq!(metadata.key_size, 32);
assert_eq!(metadata.nonce_size, 16);
assert_eq!(metadata.tag_size, 32);
assert_eq!(metadata.security_level, 1);
}
#[test]
fn test_saturnin_validation() {
let aead = SaturninAead::new();
let key = AeadKey::new(vec![0u8; 32]);
assert!(aead.validate_key(&key).is_ok());
let invalid_key = AeadKey::new(vec![0u8; 16]);
assert!(aead.validate_key(&invalid_key).is_err());
let nonce = Nonce::new(vec![0u8; 16]);
assert!(aead.validate_nonce(&nonce).is_ok());
let invalid_nonce = Nonce::new(vec![0u8; 12]);
assert!(aead.validate_nonce(&invalid_nonce).is_err());
}
#[cfg(feature = "saturnin")]
#[test]
fn test_saturnin_encrypt_decrypt() {
let aead = SaturninAead::new();
let key = AeadKey::new(vec![0u8; 32]);
let nonce = Nonce::new(vec![0u8; 16]);
let plaintext = b"Hello, World!";
let associated_data = b"metadata";
let ciphertext = aead.encrypt(&key, &nonce, plaintext, Some(associated_data.as_slice()));
assert!(ciphertext.is_ok());
let ciphertext = ciphertext.unwrap();
assert_eq!(ciphertext.len(), plaintext.len() + aead.tag_size());
let decrypted = aead.decrypt(&key, &nonce, &ciphertext, Some(associated_data.as_slice()));
assert!(decrypted.is_ok());
assert_eq!(decrypted.unwrap(), plaintext);
}
#[cfg(feature = "saturnin")]
#[test]
fn test_saturnin_authentication_failure() {
let aead = SaturninAead::new();
let key = AeadKey::new(vec![0u8; 32]);
let nonce = Nonce::new(vec![0u8; 16]);
let plaintext = b"Hello, World!";
let ciphertext = aead.encrypt(&key, &nonce, plaintext, None).unwrap();
let mut tampered = ciphertext.clone();
tampered[0] ^= 0xFF;
let result = aead.decrypt(&key, &nonce, &tampered, None);
assert!(result.is_err());
if let Err(lib_q_core::Error::VerificationFailed { operation }) = result {
assert!(operation.contains("AEAD tag verification"));
} else {
panic!("Expected VerificationFailed error");
}
}
#[cfg(feature = "saturnin")]
#[test]
fn test_saturnin_wrong_key() {
let aead = SaturninAead::new();
let key1 = AeadKey::new(vec![0u8; 32]);
let key2 = AeadKey::new(vec![1u8; 32]);
let nonce = Nonce::new(vec![0u8; 16]);
let plaintext = b"Hello, World!";
let ciphertext = aead.encrypt(&key1, &nonce, plaintext, None).unwrap();
let result = aead.decrypt(&key2, &nonce, &ciphertext, None);
assert!(result.is_err());
}
#[cfg(feature = "saturnin")]
#[test]
fn test_saturnin_wrong_nonce() {
let aead = SaturninAead::new();
let key = AeadKey::new(vec![0u8; 32]);
let nonce1 = Nonce::new(vec![0u8; 16]);
let nonce2 = Nonce::new(vec![1u8; 16]);
let plaintext = b"Hello, World!";
let ciphertext = aead.encrypt(&key, &nonce1, plaintext, None).unwrap();
let result = aead.decrypt(&key, &nonce2, &ciphertext, None);
assert!(result.is_err());
}
}