Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ members = [
resolver = "2"

[workspace.package]
version = "5.0.0-alpha.2"
version = "5.0.0"
repository = "https://github.com/cloudflare/boring"
edition = "2021"

Expand All @@ -19,9 +19,9 @@ tag-prefix = ""
publish = false

[workspace.dependencies]
boring-sys = { version = "5.0.0-alpha.2", path = "./boring-sys" }
boring = { version = "5.0.0-alpha.2", path = "./boring" }
tokio-boring = { version = "5.0.0-alpha.2", path = "./tokio-boring" }
boring-sys = { version = ">=4.21.1, <6.0.0", path = "./boring-sys" } # Cargo doesn't like prerelease ranges. ok as of "5.0.0"
boring = { version = "5.0.0", path = "./boring" }
tokio-boring = { version = "5.0.0", path = "./tokio-boring" }

bindgen = { version = "0.72.0", default-features = false, features = ["runtime"] }
bitflags = "2.9"
Expand Down
5 changes: 4 additions & 1 deletion boring/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ features = ["rpk", "underscore-wildcards"]
rustdoc-args = ["--cfg", "docsrs"]

[features]
# Enable SslCredential (requires boring-sys v5)
credential = []

# Controlling the build

# Use a FIPS-validated version of BoringSSL.
Expand All @@ -30,7 +33,7 @@ legacy-compat-deprecated = []
# default branch of boringSSL. Alternatively, a version of boringSSL that
# implements the same feature set can be provided by setting
# `BORING_BSSL{,_FIPS}_SOURCE_PATH` and `BORING_BSSL{,_FIPS}_ASSUME_PATCHED`.
rpk = ["boring-sys/rpk"]
rpk = ["credential", "boring-sys/rpk"]

# Applies a patch to enable `ffi::X509_CHECK_FLAG_UNDERSCORE_WILDCARDS`. This
# feature is necessary in order to compile the bindings for the default branch
Expand Down
3 changes: 3 additions & 0 deletions boring/src/ssl/async_callbacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use super::{
Ssl, SslAlert, SslContextBuilder, SslRef, SslSession, SslSignatureAlgorithm, SslVerifyError,
SslVerifyMode,
};
#[cfg(feature = "credential")]
use crate::error::ErrorStack;
use crate::ex_data::Index;
#[cfg(feature = "credential")]
use crate::ssl::SslCredentialBuilder;
use std::convert::identity;
use std::future::Future;
Expand Down Expand Up @@ -173,6 +175,7 @@ impl SslContextBuilder {
}
}

#[cfg(feature = "credential")]
impl SslCredentialBuilder {
/// Configures a custom private key method on the context.
///
Expand Down
211 changes: 211 additions & 0 deletions boring/src/ssl/credential.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#[cfg(feature = "rpk")]
use crate::cvt_p;
use crate::error::ErrorStack;
use crate::ex_data::Index;
use crate::pkey::{PKeyRef, Private};
use crate::ssl::callbacks;
use crate::ssl::PrivateKeyMethod;
use crate::{cvt_0i, cvt_n};
use crate::{ffi, free_data_box};
use foreign_types::{ForeignType, ForeignTypeRef};
use openssl_macros::corresponds;
use std::any::TypeId;
use std::collections::HashMap;
use std::ffi::{c_int, c_void};
use std::mem;
use std::ptr;
use std::sync::{LazyLock, Mutex};

static SSL_CREDENTIAL_INDEXES: LazyLock<Mutex<HashMap<TypeId, c_int>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));

foreign_type_and_impl_send_sync! {
type CType = ffi::SSL_CREDENTIAL;
fn drop = ffi::SSL_CREDENTIAL_free;

/// A credential.
pub struct SslCredential;
}

impl SslCredential {
/// Create a credential suitable for a handshake using a raw public key.
#[corresponds(SSL_CREDENTIAL_new_raw_public_key)]
#[cfg(feature = "rpk")]
pub fn new_raw_public_key() -> Result<SslCredentialBuilder, ErrorStack> {
unsafe {
Ok(SslCredentialBuilder(Self::from_ptr(cvt_p(
ffi::SSL_CREDENTIAL_new_raw_public_key(),
)?)))
}
}

/// Returns a new extra data index.
///
/// Each invocation of this function is guaranteed to return a distinct index. These can be used
/// to store data in the context that can be retrieved later by callbacks, for example.
#[corresponds(SSL_C_get_ex_new_index)]
pub fn new_ex_index<T>() -> Result<Index<Self, T>, ErrorStack>
where
T: 'static + Sync + Send,
{
unsafe {
ffi::init();
let idx = cvt_n(get_new_ssl_credential_idx(Some(free_data_box::<T>)))?;
Ok(Index::from_raw(idx))
}
}

// FIXME should return a result?
pub(crate) fn cached_ex_index<T>() -> Index<Self, T>
where
T: 'static + Sync + Send,
{
unsafe {
let idx = *SSL_CREDENTIAL_INDEXES
.lock()
.unwrap_or_else(|e| e.into_inner())
.entry(TypeId::of::<T>())
.or_insert_with(|| Self::new_ex_index::<T>().unwrap().as_raw());
Index::from_raw(idx)
}
}
}

impl SslCredentialRef {
/// Returns a reference to the extra data at the specified index.
#[corresponds(SSL_CREDENTIAL_get_ex_data)]
#[must_use]
pub fn ex_data<T>(&self, index: Index<SslCredential, T>) -> Option<&T> {
unsafe {
let data = ffi::SSL_CREDENTIAL_get_ex_data(self.as_ptr(), index.as_raw());
if data.is_null() {
None
} else {
Some(&*(data as *const T))
}
}
}

// Unsafe because SSL contexts are not guaranteed to be unique, we call
// this only from SslCredentialBuilder.
#[corresponds(SSL_CREDENTIAL_get_ex_data)]
pub(crate) unsafe fn ex_data_mut<T>(
&mut self,
index: Index<SslCredential, T>,
) -> Option<&mut T> {
let data = ffi::SSL_CREDENTIAL_get_ex_data(self.as_ptr(), index.as_raw());
if data.is_null() {
None
} else {
Some(&mut *(data as *mut T))
}
}

// Unsafe because SSL contexts are not guaranteed to be unique, we call
// this only from SslCredentialBuilder.
#[corresponds(SSL_CREDENTIAL_set_ex_data)]
pub(crate) unsafe fn replace_ex_data<T>(
&mut self,
index: Index<SslCredential, T>,
data: T,
) -> Option<T> {
if let Some(old) = self.ex_data_mut(index) {
return Some(mem::replace(old, data));
}

unsafe {
let data = Box::into_raw(Box::new(data)) as *mut c_void;
ffi::SSL_CREDENTIAL_set_ex_data(self.as_ptr(), index.as_raw(), data);
}

None
}
}

/// A builder for [`SslCredential`]
pub struct SslCredentialBuilder(SslCredential);

impl SslCredentialBuilder {
/// Sets or overwrites the extra data at the specified index.
///
/// This can be used to provide data to callbacks registered with the context. Use the
/// `SslCredential::new_ex_index` method to create an `Index`.
///
/// Any previous value will be returned and replaced by the new one.
#[corresponds(SSL_CREDENTIAL_set_ex_data)]
pub fn replace_ex_data<T>(&mut self, index: Index<SslCredential, T>, data: T) -> Option<T> {
unsafe { self.0.replace_ex_data(index, data) }
}

// Sets the private key of the credential.
#[corresponds(SSL_CREDENTIAL_set1_private_key)]
pub fn set_private_key(&mut self, private_key: &PKeyRef<Private>) -> Result<(), ErrorStack> {
unsafe {
cvt_0i(ffi::SSL_CREDENTIAL_set1_private_key(
self.0.as_ptr(),
private_key.as_ptr(),
))
.map(|_| ())
}
}

/// Configures a custom private key method on the credential.
///
/// See [`PrivateKeyMethod`] for more details.
#[corresponds(SSL_CREDENTIAL_set_private_key_method)]
pub fn set_private_key_method<M>(&mut self, method: M) -> Result<(), ErrorStack>
where
M: PrivateKeyMethod,
{
unsafe {
self.replace_ex_data(SslCredential::cached_ex_index::<M>(), method);

cvt_0i(ffi::SSL_CREDENTIAL_set_private_key_method(
self.0.as_ptr(),
&ffi::SSL_PRIVATE_KEY_METHOD {
sign: Some(callbacks::raw_sign::<M>),
decrypt: Some(callbacks::raw_decrypt::<M>),
complete: Some(callbacks::raw_complete::<M>),
},
))
.map(|_| ())
}
}

// Sets the SPKI of the raw public key credential.
//
// If `spki` is `None`, the SPKI is extracted from the credential's private key.
#[corresponds(SSL_CREDENTIAL_set1_spki)]
#[cfg(feature = "rpk")]
pub fn set_spki_bytes(&mut self, spki: Option<&[u8]>) -> Result<(), ErrorStack> {
unsafe {
let spki = spki
.map(|spki| {
cvt_p(ffi::CRYPTO_BUFFER_new(
spki.as_ptr(),
spki.len(),
ptr::null_mut(),
))
})
.transpose()?
.unwrap_or(ptr::null_mut());

let ret = cvt_0i(ffi::SSL_CREDENTIAL_set1_spki(self.0.as_ptr(), spki)).map(|_| ());

if !spki.is_null() {
ffi::CRYPTO_BUFFER_free(spki);
}

ret
}
}

#[must_use]
pub fn build(self) -> SslCredential {
self.0
}
}

unsafe fn get_new_ssl_credential_idx(f: ffi::CRYPTO_EX_free) -> c_int {
ffi::SSL_CREDENTIAL_get_ex_new_index(0, ptr::null_mut(), ptr::null_mut(), None, f)
}
Loading
Loading