use aws_lc_rs::aead::quic::{self as aws_quic, HeaderProtectionKey};
use aws_lc_rs::aead::{AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305, UnboundKey};
use aws_lc_rs::aead::{Aad as AwsAad, LessSafeKey, Nonce as AwsNonce};
use aws_lc_rs::cipher::{self as aws_cipher, AES_128, AES_256, EncryptingKey, UnboundCipherKey};
use super::super::{Cipher, SupportedDtls12CipherSuite, SupportedDtls13CipherSuite};
use crate::buffer::{Buf, TmpBuf};
use crate::crypto::{Aad, Nonce};
use crate::dtls12::message::Dtls12CipherSuite;
use crate::types::{Dtls13CipherSuite, HashAlgorithm};
struct AesGcm {
key: LessSafeKey,
}
impl std::fmt::Debug for AesGcm {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AesGcm").finish_non_exhaustive()
}
}
impl AesGcm {
fn new(key: &[u8]) -> Result<Self, String> {
let algorithm = match key.len() {
16 => &AES_128_GCM,
32 => &AES_256_GCM,
_ => return Err(format!("Invalid key size for AES-GCM: {}", key.len())),
};
let unbound_key = UnboundKey::new(algorithm, key)
.map_err(|_| "Failed to create AES-GCM cipher".to_string())?;
Ok(AesGcm {
key: LessSafeKey::new(unbound_key),
})
}
}
impl Cipher for AesGcm {
fn encrypt(&mut self, plaintext: &mut Buf, aad: Aad, nonce: Nonce) -> Result<(), String> {
let aws_nonce =
AwsNonce::try_assume_unique_for_key(&nonce).map_err(|_| "Invalid nonce".to_string())?;
let aws_aad = AwsAad::from(&aad[..]);
self.key
.seal_in_place_append_tag(aws_nonce, aws_aad, plaintext)
.map_err(|_| "AES-GCM encryption failed".to_string())?;
Ok(())
}
fn decrypt(&mut self, ciphertext: &mut TmpBuf, aad: Aad, nonce: Nonce) -> Result<(), String> {
if ciphertext.len() < 16 {
return Err(format!("Ciphertext too short: {}", ciphertext.len()));
}
let aws_nonce =
AwsNonce::try_assume_unique_for_key(&nonce).map_err(|_| "Invalid nonce".to_string())?;
let aws_aad = AwsAad::from(&aad[..]);
let plaintext = self
.key
.open_in_place(aws_nonce, aws_aad, ciphertext.as_mut())
.map_err(|_| "AES-GCM decryption failed".to_string())?;
let plaintext_len = plaintext.len();
ciphertext.truncate(plaintext_len);
Ok(())
}
}
struct ChaCha20Poly1305Cipher {
key: LessSafeKey,
}
impl std::fmt::Debug for ChaCha20Poly1305Cipher {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChaCha20Poly1305Cipher")
.finish_non_exhaustive()
}
}
impl ChaCha20Poly1305Cipher {
fn new(key: &[u8]) -> Result<Self, String> {
if key.len() != 32 {
return Err(format!(
"Invalid key size for CHACHA20-POLY1305: {}",
key.len()
));
}
let unbound_key = UnboundKey::new(&CHACHA20_POLY1305, key)
.map_err(|_| "Failed to create ChaCha20-Poly1305 cipher".to_string())?;
Ok(ChaCha20Poly1305Cipher {
key: LessSafeKey::new(unbound_key),
})
}
}
impl Cipher for ChaCha20Poly1305Cipher {
fn encrypt(&mut self, plaintext: &mut Buf, aad: Aad, nonce: Nonce) -> Result<(), String> {
let aws_nonce =
AwsNonce::try_assume_unique_for_key(&nonce).map_err(|_| "Invalid nonce".to_string())?;
let aws_aad = AwsAad::from(&aad[..]);
self.key
.seal_in_place_append_tag(aws_nonce, aws_aad, plaintext)
.map_err(|_| "ChaCha20-Poly1305 encryption failed".to_string())?;
Ok(())
}
fn decrypt(&mut self, ciphertext: &mut TmpBuf, aad: Aad, nonce: Nonce) -> Result<(), String> {
if ciphertext.len() < 16 {
return Err(format!("Ciphertext too short: {}", ciphertext.len()));
}
let aws_nonce =
AwsNonce::try_assume_unique_for_key(&nonce).map_err(|_| "Invalid nonce".to_string())?;
let aws_aad = AwsAad::from(&aad[..]);
let plaintext = self
.key
.open_in_place(aws_nonce, aws_aad, ciphertext.as_mut())
.map_err(|_| "ChaCha20-Poly1305 decryption failed".to_string())?;
let plaintext_len = plaintext.len();
ciphertext.truncate(plaintext_len);
Ok(())
}
}
#[derive(Debug)]
struct Aes128GcmSha256;
impl SupportedDtls12CipherSuite for Aes128GcmSha256 {
fn suite(&self) -> Dtls12CipherSuite {
Dtls12CipherSuite::ECDHE_ECDSA_AES128_GCM_SHA256
}
fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA256
}
fn key_lengths(&self) -> (usize, usize, usize) {
(0, 16, 4) }
fn explicit_nonce_len(&self) -> usize {
8
}
fn tag_len(&self) -> usize {
16
}
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(AesGcm::new(key)?))
}
}
#[derive(Debug)]
struct Aes256GcmSha384;
impl SupportedDtls12CipherSuite for Aes256GcmSha384 {
fn suite(&self) -> Dtls12CipherSuite {
Dtls12CipherSuite::ECDHE_ECDSA_AES256_GCM_SHA384
}
fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA384
}
fn key_lengths(&self) -> (usize, usize, usize) {
(0, 32, 4) }
fn explicit_nonce_len(&self) -> usize {
8
}
fn tag_len(&self) -> usize {
16
}
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(AesGcm::new(key)?))
}
}
#[derive(Debug)]
struct ChaCha20Poly1305Sha256;
impl SupportedDtls12CipherSuite for ChaCha20Poly1305Sha256 {
fn suite(&self) -> Dtls12CipherSuite {
Dtls12CipherSuite::ECDHE_ECDSA_CHACHA20_POLY1305_SHA256
}
fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA256
}
fn key_lengths(&self) -> (usize, usize, usize) {
(0, 32, 12) }
fn explicit_nonce_len(&self) -> usize {
0
}
fn tag_len(&self) -> usize {
16
}
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(ChaCha20Poly1305Cipher::new(key)?))
}
}
#[derive(Debug)]
struct PskAes128Ccm8;
impl SupportedDtls12CipherSuite for PskAes128Ccm8 {
fn suite(&self) -> Dtls12CipherSuite {
Dtls12CipherSuite::PSK_AES128_CCM_8
}
fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA256
}
fn key_lengths(&self) -> (usize, usize, usize) {
(0, 16, 4) }
fn explicit_nonce_len(&self) -> usize {
8
}
fn tag_len(&self) -> usize {
8
}
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(crate::crypto::ccm_cipher::AesCcm8Cipher::new(
key,
)?))
}
}
static AES_128_GCM_SHA256: Aes128GcmSha256 = Aes128GcmSha256;
static AES_256_GCM_SHA384: Aes256GcmSha384 = Aes256GcmSha384;
static CHACHA20_POLY1305_SHA256: ChaCha20Poly1305Sha256 = ChaCha20Poly1305Sha256;
static PSK_AES_128_CCM_8: PskAes128Ccm8 = PskAes128Ccm8;
pub(super) static ALL_CIPHER_SUITES: &[&dyn SupportedDtls12CipherSuite] = &[
&AES_128_GCM_SHA256,
&AES_256_GCM_SHA384,
&CHACHA20_POLY1305_SHA256,
&PSK_AES_128_CCM_8,
];
#[derive(Debug)]
struct Tls13Aes128GcmSha256;
impl SupportedDtls13CipherSuite for Tls13Aes128GcmSha256 {
fn suite(&self) -> Dtls13CipherSuite {
Dtls13CipherSuite::AES_128_GCM_SHA256
}
fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA256
}
fn key_len(&self) -> usize {
16 }
fn iv_len(&self) -> usize {
12 }
fn tag_len(&self) -> usize {
16 }
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(AesGcm::new(key)?))
}
fn encrypt_sn(&self, sn_key: &[u8], sample: &[u8; 16]) -> [u8; 16] {
aes_ecb_encrypt(&AES_128, sn_key, sample)
}
}
#[derive(Debug)]
struct Tls13Aes256GcmSha384;
impl SupportedDtls13CipherSuite for Tls13Aes256GcmSha384 {
fn suite(&self) -> Dtls13CipherSuite {
Dtls13CipherSuite::AES_256_GCM_SHA384
}
fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA384
}
fn key_len(&self) -> usize {
32 }
fn iv_len(&self) -> usize {
12 }
fn tag_len(&self) -> usize {
16 }
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(AesGcm::new(key)?))
}
fn encrypt_sn(&self, sn_key: &[u8], sample: &[u8; 16]) -> [u8; 16] {
aes_ecb_encrypt(&AES_256, sn_key, sample)
}
}
#[derive(Debug)]
struct Tls13ChaCha20Poly1305Sha256;
impl SupportedDtls13CipherSuite for Tls13ChaCha20Poly1305Sha256 {
fn suite(&self) -> Dtls13CipherSuite {
Dtls13CipherSuite::CHACHA20_POLY1305_SHA256
}
fn hash_algorithm(&self) -> HashAlgorithm {
HashAlgorithm::SHA256
}
fn key_len(&self) -> usize {
32 }
fn iv_len(&self) -> usize {
12 }
fn tag_len(&self) -> usize {
16 }
fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String> {
Ok(Box::new(ChaCha20Poly1305Cipher::new(key)?))
}
fn encrypt_sn(&self, sn_key: &[u8], sample: &[u8; 16]) -> [u8; 16] {
let hp_key = HeaderProtectionKey::new(&aws_quic::CHACHA20, sn_key).unwrap();
let mask = hp_key.new_mask(sample).unwrap();
let mut out = [0u8; 16];
out[..5].copy_from_slice(&mask);
out
}
}
static TLS13_AES_128_GCM_SHA256: Tls13Aes128GcmSha256 = Tls13Aes128GcmSha256;
static TLS13_AES_256_GCM_SHA384: Tls13Aes256GcmSha384 = Tls13Aes256GcmSha384;
static TLS13_CHACHA20_POLY1305_SHA256: Tls13ChaCha20Poly1305Sha256 = Tls13ChaCha20Poly1305Sha256;
pub(super) static ALL_DTLS13_CIPHER_SUITES: &[&dyn SupportedDtls13CipherSuite] = &[
&TLS13_AES_128_GCM_SHA256,
&TLS13_AES_256_GCM_SHA384,
&TLS13_CHACHA20_POLY1305_SHA256,
];
fn aes_ecb_encrypt(
algorithm: &'static aws_cipher::Algorithm,
key: &[u8],
input: &[u8; 16],
) -> [u8; 16] {
let unbound = UnboundCipherKey::new(algorithm, key).unwrap();
let ecb_key = EncryptingKey::ecb(unbound).unwrap();
let mut block = *input;
ecb_key.encrypt(&mut block).unwrap();
block
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn chacha20_poly1305_key_len_validation() {
let result = ChaCha20Poly1305Cipher::new(&[0, 1, 2, 3, 4, 5]);
assert_eq!(
"Invalid key size for CHACHA20-POLY1305: 6",
&result.unwrap_err()
);
}
}