Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for SSL_group_to_name and SSL_get_negotiated_group #2201

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions openssl-sys/src/handwritten/ssl.rs
Expand Up @@ -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: *mut SSL, id: c_int) -> *const c_char;
}
6 changes: 6 additions & 0 deletions openssl-sys/src/ssl.rs
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
}
}

Expand Down
70 changes: 70 additions & 0 deletions openssl/src/nid.rs
Expand Up @@ -1096,6 +1096,76 @@ 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));
/// 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
///
/// 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<Nid> {
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<i32> {
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;
Expand Down
49 changes: 49 additions & 0 deletions openssl/src/ssl/mod.rs
Expand Up @@ -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;
Expand All @@ -67,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};
Expand Down Expand Up @@ -3484,6 +3488,51 @@ 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<NegotiatedGroup, ErrorStack> {
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
/// 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(&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)
.to_str()
.expect("Invalid UTF8 in input")),
romen marked this conversation as resolved.
Show resolved Hide resolved
Err(e) => Err(e),
}
}
}
}

/// An SSL stream midway through the handshake process.
Expand Down