blob: 11bc865cad1df3268fd1ad9364c3c5b69d07e479 [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;
use core::{
ffi::c_int,
ptr::{
NonNull,
null_mut, //
}, //
};
use once_cell::sync::Lazy;
use crate::{
Methods,
PrivateKeyMethods,
abort_on_panic,
connection::methods::private_key_op_from_ssl,
credentials::{
DecryptionOperation,
PrivateKeyDelegate,
PrivateKeyOperationResult,
SignatureOperation,
waker_data_from_ssl, //
},
ffi::{
sanitise_mut_byteslice,
sanitize_slice, //
},
methods::drop_box_rust_methods, //
};
pub(super) static TLS_CREDENTIAL_METHOD: Lazy<c_int> = Lazy::new(|| unsafe {
// Safety: this a one-time registration uses only valid function pointers.
let ret = bssl_sys::SSL_CREDENTIAL_get_ex_new_index(
0,
null_mut(),
null_mut(),
None,
Some(drop_box_rust_methods::<RustCredentialMethods>),
);
if ret < 0 {
panic!("Failed to register TLS Credential ex-data")
} else {
ret
}
});
#[derive(Default)]
pub(crate) struct RustCredentialMethods {
pub(crate) private_key_methods: Option<Box<dyn PrivateKeyDelegate>>,
}
impl Methods for RustCredentialMethods {
unsafe extern "C" fn from_ssl<'a>(ssl: *mut bssl_sys::SSL) -> Option<&'a Self> {
unsafe {
// Safety: `ssl` is valid per BoringSSL invariant.
let cred = bssl_sys::SSL_get0_selected_credential(ssl);
if cred.is_null() {
return None;
}
// Safety: `cred` is valid and originated from `TlsCredential::new`.
let methods = bssl_sys::SSL_CREDENTIAL_get_ex_data(cred, *TLS_CREDENTIAL_METHOD);
if methods.is_null() {
return None;
}
// Safety: `cred` is originated from `Box::into_raw`.
Some(&*(methods as *mut RustCredentialMethods))
}
}
}
impl PrivateKeyMethods for RustCredentialMethods {
fn private_key_methods(&self) -> Option<&dyn PrivateKeyDelegate> {
self.private_key_methods.as_deref()
}
}
macro_rules! private_key_method_prelude {
($M:ty, $ssl:ident => $context:ident, $private_key_methods:ident, $task:ident) => {
let Some(ssl) = NonNull::new($ssl) else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
let Some(methods) = (unsafe {
// Safety: `ssl` outlives `methods` and must be valid by BoringSSL contract.
<$M>::from_ssl(ssl.as_ptr())
}) else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
let waker = unsafe {
// Safety: `ssl` must be constructed by `TlsConnection` as this is called from
waker_data_from_ssl(ssl)
};
let mut $context = if let Some(waker) = &waker {
Some(core::task::Context::from_waker(waker))
} else {
None
};
#[allow(unused_mut)]
let $task = unsafe {
// Safety: `ssl` must be constructed by `TlsConnection` as this is called from
private_key_op_from_ssl(ssl)
};
let Some($private_key_methods) = methods.private_key_methods() else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
};
}
pub(crate) unsafe extern "C" fn sign<Method: PrivateKeyMethods>(
ssl: *mut bssl_sys::SSL,
out: *mut u8,
out_len: *mut usize,
max_out: usize,
sig_alg: u16,
msg: *const u8,
msg_len: usize,
) -> bssl_sys::ssl_private_key_result_t {
private_key_method_prelude!(Method, ssl => context, private_key_methods, task);
if task.is_some() {
abort_on_panic(|| {
let _ = task.take();
});
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
}
// Unwind-safety: later when panic happens, we detach the poisoned private key method
// without calling destructor.
let algorithm = match sig_alg.try_into() {
Ok(sig_alg) => sig_alg,
// TODO(@xfding) maybe we should log this error?
Err(_) => return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure,
};
let Some(output) = (unsafe {
// Safety: the slice will only be used within this callback.
sanitise_mut_byteslice(out, max_out)
}) else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
if output.is_empty() {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
}
let Some(message) = (unsafe {
// Safety: `msg` outlives `message` because it is owned by BoringSSL.
sanitize_slice(msg, msg_len)
}) else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
// Unwind-safety: when panic happens, we will not inspect the `output` buffer.
abort_on_panic(move || {
let sign_op = SignatureOperation {
output,
message,
algorithm,
};
let mut outstanding_task = private_key_methods.sign(sign_op);
match outstanding_task.complete(context.as_mut(), output) {
PrivateKeyOperationResult::Success(len) => {
unsafe {
// Safety: `out_len` is a valid pointer by BoringSSL invariant.
*out_len = len;
}
bssl_sys::ssl_private_key_result_t_ssl_private_key_success
}
PrivateKeyOperationResult::Pending => {
*task = Some(outstanding_task);
bssl_sys::ssl_private_key_result_t_ssl_private_key_retry
}
PrivateKeyOperationResult::Error => {
bssl_sys::ssl_private_key_result_t_ssl_private_key_failure
}
}
})
}
pub(crate) unsafe extern "C" fn decrypt<Method: PrivateKeyMethods>(
ssl: *mut bssl_sys::SSL,
out: *mut u8,
out_len: *mut usize,
max_out: usize,
ciphertext: *const u8,
ciphertext_len: usize,
) -> bssl_sys::ssl_private_key_result_t {
private_key_method_prelude!(Method, ssl => context, private_key_methods, task);
if task.is_some() {
abort_on_panic(|| {
let _ = task.take();
});
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
}
// Unwind-safety: later when panic happens, we detach the poisoned private key method
// without calling destructor.
let Some(output) = (unsafe {
// Safety: the slice will only be used within this callback.
sanitise_mut_byteslice(out, max_out)
}) else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
if output.is_empty() {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
}
let Some(ciphertext) = (unsafe {
// Safety: `ciphertext` is now owned by BoringSSL and outlives the slice.
sanitize_slice(ciphertext, ciphertext_len)
}) else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
// Unwind-safety: when panic happens, we will not inspect the `output` buffer.
abort_on_panic(move || {
let decrypt_op = DecryptionOperation { output, ciphertext };
let mut outstanding_task = private_key_methods.decrypt(decrypt_op);
match outstanding_task.complete(context.as_mut(), output) {
PrivateKeyOperationResult::Success(len) => {
unsafe {
// Safety: `out_len` is a valid pointer by BoringSSL invariant.
*out_len = len;
}
bssl_sys::ssl_private_key_result_t_ssl_private_key_success
}
PrivateKeyOperationResult::Pending => {
*task = Some(outstanding_task);
bssl_sys::ssl_private_key_result_t_ssl_private_key_retry
}
PrivateKeyOperationResult::Error => {
bssl_sys::ssl_private_key_result_t_ssl_private_key_failure
}
}
})
}
pub(crate) unsafe extern "C" fn complete<Method: PrivateKeyMethods>(
ssl: *mut bssl_sys::SSL,
out: *mut u8,
out_len: *mut usize,
max_out: usize,
) -> bssl_sys::ssl_private_key_result_t {
private_key_method_prelude!(Method, ssl => context, _private_key_methods, task);
// Unwind-safety: later when panic happens, we detach the poisoned private key method
// without calling destructor.
let Some(output) = (unsafe {
// Safety: the slice will only be used within this callback.
sanitise_mut_byteslice(out, max_out)
}) else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
if output.is_empty() {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
}
// Unwind-safety: when panic happens, we will not inspect the `output` buffer.
abort_on_panic(move || {
let Some(outstanding_task) = task else {
return bssl_sys::ssl_private_key_result_t_ssl_private_key_failure;
};
match outstanding_task.complete(context.as_mut(), output) {
PrivateKeyOperationResult::Success(len) => {
unsafe {
// Safety: `out_len` is a valid pointer by BoringSSL invariant.
*out_len = len;
}
bssl_sys::ssl_private_key_result_t_ssl_private_key_success
}
PrivateKeyOperationResult::Pending => {
bssl_sys::ssl_private_key_result_t_ssl_private_key_retry
}
PrivateKeyOperationResult::Error => {
bssl_sys::ssl_private_key_result_t_ssl_private_key_failure
}
}
})
}
pub(super) const PRIVATE_KEY_METHODS: *const bssl_sys::SSL_PRIVATE_KEY_METHOD = {
&bssl_sys::SSL_PRIVATE_KEY_METHOD {
sign: Some(sign::<RustCredentialMethods>),
decrypt: Some(decrypt::<RustCredentialMethods>),
complete: Some(complete::<RustCredentialMethods>),
} as _
};