blob: 08baed7a8da00ae8d2586a4b003382ff1f0c2c92 [file]
// Copyright 2026 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use alloc::{
boxed::Box,
ffi::CString, //
};
use core::{
ffi::CStr,
ptr::null, //
};
use bssl_x509::{params::CertificateVerificationParams, store::X509Store};
use super::{
Client,
methods::HasTlsConnectionMethod, //
};
use crate::{
check_lib_error,
config::ConfigurationError,
connection::{
TlsConnectionBuilder,
lifecycle::{
EstablishedTlsConnection,
TlsConnectionInHandshake, //
}, //
}, //
credentials::{
CertificateVerificationMode,
SignatureAlgorithm,
TlsCredential,
VerifyCertificate,
cert_cb, //
},
errors::Error,
ffi::slice_into_ffi_raw_parts,
has_duplicates, //
};
impl<R, M> TlsConnectionBuilder<R, M>
where
M: HasTlsConnectionMethod,
{
/// Configure the certificate verification mode.
pub fn with_certificate_verification_mode(
&mut self,
mode: CertificateVerificationMode,
) -> &mut Self {
let ctx = self.ptr();
unsafe {
// Safety: this method only updates the mode value.
bssl_sys::SSL_set_verify(ctx, mode as _, None);
}
self
}
/// Configure the certificate verifier.
///
/// See [`VerifyCertificate`] for how to implement a custom verifier.
///
/// If raw public key authentication, per [RFC 7250], is configured,
/// the authentication through this mechanism will **fail** unless a certificate verifier
/// is configured.
///
/// [RFC 7250]: <https://datatracker.ietf.org/doc/html/rfc7250>
pub fn with_certificate_verifier<V>(
&mut self,
mode: CertificateVerificationMode,
verifier: V,
) -> &mut Self
where
V: VerifyCertificate + 'static,
{
let ctx = self.ptr();
unsafe {
// Safety: we only install our own vtable.
bssl_sys::SSL_set_custom_verify(
ctx,
mode as _,
Some(cert_cb::<super::methods::RustConnectionMethods<M>>),
);
}
self.get_connection_methods().verify_certificate_methods = Some(Box::new(verifier) as _);
self
}
/// Remove custom certificate verifier.
pub fn without_certificate_verifier(&mut self, mode: CertificateVerificationMode) -> &mut Self {
let ctx = self.ptr();
unsafe {
// Safety: we only uninstall the vtable.
bssl_sys::SSL_set_custom_verify(ctx, mode as _, None);
}
self.get_connection_methods().verify_certificate_methods = None;
self
}
}
/// # Custom certificate verification
impl<M> TlsConnectionInHandshake<'_, Client, M>
where
M: HasTlsConnectionMethod,
{
/// Get the certificate verification mode set by [`Self::set_certificate_verification_mode`].
pub fn get_certificate_verification_mode(&self) -> Option<CertificateVerificationMode> {
unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::SSL_get_verify_mode(self.ptr())
}
.try_into()
.ok()
}
}
/// # Authenticating with the peer
impl<M> TlsConnectionInHandshake<'_, Client, M>
where
M: HasTlsConnectionMethod,
{
/// Append `credential` to the list of credentials of this connection.
///
/// Earlier calls to this method appends a credential that is preferred over those added
/// in the later calls.
pub fn add_credential(&mut self, credential: &TlsCredential) -> Result<&mut Self, Error> {
check_lib_error!(unsafe {
// Safety: `credential` is still valid.
bssl_sys::SSL_add1_credential(self.ptr(), credential.ptr())
});
Ok(self)
}
/// Clear all credentials.
pub fn clear_credentials(&mut self) -> &mut Self {
unsafe {
// Safety: `credential` is still valid.
bssl_sys::SSL_certs_clear(self.ptr());
}
self
}
}
/// # Certificate verification
impl<M> TlsConnectionInHandshake<'_, Client, M> {
/// Enable signed certificate timestamps.
///
/// This method will instruct the client connections to request Signed Certificate Timestamps.
/// See [RFC 6962] for more information.
///
/// [RFC 6962]: <https://datatracker.ietf.org/doc/html/rfc6962>
pub fn enable_signed_certificate_timestamps(&mut self) -> &mut Self {
unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::SSL_enable_signed_cert_timestamps(self.ptr());
}
self
}
}
/// # Certificate verification
impl<M> TlsConnectionInHandshake<'_, Client, M> {
/// Set certificate verification store.
pub fn set_certificate_store(&mut self, store: &X509Store) -> &mut Self {
unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`.
// - when pending handshake, the assignment is always successful.
// - `SSL_set1_verify_cert_store` bumps the ref-count on the store.
bssl_sys::SSL_set1_verify_cert_store(self.ptr(), store.as_mut_ptr());
}
self
}
/// Set a preference list of signature algorithms.
///
/// This method returns [`ConfigurationError::InvalidParameters`] if the list of algorithms
/// contains duplicate entries.
pub fn set_certificate_verification_preferences(
&mut self,
algs: &[SignatureAlgorithm],
) -> Result<&mut Self, Error> {
let algs: &[u16] = unsafe {
// Safety: `SignatureAlgorithm` has a `repr(u16)` and maps to preferences correctly
// by construction.
core::mem::transmute(algs)
};
if has_duplicates(algs) {
return Err(Error::Configuration(ConfigurationError::InvalidParameters));
}
let (prefs, prefs_len) = slice_into_ffi_raw_parts(algs);
check_lib_error!(unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::SSL_set_verify_algorithm_prefs(self.ptr(), prefs, prefs_len)
});
Ok(self)
}
}
/// # Certificate verification.
impl<M> TlsConnectionInHandshake<'_, Client, M> {
/// Set host name.
pub fn set_host(&mut self, host_name: &str) -> Result<&mut Self, Error> {
let host_name = CString::new(host_name)
.map_err(|_| Error::Configuration(ConfigurationError::InvalidString))?;
check_lib_error!(unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`.
// - the host name string has been sanitised for internal NUL-bytes and NUL-terminated.
bssl_sys::SSL_set1_host(self.ptr(), host_name.as_ptr())
});
Ok(self)
}
}
/// # Certificate verification.
impl<R, M> TlsConnectionInHandshake<'_, R, M> {
/// Set depth of a potential certificate chain acceptable.
pub fn set_verify_depth(&mut self, depth: u16) -> Result<&mut Self, Error> {
let depth = depth
.try_into()
.map_err(|_| Error::Configuration(ConfigurationError::ValueOutOfRange))?;
unsafe {
// Safety: the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::SSL_set_verify_depth(self.ptr(), depth);
}
Ok(self)
}
/// Get certificate verification depth.
///
/// This method returns [`None`] if the depth is set but does not fit in a [`u16`].
pub fn get_verify_depth(&self) -> Option<u16> {
unsafe {
// Safety: the validity and state of the handle `self.0` is witnessed by `self`.
bssl_sys::SSL_get_verify_depth(self.ptr()).try_into().ok()
}
}
/// Set certificate verification parameters.
pub fn set_certificate_verification_params(
&mut self,
params: &CertificateVerificationParams,
) -> Result<&mut Self, Error> {
check_lib_error!(unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`.
// - `SSL_set1_param` claims shared ownership of `params` by bumping ref-count.
bssl_sys::SSL_set1_param(self.ptr(), params.as_ptr())
});
Ok(self)
}
}
impl<'a, R, M> EstablishedTlsConnection<'a, R, M> {
/// Export keying material from this connection into a buffer of a chosen length,
/// as per [RFC 5705].
///
/// To derive the same value, both sides of a connection must use the same output length, label
/// and context.
/// - In TLS 1.2 and earlier, using a zero-length context and using no context would give
/// different output.
/// - In TLS 1.3 and later, the output length controls the derivation so that a truncated
/// longer export will not match a shorter export.
///
/// [RFC 5705]: <https://datatracker.ietf.org/doc/html/rfc5705>
pub fn export_keying_material(
&self,
label: &CStr,
context: Option<&[u8]>,
output: &mut [u8],
) -> Result<(), Error> {
let (context, context_len, use_context) = if let Some(context) = context {
let (context, context_len) = slice_into_ffi_raw_parts(context);
(context, context_len, 1)
} else {
(null(), 0, 0)
};
let (output, output_len) = crate::ffi::mut_slice_into_ffi_raw_parts(output);
check_lib_error!(unsafe {
// Safety:
// - the validity of the handle `self.0` is witnessed by `self`.
bssl_sys::SSL_export_keying_material(
self.ptr(),
output,
output_len,
label.as_ptr(),
label.count_bytes(),
context,
context_len,
use_context,
)
});
Ok(())
}
}