diff --git a/.github/workflows/macos-build.yml b/.github/workflows/macos-build.yml index 08136ac..9faa126 100644 --- a/.github/workflows/macos-build.yml +++ b/.github/workflows/macos-build.yml @@ -46,6 +46,7 @@ jobs: cd rustls-wolfcrypt-provider make build make test + make test-quic - name: Check formatting run: | diff --git a/.github/workflows/ubuntu-build.yml b/.github/workflows/ubuntu-build.yml index 1081846..fb90acd 100644 --- a/.github/workflows/ubuntu-build.yml +++ b/.github/workflows/ubuntu-build.yml @@ -46,6 +46,7 @@ jobs: cd rustls-wolfcrypt-provider make build make test + make test-quic - name: Check formatting run: | diff --git a/rustls-wolfcrypt-provider/Cargo.toml b/rustls-wolfcrypt-provider/Cargo.toml index d324c72..5c809e3 100644 --- a/rustls-wolfcrypt-provider/Cargo.toml +++ b/rustls-wolfcrypt-provider/Cargo.toml @@ -18,10 +18,10 @@ webpki = { package = "rustls-webpki", version = "0.103.13", features = ["alloc"] foreign-types = { version = "0.5.0", default-features = false } rustls-pki-types = { version = "1.11.0", default-features = false } log = { version = "0.4.25", default-features = false } -env_logger = { version = "0.11.6", default-features = false } wolfcrypt-rs = { path = "../wolfcrypt-rs" } -rustls-pemfile = { version = "2.2.0", default-features = false } -hex = { version = "0.4.3", default-features = false, features = ["alloc"]} +zeroize = { version = "1", default-features = false, features = ["alloc", "derive"] } + +[dev-dependencies] wycheproof = { version = "0.6.0", default-features = false, features = [ "aead", "hkdf", @@ -30,11 +30,9 @@ rayon = "1.10.0" anyhow = "1.0.95" num_cpus = "1.16.0" lazy_static = "1.5.0" +hex = { version = "0.4.3", default-features = false, features = ["alloc"]} hex-literal = "0.4.1" -zeroize = { version = "1", default-features = false, features = ["alloc", "derive"] } - - -[dev-dependencies] +env_logger = { version = "0.11.6", default-features = false } rcgen = { version = "0.13" } serial_test = { version = "3.2.0", default-features = false } tokio = { version = "1.43", features = ["macros", "rt", "net", "io-util", "io-std"], default-features = false } diff --git a/rustls-wolfcrypt-provider/Makefile b/rustls-wolfcrypt-provider/Makefile index c8b1eb3..9aa6973 100644 --- a/rustls-wolfcrypt-provider/Makefile +++ b/rustls-wolfcrypt-provider/Makefile @@ -1,7 +1,11 @@ .PHONY: test -test: +test: @cargo test +.PHONY: test-quic +test-quic: + @cargo test --features quic + .PHONY: build build: @cargo build --release diff --git a/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs b/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs index 9371ebb..585dfa5 100644 --- a/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs +++ b/rustls-wolfcrypt-provider/src/aead/aes128gcm.rs @@ -140,12 +140,15 @@ impl MessageEncrypter for WCTls12Encrypter { // 8 bytes. let payload_start = GCM_NONCE_LENGTH - 4; let payload_end = m.payload.len() + (GCM_NONCE_LENGTH - 4); + let buf = &mut payload.as_mut()[payload_start..payload_end]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmEncrypt( aes_object.as_ptr(), - payload.as_mut()[payload_start..payload_end].as_mut_ptr(), - payload.as_ref()[payload_start..payload_end].as_ptr(), - payload.as_ref()[payload_start..payload_end].len() as word32, + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.as_ptr(), nonce.len() as word32, auth_tag.as_mut_ptr(), @@ -173,7 +176,7 @@ impl MessageDecrypter for WCTls12Decrypter { seq: u64, ) -> Result, rustls::Error> { let payload = &mut m.payload; - if payload.len() < GCM_TAG_LENGTH { + if payload.len() < (GCM_NONCE_LENGTH - 4) + GCM_TAG_LENGTH { return Err(rustls::Error::DecryptError); } let payload_len = payload.len(); @@ -212,15 +215,15 @@ impl MessageDecrypter for WCTls12Decrypter { // from the payload. let payload_start = GCM_NONCE_LENGTH - 4; let payload_end = payload_len - GCM_TAG_LENGTH; + let buf = &mut payload[payload_start..payload_end]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmDecrypt( aes_object.as_ptr(), - payload[payload_start..payload_end].as_mut_ptr(), - payload[payload_start..payload_end].as_ptr(), - payload[payload_start..payload_end] - .len() - .try_into() - .unwrap(), + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.as_ptr(), nonce.len() as word32, auth_tag.as_ptr(), @@ -318,12 +321,15 @@ impl MessageEncrypter for WCTls13Cipher { // authIn, into the authentication tag, authTag. // Apparently we need to also need to include for the encoding type into the encrypted // payload, hence the + 1 otherwise the rustls returns EoF. + let buf = &mut payload.as_mut()[..payload_len + 1]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmEncrypt( aes_object.as_ptr(), - payload.as_mut()[..payload_len + 1].as_mut_ptr(), - payload.as_ref()[..payload_len + 1].as_ptr(), - payload.as_ref()[..payload_len + 1].len() as word32, + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.0.as_ptr(), nonce.0.len() as word32, auth_tag.as_mut_ptr(), @@ -384,12 +390,15 @@ impl MessageDecrypter for WCTls13Cipher { // Finally, we have everything to decrypt the message // from the payload. + let buf = &mut payload[..message_len]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmDecrypt( aes_object.as_ptr(), - payload[..message_len].as_mut_ptr(), - payload[..message_len].as_ptr(), - payload[..message_len].len().try_into().unwrap(), + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.0.as_ptr(), nonce.0.len() as word32, auth_tag.as_ptr(), diff --git a/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs b/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs index 4dc1acd..6f1f7bd 100644 --- a/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs +++ b/rustls-wolfcrypt-provider/src/aead/aes256gcm.rs @@ -140,12 +140,15 @@ impl MessageEncrypter for WCTls12Encrypter { // 8 bytes. let payload_start = GCM_NONCE_LENGTH - 4; let payload_end = m.payload.len() + (GCM_NONCE_LENGTH - 4); + let buf = &mut payload.as_mut()[payload_start..payload_end]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmEncrypt( aes_object.as_ptr(), - payload.as_mut()[payload_start..payload_end].as_mut_ptr(), - payload.as_ref()[payload_start..payload_end].as_ptr(), - payload.as_ref()[payload_start..payload_end].len() as word32, + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.as_ptr(), nonce.len() as word32, auth_tag.as_mut_ptr(), @@ -173,7 +176,7 @@ impl MessageDecrypter for WCTls12Decrypter { seq: u64, ) -> Result, rustls::Error> { let payload = &mut m.payload; - if payload.len() < GCM_TAG_LENGTH { + if payload.len() < (GCM_NONCE_LENGTH - 4) + GCM_TAG_LENGTH { return Err(rustls::Error::DecryptError); } let payload_len = payload.len(); @@ -212,15 +215,15 @@ impl MessageDecrypter for WCTls12Decrypter { // from the payload. let payload_start = GCM_NONCE_LENGTH - 4; let payload_end = payload_len - GCM_TAG_LENGTH; + let buf = &mut payload[payload_start..payload_end]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmDecrypt( aes_object.as_ptr(), - payload[payload_start..payload_end].as_mut_ptr(), - payload[payload_start..payload_end].as_ptr(), - payload[payload_start..payload_end] - .len() - .try_into() - .unwrap(), + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.as_ptr(), nonce.len() as word32, auth_tag.as_ptr(), @@ -318,12 +321,15 @@ impl MessageEncrypter for WCTls13Cipher { // authIn, into the authentication tag, authTag. // Apparently we need to also need to include for the encoding type into the encrypted // payload, hence the + 1 otherwise the rustls returns EoF. + let buf = &mut payload.as_mut()[..payload_len + 1]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmEncrypt( aes_object.as_ptr(), - payload.as_mut()[..payload_len + 1].as_mut_ptr(), - payload.as_ref()[..payload_len + 1].as_ptr(), - payload.as_ref()[..payload_len + 1].len() as word32, + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.0.as_ptr(), nonce.0.len() as word32, auth_tag.as_mut_ptr(), @@ -385,12 +391,15 @@ impl MessageDecrypter for WCTls13Cipher { // Finally, we have everything to decrypt the message // from the payload. + let buf = &mut payload[..message_len]; + let buf_len = buf.len() as word32; + let buf_ptr = buf.as_mut_ptr(); ret = unsafe { wc_AesGcmDecrypt( aes_object.as_ptr(), - payload[..message_len].as_mut_ptr(), - payload[..message_len].as_ptr(), - payload[..message_len].len().try_into().unwrap(), + buf_ptr, + buf_ptr as *const u8, + buf_len, nonce.0.as_ptr(), nonce.0.len() as word32, auth_tag.as_ptr(), diff --git a/rustls-wolfcrypt-provider/src/aead/chacha20.rs b/rustls-wolfcrypt-provider/src/aead/chacha20.rs index 1a47f37..6dd3072 100644 --- a/rustls-wolfcrypt-provider/src/aead/chacha20.rs +++ b/rustls-wolfcrypt-provider/src/aead/chacha20.rs @@ -153,16 +153,17 @@ impl MessageDecrypter for WCTls12Cipher { // to an authentication generated with the inAAD (arbitrary length additional authentication data). // Note: If the generated authentication tag does not match the supplied // authentication tag, the text is not decrypted. + let buf_ptr = payload[..message_len].as_mut_ptr(); let ret = unsafe { wc_ChaCha20Poly1305_Decrypt( self.key.as_ptr(), nonce.0.as_ptr(), aad.as_ptr(), aad.len() as word32, - payload[..message_len].as_ptr(), // we decrypt only the payload, we don't include the tag. + buf_ptr as *const u8, // we decrypt only the payload, we don't include the tag. message_len as word32, auth_tag.as_ptr(), - payload[..message_len].as_mut_ptr(), + buf_ptr, ) }; @@ -245,15 +246,16 @@ impl MessageEncrypter for WCTls13Cipher { // and stores the generated authentication tag in the output buffer, outAuthTag. // We need to also need to include for the encoding type, apparently, hence the + 1 // otherwise the rustls returns EoF. + let buf_ptr = payload.as_mut()[..m.payload.len() + 1].as_mut_ptr(); let ret = unsafe { wc_ChaCha20Poly1305_Encrypt( self.key.as_ptr(), nonce.0.as_ptr(), aad.as_ptr(), aad.len() as word32, - payload.as_ref()[..m.payload.len() + 1].as_ptr(), + buf_ptr as *const u8, (m.payload.len() + 1) as word32, - payload.as_mut()[..m.payload.len() + 1].as_mut_ptr(), + buf_ptr, auth_tag.as_mut_ptr(), ) }; @@ -298,6 +300,7 @@ impl MessageDecrypter for WCTls13Cipher { // to an authentication generated with the inAAD (arbitrary length additional authentication data). // Note: If the generated authentication tag does not match the supplied // authentication tag, the text is not decrypted. + let buf_ptr = payload[..message_len].as_mut_ptr(); let ret = unsafe { wc_ChaCha20Poly1305_Decrypt( self.key.as_ptr(), @@ -306,10 +309,10 @@ impl MessageDecrypter for WCTls13Cipher { aad.len() as word32, // [..message_len] since we want to exclude the // the auth_tag. - payload[..message_len].as_ptr(), + buf_ptr as *const u8, message_len as word32, auth_tag.as_ptr(), - payload[..message_len].as_mut_ptr(), + buf_ptr, ) }; diff --git a/rustls-wolfcrypt-provider/src/aead/quic.rs b/rustls-wolfcrypt-provider/src/aead/quic.rs index 9fd0977..4aeb23b 100644 --- a/rustls-wolfcrypt-provider/src/aead/quic.rs +++ b/rustls-wolfcrypt-provider/src/aead/quic.rs @@ -7,6 +7,7 @@ use core::mem; use foreign_types::ForeignType; use zeroize::Zeroizing; +use crate::alloc::string::ToString; use crate::error::check_if_zero; use crate::types::{AesObject, ChaChaObject}; use alloc::boxed::Box; @@ -457,6 +458,9 @@ impl quic::PacketKey for PacketKey { payload: &'a mut [u8], ) -> Result<&'a [u8], Error> { let payload_len = payload.len(); + if payload_len < TAG_LEN { + return Err(rustls::Error::DecryptError); + } let aad = header; let nonce = Nonce::new(&self.iv, packet_number).0; (self.algorithm.decrypt)(&self.packet_cipher, &nonce, aad, payload)?; @@ -523,48 +527,53 @@ impl quic::Algorithm for KeyFactory { } pub struct AesCipher { - aes_object: AesObject, key: Zeroizing>, } impl AesCipher { pub fn new() -> Result { Ok(Self { - aes_object: new_aes_object()?, key: Zeroizing::new(Vec::new()), }) } - /// It initializes an AES cipher with the given key. + /// Stores the key used to (re)initialize a fresh AES context on each call. pub fn set_key(&mut self, key: &[u8]) -> Result<(), Error> { if key.len() != AES_256_KEY_LEN && key.len() != AES_128_KEY_LEN { return Err(Error::General("Invalid key length".into())); } - let ret = unsafe { - wc_AesSetKey( - self.aes_object.as_ptr(), - key.as_ptr(), - key.len() as word32, - ptr::null_mut(), - 0, - ) - }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("Function AesSetKey failed".into()))?; self.key = Zeroizing::new(key.to_vec()); Ok(()) } pub fn encrypt_sample(&self, sample: &[u8]) -> Result, Error> { let mut out_block = vec![0; TAG_LEN]; + if sample.len() < SAMPLE_LEN { + return Err(Error::General("Invalid sample length".to_string())); + } - let ret = unsafe { - wc_AesEncryptDirect( - self.aes_object.as_ptr(), - out_block.as_mut_ptr(), - sample.as_ptr(), + // Use a fresh AES context per call so &self never mutates shared state. + let mut aes_c_type: Aes = unsafe { mem::zeroed() }; + let aes_object = unsafe { AesObject::from_ptr(&mut aes_c_type) }; + + let mut ret = unsafe { wc_AesInit(aes_object.as_ptr(), ptr::null_mut(), INVALID_DEVID) }; + check_if_zero(ret).map_err(|_| rustls::Error::General("wc_AesInit failed".into()))?; + + ret = unsafe { + wc_AesSetKey( + aes_object.as_ptr(), + self.key.as_ptr(), + self.key.len() as word32, + ptr::null_mut(), + 0, ) }; + check_if_zero(ret) + .map_err(|_| rustls::Error::General("Function AesSetKey failed".into()))?; + + ret = unsafe { + wc_AesEncryptDirect(aes_object.as_ptr(), out_block.as_mut_ptr(), sample.as_ptr()) + }; check_if_zero(ret).map_err(|_| rustls::Error::EncryptError)?; Ok(out_block) @@ -577,12 +586,18 @@ impl AesCipher { payload: &mut [u8], ) -> Result { let mut auth_tag = vec![0u8; TAG_LEN]; - let mut ret; + + // Use a fresh AES context per call so &self never mutates shared state. + let mut aes_c_type: Aes = unsafe { mem::zeroed() }; + let aes_object = unsafe { AesObject::from_ptr(&mut aes_c_type) }; + + let mut ret = unsafe { wc_AesInit(aes_object.as_ptr(), ptr::null_mut(), INVALID_DEVID) }; + check_if_zero(ret).map_err(|_| rustls::Error::General("wc_AesInit failed".into()))?; // Prepare aes_object for encryption ret = unsafe { wc_AesGcmSetKey( - self.aes_object.as_ptr(), + aes_object.as_ptr(), self.key.as_ptr(), self.key.len() as word32, ) @@ -598,7 +613,7 @@ impl AesCipher { ret = unsafe { wc_AesGcmEncrypt( - self.aes_object.as_ptr(), + aes_object.as_ptr(), payload.as_mut_ptr(), payload.as_ptr(), payload.as_ref().len() as word32, @@ -616,15 +631,23 @@ impl AesCipher { } pub fn decrypt(&self, nonce: &[u8], aad: &[u8], payload: &mut [u8]) -> Result<(), Error> { let mut auth_tag = [0u8; TAG_LEN]; + if payload.len() < TAG_LEN { + return Err(rustls::Error::DecryptError); + } let message_len = payload.len() - TAG_LEN; auth_tag.copy_from_slice(&payload[message_len..]); - let mut ret; + // Use a fresh AES context per call so &self never mutates shared state. + let mut aes_c_type: Aes = unsafe { mem::zeroed() }; + let aes_object = unsafe { AesObject::from_ptr(&mut aes_c_type) }; + + let mut ret = unsafe { wc_AesInit(aes_object.as_ptr(), ptr::null_mut(), INVALID_DEVID) }; + check_if_zero(ret).map_err(|_| rustls::Error::General("wc_AesInit failed".into()))?; // Prepare aes_object for decryption ret = unsafe { wc_AesGcmSetKey( - self.aes_object.as_ptr(), + aes_object.as_ptr(), self.key.as_ptr(), self.key.len() as word32, ) @@ -636,7 +659,7 @@ impl AesCipher { // from the payload. ret = unsafe { wc_AesGcmDecrypt( - self.aes_object.as_ptr(), + aes_object.as_ptr(), payload[..message_len].as_mut_ptr(), payload[..message_len].as_ptr(), payload[..message_len] @@ -658,37 +681,20 @@ impl AesCipher { } pub struct ChaChaCipher { - chacha_cipher: Option, - key: Option>, // In case of packet protection, no need to initiate a cipher + key: Option>, } impl ChaChaCipher { pub fn new(key: Option<[u8; CHACHA_KEY_LEN]>) -> Result { - match key { - None => Ok(Self { - chacha_cipher: Some(new_chacha_object()?), - key: None, - }), - Some(key_bytes) => Ok(Self { - chacha_cipher: None, - key: Some(Zeroizing::new(key_bytes)), - }), - } + Ok(Self { + key: key.map(Zeroizing::new), + }) } fn set_key(&mut self, key: &[u8]) -> Result<(), Error> { if key.len() != CHACHA_KEY_LEN { return Err(Error::General("Invalid key length".into())); } - - let chacha_cipher = self.chacha_cipher.as_ref().ok_or_else(|| { - Error::General("Cipher is none. Create a cipher object before setting key".into()) - })?; - //Set key for ChaCha object - let ret = - unsafe { wc_Chacha_SetKey(chacha_cipher.as_ptr(), key.as_ptr(), key.len() as word32) }; - check_if_zero(ret) - .map_err(|_| rustls::Error::General("Function wc_Chacha_SetKey failed".into()))?; self.key = Some(Zeroizing::new(key.try_into().map_err(|_| { Error::General("Key must be exactly 32 bytes".into()) @@ -701,27 +707,46 @@ impl ChaChaCipher { } pub fn encrypt_sample(&self, sample: &[u8]) -> Result, Error> { - let chacha_cipher = self.chacha_cipher.as_ref().ok_or_else(|| { + let chacha_key = self.key.as_ref().ok_or_else(|| { Error::General("Cipher is none. Create a cipher object before encryption".into()) })?; let mut out = vec![0; TAG_LEN]; + if sample.len() < SAMPLE_LEN { + return Err(Error::General("Invalid sample length".to_string())); + } + let (ctr, nonce) = sample.split_at(4); let ctr = u32::from_le_bytes( ctr.try_into() .map_err(|_| rustls::Error::General("Function try_into() failed".into()))?, ); + // Use a fresh ChaCha context per call so &self never mutates shared state. + let mut chacha_c_type: ChaCha = unsafe { mem::zeroed() }; + let chacha_object = unsafe { ChaChaObject::from_ptr(&mut chacha_c_type) }; + + //Set key for ChaCha object + let mut ret = unsafe { + wc_Chacha_SetKey( + chacha_object.as_ptr(), + chacha_key.as_ptr(), + chacha_key.len() as word32, + ) + }; + check_if_zero(ret) + .map_err(|_| rustls::Error::General("Function wc_Chacha_SetKey failed".into()))?; + //Set IV for ChaCha object - let mut ret = unsafe { wc_Chacha_SetIV(chacha_cipher.as_ptr(), nonce.as_ptr(), ctr) }; + ret = unsafe { wc_Chacha_SetIV(chacha_object.as_ptr(), nonce.as_ptr(), ctr) }; check_if_zero(ret) .map_err(|_| rustls::Error::General("Function wc_Chacha_SetIV failed".into()))?; //Encrypt sample ret = unsafe { wc_Chacha_Process( - chacha_cipher.as_ptr(), + chacha_object.as_ptr(), out.as_mut_ptr(), [0; TAG_LEN].as_ptr(), TAG_LEN as word32, @@ -771,6 +796,9 @@ impl ChaChaCipher { Error::General("Key is none. Generate a key before decryption".into()) })?; let mut auth_tag = [0u8; TAG_LEN]; + if payload.len() < TAG_LEN { + return Err(rustls::Error::DecryptError); + } let message_len = payload.len() - TAG_LEN; auth_tag.copy_from_slice(&payload[message_len..]); @@ -799,26 +827,6 @@ impl ChaChaCipher { } } -fn new_aes_object() -> Result { - let aes_c_type_box = Box::new(unsafe { mem::zeroed::() }); - let aes_c_type_ptr = Box::into_raw(aes_c_type_box); - let aes_object = unsafe { AesObject::from_ptr(aes_c_type_ptr) }; - - // Initialize Aes structure. - let ret = unsafe { wc_AesInit(aes_object.as_ptr(), ptr::null_mut(), INVALID_DEVID) }; - check_if_zero(ret).map_err(|_| rustls::Error::General("Function AesInit failed".into()))?; - Ok(aes_object) -} - -fn new_chacha_object() -> Result { - //Create ChaCha object - let chacha_c_typ_box = Box::new(unsafe { mem::zeroed::() }); - let chacha_c_typ_ptr = Box::into_raw(chacha_c_typ_box); - let chacha_object = unsafe { ChaChaObject::from_ptr(chacha_c_typ_ptr) }; - - Ok(chacha_object) -} - #[cfg(test)] mod tests { use hex_literal::hex; diff --git a/rustls-wolfcrypt-provider/src/hkdf.rs b/rustls-wolfcrypt-provider/src/hkdf.rs index fd1e9a5..0e5a414 100644 --- a/rustls-wolfcrypt-provider/src/hkdf.rs +++ b/rustls-wolfcrypt-provider/src/hkdf.rs @@ -2,11 +2,13 @@ use alloc::boxed::Box; use alloc::vec; use alloc::vec::Vec; use core::mem; +use foreign_types::ForeignType; use rustls::crypto::tls13::{self, Hkdf as RustlsHkdf}; use wolfcrypt_rs::*; use crate::error::check_if_zero; use crate::hmac::WCShaHmac; +use crate::types::HmacObject; use zeroize::Zeroizing; pub struct WCHkdfUsingHmac(pub WCShaHmac); @@ -67,11 +69,12 @@ impl RustlsHkdf for WCHkdfUsingHmac { message: &[u8], ) -> rustls::crypto::hmac::Tag { let mut hmac = vec![0u8; self.0.hash_len()]; - let mut hmac_ctx = unsafe { mem::zeroed() }; + let mut hmac_c_type: Hmac = unsafe { mem::zeroed() }; + let hmac_object = unsafe { HmacObject::from_ptr(&mut hmac_c_type) }; let mut ret = unsafe { wc_HmacSetKey( - &mut hmac_ctx, + hmac_object.as_ptr(), self.0.hash_type().try_into().unwrap(), key.as_ref().as_ptr(), key.as_ref().len() as u32, @@ -79,14 +82,13 @@ impl RustlsHkdf for WCHkdfUsingHmac { }; check_if_zero(ret).expect("wc_HmacSetKey failed in hmac_sign"); - ret = unsafe { wc_HmacUpdate(&mut hmac_ctx, message.as_ptr(), message.len() as u32) }; + ret = + unsafe { wc_HmacUpdate(hmac_object.as_ptr(), message.as_ptr(), message.len() as u32) }; check_if_zero(ret).expect("wc_HmacUpdate failed in hmac_sign"); - ret = unsafe { wc_HmacFinal(&mut hmac_ctx, hmac.as_mut_ptr()) }; + ret = unsafe { wc_HmacFinal(hmac_object.as_ptr(), hmac.as_mut_ptr()) }; check_if_zero(ret).expect("wc_HmacFinal failed in hmac_sign"); - unsafe { wc_HmacFree(&mut hmac_ctx) }; - rustls::crypto::hmac::Tag::new(&hmac) } } @@ -139,7 +141,7 @@ impl tls13::HkdfExpander for WolfHkdfExpander { fn expand_block(&self, info: &[&[u8]]) -> tls13::OkmBlock { let mut output = vec![0u8; self.hash_len]; self.expand_slice(info, &mut output) - .expect("expand_block failed"); + .expect("HKDF-Expand failed during TLS 1.3 key derivation"); tls13::OkmBlock::new(&output) } diff --git a/rustls-wolfcrypt-provider/src/hmac/mod.rs b/rustls-wolfcrypt-provider/src/hmac/mod.rs index 4f8cc85..9c214b9 100644 --- a/rustls-wolfcrypt-provider/src/hmac/mod.rs +++ b/rustls-wolfcrypt-provider/src/hmac/mod.rs @@ -1,6 +1,8 @@ use crate::error::check_if_zero; +use crate::types::HmacObject; use alloc::{boxed::Box, vec, vec::Vec}; use core::mem; +use foreign_types::ForeignType; use rustls::crypto; use wolfcrypt_rs::*; use zeroize::Zeroizing; @@ -69,13 +71,16 @@ struct WCHmacKey { impl crypto::hmac::Key for WCHmacKey { fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag { - let hmac_object = self.hmac_init(); - self.hmac_update(hmac_object, first); + let mut hmac_c_type: Hmac = unsafe { mem::zeroed() }; + let hmac_object = unsafe { HmacObject::from_ptr(&mut hmac_c_type) }; + + self.hmac_init(&hmac_object); + self.hmac_update(&hmac_object, first); for m in middle { - self.hmac_update(hmac_object, m) + self.hmac_update(&hmac_object, m) } - self.hmac_update(hmac_object, last); - let digest = self.hmac_final(hmac_object); + self.hmac_update(&hmac_object, last); + let digest = self.hmac_final(&hmac_object); crypto::hmac::Tag::new(&digest) } @@ -85,36 +90,29 @@ impl crypto::hmac::Key for WCHmacKey { } impl WCHmacKey { - fn hmac_init(&self) -> *mut Hmac { - let hmac_ptr = Box::into_raw(Box::new(unsafe { mem::zeroed::() })); - + fn hmac_init(&self, hmac_object: &HmacObject) { let ret = unsafe { wc_HmacSetKey( - hmac_ptr, + hmac_object.as_ptr(), self.variant.algorithm(), self.key.as_ptr(), self.key.len() as word32, ) }; check_if_zero(ret).expect("wc_HmacSetKey failed"); - hmac_ptr } - fn hmac_update(&self, hmac_ptr: *mut Hmac, input: &[u8]) { - let ret = unsafe { wc_HmacUpdate(hmac_ptr, input.as_ptr(), input.len() as word32) }; + fn hmac_update(&self, hmac_object: &HmacObject, input: &[u8]) { + let ret = + unsafe { wc_HmacUpdate(hmac_object.as_ptr(), input.as_ptr(), input.len() as word32) }; check_if_zero(ret).expect("wc_HmacUpdate failed"); } - fn hmac_final(&self, hmac_ptr: *mut Hmac) -> Vec { + fn hmac_final(&self, hmac_object: &HmacObject) -> Vec { let mut digest = vec![0u8; self.variant.digest_size()]; - let ret = unsafe { wc_HmacFinal(hmac_ptr, digest.as_mut_ptr()) }; + let ret = unsafe { wc_HmacFinal(hmac_object.as_ptr(), digest.as_mut_ptr()) }; check_if_zero(ret).expect("wc_HmacFinal failed"); - unsafe { - wc_HmacFree(hmac_ptr); - drop(Box::from_raw(hmac_ptr)); - } - digest } } diff --git a/rustls-wolfcrypt-provider/src/hmac/sha256hmac.rs b/rustls-wolfcrypt-provider/src/hmac/sha256hmac.rs deleted file mode 100644 index 6209e12..0000000 --- a/rustls-wolfcrypt-provider/src/hmac/sha256hmac.rs +++ /dev/null @@ -1,129 +0,0 @@ -use crate::error::check_if_zero; -use alloc::boxed::Box; -use alloc::vec::Vec; -use core::mem; -use rustls::crypto; -use zeroize::Zeroizing; - -use wolfcrypt_rs::*; - -pub struct WCSha256Hmac; - -impl crypto::hmac::Hmac for WCSha256Hmac { - fn with_key(&self, key: &[u8]) -> Box { - Box::new(WCHmac256Key { key: Zeroizing::new(key.to_vec()) }) - } - - fn hash_output_len(&self) -> usize { - 32_usize - } -} - -struct WCHmac256Key { - key: Zeroizing>, -} - -impl crypto::hmac::Key for WCHmac256Key { - fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag { - let hmac_object = self.hmac_init(); - - self.hmac_update(hmac_object, first); - - for m in middle { - self.hmac_update(hmac_object, m) - } - - self.hmac_update(hmac_object, last); - - let digest = self.hmac_final(hmac_object); - let digest_length = digest.len(); - - crypto::hmac::Tag::new(&digest[..digest_length]) - } - - fn tag_len(&self) -> usize { - 32_usize - } -} - -impl WCHmac256Key { - fn hmac_init(&self) -> *mut Hmac { - let hmac_ptr = Box::into_raw(Box::new(unsafe { mem::zeroed::() })); - - // This function initializes an Hmac object, setting - // its encryption type, key and HMAC length. - let ret = unsafe { - wc_HmacSetKey( - hmac_ptr, - WC_SHA256.try_into().unwrap(), - self.key.as_ptr(), - self.key.len() as word32, - ) - }; - check_if_zero(ret).expect("wc_HmacSetKey failed"); - - hmac_ptr - } - - fn hmac_update(&self, hmac_ptr: *mut Hmac, input: &[u8]) { - let ret = - unsafe { wc_HmacUpdate(hmac_ptr, input.as_ptr(), input.len() as word32) }; - - check_if_zero(ret).expect("wc_HmacUpdate failed"); - } - - fn hmac_final(&self, hmac_ptr: *mut Hmac) -> [u8; WC_SHA256_DIGEST_SIZE as usize] { - let mut digest: [u8; WC_SHA256_DIGEST_SIZE as usize] = [0; WC_SHA256_DIGEST_SIZE as usize]; - - // This function computes the final hash of an Hmac object's message. - let ret = unsafe { wc_HmacFinal(hmac_ptr, digest.as_mut_ptr()) }; - check_if_zero(ret).expect("wc_HmacFinal failed"); - - unsafe { - wc_HmacFree(hmac_ptr); - drop(Box::from_raw(hmac_ptr)); - } - - digest - } -} - -#[cfg(test)] -mod tests { - use super::*; - use rustls::crypto::hmac::Hmac; - - #[test] - fn test_sha_256_hmac() { - let hmac = WCSha256Hmac; - let key = "this is my key".as_bytes(); - let hash = hmac.with_key(key); - - // First call to sign_concat - let tag1 = hash.sign_concat( - &[], - &[ - "fake it".as_bytes(), - "till you".as_bytes(), - "make".as_bytes(), - "it".as_bytes(), - ], - &[], - ); - - // Second call to sign_concat with the same input - let tag2 = hash.sign_concat( - &[], - &[ - "fake it".as_bytes(), - "till you".as_bytes(), - "make".as_bytes(), - "it".as_bytes(), - ], - &[], - ); - - // Assert that both tags are equal - assert_eq!(tag1.as_ref(), tag2.as_ref()); - } -} diff --git a/rustls-wolfcrypt-provider/src/hmac/sha384hmac.rs b/rustls-wolfcrypt-provider/src/hmac/sha384hmac.rs deleted file mode 100644 index 97295c5..0000000 --- a/rustls-wolfcrypt-provider/src/hmac/sha384hmac.rs +++ /dev/null @@ -1,130 +0,0 @@ -use crate::error::check_if_zero; -use alloc::boxed::Box; -use alloc::vec::Vec; -use core::mem; -use rustls::crypto; -use zeroize::Zeroizing; -use wolfcrypt_rs::*; - -pub struct WCSha384Hmac; - -impl crypto::hmac::Hmac for WCSha384Hmac { - fn with_key(&self, key: &[u8]) -> Box { - Box::new(WCHmac384Key { key: Zeroizing::new(key.to_vec()) }) - } - - fn hash_output_len(&self) -> usize { - 48_usize - } -} - -struct WCHmac384Key { - key: Zeroizing>, -} - -impl crypto::hmac::Key for WCHmac384Key { - fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag { - let hmac_object = self.hmac_init(); - - self.hmac_update(hmac_object, first); - - for m in middle { - self.hmac_update(hmac_object, m) - } - - self.hmac_update(hmac_object, last); - - let digest = self.hmac_final(hmac_object); - let digest_length = digest.len(); - - crypto::hmac::Tag::new(&digest[..digest_length]) - } - - fn tag_len(&self) -> usize { - 48_usize - } -} - -impl WCHmac384Key { - fn hmac_init(&self) -> *mut wolfcrypt_rs::Hmac { - let hmac_ptr = Box::into_raw(Box::new(unsafe { mem::zeroed::() })); - - // This function initializes an Hmac object, setting - // its encryption type, key and HMAC length. - let ret = unsafe { - wc_HmacSetKey( - hmac_ptr, - WC_SHA384.try_into().unwrap(), - self.key.as_ptr(), - self.key.len() as word32, - ) - }; - check_if_zero(ret).expect("wc_HmacSetKey failed"); - - hmac_ptr - } - - fn hmac_update(&self, hmac_ptr: *mut wolfcrypt_rs::Hmac, input: &[u8]) { - let ret = - unsafe { wc_HmacUpdate(hmac_ptr, input.as_ptr(), input.len() as word32) }; - - check_if_zero(ret).expect("wc_HmacUpdate failed"); - } - - fn hmac_final(&self, hmac_ptr: *mut wolfcrypt_rs::Hmac) -> [u8; WC_SHA384_DIGEST_SIZE as usize] { - let mut digest: [u8; WC_SHA384_DIGEST_SIZE as usize] = - [0; WC_SHA384_DIGEST_SIZE as usize]; - - // This function computes the final hash of an Hmac object's message. - let ret = unsafe { wc_HmacFinal(hmac_ptr, digest.as_mut_ptr()) }; - - check_if_zero(ret).expect("wc_HmacFinal failed"); - - unsafe { - wc_HmacFree(hmac_ptr); - drop(Box::from_raw(hmac_ptr)); - } - - digest - } -} - -#[cfg(test)] -mod tests { - use super::*; - use rustls::crypto::hmac::Hmac; - - #[test] - fn test_sha_384_hmac() { - let hmac = WCSha384Hmac; - let key = "this is my key".as_bytes(); - let hash = hmac.with_key(key); - - // First call to sign_concat - let tag1 = hash.sign_concat( - &[], - &[ - "fake it".as_bytes(), - "till you".as_bytes(), - "make".as_bytes(), - "it".as_bytes(), - ], - &[], - ); - - // Second call to sign_concat with the same input - let tag2 = hash.sign_concat( - &[], - &[ - "fake it".as_bytes(), - "till you".as_bytes(), - "make".as_bytes(), - "it".as_bytes(), - ], - &[], - ); - - // Assert that both tags are equal - assert_eq!(tag1.as_ref(), tag2.as_ref()); - } -} diff --git a/rustls-wolfcrypt-provider/src/kx/sec256r1.rs b/rustls-wolfcrypt-provider/src/kx/sec256r1.rs index f5dfd6c..c9c08cc 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec256r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec256r1.rs @@ -32,12 +32,17 @@ impl KeyExchangeSecP256r1 { qy_len: 32, }; - key_object.init(); - rng_object.init(); + key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; let key_size = unsafe { wc_ecc_get_curve_size_from_id(ecc_curve_ids_ECC_SECP256R1) }; - let mut priv_key_raw = [0u8; 32]; + let mut priv_key_raw: Zeroizing> = + Zeroizing::new(alloc::vec![0u8; 32].into_boxed_slice()); let mut priv_key_raw_len: word32 = priv_key_raw.len() as word32; ret = unsafe { @@ -82,7 +87,7 @@ impl KeyExchangeSecP256r1 { pub_key_bytes[33..65].copy_from_slice(&pub_key_raw.qy); Ok(KeyExchangeSecP256r1 { - priv_key_bytes: Zeroizing::new(Box::new(priv_key_raw)), + priv_key_bytes: priv_key_raw, pub_key_bytes: Box::new(pub_key_bytes), }) } @@ -102,8 +107,12 @@ impl KeyExchangeSecP256r1 { let rng_object = WCRngObject::new(&mut rng); let mut ret: i32; - priv_key_object.init(); - pub_key_object.init(); + priv_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; + pub_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; ret = unsafe { wc_ecc_import_private_key_ex( @@ -130,7 +139,9 @@ impl KeyExchangeSecP256r1 { check_if_zero(ret) .map_err(|_| rustls::Error::General("Failed to import peer ECC public key".into()))?; - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; ret = unsafe { wc_ecc_set_rng(pub_key_object.as_ptr(), rng_object.as_ptr()) }; check_if_zero(ret) diff --git a/rustls-wolfcrypt-provider/src/kx/sec384r1.rs b/rustls-wolfcrypt-provider/src/kx/sec384r1.rs index 4754101..a9a178d 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec384r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec384r1.rs @@ -33,9 +33,13 @@ impl KeyExchangeSecP384r1 { qy_len: 48, }; - key_object.init(); + key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; let key_size = unsafe { wc_ecc_get_curve_size_from_id(ecc_curve_ids_ECC_SECP384R1) }; @@ -50,7 +54,8 @@ impl KeyExchangeSecP384r1 { check_if_zero(ret) .map_err(|_| rustls::Error::General("wc_ecc_make_key_ex failed".into()))?; - let mut priv_key_raw = [0u8; 48]; + let mut priv_key_raw: Zeroizing> = + Zeroizing::new(alloc::vec![0u8; 48].into_boxed_slice()); let mut priv_key_raw_len: word32 = priv_key_raw.len() as word32; ret = unsafe { @@ -85,7 +90,7 @@ impl KeyExchangeSecP384r1 { pub_key_bytes[49..97].copy_from_slice(&pub_key_raw.qy); Ok(KeyExchangeSecP384r1 { - priv_key_bytes: Zeroizing::new(Box::new(priv_key_raw)), + priv_key_bytes: priv_key_raw, pub_key_bytes: Box::new(pub_key_bytes), }) } @@ -105,8 +110,12 @@ impl KeyExchangeSecP384r1 { let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object: WCRngObject = WCRngObject::new(&mut rng); - priv_key_object.init(); - pub_key_object.init(); + priv_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; + pub_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; ret = unsafe { wc_ecc_import_private_key_ex( @@ -137,7 +146,9 @@ impl KeyExchangeSecP384r1 { check_if_zero(ret) .map_err(|_| rustls::Error::General("Failed to import peer ECC public key".into()))?; - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; ret = unsafe { wc_ecc_set_rng(pub_key_object.as_ptr(), rng_object.as_ptr()) }; check_if_zero(ret) diff --git a/rustls-wolfcrypt-provider/src/kx/sec521r1.rs b/rustls-wolfcrypt-provider/src/kx/sec521r1.rs index 1887f5d..df06a55 100644 --- a/rustls-wolfcrypt-provider/src/kx/sec521r1.rs +++ b/rustls-wolfcrypt-provider/src/kx/sec521r1.rs @@ -33,10 +33,14 @@ impl KeyExchangeSecP521r1 { }; // We initiliaze the key pair. - key_object.init(); + key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; // We initiliaze the rng object. - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; let key_size = unsafe { wc_ecc_get_curve_size_from_id(ecc_curve_ids_ECC_SECP521R1) }; @@ -51,7 +55,8 @@ impl KeyExchangeSecP521r1 { check_if_zero(ret) .map_err(|_| rustls::Error::General("wc_ecc_make_key_ex failed".into()))?; - let mut priv_key_raw = [0u8; 66]; + let mut priv_key_raw: Zeroizing> = + Zeroizing::new(alloc::vec![0u8; 66].into_boxed_slice()); let mut priv_key_raw_len: word32 = priv_key_raw.len() as word32; ret = unsafe { @@ -85,7 +90,7 @@ impl KeyExchangeSecP521r1 { pub_key_bytes[67..133].copy_from_slice(&pub_key_raw.qy); Ok(KeyExchangeSecP521r1 { - priv_key_bytes: Zeroizing::new(Box::new(priv_key_raw)), + priv_key_bytes: priv_key_raw, pub_key_bytes: Box::new(pub_key_bytes), }) } @@ -105,9 +110,13 @@ impl KeyExchangeSecP521r1 { let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object: WCRngObject = WCRngObject::new(&mut rng); - priv_key_object.init(); + priv_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; - pub_key_object.init(); + pub_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; ret = unsafe { wc_ecc_import_private_key_ex( @@ -138,7 +147,9 @@ impl KeyExchangeSecP521r1 { check_if_zero(ret) .map_err(|_| rustls::Error::General("Failed to import peer ECC public key".into()))?; - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; ret = unsafe { wc_ecc_set_rng(pub_key_object.as_ptr(), rng_object.as_ptr()) }; check_if_zero(ret) diff --git a/rustls-wolfcrypt-provider/src/kx/x25519.rs b/rustls-wolfcrypt-provider/src/kx/x25519.rs index 685cf6c..5042a72 100644 --- a/rustls-wolfcrypt-provider/src/kx/x25519.rs +++ b/rustls-wolfcrypt-provider/src/kx/x25519.rs @@ -19,15 +19,22 @@ impl KeyExchangeX25519 { let mut ret; let mut pub_key_raw: [u8; 32] = [0; 32]; let mut pub_key_raw_len: word32 = pub_key_raw.len() as word32; - let mut priv_key_raw: [u8; 32] = [0; 32]; + // Export the private key straight into a zeroizing heap buffer so the + // raw secret never lives in an un-wiped stack array. + let mut priv_key_raw: Zeroizing> = + Zeroizing::new(alloc::vec![0u8; 32].into_boxed_slice()); let mut priv_key_raw_len: word32 = priv_key_raw.len() as word32; let endian: u32 = EC25519_LITTLE_ENDIAN; // We initialize the curve25519 key object. - key_object.init(); + key_object + .init() + .map_err(|_| rustls::Error::General("wc_curve25519_init failed".into()))?; // We initialize the rng object. - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; // This function generates a Curve25519 key using the given random number generator, rng, // of the size given (keysize), and stores it in the given curve25519_key structure. @@ -51,7 +58,7 @@ impl KeyExchangeX25519 { Ok(KeyExchangeX25519 { pub_key_bytes: Box::new(pub_key_raw), - priv_key_bytes: Zeroizing::new(Box::new(priv_key_raw)), + priv_key_bytes: priv_key_raw, }) } @@ -84,7 +91,9 @@ impl KeyExchangeX25519 { .map_err(|_| rustls::Error::General("Invalid Curve25519 public key".into()))?; // We initialize the curve25519 key object before we import the public key in it. - pub_key_provided_object.init(); + pub_key_provided_object + .init() + .map_err(|_| rustls::Error::General("wc_curve25519_init failed".into()))?; // This function imports a public key from the given input buffer // and stores it in the curve25519_key structure. @@ -100,7 +109,9 @@ impl KeyExchangeX25519 { .map_err(|_| rustls::Error::General("Failed to import Curve25519 public key".into()))?; // We initialize the curve25519 key object before we import the private key in it. - private_key_object.init(); + private_key_object + .init() + .map_err(|_| rustls::Error::General("wc_curve25519_init failed".into()))?; // This function imports a private key from the given input buffer // and stores it in the the curve25519_key structure. @@ -124,7 +135,9 @@ impl KeyExchangeX25519 { // On non-blinding (asm) builds curve25519_set_rng is a no-op. let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object = WCRngObject::new(&mut rng); - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; ret = unsafe { curve25519_set_rng(private_key_object.as_ptr(), rng_object.as_ptr()) }; check_if_zero(ret).map_err(|_| { diff --git a/rustls-wolfcrypt-provider/src/random.rs b/rustls-wolfcrypt-provider/src/random.rs index ee3940c..a629b71 100644 --- a/rustls-wolfcrypt-provider/src/random.rs +++ b/rustls-wolfcrypt-provider/src/random.rs @@ -13,7 +13,9 @@ pub fn wolfcrypt_random_buffer_generator(buff: &mut [u8]) -> WCResult { // rng->drbg (deterministic random bit generator) allocated // (should be deallocated with wc_FreeRng). // This is a blocking operation. - rng_object.init(); + // Propagate an init failure (e.g. transient OS entropy/DRBG seeding error) + // instead of panicking, so SecureRandom::fill can surface GetRandomFailed. + rng_object.init()?; // Copies a sz bytes of pseudorandom data to output. // Will reseed rng if needed (blocking). diff --git a/rustls-wolfcrypt-provider/src/sign/ecdsa.rs b/rustls-wolfcrypt-provider/src/sign/ecdsa.rs index 9e4f612..c178e80 100644 --- a/rustls-wolfcrypt-provider/src/sign/ecdsa.rs +++ b/rustls-wolfcrypt-provider/src/sign/ecdsa.rs @@ -59,7 +59,9 @@ impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKey { let mut ecc_c_type: ecc_key = unsafe { mem::zeroed() }; let ecc_key_object = ECCKeyObject::new(&mut ecc_c_type); - ecc_key_object.init(); + ecc_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; let mut idx: u32 = 0; let ret = unsafe { @@ -80,7 +82,7 @@ impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKey { )); } - let mut priv_key_bytes = vec![0u8; key_size as usize]; + let mut priv_key_bytes = Zeroizing::new(vec![0u8; key_size as usize]); let mut priv_key_bytes_len = priv_key_bytes.len() as word32; let ret = unsafe { @@ -99,7 +101,7 @@ impl TryFrom<&PrivateKeyDer<'_>> for EcdsaSigningKey { curve_id_to_scheme(key_size).map_err(|e| rustls::Error::General(e.to_string()))?; Ok(Self { - key: Arc::new(Zeroizing::new(priv_key_bytes)), + key: Arc::new(priv_key_bytes), scheme, }) } @@ -137,11 +139,15 @@ impl Signer for EcdsaSigningKey { let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object: WCRngObject = WCRngObject::new(&mut rng); - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; let mut ecc_c_type: ecc_key = unsafe { mem::zeroed() }; let ecc_key_object = ECCKeyObject::new(&mut ecc_c_type); - ecc_key_object.init(); + ecc_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ecc_init failed".into()))?; let curve_id = scheme_to_curve_id(self.scheme) .map_err(|e| rustls::Error::General(format!("scheme_to_curve_id unsupported: {e}")))?; @@ -159,10 +165,6 @@ impl Signer for EcdsaSigningKey { check_if_zero(ret) .map_err(|_| rustls::Error::General("wc_ecc_import_private_key_ex failed".into()))?; - let ret = - unsafe { wc_ecc_set_curve(ecc_key_object.as_ptr(), self.key.len() as i32, curve_id) }; - check_if_zero(ret).map_err(|_| rustls::Error::General("wc_ecc_set_curve failed".into()))?; - let mut sig = [0u8; ECC_MAX_SIG_SIZE as usize]; let mut sig_sz: word32 = sig.len() as word32; diff --git a/rustls-wolfcrypt-provider/src/sign/eddsa.rs b/rustls-wolfcrypt-provider/src/sign/eddsa.rs index 42478b1..111bdea 100644 --- a/rustls-wolfcrypt-provider/src/sign/eddsa.rs +++ b/rustls-wolfcrypt-provider/src/sign/eddsa.rs @@ -16,6 +16,9 @@ use zeroize::Zeroizing; const ALL_EDDSA_SCHEMES: &[SignatureScheme] = &[SignatureScheme::ED25519]; +/// An ED25519 private key with its optional embedded public key. +type Ed25519KeyPair = (Zeroizing>, Option<[u8; 32]>); + #[derive(Clone)] pub struct Ed25519PrivateKey { priv_key: Arc>>, @@ -32,8 +35,9 @@ impl fmt::Debug for Ed25519PrivateKey { } impl Ed25519PrivateKey { /// Extract ED25519 private and if available public key values from a PKCS#8 DER formatted key - fn extract_key_pair(input_key: &[u8]) -> Result<([u8; 32], Option<[u8; 32]>), rustls::Error> { - let mut private_key_raw: [u8; 32] = [0; ED25519_KEY_SIZE as usize]; + fn extract_key_pair(input_key: &[u8]) -> Result { + let mut private_key_raw: Zeroizing> = + Zeroizing::new(alloc::vec![0u8; ED25519_KEY_SIZE as usize]); let mut private_key_raw_len: word32 = private_key_raw.len() as word32; // Scratch buffer for the optional embedded public key. DecodeAsymKey // reports it either as the bare 32-byte key, or as the raw DER BIT STRING @@ -126,7 +130,9 @@ impl TryFrom<&PrivateKeyDer<'_>> for Ed25519PrivateKey { let ed25519_key_object = ED25519KeyObject::new(&mut ed25519_c_type); // This function initiliazes an ed25519_key object for // using it to sign a message. - ed25519_key_object.init(); + ed25519_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ed25519_init failed".into()))?; ret = unsafe { wc_ed25519_import_private_only( @@ -150,7 +156,7 @@ impl TryFrom<&PrivateKeyDer<'_>> for Ed25519PrivateKey { } Ok(Self { - priv_key: Arc::new(Zeroizing::new(priv_key_raw.to_vec())), + priv_key: Arc::new(priv_key_raw), pub_key: Arc::new(match pub_option { Some(pub_value) => pub_value.to_vec(), None => pub_key_raw.to_vec(), @@ -212,7 +218,9 @@ impl Signer for Ed25519Signer { let mut ed25519_c_type: ed25519_key = unsafe { mem::zeroed() }; let ed25519_key_object = ED25519KeyObject::new(&mut ed25519_c_type); - ed25519_key_object.init(); + ed25519_key_object + .init() + .map_err(|_| rustls::Error::General("wc_ed25519_init failed".into()))?; ret = unsafe { wc_ed25519_import_private_key( diff --git a/rustls-wolfcrypt-provider/src/sign/rsa.rs b/rustls-wolfcrypt-provider/src/sign/rsa.rs index 40dcfba..2135be1 100644 --- a/rustls-wolfcrypt-provider/src/sign/rsa.rs +++ b/rustls-wolfcrypt-provider/src/sign/rsa.rs @@ -1,6 +1,7 @@ use crate::error::*; use crate::types::*; use alloc::boxed::Box; +use alloc::format; use alloc::sync::Arc; use alloc::vec; use alloc::vec::Vec; @@ -105,9 +106,15 @@ impl TryFrom<&PrivateKeyDer<'_>> for RsaPrivateKey { type Error = rustls::Error; fn try_from(value: &PrivateKeyDer<'_>) -> Result { - let (der_bytes, format) = match value { - PrivateKeyDer::Pkcs8(der) => (der.secret_pkcs8_der().to_vec(), RsaKeyFormat::Pkcs8), - PrivateKeyDer::Pkcs1(der) => (der.secret_pkcs1_der().to_vec(), RsaKeyFormat::Pkcs1), + let (der_bytes, format): (Zeroizing>, RsaKeyFormat) = match value { + PrivateKeyDer::Pkcs8(der) => ( + Zeroizing::new(der.secret_pkcs8_der().to_vec()), + RsaKeyFormat::Pkcs8, + ), + PrivateKeyDer::Pkcs1(der) => ( + Zeroizing::new(der.secret_pkcs1_der().to_vec()), + RsaKeyFormat::Pkcs1, + ), _ => { return Err(rustls::Error::General( "Unsupported private key format".into(), @@ -119,7 +126,7 @@ impl TryFrom<&PrivateKeyDer<'_>> for RsaPrivateKey { let _rsa_key = import_rsa_key(&der_bytes, &format)?; Ok(Self { - der_bytes: Arc::new(Zeroizing::new(der_bytes)), + der_bytes: Arc::new(der_bytes), format, algo: SignatureAlgorithm::RSA, }) @@ -170,7 +177,9 @@ impl Signer for RsaSigner { // Prepare a random generator let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object = WCRngObject::new(&mut rng); - rng_object.init(); + rng_object + .init() + .map_err(|_| rustls::Error::General("wc_InitRng failed".into()))?; // Allocate enough space for the signature let mut sig_buf = [0u8; MAX_RSA_SIG_SIZE]; @@ -262,7 +271,6 @@ impl Signer for RsaSigner { let mut sig_len: u32 = sig_buf.len() as u32; // wc_SignatureGenerate will produce a PKCS#1 signature, including hashing. - let deref_rsa_key_c_type = unsafe { *(rsa_key.as_ptr()) }; let ret = unsafe { wc_SignatureGenerate( hash_ty, @@ -272,7 +280,7 @@ impl Signer for RsaSigner { sig_buf.as_mut_ptr(), &mut sig_len, rsa_key.as_ptr() as *mut core::ffi::c_void, - mem::size_of_val(&deref_rsa_key_c_type).try_into().unwrap(), + mem::size_of::().try_into().unwrap(), rng_object.as_ptr(), ) }; @@ -284,9 +292,12 @@ impl Signer for RsaSigner { wc_SignatureGetSize( wc_SignatureType_WC_SIGNATURE_TYPE_RSA_W_ENC, rsa_key.as_ptr() as *const core::ffi::c_void, - mem::size_of_val(&deref_rsa_key_c_type).try_into().unwrap(), + mem::size_of::().try_into().unwrap(), ) }; + check_if_greater_than_zero(actual_sig_size).map_err(|_| { + rustls::Error::General(format!("wc_SignatureGetSize failed: {actual_sig_size}")) + })?; let mut sig_vec = sig_buf.to_vec(); // Truncate to the size returned by wc_SignatureGetSize or the updated `sig_len`. diff --git a/rustls-wolfcrypt-provider/src/types/mod.rs b/rustls-wolfcrypt-provider/src/types/mod.rs index 9768742..a9ca9a6 100644 --- a/rustls-wolfcrypt-provider/src/types/mod.rs +++ b/rustls-wolfcrypt-provider/src/types/mod.rs @@ -40,11 +40,10 @@ macro_rules! define_foreign_type { } /// Given an $init_function, it calls it with the object's ptr as argument. - pub fn init(&self) { - unsafe { - check_if_zero($init_function(self.as_ptr())) - .expect(concat!(stringify!($init_function), " failed")) - } + /// Returns the result so callers can propagate an init failure instead of + /// panicking (e.g. so a failed wc_InitRng surfaces as a recoverable error). + pub fn init(&self) -> WCResult { + unsafe { check_if_zero($init_function(self.as_ptr())) } } } }; @@ -84,33 +83,8 @@ macro_rules! define_foreign_type { }; } -macro_rules! define_foreign_type_with_copy { - ($struct_name:ident, $ref_name:ident, $c_type:ty) => { - pub struct $ref_name(Opaque); - unsafe impl ForeignTypeRef for $ref_name { - type CType = $c_type; - } - - #[derive(Debug, Clone, Copy)] - pub struct $struct_name(NonNull<$c_type>); - unsafe impl Sync for $struct_name {} - unsafe impl Send for $struct_name {} - unsafe impl ForeignType for $struct_name { - type CType = $c_type; - type Ref = $ref_name; - - unsafe fn from_ptr(ptr: *mut Self::CType) -> Self { - Self(NonNull::new_unchecked(ptr)) - } - - fn as_ptr(&self) -> *mut Self::CType { - self.0.as_ptr() - } - } - }; -} - -/// Like define_foreign_type_with_copy but without Copy (needed when Drop is implemented). +/// Defines a foreign type without Copy (needed when Drop is implemented, so the +/// resource is freed exactly once). macro_rules! define_foreign_type_no_copy { ($struct_name:ident, $ref_name:ident, $c_type:ty) => { pub struct $ref_name(Opaque); @@ -204,9 +178,14 @@ define_foreign_type!( ); define_foreign_type_no_copy!(RsaKeyObject, RsaKeyObjectRef, RsaKey, drop(wc_FreeRsaKey)); -define_foreign_type_with_copy!(HmacObject, HmacObjectRef, wolfcrypt_rs::Hmac); +define_foreign_type_no_copy!( + HmacObject, + HmacObjectRef, + wolfcrypt_rs::Hmac, + drop_void(wc_HmacFree) +); define_foreign_type_no_copy!(AesObject, AesObjectRef, Aes, drop_void(wc_AesFree)); -define_foreign_type_with_copy!(ChaChaObject, ChaChaObjectRef, ChaCha); +define_foreign_type_no_copy!(ChaChaObject, ChaChaObjectRef, ChaCha); define_foreign_type_no_copy!( Sha256Object, Sha256ObjectRef, diff --git a/rustls-wolfcrypt-provider/src/verify.rs b/rustls-wolfcrypt-provider/src/verify.rs index 6cec2e8..0ab32b8 100644 --- a/rustls-wolfcrypt-provider/src/verify.rs +++ b/rustls-wolfcrypt-provider/src/verify.rs @@ -11,6 +11,7 @@ pub static ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { all: &[ RSA_PSS_SHA256, RSA_PSS_SHA384, + RSA_PSS_SHA512, RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512, @@ -18,8 +19,6 @@ pub static ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { ECDSA_P384_SHA384, ECDSA_P521_SHA512, ED25519, - RSA_PSS_SHA512, - RSA_PKCS1_SHA512, ], mapping: &[ (SignatureScheme::RSA_PSS_SHA256, &[RSA_PSS_SHA256]), diff --git a/rustls-wolfcrypt-provider/src/verify/ecdsa.rs b/rustls-wolfcrypt-provider/src/verify/ecdsa.rs index 416431f..a0d861d 100644 --- a/rustls-wolfcrypt-provider/src/verify/ecdsa.rs +++ b/rustls-wolfcrypt-provider/src/verify/ecdsa.rs @@ -62,7 +62,7 @@ impl SignatureVerificationAlgorithm for EcdsaVerifier { // Initialize WolfSSL ECC key let mut ecc_c_type: ecc_key = mem::zeroed(); let ecc_key_object = ECCKeyObject::from_ptr(&mut ecc_c_type); - ecc_key_object.init(); + ecc_key_object.init().map_err(|_| InvalidSignature)?; let mut ret; let mut stat: i32 = 0; @@ -113,6 +113,10 @@ impl SignatureVerificationAlgorithm for EcdsaVerifier { // The returned size is used to make sure the output buffer is large enough. let digest_sz = wc_HashGetDigestSize(wc_hash_type); + if digest_sz <= 0 { + return Err(InvalidSignature); + } + // This function performs a hash on the provided data buffer and // returns it in the hash buffer provided. // We hash the message since it's not pre-hashed. diff --git a/rustls-wolfcrypt-provider/src/verify/eddsa.rs b/rustls-wolfcrypt-provider/src/verify/eddsa.rs index 015392d..5728f81 100644 --- a/rustls-wolfcrypt-provider/src/verify/eddsa.rs +++ b/rustls-wolfcrypt-provider/src/verify/eddsa.rs @@ -28,7 +28,7 @@ impl SignatureVerificationAlgorithm for Ed25519 { let ed25519_key_object = ED25519KeyObject::from_ptr(&mut ed25519_c_type); let mut stat: i32 = 0; - ed25519_key_object.init(); + ed25519_key_object.init().map_err(|_| InvalidSignature)?; let mut ret = wc_ed25519_import_public( public_key.as_ptr(), diff --git a/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs b/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs index da5d770..dd73fe7 100644 --- a/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs +++ b/rustls-wolfcrypt-provider/src/verify/rsapkcs1.rs @@ -47,8 +47,6 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha256Verify { }; check_if_zero(ret).map_err(|_| InvalidSignature)?; - let derefenced_rsa_key_c_type = unsafe { *(rsa_key_object.as_ptr()) }; - // Verify the message signed with RSA-PSS. // In this case 'message' has been, supposedly, // been signed by 'signature'. @@ -63,9 +61,7 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha256Verify { signature.as_ptr(), signature.len() as word32, rsa_key_object.as_ptr() as *mut c_void, - mem::size_of_val(&derefenced_rsa_key_c_type) - .try_into() - .unwrap(), + mem::size_of::().try_into().unwrap(), ) }; @@ -113,8 +109,6 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha384Verify { }; check_if_zero(ret).map_err(|_| InvalidSignature)?; - let dereferenced_rsa_key_c_type = unsafe { *(rsa_key_object.as_ptr()) }; - // Verify the message signed with RSA-PSS. // In this case 'message' has been, supposedly, // been signed by 'signature'. @@ -129,9 +123,7 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha384Verify { signature.as_ptr(), signature.len() as word32, rsa_key_object.as_ptr() as *mut c_void, - mem::size_of_val(&dereferenced_rsa_key_c_type) - .try_into() - .unwrap(), + mem::size_of::().try_into().unwrap(), ) }; @@ -179,8 +171,6 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha512Verify { }; check_if_zero(ret).map_err(|_| InvalidSignature)?; - let dereferenced_rsa_key_c_type = unsafe { *(rsa_key_object.as_ptr()) }; - // Verify the message signed with RSA-PSS. // In this case 'message' has been, supposedly, // been signed by 'signature'. @@ -195,9 +185,7 @@ impl SignatureVerificationAlgorithm for RsaPkcs1Sha512Verify { signature.as_ptr(), signature.len() as word32, rsa_key_object.as_ptr() as *mut c_void, - mem::size_of_val(&dereferenced_rsa_key_c_type) - .try_into() - .unwrap(), + mem::size_of::().try_into().unwrap(), ) }; diff --git a/rustls-wolfcrypt-provider/src/verify/rsapss.rs b/rustls-wolfcrypt-provider/src/verify/rsapss.rs index 29ac311..21513f0 100644 --- a/rustls-wolfcrypt-provider/src/verify/rsapss.rs +++ b/rustls-wolfcrypt-provider/src/verify/rsapss.rs @@ -43,6 +43,10 @@ impl SignatureVerificationAlgorithm for RsaPssSha256Verify { // provided to wc_Hash is large enough. let digest_sz = unsafe { wc_HashGetDigestSize(wc_HashType_WC_HASH_TYPE_SHA256) }; + if digest_sz <= 0 { + return Err(InvalidSignature); + } + // This function performs a hash on the provided data buffer and // returns it in the hash buffer provided. // In this case we hash with Sha256 (RSA_PSS_SHA256). @@ -126,6 +130,10 @@ impl SignatureVerificationAlgorithm for RsaPssSha384Verify { // provided to wc_Hash is large enough. let digest_sz = unsafe { wc_HashGetDigestSize(wc_HashType_WC_HASH_TYPE_SHA384) }; + if digest_sz <= 0 { + return Err(InvalidSignature); + } + // This function performs a hash on the provided data buffer and // returns it in the hash buffer provided. // In this case we hash with Sha384 (RSA_PSS_SHA384). @@ -209,6 +217,10 @@ impl SignatureVerificationAlgorithm for RsaPssSha512Verify { // provided to wc_Hash is large enough. let digest_sz = unsafe { wc_HashGetDigestSize(wc_HashType_WC_HASH_TYPE_SHA512) }; + if digest_sz <= 0 { + return Err(InvalidSignature); + } + // This function performs a hash on the provided data buffer and // returns it in the hash buffer provided. // In this case we hash with Sha512 (RSA_PSS_SHA512). diff --git a/rustls-wolfcrypt-provider/tests/e2e.rs b/rustls-wolfcrypt-provider/tests/e2e.rs index 6044fa9..a054bc7 100644 --- a/rustls-wolfcrypt-provider/tests/e2e.rs +++ b/rustls-wolfcrypt-provider/tests/e2e.rs @@ -430,10 +430,10 @@ mod tests { // Initialize RNG and ECC key objects let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object: WCRngObject = WCRngObject::new(&mut rng); - rng_object.init(); + rng_object.init().unwrap(); let mut ecc_key_c_type: ecc_key = unsafe { mem::zeroed() }; let key_object = ECCKeyObject::new(&mut ecc_key_c_type); - key_object.init(); + key_object.init().unwrap(); let mut pub_key_raw = ECCPubKey { qx: vec![0; key_size], @@ -514,11 +514,11 @@ mod tests { // Initialize RNG and ECC key objects let mut rng: WC_RNG = unsafe { mem::zeroed() }; let rng_object: WCRngObject = WCRngObject::new(&mut rng); - rng_object.init(); + rng_object.init().unwrap(); let mut key_c_type: ed25519_key = unsafe { mem::zeroed() }; let key_object = ED25519KeyObject::new(&mut key_c_type); - key_object.init(); + key_object.init().unwrap(); let mut der_ed25519_key: Vec = vec![0; 200]; // Adjust size if needed let mut pub_key_raw: [u8; 32] = [0; 32]; @@ -618,7 +618,7 @@ mod tests { let mut rng_c_type: WC_RNG = unsafe { mem::zeroed() }; let rng_object = WCRngObject::new(&mut rng_c_type); - rng_object.init(); + rng_object.init().unwrap(); unsafe { wc_RsaSetRNG(rsa_key_object.as_ptr(), rng_object.as_ptr()) }; @@ -709,7 +709,7 @@ mod tests { let mut rng_c_type: WC_RNG = unsafe { mem::zeroed() }; let rng_object = WCRngObject::new(&mut rng_c_type); - rng_object.init(); + rng_object.init().unwrap(); unsafe { wc_RsaSetRNG(rsa_key_object.as_ptr(), rng_object.as_ptr()) };