diff --git a/openssl/CHANGELOG.md b/openssl/CHANGELOG.md index e939d4784..bc314c2ed 100644 --- a/openssl/CHANGELOG.md +++ b/openssl/CHANGELOG.md @@ -2,6 +2,18 @@ ## [Unreleased] +## [v0.10.69] - 2025-01-25 + +### Fixed + +* Fixed the version constraint on `openssl-macros`. + +### Added + +* Added `SslContextBuilder::load_verify_locations`. +* Added `Hasher::squeeze_xof`. +* Added `SslContextBuilder::set_alpn_select_callback` support for boringssl. + ## [v0.10.68] - 2024-10-16 ### Fixed diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml index 7bd6bdbb4..43cef06d2 100644 --- a/openssl/Cargo.toml +++ b/openssl/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openssl" -version = "0.10.68" +version = "0.10.69" authors = ["Steven Fackler "] license = "Apache-2.0" description = "OpenSSL bindings" @@ -30,7 +30,7 @@ foreign-types = "0.3.1" libc = "0.2" once_cell = "1.5.2" -openssl-macros = { version = "0.1.0", path = "../openssl-macros" } +openssl-macros = { version = "0.1.1", path = "../openssl-macros" } ffi = { package = "openssl-sys", version = "0.9.104", path = "../openssl-sys" } [dev-dependencies] diff --git a/openssl/src/hash.rs b/openssl/src/hash.rs index b25eded94..f66c5ce01 100644 --- a/openssl/src/hash.rs +++ b/openssl/src/hash.rs @@ -194,6 +194,8 @@ unsafe impl Send for MessageDigest {} enum State { Reset, Updated, + #[cfg(ossl330)] + Squeeze, Finalized, } @@ -260,6 +262,8 @@ impl Hasher { Updated => { self.finish()?; } + #[cfg(ossl330)] + Squeeze => (), Finalized => (), } unsafe { @@ -274,6 +278,19 @@ impl Hasher { if self.state == Finalized { self.init()?; } + #[cfg(ossl330)] + if self.state == Squeeze { + // [`EVP_DigestUpdate`], depending on the implementation, may allow Updates after Squeezes. + // But, [FIPS 202], as shown in Figure 7, has a distinguished absorbing phase followed by a squeezing phase. + // Indeed, the [`sha3.c`] implmentation disallows Updates after Squeezes. + // For consistency, we always return an error when Update is called after Squeeze. + // + // [`EVP_DigestUpdate`]: https://github.com/openssl/openssl/blob/b3bb214720f20f3b126ae4b9c330e9a48b835415/crypto/evp/digest.c#L385-L393 + // [FIPS 202]: https://dx.doi.org/10.6028/NIST.FIPS.202 + // [`sha3.c`]: https://github.com/openssl/openssl/blob/b3bb214720f20f3b126ae4b9c330e9a48b835415/crypto/sha/sha3.c#L52-L63 + let errors = ErrorStack::get(); + return Err(errors); + } unsafe { cvt(ffi::EVP_DigestUpdate( self.ctx, @@ -285,6 +302,21 @@ impl Hasher { Ok(()) } + /// Squeezes buf out of the hasher. Can be called multiple times, unlike `finish_xof`. + /// The output will be as long as the buf. + #[cfg(ossl330)] + pub fn squeeze_xof(&mut self, buf: &mut [u8]) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::EVP_DigestSqueeze( + self.ctx, + buf.as_mut_ptr(), + buf.len(), + ))?; + self.state = Squeeze; + Ok(()) + } + } + /// Returns the hash of the data written and resets the non-XOF hasher. pub fn finish(&mut self) -> Result { if self.state == Finalized { @@ -481,6 +513,21 @@ mod tests { assert_eq!(buf, expected); } + /// Squeezes the expected length by doing two squeezes. + #[cfg(ossl330)] + fn hash_xof_squeeze_test(hashtype: MessageDigest, hashtest: &(&str, &str)) { + let data = Vec::from_hex(hashtest.0).unwrap(); + let mut h = Hasher::new(hashtype).unwrap(); + h.update(&data).unwrap(); + + let expected = Vec::from_hex(hashtest.1).unwrap(); + let mut buf = vec![0; expected.len()]; + assert!(expected.len() > 10); + h.squeeze_xof(&mut buf[..10]).unwrap(); + h.squeeze_xof(&mut buf[10..]).unwrap(); + assert_eq!(buf, expected); + } + fn hash_recycle_test(h: &mut Hasher, hashtest: &(&str, &str)) { h.write_all(&Vec::from_hex(hashtest.0).unwrap()).unwrap(); let res = h.finish().unwrap(); @@ -537,6 +584,40 @@ mod tests { assert_eq!(&*res, &*null); } + #[cfg(ossl330)] + #[test] + fn test_finish_then_squeeze() { + let digest = MessageDigest::shake_128(); + let mut h = Hasher::new(digest).unwrap(); + let mut buf = vec![0; digest.size()]; + h.finish_xof(&mut buf).unwrap(); + h.squeeze_xof(&mut buf) + .expect_err("squeezing after finalize should fail"); + } + + #[cfg(ossl330)] + #[test] + fn test_squeeze_then_update() { + let digest = MessageDigest::shake_128(); + let data = Vec::from_hex(MD5_TESTS[6].0).unwrap(); + let mut h = Hasher::new(digest).unwrap(); + let mut buf = vec![0; digest.size()]; + h.squeeze_xof(&mut buf).unwrap(); + h.update(&data) + .expect_err("updating after squeeze should fail"); + } + + #[cfg(ossl330)] + #[test] + fn test_squeeze_then_finalize() { + let digest = MessageDigest::shake_128(); + let mut h = Hasher::new(digest).unwrap(); + let mut buf = vec![0; digest.size()]; + h.squeeze_xof(&mut buf).unwrap(); + h.finish_xof(&mut buf) + .expect_err("finalize after squeeze should fail"); + } + #[test] #[allow(clippy::redundant_clone)] fn test_clone() { @@ -710,6 +791,8 @@ mod tests { for test in tests.iter() { hash_xof_test(MessageDigest::shake_128(), test); + #[cfg(ossl330)] + hash_xof_squeeze_test(MessageDigest::shake_128(), test); } assert_eq!(MessageDigest::shake_128().block_size(), 168); @@ -730,6 +813,8 @@ mod tests { for test in tests.iter() { hash_xof_test(MessageDigest::shake_256(), test); + #[cfg(ossl330)] + hash_xof_squeeze_test(MessageDigest::shake_256(), test); } assert_eq!(MessageDigest::shake_256().block_size(), 136); diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index add783048..f30f06973 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -1087,14 +1087,14 @@ mod test { #[cfg(ossl320)] fn ecdsa_deterministic_signature() { let private_key_pem = "-----BEGIN PRIVATE KEY----- -MDkCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQEEHzAdAgEBBBhvqwNJNOTA/Jrmf1tWWanX0f79GH7g -n9Q= +MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDJr6nYRbp1FmtcIVdnsdaTTlDD2zbo +mxJ7imIrEg9nIQ== -----END PRIVATE KEY-----"; let key1 = EcKey::private_key_from_pem(private_key_pem.as_bytes()).unwrap(); let key1 = PKey::from_ec_key(key1).unwrap(); let input = "sample"; - let expected_output = hex::decode("303502190098C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF021857A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64").unwrap(); + let expected_output = hex::decode("3044022061340C88C3AAEBEB4F6D667F672CA9759A6CCAA9FA8811313039EE4A35471D3202206D7F147DAC089441BB2E2FE8F7A3FA264B9C475098FDCF6E00D7C996E1B8B7EB").unwrap(); let hashed_input = hash(MessageDigest::sha1(), input.as_bytes()).unwrap(); let mut ctx = PkeyCtx::new(&key1).unwrap(); diff --git a/openssl/src/ssl/callbacks.rs b/openssl/src/ssl/callbacks.rs index ccf530850..f7e51a5d3 100644 --- a/openssl/src/ssl/callbacks.rs +++ b/openssl/src/ssl/callbacks.rs @@ -19,7 +19,7 @@ use crate::dh::Dh; use crate::ec::EcKey; use crate::error::ErrorStack; use crate::pkey::Params; -#[cfg(any(ossl102, libressl261))] +#[cfg(any(ossl102, libressl261, boringssl))] use crate::ssl::AlpnError; use crate::ssl::{ try_get_session_ctx_index, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, @@ -178,7 +178,7 @@ where } } -#[cfg(any(ossl102, libressl261))] +#[cfg(any(ossl102, libressl261, boringssl))] pub extern "C" fn raw_alpn_select( ssl: *mut ffi::SSL, out: *mut *const c_uchar, diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index d9b2a724f..c341642a2 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -602,17 +602,17 @@ impl SslAlert { /// An error returned from an ALPN selection callback. /// -/// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. -#[cfg(any(ossl102, libressl261))] +/// Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. +#[cfg(any(ossl102, libressl261, boringssl))] #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct AlpnError(c_int); -#[cfg(any(ossl102, libressl261))] +#[cfg(any(ossl102, libressl261, boringssl))] impl AlpnError { /// Terminate the handshake with a fatal alert. /// - /// Requires OpenSSL 1.1.0 or newer. - #[cfg(ossl110)] + /// Requires BoringSSL or OpenSSL 1.1.0 or newer. + #[cfg(any(ossl110, boringssl))] pub const ALERT_FATAL: AlpnError = AlpnError(ffi::SSL_TLSEXT_ERR_ALERT_FATAL); /// Do not select a protocol, but continue the handshake. @@ -924,12 +924,23 @@ impl SslContextBuilder { /// The file should contain a sequence of PEM-formatted CA certificates. #[corresponds(SSL_CTX_load_verify_locations)] pub fn set_ca_file>(&mut self, file: P) -> Result<(), ErrorStack> { - let file = CString::new(file.as_ref().as_os_str().to_str().unwrap()).unwrap(); + self.load_verify_locations(Some(file.as_ref()), None) + } + + /// Loads trusted root certificates from a file and/or a directory. + #[corresponds(SSL_CTX_load_verify_locations)] + pub fn load_verify_locations( + &mut self, + ca_file: Option<&Path>, + ca_path: Option<&Path>, + ) -> Result<(), ErrorStack> { + let ca_file = ca_file.map(|p| CString::new(p.as_os_str().to_str().unwrap()).unwrap()); + let ca_path = ca_path.map(|p| CString::new(p.as_os_str().to_str().unwrap()).unwrap()); unsafe { cvt(ffi::SSL_CTX_load_verify_locations( self.as_ptr(), - file.as_ptr() as *const _, - ptr::null(), + ca_file.as_ref().map_or(ptr::null(), |s| s.as_ptr()), + ca_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), )) .map(|_| ()) } @@ -1267,23 +1278,30 @@ impl SslContextBuilder { /// of those protocols on success. The [`select_next_proto`] function implements the standard /// protocol selection algorithm. /// - /// Requires OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. + /// Requires BoringSSL or OpenSSL 1.0.2 or LibreSSL 2.6.1 or newer. /// /// [`SslContextBuilder::set_alpn_protos`]: struct.SslContextBuilder.html#method.set_alpn_protos /// [`select_next_proto`]: fn.select_next_proto.html #[corresponds(SSL_CTX_set_alpn_select_cb)] - #[cfg(any(ossl102, libressl261))] + #[cfg(any(ossl102, libressl261, boringssl))] pub fn set_alpn_select_callback(&mut self, callback: F) where F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send, { unsafe { self.set_ex_data(SslContext::cached_ex_index::(), callback); + #[cfg(not(boringssl))] ffi::SSL_CTX_set_alpn_select_cb__fixed_rust( self.as_ptr(), Some(callbacks::raw_alpn_select::), ptr::null_mut(), ); + #[cfg(boringssl)] + ffi::SSL_CTX_set_alpn_select_cb( + self.as_ptr(), + Some(callbacks::raw_alpn_select::), + ptr::null_mut(), + ); } } diff --git a/openssl/src/ssl/test/mod.rs b/openssl/src/ssl/test/mod.rs index 2c5fd000a..282558f80 100644 --- a/openssl/src/ssl/test/mod.rs +++ b/openssl/src/ssl/test/mod.rs @@ -502,7 +502,7 @@ fn test_connect_with_srtp_ssl() { /// Tests that when the `SslStream` is created as a server stream, the protocols /// are correctly advertised to the client. #[test] -#[cfg(any(ossl102, libressl261))] +#[cfg(any(ossl102, libressl261, boringssl))] fn test_alpn_server_advertise_multiple() { let mut server = Server::builder(); server.ctx().set_alpn_select_callback(|_, client| { @@ -517,7 +517,7 @@ fn test_alpn_server_advertise_multiple() { } #[test] -#[cfg(ossl110)] +#[cfg(any(ossl110, boringssl))] fn test_alpn_server_select_none_fatal() { let mut server = Server::builder(); server.ctx().set_alpn_select_callback(|_, client| { @@ -533,7 +533,7 @@ fn test_alpn_server_select_none_fatal() { } #[test] -#[cfg(any(ossl102, libressl261))] +#[cfg(any(ossl102, libressl261, boringssl))] fn test_alpn_server_select_none() { static CALLED_BACK: AtomicBool = AtomicBool::new(false); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy