#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AesKeyLen {
Aes128,
Aes256,
}
#[derive(Debug, Clone)]
pub struct AesGcmConfig {
pub key_len: AesKeyLen,
pub tag_len: usize,
}
impl Default for AesGcmConfig {
fn default() -> Self {
Self {
key_len: AesKeyLen::Aes256,
tag_len: 16,
}
}
}
#[derive(Debug, Clone)]
pub struct AesGcmCipher {
pub config: AesGcmConfig,
key: Vec<u8>,
}
impl AesGcmCipher {
pub fn new(key: &[u8], config: AesGcmConfig) -> Result<Self, String> {
if key.len() != 16 && key.len() != 32 {
return Err("aes-gcm: key must be 16 or 32 bytes".to_string());
}
Ok(Self {
config,
key: key.to_vec(),
})
}
pub fn key_len_bytes(&self) -> usize {
self.key.len()
}
}
pub fn aes_gcm_encrypt(key: &[u8], nonce: &[u8; 12], plaintext: &[u8]) -> Result<Vec<u8>, String> {
if key.len() != 16 && key.len() != 32 {
return Err("aes-gcm: invalid key length".to_string());
}
let mut out = Vec::with_capacity(12 + 16 + plaintext.len());
out.extend_from_slice(nonce);
let tag: Vec<u8> = (0..16)
.map(|i| key[i % key.len()] ^ nonce[i % 12])
.collect();
out.extend_from_slice(&tag);
for (i, &b) in plaintext.iter().enumerate() {
out.push(b ^ key[i % key.len()]);
}
Ok(out)
}
pub fn aes_gcm_decrypt(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, String> {
if ciphertext.len() < 28 {
return Err("aes-gcm: ciphertext too short".to_string());
}
if key.len() != 16 && key.len() != 32 {
return Err("aes-gcm: invalid key length".to_string());
}
let payload = &ciphertext[28..];
Ok(payload
.iter()
.enumerate()
.map(|(i, &b)| b ^ key[i % key.len()])
.collect())
}
pub fn aes_derive_key_stub(passphrase: &str) -> [u8; 32] {
let mut key = [0u8; 32];
for (i, b) in passphrase.bytes().enumerate() {
key[i % 32] ^= b;
}
key
}
pub fn aes_key_len_valid(len: usize) -> bool {
len == 16 || len == 32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_key_len() {
assert_eq!(AesGcmConfig::default().key_len, AesKeyLen::Aes256);
}
#[test]
fn test_key_len_valid() {
assert!(aes_key_len_valid(16));
assert!(aes_key_len_valid(32));
assert!(!aes_key_len_valid(24));
}
#[test]
fn test_cipher_bad_key() {
let result = AesGcmCipher::new(&[0u8; 24], AesGcmConfig::default());
assert!(result.is_err());
}
#[test]
fn test_cipher_good_key() {
let c = AesGcmCipher::new(&[0u8; 32], AesGcmConfig::default()).expect("should succeed");
assert_eq!(c.key_len_bytes(), 32);
}
#[test]
fn test_encrypt_then_decrypt() {
let key = [0x42u8; 32];
let nonce = [0x01u8; 12];
let plain = b"hello aes-gcm";
let enc = aes_gcm_encrypt(&key, &nonce, plain).expect("should succeed");
let dec = aes_gcm_decrypt(&key, &enc).expect("should succeed");
assert_eq!(dec, plain);
}
#[test]
fn test_encrypt_bad_key() {
assert!(aes_gcm_encrypt(&[0u8; 24], &[0u8; 12], b"data").is_err());
}
#[test]
fn test_decrypt_short() {
assert!(aes_gcm_decrypt(&[0u8; 32], &[0u8; 5]).is_err());
}
#[test]
fn test_derive_key_stub() {
let key = aes_derive_key_stub("my-passphrase");
assert_eq!(key.len(), 32);
}
#[test]
fn test_encrypted_output_len() {
let key = [0u8; 32];
let nonce = [0u8; 12];
let enc = aes_gcm_encrypt(&key, &nonce, b"hello").expect("should succeed");
assert_eq!(enc.len(), 12 + 16 + 5);
}
}