use crate::error::Result;
pub trait SegmentCipher: Send + Sync {
fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>>;
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>>;
}
#[cfg(feature = "encryption")]
mod private {
use crate::error::{Result, SegmentError};
pub struct AesGcmCipher {
cipher: aes_gcm::Aes256Gcm,
}
impl AesGcmCipher {
pub fn from_slice(key_bytes: &[u8]) -> Result<Self> {
use aes_gcm::KeyInit;
let cipher = aes_gcm::Aes256Gcm::new_from_slice(key_bytes)
.map_err(|e| SegmentError::Cipher(format!("invalid AES-256 key: {e}")))?;
Ok(Self { cipher })
}
pub fn new(key_bytes: &[u8; 32]) -> Self {
use aes_gcm::KeyInit;
Self {
cipher: aes_gcm::Aes256Gcm::new_from_slice(key_bytes)
.expect("32-byte key is always valid for AES-256"),
}
}
}
impl super::SegmentCipher for AesGcmCipher {
fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
use aes_gcm::aead::Aead;
use rand::RngCore;
let mut nonce_bytes = [0u8; 12];
rand::thread_rng().fill_bytes(&mut nonce_bytes);
let nonce = aes_gcm::Nonce::from_slice(&nonce_bytes);
let ciphertext = self
.cipher
.encrypt(nonce, plaintext)
.map_err(|e| SegmentError::Cipher(format!("AES-GCM encryption: {e}")))?;
let mut out = Vec::with_capacity(12 + ciphertext.len());
out.extend_from_slice(&nonce_bytes);
out.extend_from_slice(&ciphertext);
Ok(out)
}
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
use aes_gcm::aead::Aead;
if ciphertext.len() < 12 {
return Err(SegmentError::Integrity(
"ciphertext too small for nonce prefix".into(),
));
}
let (nonce_bytes, encrypted) = ciphertext.split_at(12);
let nonce = aes_gcm::Nonce::from_slice(nonce_bytes);
self.cipher
.decrypt(nonce, encrypted)
.map_err(|e| SegmentError::Cipher(format!("AES-GCM decryption: {e}")))
}
}
}
#[cfg(feature = "encryption")]
pub use private::AesGcmCipher;