Skip to content

Commit

Permalink
crypto: replace ring AEAD API for header protection
Browse files Browse the repository at this point in the history
This is currently only used for generating header protection masks,
which, again, doesn't seem like something worth pulling a whole
dependency for. This may or may not fall under FIPS scope.

Note that the ring dependency itself is still needed to build examples,
but it can be downgraded to `dev-dependencies`.
  • Loading branch information
ghedo committed Jan 14, 2025
1 parent 75d7d9a commit 6592418
Show file tree
Hide file tree
Showing 4 changed files with 201 additions and 53 deletions.
2 changes: 1 addition & 1 deletion quiche/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ either = { version = "1.8", default-features = false }
log = { version = "0.4", features = ["std"] }
libc = "0.2"
libm = "0.2"
ring = "0.17"
slab = "0.4"
once_cell = "1"
octets = { version = "0.3", path = "../octets" }
Expand All @@ -82,6 +81,7 @@ windows-sys = { version = "0.59", features = ["Win32_Networking_WinSock", "Win32

[dev-dependencies]
mio = { version = "0.8", features = ["net", "os-poll"] }
ring = "0.17"
url = "2.5"

[lib]
Expand Down
93 changes: 93 additions & 0 deletions quiche/src/crypto/boringssl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <openssl/aead.h> in order to be able to
// statically allocate it. While it is not often modified upstream, it needs to
Expand All @@ -15,6 +17,13 @@ struct EVP_AEAD_CTX {
tag_len: u8,
}

#[derive(Clone)]
#[repr(C)]
pub(crate) struct AES_KEY {
rd_key: [u32; 4 * (14 + 1)],
rounds: c_int,
}

impl Algorithm {
fn get_evp_aead(self) -> *const EVP_AEAD {
match self {
Expand Down Expand Up @@ -142,6 +151,75 @@ impl PacketKey {
}
}

#[derive(Clone)]
#[allow(clippy::large_enum_variant)]
pub(crate) enum HeaderProtectionKey {
Aes(AES_KEY),

ChaCha(Vec<u8>),
}

impl HeaderProtectionKey {
pub fn new(alg: Algorithm, hp_key: Vec<u8>) -> Result<Self> {
match alg {
Algorithm::AES128_GCM | Algorithm::AES256_GCM => unsafe {
let key_len_bits = alg.key_len() as u32 * 8;

let mut aes_key = MaybeUninit::<AES_KEY>::uninit();

let rc = AES_set_encrypt_key(
hp_key.as_ptr(),
key_len_bits,
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)),
}
}

pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> {
let mut new_mask = [0_u8; 5];

match self {
Self::Aes(aes_key) => unsafe {
AES_ecb_encrypt(
sample.as_ptr(),
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(
new_mask.as_mut_ptr(),
PLAINTEXT.as_ptr(),
PLAINTEXT.len(),
key.as_ptr(),
sample[std::mem::size_of::<u32>()..].as_ptr(),
counter,
);
},
}

Ok(new_mask)
}
}

fn make_aead_ctx(alg: Algorithm, key: &[u8]) -> Result<EVP_AEAD_CTX> {
let mut ctx = MaybeUninit::uninit();

Expand Down Expand Up @@ -249,4 +327,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;
}
52 changes: 6 additions & 46 deletions quiche/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use ring::aead;

use libc::c_int;
use libc::c_void;

Expand Down Expand Up @@ -70,17 +68,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() },
Expand Down Expand Up @@ -178,13 +168,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 {
Expand All @@ -202,10 +186,7 @@ impl Open {

secret: next_secret,

header: HeaderProtectionKey::new(
self.alg,
self.header.hp_key.clone(),
)?,
header: self.header.clone(),

packet: next_packet_key,
})
Expand Down Expand Up @@ -280,13 +261,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 {
Expand All @@ -304,10 +279,7 @@ impl Seal {

secret: next_secret,

header: HeaderProtectionKey::new(
self.alg,
self.header.hp_key.clone(),
)?,
header: self.header.clone(),

packet: next_packet_key,
})
Expand All @@ -331,19 +303,7 @@ impl Seal {
}
}

pub struct HeaderProtectionKey {
hpk: aead::quic::HeaderProtectionKey,

hp_key: Vec<u8>,
}

impl HeaderProtectionKey {
pub fn new(alg: Algorithm, hp_key: Vec<u8>) -> Result<Self> {
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<Self> {
let key_len = aead.key_len();

Expand Down
107 changes: 101 additions & 6 deletions quiche/src/crypto/openssl_quictls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -291,9 +285,108 @@ 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 {}

pub(crate) struct HeaderProtectionKey {
ctx: *mut EVP_CIPHER_CTX,

key: Vec<u8>,
}

impl HeaderProtectionKey {
pub fn new(alg: Algorithm, hp_key: Vec<u8>) -> Result<Self> {
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 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);
}

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 {}

fn make_evp_cipher_ctx_basic(
alg: Algorithm, enc: u32,
) -> Result<*mut EVP_CIPHER_CTX> {
Expand Down Expand Up @@ -399,6 +492,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(
Expand Down

0 comments on commit 6592418

Please sign in to comment.