From f79c0373867d158f1e106967d6dc8188b636177d Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Thu, 7 Mar 2024 21:42:47 +0200 Subject: [PATCH 1/7] Add support for SSL_group_to_name and SSL_get_negotiated_group --- openssl-sys/src/handwritten/ssl.rs | 5 ++++ openssl-sys/src/ssl.rs | 6 +++++ openssl/src/ssl/mod.rs | 43 +++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/openssl-sys/src/handwritten/ssl.rs b/openssl-sys/src/handwritten/ssl.rs index cdcdea5881..ea77cc50f4 100644 --- a/openssl-sys/src/handwritten/ssl.rs +++ b/openssl-sys/src/handwritten/ssl.rs @@ -951,3 +951,8 @@ extern "C" { #[cfg(any(ossl110, libressl360))] pub fn SSL_get_security_level(s: *const SSL) -> c_int; } + +extern "C" { + #[cfg(ossl300)] + pub fn SSL_group_to_name(ssl: *const SSL, id: c_int) -> *const c_char; +} diff --git a/openssl-sys/src/ssl.rs b/openssl-sys/src/ssl.rs index 52ea5b2135..e3c2abf682 100644 --- a/openssl-sys/src/ssl.rs +++ b/openssl-sys/src/ssl.rs @@ -363,6 +363,8 @@ pub const SSL_CTRL_GET_MIN_PROTO_VERSION: c_int = 130; pub const SSL_CTRL_GET_MAX_PROTO_VERSION: c_int = 131; #[cfg(ossl300)] pub const SSL_CTRL_GET_TMP_KEY: c_int = 133; +#[cfg(ossl300)] +pub const SSL_CTRL_GET_NEGOTIATED_GROUP: c_int = 134; pub unsafe fn SSL_CTX_set_tmp_dh(ctx: *mut SSL_CTX, dh: *mut DH) -> c_long { SSL_CTX_ctrl(ctx, SSL_CTRL_SET_TMP_DH, 0, dh as *mut c_void) @@ -519,6 +521,10 @@ cfg_if! { pub unsafe fn SSL_get_tmp_key(ssl: *mut SSL, key: *mut *mut EVP_PKEY) -> c_long { SSL_ctrl(ssl, SSL_CTRL_GET_TMP_KEY, 0, key as *mut c_void) } + + pub unsafe fn SSL_get_negotiated_group(ssl: *mut SSL) -> c_int { + SSL_ctrl(ssl, SSL_CTRL_GET_NEGOTIATED_GROUP, 0, ptr::null_mut()) as c_int + } } } diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 2ff9dac1fd..7baf6f02b9 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -82,7 +82,7 @@ use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef}; #[cfg(any(ossl102, boringssl, libressl261))] use crate::x509::verify::X509VerifyParamRef; use crate::x509::{X509Name, X509Ref, X509StoreContextRef, X509VerifyResult, X509}; -use crate::{cvt, cvt_n, cvt_p, init}; +use crate::{cvt, cvt_n, cvt_p, cvt_p_const, init}; use bitflags::bitflags; use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; @@ -3484,6 +3484,47 @@ impl SslRef { } } } + + /// Returns the NID of the negotiated group used for the handshake key + /// exchange process. + /// For TLSv1.3 connections this typically reflects the state of the + /// current connection, though in the case of PSK-only resumption, the + /// returned value will be from a previous connection. + /// For earlier TLS versions, when a session has been resumed, it always + /// reflects the group used for key exchange during the initial handshake + /// (otherwise it is from the current, non-resumption, connection). + /// This can be called by either client or server. + /// If the NID for the shared group is unknown then the value is set to the + /// bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the group. + #[corresponds(SSL_get_negotiated_group)] + #[cfg(ossl300)] + pub fn negotiated_group(&self) -> Result { + unsafe { cvt(ffi::SSL_get_negotiated_group(self.as_ptr())) } + } + + /// Return retrieve the TLS group name associated with a given TLS + /// group ID, as registered via built-in or external providers and as + /// returned by a call to SSL_get1_groups() or SSL_get_shared_group(). + /// + /// If non-NULL, SSL_group_to_name() returns the TLS group name + /// corresponding to the given id as a NUL-terminated string. + /// If SSL_group_to_name() returns NULL, an error occurred; possibly no + /// corresponding tlsname was registered during provider initialisation. + /// + /// Note that the return value is valid only during the lifetime of the + /// SSL object ssl. + #[corresponds(SSL_group_to_name)] + #[cfg(ossl300)] + pub fn group_to_name<'s>(&'s self, id: c_int) -> Result<&'s str, ErrorStack> { + unsafe { + match cvt_p_const(ffi::SSL_group_to_name(self.as_ptr(), id)) { + Ok(constp) => Ok(CStr::from_ptr(constp) + .to_str() + .expect("Invalid UTF8 in input")), + Err(e) => Err(e), + } + } + } } /// An SSL stream midway through the handshake process. From a988bdf1c5376c943a31fe5000174a8ca3fba8e5 Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Fri, 8 Mar 2024 14:36:49 +0200 Subject: [PATCH 2/7] fixup! Add support for SSL_group_to_name and SSL_get_negotiated_group There is a mismatch upstream in OpenSSL between documentation and header file regarding the `const` for the SSL argument. See https://github.com/sfackler/rust-openssl/pull/2201#discussion_r1516756496 --- openssl-sys/src/handwritten/ssl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openssl-sys/src/handwritten/ssl.rs b/openssl-sys/src/handwritten/ssl.rs index ea77cc50f4..eab821955b 100644 --- a/openssl-sys/src/handwritten/ssl.rs +++ b/openssl-sys/src/handwritten/ssl.rs @@ -954,5 +954,5 @@ extern "C" { extern "C" { #[cfg(ossl300)] - pub fn SSL_group_to_name(ssl: *const SSL, id: c_int) -> *const c_char; + pub fn SSL_group_to_name(ssl: *mut SSL, id: c_int) -> *const c_char; } From 22c085c64aa5c20853c9ea7f6afe1048f57af280 Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Fri, 8 Mar 2024 14:39:52 +0200 Subject: [PATCH 3/7] fixup! Add support for SSL_group_to_name and SSL_get_negotiated_group Clippy suggests removing superfluous lifetime annotations --- openssl/src/ssl/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 7baf6f02b9..295a539731 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -3515,7 +3515,7 @@ impl SslRef { /// SSL object ssl. #[corresponds(SSL_group_to_name)] #[cfg(ossl300)] - pub fn group_to_name<'s>(&'s self, id: c_int) -> Result<&'s str, ErrorStack> { + pub fn group_to_name(&self, id: c_int) -> Result<&str, ErrorStack> { unsafe { match cvt_p_const(ffi::SSL_group_to_name(self.as_ptr(), id)) { Ok(constp) => Ok(CStr::from_ptr(constp) From 0d838a5243a2e71157147771e421ad52de8e00a6 Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Fri, 8 Mar 2024 14:45:50 +0200 Subject: [PATCH 4/7] fixup! Add support for SSL_group_to_name and SSL_get_negotiated_group --- openssl/src/ssl/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 295a539731..db2aaaf41c 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -59,6 +59,8 @@ //! ``` #[cfg(ossl300)] use crate::cvt_long; +#[cfg(ossl300)] +use crate::cvt_p_const; use crate::dh::{Dh, DhRef}; #[cfg(all(ossl101, not(ossl110)))] use crate::ec::EcKey; @@ -82,7 +84,7 @@ use crate::x509::store::{X509Store, X509StoreBuilderRef, X509StoreRef}; #[cfg(any(ossl102, boringssl, libressl261))] use crate::x509::verify::X509VerifyParamRef; use crate::x509::{X509Name, X509Ref, X509StoreContextRef, X509VerifyResult, X509}; -use crate::{cvt, cvt_n, cvt_p, cvt_p_const, init}; +use crate::{cvt, cvt_n, cvt_p, init}; use bitflags::bitflags; use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; From 3f4308f2036434d626e46066cc2048d806cc0d3b Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Fri, 8 Mar 2024 14:52:55 +0200 Subject: [PATCH 5/7] fixup! Add support for SSL_group_to_name and SSL_get_negotiated_group Minor documentation fixes --- openssl/src/ssl/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index db2aaaf41c..645f60fca8 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -3496,6 +3496,7 @@ impl SslRef { /// reflects the group used for key exchange during the initial handshake /// (otherwise it is from the current, non-resumption, connection). /// This can be called by either client or server. + /// /// If the NID for the shared group is unknown then the value is set to the /// bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the group. #[corresponds(SSL_get_negotiated_group)] @@ -3504,9 +3505,9 @@ impl SslRef { unsafe { cvt(ffi::SSL_get_negotiated_group(self.as_ptr())) } } - /// Return retrieve the TLS group name associated with a given TLS - /// group ID, as registered via built-in or external providers and as - /// returned by a call to SSL_get1_groups() or SSL_get_shared_group(). + /// Return the TLS group name associated with a given TLS group ID, as + /// registered via built-in or external providers and as returned by a call + /// to SSL_get1_groups() or SSL_get_shared_group(). /// /// If non-NULL, SSL_group_to_name() returns the TLS group name /// corresponding to the given id as a NUL-terminated string. From 893ce6a796a0c16b05578b996289e49d8f008f6d Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Wed, 13 Mar 2024 21:58:54 +0200 Subject: [PATCH 6/7] Add a NegotiatedGroup wrapper struct --- openssl/src/nid.rs | 64 ++++++++++++++++++++++++++++++++++++++++++ openssl/src/ssl/mod.rs | 9 ++++-- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/openssl/src/nid.rs b/openssl/src/nid.rs index e50feb0683..3cfb6ea6fc 100644 --- a/openssl/src/nid.rs +++ b/openssl/src/nid.rs @@ -1096,6 +1096,70 @@ impl Nid { pub const CHACHA20_POLY1305: Nid = Nid(ffi::NID_chacha20_poly1305); } +/// An abstract type wrapping a numerical identifier for a negotiated group. +/// +/// This is most similar to a `Nid` (when it represents a known group) +/// but can also represent a specific "unknown shared group". +/// If the NID for the shared group is unknown then the value is set to the bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the group. +/// +/// # Examples +/// +/// To view the integer representation of a `NegotiatedGroup`: +/// +/// ``` +/// use openssl::nid::Nid; +/// use openssl::nid::NegotiatedGroup; +/// +/// assert!(Nid::AES_256_GCM.as_raw() == 901); +/// assert!(NegotiatedGroup::from_raw(901).nid() == Some(Nid::AES_256_GCM)); +/// ``` +/// +/// # External Documentation +/// +/// The following documentation provides context about returned numeric identifiers +/// for negotiated groups in OpenSSL. +/// +/// - [SSL_get_negotiated_group](https://www.openssl.org/docs/manmaster/man3/SSL_get_negotiated_group.html) +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[cfg(ossl300)] +pub struct NegotiatedGroup(c_int); + +#[cfg(ossl300)] +impl NegotiatedGroup { + #[allow(non_upper_case_globals)] + pub const TLSEXT_nid_unknown: i32 = 0x1000000; + + /// Create a `NegotiatedGroup` from an integer representation. + pub const fn from_raw(raw: c_int) -> Self { + Self(raw) + } + + /// Return the integer representation of a `NegotiatedGroup`. + #[allow(clippy::trivially_copy_pass_by_ref)] + pub const fn as_raw(&self) -> c_int { + self.0 + } + + /// Return a `Nid` for the known negotiated group or `None``. + pub fn nid(self) -> Option { + if (self.0 & Self::TLSEXT_nid_unknown) == 0 { + Some(Nid::from_raw(self.0)) + } else { + None + } + } + + /// Return a `i32` id for the unknown negotiated group or `None`. + pub fn unknown_group_id(self) -> Option { + if (self.0 & Self::TLSEXT_nid_unknown) != 0 { + let id = self.0 ^ Self::TLSEXT_nid_unknown; + Some(id) + } else { + None + } + } +} + #[cfg(test)] mod test { use super::Nid; diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index 645f60fca8..8e87fb2a59 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -69,6 +69,8 @@ use crate::error::ErrorStack; use crate::ex_data::Index; #[cfg(ossl111)] use crate::hash::MessageDigest; +#[cfg(ossl300)] +use crate::nid::NegotiatedGroup; #[cfg(any(ossl110, libressl270))] use crate::nid::Nid; use crate::pkey::{HasPrivate, PKeyRef, Params, Private}; @@ -3501,8 +3503,11 @@ impl SslRef { /// bitwise OR of TLSEXT_nid_unknown (0x1000000) and the id of the group. #[corresponds(SSL_get_negotiated_group)] #[cfg(ossl300)] - pub fn negotiated_group(&self) -> Result { - unsafe { cvt(ffi::SSL_get_negotiated_group(self.as_ptr())) } + pub fn negotiated_group(&self) -> Result { + use crate::nid::NegotiatedGroup; + + let raw = unsafe { cvt(ffi::SSL_get_negotiated_group(self.as_ptr())) }; + raw.map(NegotiatedGroup::from_raw) } /// Return the TLS group name associated with a given TLS group ID, as From 70017d1f50b1368211b27f1586ee7fdd9e5d1468 Mon Sep 17 00:00:00 2001 From: Nicola Tuveri Date: Thu, 14 Mar 2024 10:44:35 +0200 Subject: [PATCH 7/7] fixup! Add a NegotiatedGroup wrapper struct Expand example code --- openssl/src/nid.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openssl/src/nid.rs b/openssl/src/nid.rs index 3cfb6ea6fc..6fcc59b0e7 100644 --- a/openssl/src/nid.rs +++ b/openssl/src/nid.rs @@ -1112,6 +1112,12 @@ impl Nid { /// /// assert!(Nid::AES_256_GCM.as_raw() == 901); /// assert!(NegotiatedGroup::from_raw(901).nid() == Some(Nid::AES_256_GCM)); +/// assert!(NegotiatedGroup::from_raw(901).unknown_group_id() == None); +/// +/// let bogus_id: i32 = 0x6399; +/// let raw = bogus_id | NegotiatedGroup::TLSEXT_nid_unknown; +/// assert!(NegotiatedGroup::from_raw(raw).unknown_group_id() == Some(bogus_id)); +/// assert!(NegotiatedGroup::from_raw(raw).nid() == None); /// ``` /// /// # External Documentation