diff --git a/src/aead/chacha.rs b/src/aead/chacha.rs index 07eaee12a..a3d90ac82 100644 --- a/src/aead/chacha.rs +++ b/src/aead/chacha.rs @@ -52,6 +52,23 @@ impl Key { } } + #[inline] + pub fn new_mask(&self, sample: Block) -> [u8; 5] { + let mut out: [u8; 5] = [0; 5]; + let iv = Iv::assume_unique_for_key(sample); + + unsafe { + self.encrypt( + CounterOrIv::Iv(iv), + out.as_ptr(), + out.len(), + out.as_mut_ptr(), + ); + } + + out + } + pub fn encrypt_overlapping(&self, counter: Counter, in_out: &mut [u8], in_prefix_len: usize) { // XXX: The x86 and at least one branch of the ARM assembly language // code doesn't allow overlapping input and output unless they are diff --git a/src/aead/nonce.rs b/src/aead/nonce.rs index 97319fcc8..5d1c7fa58 100644 --- a/src/aead/nonce.rs +++ b/src/aead/nonce.rs @@ -131,6 +131,9 @@ where } impl Iv { + #[inline] + pub fn assume_unique_for_key(a: Block) -> Self { Iv(a) } + #[inline] pub fn into_block_less_safe(self) -> Block { self.0 } } diff --git a/src/aead/quic.rs b/src/aead/quic.rs index c89ccefc3..42482bd6e 100644 --- a/src/aead/quic.rs +++ b/src/aead/quic.rs @@ -17,7 +17,7 @@ //! See draft-ietf-quic-tls. use crate::{ - aead::{aes, block::Block}, + aead::{aes, block::Block, chacha}, cpu, error, polyfill::convert::*, }; @@ -31,6 +31,7 @@ pub struct HeaderProtectionKey { #[allow(variant_size_differences)] enum KeyInner { Aes(aes::Key), + ChaCha20(chacha::Key), } impl HeaderProtectionKey { @@ -83,6 +84,7 @@ derive_debug_via_self!(Algorithm, self.id); enum AlgorithmID { AES_128, AES_256, + CHACHA20, } impl PartialEq for Algorithm { @@ -120,7 +122,30 @@ fn aes_init_256(key: &[u8]) -> Result { fn aes_new_mask(key: &KeyInner, sample: Block) -> [u8; 5] { let aes_key = match key { KeyInner::Aes(key) => key, + _ => unreachable!(), }; aes_key.new_mask(sample) } + +/// ChaCha20. +pub static CHACHA20: Algorithm = Algorithm { + key_len: chacha::KEY_LEN, + init: chacha20_init, + new_mask: chacha20_new_mask, + id: AlgorithmID::CHACHA20, +}; + +fn chacha20_init(key: &[u8]) -> Result { + let chacha20_key: &[u8; chacha::KEY_LEN] = key.try_into_()?; + Ok(KeyInner::ChaCha20(chacha::Key::from(chacha20_key))) +} + +fn chacha20_new_mask(key: &KeyInner, sample: Block) -> [u8; 5] { + let chacha20_key = match key { + KeyInner::ChaCha20(key) => key, + _ => unreachable!(), + }; + + chacha20_key.new_mask(sample) +} diff --git a/tests/quic_chacha20_tests.txt b/tests/quic_chacha20_tests.txt new file mode 100644 index 000000000..be02a4282 --- /dev/null +++ b/tests/quic_chacha20_tests.txt @@ -0,0 +1,3 @@ +KEY = 59bdff7a5bcdaacf319d99646c6273ad96687d2c74ace678f15a1c710675bb23 +SAMPLE = 215a7c1688b4ab7d830dcd052aef9f3c +MASK = 6409a6196d diff --git a/tests/quic_tests.rs b/tests/quic_tests.rs index 87d15744d..c3c6dac55 100644 --- a/tests/quic_tests.rs +++ b/tests/quic_tests.rs @@ -39,6 +39,9 @@ fn quic_aes_128() { test_quic(&quic::AES_128, "tests/quic_aes_128_tests.txt"); } #[test] fn quic_aes_256() { test_quic(&quic::AES_256, "tests/quic_aes_256_tests.txt"); } +#[test] +fn quic_chacha20() { test_quic(&quic::CHACHA20, "tests/quic_chacha20_tests.txt"); } + fn test_quic(alg: &'static quic::Algorithm, file_path: &str) { test_sample_len(alg);