blob: bbbeb2e08cb537adb522d35c987949e0bfc09895 [file] [log] [blame]
// 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.
//! TLS credentials
use alloc::boxed::Box;
use core::{ffi::c_int, marker::PhantomData, mem::forget, ptr::NonNull};
pub(crate) mod methods;
/// TLS credentials builder
pub struct TlsCredentialBuilder<Mode>(NonNull<bssl_sys::SSL_CREDENTIAL>, PhantomData<fn() -> Mode>);
/// X.509 credential
pub enum X509Mode {}
// Safety: At this type state, the credential handle is exclusively owned.
unsafe impl<M> Send for TlsCredentialBuilder<M> {}
impl<M> Drop for TlsCredentialBuilder<M> {
fn drop(&mut self) {
unsafe {
// Safety: `self.0` is still valid at dropping.
bssl_sys::SSL_CREDENTIAL_free(self.0.as_ptr());
}
}
}
impl<M> TlsCredentialBuilder<M> {
fn ptr(&mut self) -> *mut bssl_sys::SSL_CREDENTIAL {
self.0.as_ptr()
}
fn set_ex_data(mut self) -> Self {
let rc = unsafe {
// Safety:
// - this method is called exactly once during construction.
// - the ex_data index will be generated correctly and exactly once.
// - the `SSL_CREDENTIAL*` handle is already valid, witnessed by `self`.
bssl_sys::SSL_CREDENTIAL_set_ex_data(
self.ptr(),
*methods::TLS_CREDENTIAL_METHOD,
Box::into_raw(Box::new(methods::RustCredentialMethods::default())) as _,
)
};
assert_eq!(rc, 1);
self
}
}
impl TlsCredentialBuilder<X509Mode> {
/// Construct X.509-powered credential instance.
pub fn new() -> Self {
let this = Self(
NonNull::new(unsafe {
// Safety: this call has no side-effect other than allocation.
bssl_sys::SSL_CREDENTIAL_new_x509()
})
.expect("allocation failure"),
PhantomData,
);
this.set_ex_data()
}
}
impl<M> TlsCredentialBuilder<M> {
/// Finalise the credential.
pub fn build(mut self) -> Option<TlsCredential> {
if unsafe {
// Safety: `self.0` is still valid.
bssl_sys::SSL_CREDENTIAL_is_complete(self.ptr()) == 1
} {
let Self(cred, _) = self;
forget(self);
Some(TlsCredential(cred))
} else {
None
}
}
}
/// A completely constructed TLS credential.
pub struct TlsCredential(NonNull<bssl_sys::SSL_CREDENTIAL>);
// Safety: `TlsCredential` is locked as immutable at this type state.
unsafe impl Send for TlsCredential {}
unsafe impl Sync for TlsCredential {}
impl TlsCredential {
pub(crate) fn ptr(&self) -> *mut bssl_sys::SSL_CREDENTIAL {
self.0.as_ptr()
}
/// This method releases the ownership.
pub(crate) fn into_raw(self) -> *mut bssl_sys::SSL_CREDENTIAL {
let ptr = self.0.as_ptr();
forget(self);
ptr
}
}
impl Clone for TlsCredential {
fn clone(&self) -> Self {
unsafe {
// Safety: this handle is already valid by the witness of self.
bssl_sys::SSL_CREDENTIAL_up_ref(self.ptr());
}
Self(self.0)
}
}
impl Drop for TlsCredential {
fn drop(&mut self) {
unsafe {
// Safety: `self.0` is still valid at dropping.
bssl_sys::SSL_CREDENTIAL_free(self.ptr());
}
}
}
bssl_macros::bssl_enum! {
/// Certificate verification mode
pub enum CertificateVerificationMode: i8 {
/// Verifies the server certificate on a client but does not make errors fatal.
None = bssl_sys::SSL_VERIFY_NONE as i8,
/// Verifies the server certificate on a client and makes errors fatal.
PeerCertRequested = bssl_sys::SSL_VERIFY_PEER as i8,
/// Configures a server to request a client certificate and **reject** connections if
/// the client declines to send a certificate.
PeerCertMandatory =
(bssl_sys::SSL_VERIFY_FAIL_IF_NO_PEER_CERT | bssl_sys::SSL_VERIFY_PEER) as i8,
}
}
impl TryFrom<c_int> for CertificateVerificationMode {
type Error = c_int;
fn try_from(mode: c_int) -> Result<Self, Self::Error> {
let Ok(value) = i8::try_from(mode) else {
return Err(mode);
};
if let Ok(mode) = Self::try_from(value) {
Ok(mode)
} else {
Err(mode)
}
}
}