From bbd0ccf0b8757b2c6d1634903258500146d49b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20R=C3=BCth?= <1324490+janrueth@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:13:22 +0200 Subject: [PATCH] Harden key material zeroization paths Reduce secret lifetime in HKDF and HMAC internals, avoid extra shared-secret copying in key exchange, and add SHA384 HMAC coverage to guard output sizing. --- boring-rustls-provider/src/hkdf.rs | 11 +++++----- boring-rustls-provider/src/hmac.rs | 31 +++++++++++++++++++++++------ boring-rustls-provider/src/kx/ex.rs | 2 +- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/boring-rustls-provider/src/hkdf.rs b/boring-rustls-provider/src/hkdf.rs index 64c37c4..cccaaf2 100644 --- a/boring-rustls-provider/src/hkdf.rs +++ b/boring-rustls-provider/src/hkdf.rs @@ -2,6 +2,7 @@ use std::marker::PhantomData; use boring::hash::MessageDigest; use rustls::crypto::tls13::{self, Hkdf as RustlsHkdf}; +use zeroize::Zeroizing; use crate::helper::{cvt, cvt_p}; @@ -67,7 +68,7 @@ impl RustlsHkdf for Hkdf { let digest = T::new_hash(); let hash_size = digest.size(); - let mut prk = [0u8; boring_sys::EVP_MAX_MD_SIZE as usize]; + let mut prk = Zeroizing::new([0u8; boring_sys::EVP_MAX_MD_SIZE as usize]); let mut prk_len = 0; // if salt isn't set we usen these bytes here as salt @@ -103,7 +104,7 @@ impl RustlsHkdf for Hkdf { okm: &rustls::crypto::tls13::OkmBlock, ) -> Box { let okm = okm.as_ref(); - let mut prk = [0u8; boring_sys::EVP_MAX_MD_SIZE as usize]; + let mut prk = Zeroizing::new([0u8; boring_sys::EVP_MAX_MD_SIZE as usize]); let prk_len = okm.len(); prk[..prk_len].copy_from_slice(okm); @@ -121,7 +122,7 @@ impl RustlsHkdf for Hkdf { message: &[u8], ) -> rustls::crypto::hmac::Tag { let digest = T::new_hash(); - let mut hash = [0u8; boring_sys::EVP_MAX_MD_SIZE as usize]; + let mut hash = Zeroizing::new([0u8; boring_sys::EVP_MAX_MD_SIZE as usize]); let mut hash_len = 0u32; unsafe { cvt_p(boring_sys::HMAC( @@ -140,7 +141,7 @@ impl RustlsHkdf for Hkdf { } struct HkdfExpander { - prk: [u8; boring_sys::EVP_MAX_MD_SIZE as usize], + prk: Zeroizing<[u8; boring_sys::EVP_MAX_MD_SIZE as usize]>, prk_len: usize, digest: MessageDigest, } @@ -187,7 +188,7 @@ impl tls13::HkdfExpander for HkdfExpander { /// This is infallible, because by definition `OkmBlock` is always exactly /// `HashLen` bytes long. fn expand_block(&self, info: &[&[u8]]) -> tls13::OkmBlock { - let mut output = [0u8; boring_sys::EVP_MAX_MD_SIZE as usize]; + let mut output = Zeroizing::new([0u8; boring_sys::EVP_MAX_MD_SIZE as usize]); let output_len = self.hash_len(); self.expand_slice(info, &mut output[..output_len]) diff --git a/boring-rustls-provider/src/hmac.rs b/boring-rustls-provider/src/hmac.rs index c5d403c..e0d5e49 100644 --- a/boring-rustls-provider/src/hmac.rs +++ b/boring-rustls-provider/src/hmac.rs @@ -4,6 +4,7 @@ use boring::hash::MessageDigest; use boring_additions::hmac::HmacCtx; use foreign_types::ForeignType; use rustls::crypto; +use zeroize::Zeroizing; use crate::helper::{cvt, cvt_p}; @@ -30,7 +31,7 @@ impl crypto::hmac::Hmac for BoringHmac { Box::new(BoringHmacKey { ctx, md, - key: key.to_vec(), + key: Zeroizing::new(key.to_vec()), }) } @@ -41,11 +42,10 @@ impl crypto::hmac::Hmac for BoringHmac { } } -#[derive(Clone)] struct BoringHmacKey { ctx: HmacCtx, md: MessageDigest, - key: Vec, + key: Zeroizing>, } impl BoringHmacKey { @@ -99,8 +99,8 @@ impl crypto::hmac::Key for BoringHmacKey { self.update(last); - let mut out = [0u8; 32]; - let out_len = self.finish(&mut out); + let mut out = Zeroizing::new([0u8; boring_sys::EVP_MAX_MD_SIZE as usize]); + let out_len = self.finish(&mut out[..]); crypto::hmac::Tag::new(&out[..out_len]) } @@ -112,7 +112,7 @@ impl crypto::hmac::Key for BoringHmacKey { #[cfg(test)] mod tests { - use super::SHA256; + use super::{SHA256, SHA384}; use hex_literal::hex; #[test] @@ -141,4 +141,23 @@ mod tests { hex!("11fa4a6ee97bebfad9e1087145c556fec9a786cad0659aa10702d21bd2968305") ); } + + #[test] + fn test_sha384_hmac_len() { + let hasher = SHA384.with_key("Very Secret".as_bytes()); + + let tag = hasher.sign_concat( + &[], + &[ + "yay".as_bytes(), + "this".as_bytes(), + "works".as_bytes(), + "well".as_bytes(), + ], + &[], + ); + + assert_eq!(tag.as_ref().len(), hasher.tag_len()); + assert_eq!(tag.as_ref().len(), 48); + } } diff --git a/boring-rustls-provider/src/kx/ex.rs b/boring-rustls-provider/src/kx/ex.rs index 10fe271..5e80694 100644 --- a/boring-rustls-provider/src/kx/ex.rs +++ b/boring-rustls-provider/src/kx/ex.rs @@ -139,7 +139,7 @@ impl crypto::ActiveKeyExchange for KeyExchange { peer_pub_key: &[u8], ) -> Result { self.diffie_hellman(peer_pub_key) - .map(|x| crypto::SharedSecret::from(x.as_slice())) + .map(crypto::SharedSecret::from) .map_err(|e| { log_and_map( "ex::KeyExchange::diffie_hellman",