use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Algorithm {
ChaCha20Poly1305,
Aes256Gcm,
}
pub trait AlgorithmExt {
fn key_length(&self) -> usize;
fn nonce_length(&self) -> usize;
fn tag_length(&self) -> usize;
fn is_authenticated(&self) -> bool;
fn security_level_bits(&self) -> usize;
fn is_timing_attack_resistant(&self) -> bool;
fn is_cache_timing_resistant(&self) -> bool;
fn is_post_quantum_secure(&self) -> bool;
fn algorithm_family(&self) -> AlgorithmFamily;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AlgorithmFamily {
StreamCipher,
BlockCipher,
}
impl AlgorithmExt for Algorithm {
fn key_length(&self) -> usize {
match self {
Algorithm::ChaCha20Poly1305 => 32, Algorithm::Aes256Gcm => 32, }
}
fn nonce_length(&self) -> usize {
match self {
Algorithm::ChaCha20Poly1305 => 12, Algorithm::Aes256Gcm => 12, }
}
fn tag_length(&self) -> usize {
match self {
Algorithm::ChaCha20Poly1305 => 16, Algorithm::Aes256Gcm => 16, }
}
fn is_authenticated(&self) -> bool {
true
}
fn security_level_bits(&self) -> usize {
match self {
Algorithm::ChaCha20Poly1305 => 128, Algorithm::Aes256Gcm => 128, }
}
fn is_timing_attack_resistant(&self) -> bool {
match self {
Algorithm::ChaCha20Poly1305 => true, Algorithm::Aes256Gcm => false, }
}
fn is_cache_timing_resistant(&self) -> bool {
match self {
Algorithm::ChaCha20Poly1305 => true, Algorithm::Aes256Gcm => false, }
}
fn is_post_quantum_secure(&self) -> bool {
match self {
Algorithm::ChaCha20Poly1305 => true, Algorithm::Aes256Gcm => true, }
}
fn algorithm_family(&self) -> AlgorithmFamily {
match self {
Algorithm::ChaCha20Poly1305 => AlgorithmFamily::StreamCipher,
Algorithm::Aes256Gcm => AlgorithmFamily::BlockCipher,
}
}
}
impl fmt::Display for Algorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Algorithm::ChaCha20Poly1305 => write!(f, "ChaCha20-Poly1305"),
Algorithm::Aes256Gcm => write!(f, "AES-256-GCM"),
}
}
}
impl Default for Algorithm {
fn default() -> Self {
Algorithm::ChaCha20Poly1305
}
}
impl Algorithm {
pub fn all() -> &'static [Algorithm] {
&[Algorithm::ChaCha20Poly1305, Algorithm::Aes256Gcm]
}
pub fn timing_resistant() -> Vec<Algorithm> {
Self::all()
.iter()
.filter(|alg| alg.is_timing_attack_resistant())
.copied()
.collect()
}
pub fn most_secure() -> Algorithm {
Algorithm::ChaCha20Poly1305
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_algorithm_properties() {
let alg = Algorithm::ChaCha20Poly1305;
assert_eq!(alg.key_length(), 32);
assert_eq!(alg.nonce_length(), 12);
assert_eq!(alg.tag_length(), 16);
assert!(alg.is_authenticated());
assert_eq!(alg.security_level_bits(), 128);
assert!(alg.is_timing_attack_resistant());
assert!(alg.is_cache_timing_resistant());
assert!(alg.is_post_quantum_secure());
assert_eq!(alg.algorithm_family(), AlgorithmFamily::StreamCipher);
assert_eq!(alg.to_string(), "ChaCha20-Poly1305");
}
#[test]
fn test_algorithm_security_comparison() {
let chacha = Algorithm::ChaCha20Poly1305;
let aes = Algorithm::Aes256Gcm;
assert!(chacha.is_timing_attack_resistant());
assert!(chacha.is_cache_timing_resistant());
assert!(!aes.is_timing_attack_resistant());
assert!(!aes.is_cache_timing_resistant());
assert!(chacha.is_post_quantum_secure());
assert!(aes.is_post_quantum_secure());
}
#[test]
fn test_timing_resistance_selection() {
let resistant_algs = Algorithm::timing_resistant();
assert!(resistant_algs.contains(&Algorithm::ChaCha20Poly1305));
assert!(!resistant_algs.contains(&Algorithm::Aes256Gcm));
assert_eq!(Algorithm::most_secure(), Algorithm::ChaCha20Poly1305);
}
#[test]
fn test_algorithm_security_properties() {
let chacha = Algorithm::ChaCha20Poly1305;
let aes = Algorithm::Aes256Gcm;
assert!(chacha.is_timing_attack_resistant());
assert!(chacha.is_cache_timing_resistant());
assert!(!aes.is_timing_attack_resistant());
assert!(!aes.is_cache_timing_resistant());
assert!(chacha.is_authenticated());
assert!(aes.is_authenticated());
}
}