diff --git a/quiche/src/crypto/boringssl.rs b/quiche/src/crypto/boringssl.rs index ecb479e6bc..107926f351 100644 --- a/quiche/src/crypto/boringssl.rs +++ b/quiche/src/crypto/boringssl.rs @@ -3,6 +3,8 @@ use super::*; use std::mem::MaybeUninit; use libc::c_int; +use libc::c_uint; +use libc::c_void; // NOTE: This structure is copied from in order to be able to // statically allocate it. While it is not often modified upstream, it needs to @@ -15,6 +17,13 @@ struct EVP_AEAD_CTX { tag_len: u8, } +#[derive(Clone)] +#[repr(C)] +struct AES_KEY { + rd_key: [u32; 4 * (14 + 1)], + rounds: c_int, +} + impl Algorithm { fn get_evp_aead(self) -> *const EVP_AEAD { match self { @@ -126,29 +135,87 @@ impl Seal { } } -fn make_aead_ctx(alg: Algorithm, key: &[u8]) -> Result { - let mut ctx = MaybeUninit::uninit(); +#[derive(Clone)] +pub(crate) enum HeaderProtectionKey { + Aes(AES_KEY), - let ctx = unsafe { - let aead = alg.get_evp_aead(); + ChaCha(Vec), +} - let rc = EVP_AEAD_CTX_init( - ctx.as_mut_ptr(), - aead, - key.as_ptr(), - alg.key_len(), - alg.tag_len(), - std::ptr::null_mut(), - ); +impl HeaderProtectionKey { + pub fn new(alg: Algorithm, hp_key: Vec) -> Result { + match alg { + Algorithm::AES128_GCM => unsafe { + let mut aes_key = MaybeUninit::::uninit(); - if rc != 1 { - return Err(Error::CryptoFail); + let rc = AES_set_encrypt_key( + (&hp_key).as_ptr(), + 128, + aes_key.as_mut_ptr(), + ); + + if rc != 0 { + return Err(Error::CryptoFail); + } + + let aes_key = aes_key.assume_init(); + Ok(Self::Aes(aes_key)) + }, + + Algorithm::AES256_GCM => unsafe { + let mut aes_key = MaybeUninit::::uninit(); + + let rc = AES_set_encrypt_key( + (&hp_key).as_ptr(), + 256, + aes_key.as_mut_ptr(), + ); + + if rc != 0 { + return Err(Error::CryptoFail); + } + + let aes_key = aes_key.assume_init(); + Ok(Self::Aes(aes_key)) + }, + + Algorithm::ChaCha20_Poly1305 => Ok(Self::ChaCha(hp_key)), } + } - ctx.assume_init() - }; + pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { + let mut new_mask = [0_u8; 5]; - Ok(ctx) + match self { + Self::Aes(aes_key) => unsafe { + AES_ecb_encrypt( + sample.as_ptr(), + (&mut new_mask).as_mut_ptr(), + aes_key as _, + 1, + ); + }, + + Self::ChaCha(key) => unsafe { + const PLAINTEXT: &[u8; 5] = b"\x00\x00\x00\x00\x00"; + + let counter = u32::from_le_bytes([ + sample[0], sample[1], sample[2], sample[3] + ]); + + CRYPTO_chacha_20( + (&mut new_mask).as_mut_ptr(), + PLAINTEXT.as_ptr(), + PLAINTEXT.len(), + (&key).as_ptr(), + (&sample[std::mem::size_of::()..]).as_ptr(), + counter, + ); + }, + } + + Ok(new_mask) + } } pub(crate) struct PacketKey { @@ -180,6 +247,31 @@ impl PacketKey { } } +fn make_aead_ctx(alg: Algorithm, key: &[u8]) -> Result { + let mut ctx = MaybeUninit::uninit(); + + let ctx = unsafe { + let aead = alg.get_evp_aead(); + + let rc = EVP_AEAD_CTX_init( + ctx.as_mut_ptr(), + aead, + key.as_ptr(), + alg.key_len(), + alg.tag_len(), + std::ptr::null_mut(), + ); + + if rc != 1 { + return Err(Error::CryptoFail); + } + + ctx.assume_init() + }; + + Ok(ctx) +} + pub(crate) fn hkdf_extract( alg: Algorithm, out: &mut [u8], secret: &[u8], salt: &[u8], ) -> Result<()> { @@ -262,4 +354,19 @@ extern { nonce_len: usize, inp: *const u8, in_len: usize, extra_in: *const u8, extra_in_len: usize, ad: *const u8, ad_len: usize, ) -> c_int; + + // AES + fn AES_set_encrypt_key( + key: *const u8, bits: c_uint, aeskey: *mut AES_KEY, + ) -> c_int; + + fn AES_ecb_encrypt( + inp: *const u8, out: *mut u8, key: *const AES_KEY, enc: c_int, + ) -> c_void; + + // ChaCha20 + fn CRYPTO_chacha_20( + out: *mut u8, inp: *const u8, in_len: usize, key: *const u8, + nonce: *const u8, counter: u32, + ) -> c_void; } diff --git a/quiche/src/crypto/mod.rs b/quiche/src/crypto/mod.rs index fa3b7076c7..70c9c68696 100644 --- a/quiche/src/crypto/mod.rs +++ b/quiche/src/crypto/mod.rs @@ -66,17 +66,9 @@ pub enum Algorithm { ChaCha20_Poly1305, } +// Note: some vendor-specific methods are implemented by each vendor's submodule +// (openssl-quictls / boringssl). impl Algorithm { - // Note: some vendor-specific methods are implemented by each vendor's - // submodule (openssl-quictls / boringssl). - fn get_ring_hp(self) -> &'static aead::quic::Algorithm { - match self { - Algorithm::AES128_GCM => &aead::quic::AES_128, - Algorithm::AES256_GCM => &aead::quic::AES_256, - Algorithm::ChaCha20_Poly1305 => &aead::quic::CHACHA20, - } - } - fn get_evp_digest(self) -> *const EVP_MD { match self { Algorithm::AES128_GCM => unsafe { EVP_sha256() }, @@ -173,13 +165,7 @@ impl Open { return Ok(<[u8; 5]>::default()); } - let mask = self - .header - .hpk - .new_mask(sample) - .map_err(|_| Error::CryptoFail)?; - - Ok(mask) + self.header.new_mask(sample) } pub fn alg(&self) -> Algorithm { @@ -197,10 +183,7 @@ impl Open { secret: next_secret, - header: HeaderProtectionKey::new( - self.alg, - self.header.hp_key.clone(), - )?, + header: self.header.clone(), packet: next_packet_key, }) @@ -264,13 +247,7 @@ impl Seal { return Ok(<[u8; 5]>::default()); } - let mask = self - .header - .hpk - .new_mask(sample) - .map_err(|_| Error::CryptoFail)?; - - Ok(mask) + self.header.new_mask(sample) } pub fn alg(&self) -> Algorithm { @@ -288,29 +265,15 @@ impl Seal { secret: next_secret, - header: HeaderProtectionKey::new( - self.alg, - self.header.hp_key.clone(), - )?, + header: self.header.clone(), packet: next_packet_key, }) } } -pub struct HeaderProtectionKey { - hpk: aead::quic::HeaderProtectionKey, - - hp_key: Vec, -} impl HeaderProtectionKey { - pub fn new(alg: Algorithm, hp_key: Vec) -> Result { - aead::quic::HeaderProtectionKey::new(alg.get_ring_hp(), &hp_key) - .map(|hpk| Self { hpk, hp_key }) - .map_err(|_| Error::CryptoFail) - } - pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result { let key_len = aead.key_len(); diff --git a/quiche/src/crypto/openssl_quictls.rs b/quiche/src/crypto/openssl_quictls.rs index 89be0f42f5..f95201b90d 100644 --- a/quiche/src/crypto/openssl_quictls.rs +++ b/quiche/src/crypto/openssl_quictls.rs @@ -21,12 +21,6 @@ struct OSSL_PARAM { _unused: c_void, } -impl Drop for EVP_CIPHER_CTX { - fn drop(&mut self) { - unsafe { EVP_CIPHER_CTX_free(self) } - } -} - impl Algorithm { pub fn get_evp_aead(self) -> *const EVP_AEAD { match self { @@ -271,34 +265,99 @@ impl Seal { } } -fn make_evp_cipher_ctx_basic( - alg: Algorithm, enc: u32, -) -> Result<*mut EVP_CIPHER_CTX> { - let ctx: *mut EVP_CIPHER_CTX = unsafe { - let cipher: *const EVP_AEAD = alg.get_evp_aead(); +pub(crate) struct HeaderProtectionKey { + ctx: *mut EVP_CIPHER_CTX, - let ctx = EVP_CIPHER_CTX_new(); - if ctx.is_null() { + key: Vec, +} + +impl HeaderProtectionKey { + pub fn new(alg: Algorithm, hp_key: Vec) -> Result { + Ok(Self { + ctx: make_evp_cipher_ctx_basic(alg, 1)?, + key: hp_key, + }) + } + + pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { + const PLAINTEXT: &[u8; 5] = b"\x00\x00\x00\x00\x00"; + + let mut new_mask = [0_u8; 5]; + + // Set IV (i.e. the sample). + let rc = unsafe { + EVP_CipherInit_ex2( + self.ctx, + std::ptr::null_mut(), // already set + std::ptr::null_mut(), // already set + (&sample).as_ptr(), + 1, // encrypt + std::ptr::null(), + ) + }; + + if rc != 1 { return Err(Error::CryptoFail); } - let rc = EVP_CipherInit_ex2( - ctx, - cipher, - std::ptr::null_mut(), - std::ptr::null_mut(), - enc as c_int, // Following calls can use -1 once this is set. - std::ptr::null(), - ); + let mut ciphertext_len: usize = 0; + + let mut out_len: i32 = 0; + + let rc = unsafe { + EVP_CipherUpdate( + self.ctx, + new_mask.as_mut_ptr(), + &mut out_len, + PLAINTEXT.as_ptr(), + PLAINTEXT.len() as i32, + ) + }; + + if rc != 1 { + return Err(Error::CryptoFail); + }; + + ciphertext_len += out_len as usize; + + let rc = unsafe { + EVP_CipherFinal_ex( + self.ctx, + new_mask[out_len as usize..].as_mut_ptr(), + &mut out_len, + ) + }; if rc != 1 { return Err(Error::CryptoFail); } - ctx - }; - Ok(ctx) + + ciphertext_len += out_len as usize; + + Ok(new_mask) + } +} + +impl Clone for HeaderProtectionKey { + fn clone(&self) -> Self { + let ctx = unsafe { EVP_CIPHER_CTX_dup(self.ctx) }; + + Self { + ctx, + key: self.key.clone(), + } + } } +impl Drop for HeaderProtectionKey { + fn drop(&mut self) { + unsafe { EVP_CIPHER_CTX_free(self.ctx) } + } +} + +unsafe impl std::marker::Send for HeaderProtectionKey {} +unsafe impl std::marker::Sync for HeaderProtectionKey {} + pub(crate) struct PacketKey { ctx: *mut EVP_CIPHER_CTX, nonce: Vec, @@ -310,10 +369,10 @@ pub(crate) struct PacketKey { impl PacketKey { pub fn new( - algo: Algorithm, key: Vec, iv: Vec, enc: u32, + alg: Algorithm, key: Vec, iv: Vec, enc: u32, ) -> Result { Ok(Self { - ctx: make_evp_cipher_ctx_basic(algo, enc)?, + ctx: make_evp_cipher_ctx_basic(alg, enc)?, nonce: iv, key, }) @@ -333,9 +392,43 @@ impl PacketKey { } } +impl Drop for PacketKey { + fn drop(&mut self) { + unsafe { EVP_CIPHER_CTX_free(self.ctx) } + } +} + unsafe impl std::marker::Send for PacketKey {} unsafe impl std::marker::Sync for PacketKey {} +fn make_evp_cipher_ctx_basic( + alg: Algorithm, enc: u32, +) -> Result<*mut EVP_CIPHER_CTX> { + let ctx: *mut EVP_CIPHER_CTX = unsafe { + let cipher: *const EVP_AEAD = alg.get_evp_aead(); + + let ctx = EVP_CIPHER_CTX_new(); + if ctx.is_null() { + return Err(Error::CryptoFail); + } + + let rc = EVP_CipherInit_ex2( + ctx, + cipher, + std::ptr::null_mut(), + std::ptr::null_mut(), + enc as c_int, // Following calls can use -1 once this is set. + std::ptr::null(), + ); + + if rc != 1 { + return Err(Error::CryptoFail); + } + ctx + }; + Ok(ctx) +} + pub(crate) fn hkdf_extract( alg: Algorithm, out: &mut [u8], secret: &[u8], salt: &[u8], ) -> Result<()> { @@ -411,6 +504,8 @@ extern { // EVP_CIPHER_CTX fn EVP_CIPHER_CTX_new() -> *mut EVP_CIPHER_CTX; + fn EVP_CIPHER_CTX_dup(ctx: *const EVP_CIPHER_CTX) -> *mut EVP_CIPHER_CTX; + fn EVP_CIPHER_CTX_free(ctx: *mut EVP_CIPHER_CTX); fn EVP_CipherInit_ex2(