use std::fmt;
use std::sync::Arc;
trait ErrorExt: fmt::Debug + std::error::Error {
fn as_std_error(&self) -> &(dyn std::error::Error + 'static);
}
impl<T: std::error::Error + 'static> ErrorExt for T {
fn as_std_error(&self) -> &(dyn std::error::Error + 'static) {
self
}
}
#[derive(Debug, Clone)]
pub struct CipherError {
message: String,
source: Option<Arc<dyn ErrorExt + Send + Sync>>,
}
impl CipherError {
pub fn msg(message: impl fmt::Display) -> Self {
Self {
message: message.to_string(),
source: None,
}
}
pub fn with_source<E>(message: impl fmt::Display, source: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self {
message: message.to_string(),
source: Some(Arc::new(source)),
}
}
}
impl fmt::Display for CipherError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for CipherError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|s| s.as_std_error())
}
}
pub trait SegmentCipher: Send + Sync {
fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, CipherError>;
fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, CipherError>;
}
#[cfg(feature = "encryption")]
mod private {
use super::{CipherError, SegmentCipher};
use std::fmt;
use std::sync::Arc;
#[derive(Debug, Clone)]
struct AeadError(String);
impl fmt::Display for AeadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for AeadError {}
fn wrap<E: fmt::Display>(message: &'static str, e: E) -> CipherError {
CipherError {
message: message.to_string(),
source: Some(Arc::new(AeadError(e.to_string()))),
}
}
pub struct AesGcmCipher {
cipher: aes_gcm::Aes256Gcm,
}
impl AesGcmCipher {
pub fn from_slice(key_bytes: &[u8]) -> Result<Self, CipherError> {
use aes_gcm::KeyInit;
let cipher = aes_gcm::Aes256Gcm::new_from_slice(key_bytes)
.map_err(|e| wrap("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 SegmentCipher for AesGcmCipher {
fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, CipherError> {
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| wrap("AES-GCM encryption failed", 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>, CipherError> {
use aes_gcm::aead::Aead;
if ciphertext.len() < 12 {
return Err(CipherError::msg("ciphertext too small for nonce prefix"));
}
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| wrap("AES-GCM decryption failed", e))
}
}
}
#[cfg(feature = "encryption")]
pub use private::AesGcmCipher;