diff --git a/boring/src/hmac.rs b/boring/src/hmac.rs new file mode 100644 index 00000000..4a593877 --- /dev/null +++ b/boring/src/hmac.rs @@ -0,0 +1,38 @@ +use crate::cvt; +use crate::error::ErrorStack; +use crate::hash::MessageDigest; +use std::ffi::c_void; + +use foreign_types::ForeignType; + +foreign_type_and_impl_send_sync! { + type CType = ffi::HMAC_CTX; + fn drop = ffi::HMAC_CTX_free; + + pub struct HmacCtx; +} + +impl HmacCtx { + /// Configures HmacCtx to use `md` as the hash function and `key` as the key. + /// + /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/hmac.h.html#HMAC_Init_ex + /// + /// # Safety + /// + /// The caller must ensure HMAC_CTX has been initalized. + pub unsafe fn init(&mut self, key: &[u8], md: &MessageDigest) -> Result<(), ErrorStack> { + ffi::init(); + + unsafe { + cvt(ffi::HMAC_Init_ex( + self.as_ptr(), + key.as_ptr() as *const c_void, + key.len(), + md.as_ptr(), + // ENGINE api is deprecated + core::ptr::null_mut(), + )) + .map(|_| ()) + } + } +} diff --git a/boring/src/lib.rs b/boring/src/lib.rs index 77f3e726..932bdd35 100644 --- a/boring/src/lib.rs +++ b/boring/src/lib.rs @@ -137,6 +137,7 @@ pub mod error; pub mod ex_data; pub mod fips; pub mod hash; +pub mod hmac; pub mod hpke; pub mod memcmp; pub mod nid; diff --git a/boring/src/ssl/callbacks.rs b/boring/src/ssl/callbacks.rs index 958524c8..5598b6af 100644 --- a/boring/src/ssl/callbacks.rs +++ b/boring/src/ssl/callbacks.rs @@ -8,13 +8,15 @@ use super::{ }; use crate::error::ErrorStack; use crate::ffi; +use crate::hmac::HmacCtx; use crate::ssl::TicketKeyCallbackResult; +use crate::symm::CipherCtx; use crate::x509::{X509StoreContext, X509StoreContextRef}; use foreign_types::ForeignType; use foreign_types::ForeignTypeRef; use libc::{c_char, c_int, c_uchar, c_uint, c_void}; use std::ffi::CStr; -use std::mem::MaybeUninit; +use std::mem::{ManuallyDrop, MaybeUninit}; use std::ptr; use std::slice; use std::str; @@ -288,8 +290,8 @@ where &SslRef, &mut [u8; 16], &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], - *mut ffi::EVP_CIPHER_CTX, - *mut ffi::HMAC_CTX, + &mut CipherCtx, + &mut HmacCtx, bool, ) -> TicketKeyCallbackResult + 'static @@ -325,7 +327,11 @@ where let key_name = unsafe { key_name.assume_init_mut() }; let iv = unsafe { iv.assume_init_mut() }; - callback(ssl, key_name, iv, evp_ctx, hmac_ctx, encrypt).into() + // The EVP_CIPHER_CTX and HMAC_CTX are owned by boringSSL. + let mut evp_ctx = ManuallyDrop::new(unsafe { CipherCtx::from_ptr(evp_ctx) }); + let mut hmac_ctx = ManuallyDrop::new(unsafe { HmacCtx::from_ptr(hmac_ctx) }); + + callback(ssl, key_name, iv, &mut evp_ctx, &mut hmac_ctx, encrypt).into() } pub(super) unsafe extern "C" fn raw_alpn_select( diff --git a/boring/src/ssl/mod.rs b/boring/src/ssl/mod.rs index 147ce469..1be720ac 100644 --- a/boring/src/ssl/mod.rs +++ b/boring/src/ssl/mod.rs @@ -81,6 +81,7 @@ use crate::dh::DhRef; use crate::ec::EcKeyRef; use crate::error::ErrorStack; use crate::ex_data::Index; +use crate::hmac::HmacCtx; use crate::nid::Nid; use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; use crate::srtp::{SrtpProtectionProfile, SrtpProtectionProfileRef}; @@ -88,6 +89,7 @@ use crate::ssl::bio::BioMethod; use crate::ssl::callbacks::*; use crate::ssl::error::InnerError; use crate::stack::{Stack, StackRef, Stackable}; +use crate::symm::CipherCtx; use crate::x509::store::{X509Store, X509StoreBuilder, X509StoreBuilderRef, X509StoreRef}; use crate::x509::verify::X509VerifyParamRef; use crate::x509::{ @@ -1137,6 +1139,8 @@ impl SslContextBuilder { /// prior to TLS 1.3, retroactively decrypt all application traffic from sessions using that /// ticket key. Thus ticket keys must be regularly rotated for forward secrecy. /// + /// CipherCtx and HmacCtx are guaranteed to be initialized. + /// /// # Panics /// /// This method panics if this `Ssl` is associated with a RPK context. @@ -1148,14 +1152,14 @@ impl SslContextBuilder { /// /// [`SSL_CTX_set_tlsext_ticket_key_cb`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_tlsext_ticket_key_cb #[corresponds(SSL_CTX_set_tlsext_ticket_key_cb)] - pub unsafe fn set_ticket_key_callback_unsafe(&mut self, callback: F) + pub unsafe fn set_ticket_key_callback(&mut self, callback: F) where F: Fn( &SslRef, &mut [u8; 16], &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], - *mut ffi::EVP_CIPHER_CTX, - *mut ffi::HMAC_CTX, + &mut CipherCtx, + &mut HmacCtx, bool, ) -> TicketKeyCallbackResult + 'static diff --git a/boring/src/ssl/test/session_resumption.rs b/boring/src/ssl/test/session_resumption.rs index c4b6c2d4..2eb62116 100644 --- a/boring/src/ssl/test/session_resumption.rs +++ b/boring/src/ssl/test/session_resumption.rs @@ -1,11 +1,12 @@ use super::server::Server; use crate::ssl::test::MessageDigest; +use crate::ssl::HmacCtx; use crate::ssl::SslRef; use crate::ssl::SslSession; use crate::ssl::SslSessionCacheMode; use crate::ssl::TicketKeyCallbackResult; use crate::symm::Cipher; -use std::ffi::c_void; +use crate::symm::CipherCtx; use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::OnceLock; @@ -60,7 +61,7 @@ fn custom_callback_success() { unsafe { server .ctx() - .set_ticket_key_callback_unsafe(test_success_tickey_key_callback) + .set_ticket_key_callback(test_success_tickey_key_callback) }; let server = server.build(); @@ -105,7 +106,7 @@ fn custom_callback_unrecognized_decryption_ticket() { unsafe { server .ctx() - .set_ticket_key_callback_unsafe(test_noop_tickey_key_callback) + .set_ticket_key_callback(test_noop_tickey_key_callback) }; let server = server.build(); @@ -147,8 +148,8 @@ fn test_noop_tickey_key_callback( _ssl: &SslRef, key_name: &mut [u8; 16], iv: &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], - evp_ctx: *mut ffi::EVP_CIPHER_CTX, - hmac_ctx: *mut ffi::HMAC_CTX, + evp_ctx: &mut CipherCtx, + hmac_ctx: &mut HmacCtx, encrypt: bool, ) -> TicketKeyCallbackResult { // These should only be used for testing purposes. @@ -164,31 +165,16 @@ fn test_noop_tickey_key_callback( assert_eq!(iv, &[0; 16]); NOOP_ENCRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst); + // Set the encryption context. - let ret = unsafe { - ffi::EVP_EncryptInit_ex( - evp_ctx, - cipher.as_ptr(), - // ENGINE api is deprecated - core::ptr::null_mut(), - TEST_AES_128_CBC_KEY.as_ptr(), - TEST_CBC_IV.as_ptr(), - ) + unsafe { + evp_ctx + .init_encrypt(&cipher, &TEST_AES_128_CBC_KEY, &TEST_CBC_IV) + .unwrap() }; - assert!(ret == 1); // Set the hmac context. - let ret = unsafe { - ffi::HMAC_Init_ex( - hmac_ctx, - TEST_HMAC_KEY.as_ptr() as *const c_void, - TEST_HMAC_KEY.len(), - digest.as_ptr(), - // ENGINE api is deprecated - core::ptr::null_mut(), - ) - }; - assert!(ret == 1); + unsafe { hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap() }; TicketKeyCallbackResult::Success } else { @@ -202,8 +188,8 @@ fn test_success_tickey_key_callback( _ssl: &SslRef, key_name: &mut [u8; 16], iv: &mut [u8; ffi::EVP_MAX_IV_LENGTH as usize], - evp_ctx: *mut ffi::EVP_CIPHER_CTX, - hmac_ctx: *mut ffi::HMAC_CTX, + evp_ctx: &mut CipherCtx, + hmac_ctx: &mut HmacCtx, encrypt: bool, ) -> TicketKeyCallbackResult { // These should only be used for testing purposes. @@ -219,58 +205,27 @@ fn test_success_tickey_key_callback( assert_eq!(iv, &[0; 16]); SUCCESS_ENCRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst); + // Set the encryption context. - let ret = unsafe { - ffi::EVP_EncryptInit_ex( - evp_ctx, - cipher.as_ptr(), - // ENGINE api is deprecated - core::ptr::null_mut(), - TEST_AES_128_CBC_KEY.as_ptr(), - TEST_CBC_IV.as_ptr(), - ) + unsafe { + evp_ctx + .init_encrypt(&cipher, &TEST_AES_128_CBC_KEY, &TEST_CBC_IV) + .unwrap() }; - assert!(ret == 1); // Set the hmac context. - let ret = unsafe { - ffi::HMAC_Init_ex( - hmac_ctx, - TEST_HMAC_KEY.as_ptr() as *const c_void, - TEST_HMAC_KEY.len(), - digest.as_ptr(), - // ENGINE api is deprecated - core::ptr::null_mut(), - ) - }; - assert!(ret == 1); + unsafe { hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap() }; } else { SUCCESS_DECRYPTION_CALLED_BACK.fetch_add(1, Ordering::SeqCst); // Set the decryption context. - let ret = unsafe { - ffi::EVP_DecryptInit_ex( - evp_ctx, - cipher.as_ptr(), - // ENGINE api is deprecated - core::ptr::null_mut(), - TEST_AES_128_CBC_KEY.as_ptr(), - TEST_CBC_IV.as_ptr(), - ) + unsafe { + evp_ctx + .init_decrypt(&cipher, &TEST_AES_128_CBC_KEY, &TEST_CBC_IV) + .unwrap() }; - assert!(ret == 1); // Set the hmac context. - let ret = unsafe { - ffi::HMAC_Init_ex( - hmac_ctx, - TEST_HMAC_KEY.as_ptr() as *const c_void, - TEST_HMAC_KEY.len(), - digest.as_ptr(), - // ENGINE api is deprecated - core::ptr::null_mut(), - ) - }; - assert!(ret == 1); + unsafe { hmac_ctx.init(&TEST_HMAC_KEY, &digest).unwrap() }; } TicketKeyCallbackResult::Success diff --git a/boring/src/symm.rs b/boring/src/symm.rs index fff8a4a1..831430f5 100644 --- a/boring/src/symm.rs +++ b/boring/src/symm.rs @@ -53,6 +53,7 @@ //! ``` use crate::ffi; +use foreign_types::ForeignType; use libc::{c_int, c_uint}; use openssl_macros::corresponds; use std::cmp; @@ -68,6 +69,77 @@ pub enum Mode { Decrypt, } +foreign_type_and_impl_send_sync! { + type CType = ffi::EVP_CIPHER_CTX; + fn drop = ffi::EVP_CIPHER_CTX_free; + + pub struct CipherCtx; +} + +impl CipherCtx { + /// Configures CipherCtx for a fresh encryption operation using `cipher`. + /// + /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/cipher.h.html#EVP_EncryptInit_ex + /// + /// # Safety + /// + /// The caller must ensure EVP_CIPHER_CTX has been initalized. + /// + /// The caller is responsible for ensuring the length of `key` and `iv` are appropriate for the + /// chosen Cipher. + pub unsafe fn init_encrypt( + &mut self, + cipher: &Cipher, + key: &[u8], + iv: &[u8; ffi::EVP_MAX_IV_LENGTH as usize], + ) -> Result<(), ErrorStack> { + ffi::init(); + + unsafe { + cvt(ffi::EVP_EncryptInit_ex( + self.as_ptr(), + cipher.as_ptr(), + // ENGINE api is deprecated + ptr::null_mut(), + key.as_ptr(), + iv.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Configures CipherCtx for a fresh decryption operation using `cipher`. + /// + /// https://commondatastorage.googleapis.com/chromium-boringssl-docs/cipher.h.html#EVP_DecryptInit_ex + /// + /// # Safety + /// + /// The caller must ensure EVP_CIPHER_CTX has been initalized. + /// + /// The caller is responsible for ensuring the length of `key` and `iv` are appropriate for the + /// chosen Cipher. + pub unsafe fn init_decrypt( + &mut self, + cipher: &Cipher, + key: &[u8], + iv: &[u8; ffi::EVP_MAX_IV_LENGTH as usize], + ) -> Result<(), ErrorStack> { + ffi::init(); + + unsafe { + cvt(ffi::EVP_DecryptInit_ex( + self.as_ptr(), + cipher.as_ptr(), + // ENGINE api is deprecated + ptr::null_mut(), + key.as_ptr(), + iv.as_ptr(), + )) + .map(|_| ()) + } + } +} + /// Represents a particular cipher algorithm. /// /// See OpenSSL doc at [`EVP_EncryptInit`] for more information on each algorithms.