Skip to content

Commit 887f6fd

Browse files
committed
Implement SslContextBuilder::set_private_key_method
1 parent 853d010 commit 887f6fd

File tree

5 files changed

+514
-32
lines changed

5 files changed

+514
-32
lines changed

boring/src/ssl/callbacks.rs

+98-13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
#![forbid(unsafe_op_in_unsafe_fn)]
22

3+
use super::{
4+
AlpnError, ClientHello, PrivateKeyMethod, PrivateKeyMethodError, SelectCertError, SniError,
5+
Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef,
6+
SslSignatureAlgorithm, SESSION_CTX_INDEX,
7+
};
8+
use crate::error::ErrorStack;
39
use crate::ffi;
10+
use crate::x509::{X509StoreContext, X509StoreContextRef};
411
use foreign_types::ForeignType;
512
use foreign_types::ForeignTypeRef;
613
use libc::c_char;
@@ -12,19 +19,7 @@ use std::slice;
1219
use std::str;
1320
use std::sync::Arc;
1421

15-
use crate::error::ErrorStack;
16-
use crate::ssl::AlpnError;
17-
use crate::ssl::{ClientHello, SelectCertError};
18-
use crate::ssl::{
19-
SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef,
20-
SESSION_CTX_INDEX,
21-
};
22-
use crate::x509::{X509StoreContext, X509StoreContextRef};
23-
24-
pub(super) unsafe extern "C" fn raw_verify<F>(
25-
preverify_ok: c_int,
26-
x509_ctx: *mut ffi::X509_STORE_CTX,
27-
) -> c_int
22+
pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
2823
where
2924
F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
3025
{
@@ -372,3 +367,93 @@ where
372367

373368
callback(ssl, line);
374369
}
370+
371+
pub(super) unsafe extern "C" fn raw_sign<M>(
372+
ssl: *mut ffi::SSL,
373+
out: *mut u8,
374+
out_len: *mut usize,
375+
max_out: usize,
376+
signature_algorithm: u16,
377+
in_: *const u8,
378+
in_len: usize,
379+
) -> ffi::ssl_private_key_result_t
380+
where
381+
M: PrivateKeyMethod,
382+
{
383+
// SAFETY: boring provides valid inputs.
384+
let input = unsafe { slice::from_raw_parts(in_, in_len) };
385+
386+
let signature_algorithm = SslSignatureAlgorithm(signature_algorithm);
387+
388+
let callback = |method: &M, ssl: &mut _, output: &mut _| {
389+
method.sign(ssl, input, signature_algorithm, output)
390+
};
391+
392+
// SAFETY: boring provides valid inputs.
393+
unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) }
394+
}
395+
396+
pub(super) unsafe extern "C" fn raw_decrypt<M>(
397+
ssl: *mut ffi::SSL,
398+
out: *mut u8,
399+
out_len: *mut usize,
400+
max_out: usize,
401+
in_: *const u8,
402+
in_len: usize,
403+
) -> ffi::ssl_private_key_result_t
404+
where
405+
M: PrivateKeyMethod,
406+
{
407+
// SAFETY: boring provides valid inputs.
408+
let input = unsafe { slice::from_raw_parts(in_, in_len) };
409+
410+
let callback = |method: &M, ssl: &mut _, output: &mut _| method.decrypt(ssl, input, output);
411+
412+
// SAFETY: boring provides valid inputs.
413+
unsafe { raw_private_key_callback(ssl, out, out_len, max_out, callback) }
414+
}
415+
416+
pub(super) unsafe extern "C" fn raw_complete<M>(
417+
ssl: *mut ffi::SSL,
418+
out: *mut u8,
419+
out_len: *mut usize,
420+
max_out: usize,
421+
) -> ffi::ssl_private_key_result_t
422+
where
423+
M: PrivateKeyMethod,
424+
{
425+
// SAFETY: boring provides valid inputs.
426+
unsafe { raw_private_key_callback::<M>(ssl, out, out_len, max_out, M::complete) }
427+
}
428+
429+
unsafe fn raw_private_key_callback<M>(
430+
ssl: *mut ffi::SSL,
431+
out: *mut u8,
432+
out_len: *mut usize,
433+
max_out: usize,
434+
callback: impl FnOnce(&M, &mut SslRef, &mut [u8]) -> Result<usize, PrivateKeyMethodError>,
435+
) -> ffi::ssl_private_key_result_t
436+
where
437+
M: PrivateKeyMethod,
438+
{
439+
// SAFETY: boring provides valid inputs.
440+
let ssl = unsafe { SslRef::from_ptr_mut(ssl) };
441+
let output = unsafe { slice::from_raw_parts_mut(out, max_out) };
442+
let out_len = unsafe { &mut *out_len };
443+
444+
let ssl_context = ssl.ssl_context().to_owned();
445+
let method = ssl_context
446+
.ex_data(SslContext::cached_ex_index::<M>())
447+
.expect("BUG: private key method missing");
448+
449+
match callback(method, ssl, output) {
450+
Ok(written) => {
451+
assert!(written <= max_out);
452+
453+
*out_len = written;
454+
455+
ffi::ssl_private_key_result_t::ssl_private_key_success
456+
}
457+
Err(err) => err.0,
458+
}
459+
}

boring/src/ssl/mod.rs

+96
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,31 @@ impl SslContextBuilder {
13911391
}
13921392
}
13931393

1394+
/// Configures a custom private key method on the context.
1395+
///
1396+
/// See [`PrivateKeyMethod`] for more details.
1397+
///
1398+
/// This corresponds to [`SSL_CTX_set_private_key_method`]
1399+
///
1400+
/// [`SSL_CTX_set_private_key_method`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_private_key_method
1401+
pub fn set_private_key_method<M>(&mut self, method: M)
1402+
where
1403+
M: PrivateKeyMethod,
1404+
{
1405+
unsafe {
1406+
self.set_ex_data(SslContext::cached_ex_index::<M>(), method);
1407+
1408+
ffi::SSL_CTX_set_private_key_method(
1409+
self.as_ptr(),
1410+
&ffi::SSL_PRIVATE_KEY_METHOD {
1411+
sign: Some(callbacks::raw_sign::<M>),
1412+
decrypt: Some(callbacks::raw_decrypt::<M>),
1413+
complete: Some(callbacks::raw_complete::<M>),
1414+
},
1415+
)
1416+
}
1417+
}
1418+
13941419
/// Checks for consistency between the private key and certificate.
13951420
///
13961421
/// This corresponds to [`SSL_CTX_check_private_key`].
@@ -3719,6 +3744,77 @@ bitflags! {
37193744
}
37203745
}
37213746

3747+
/// Describes private key hooks. This is used to off-load signing operations to
3748+
/// a custom, potentially asynchronous, backend. Metadata about the key such as
3749+
/// the type and size are parsed out of the certificate.
3750+
///
3751+
/// Corresponds to [`ssl_private_key_method_st`].
3752+
///
3753+
/// [`ssl_private_key_method_st`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
3754+
pub trait PrivateKeyMethod: Send + Sync + 'static {
3755+
/// Signs the message `input` using the specified signature algorithm.
3756+
///
3757+
/// On success, it returns `Ok(written)` where `written` is the number of
3758+
/// bytes written into `output`. On failure, it returns
3759+
/// `Err(PrivateKeyMethodError::FAILURE)`. If the operation has not completed,
3760+
/// it returns `Err(PrivateKeyMethodError::RETRY)`.
3761+
///
3762+
/// The caller should arrange for the high-level operation on `ssl` to be
3763+
/// retried when the operation is completed. This will result in a call to
3764+
/// [`Self::complete`].
3765+
fn sign(
3766+
&self,
3767+
ssl: &mut SslRef,
3768+
input: &[u8],
3769+
signature_algorithm: SslSignatureAlgorithm,
3770+
output: &mut [u8],
3771+
) -> Result<usize, PrivateKeyMethodError>;
3772+
3773+
/// Decrypts `input`.
3774+
///
3775+
/// On success, it returns `Ok(written)` where `written` is the number of
3776+
/// bytes written into `output`. On failure, it returns
3777+
/// `Err(PrivateKeyMethodError::FAILURE)`. If the operation has not completed,
3778+
/// it returns `Err(PrivateKeyMethodError::RETRY)`.
3779+
///
3780+
/// The caller should arrange for the high-level operation on `ssl` to be
3781+
/// retried when the operation is completed. This will result in a call to
3782+
/// [`Self::complete`].
3783+
///
3784+
/// This method only works with RSA keys and should perform a raw RSA
3785+
/// decryption operation with no padding.
3786+
// NOTE(nox): What does it mean that it is an error?
3787+
fn decrypt(
3788+
&self,
3789+
ssl: &mut SslRef,
3790+
input: &[u8],
3791+
output: &mut [u8],
3792+
) -> Result<usize, PrivateKeyMethodError>;
3793+
3794+
/// Completes a pending operation.
3795+
///
3796+
/// On success, it returns `Ok(written)` where `written` is the number of
3797+
/// bytes written into `output`. On failure, it returns
3798+
/// `Err(PrivateKeyMethodError::FAILURE)`. If the operation has not completed,
3799+
/// it returns `Err(PrivateKeyMethodError::RETRY)`.
3800+
///
3801+
/// This method may be called arbitrarily many times before completion.
3802+
fn complete(&self, ssl: &mut SslRef, output: &mut [u8])
3803+
-> Result<usize, PrivateKeyMethodError>;
3804+
}
3805+
3806+
/// An error returned from a private key method.
3807+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3808+
pub struct PrivateKeyMethodError(ffi::ssl_private_key_result_t);
3809+
3810+
impl PrivateKeyMethodError {
3811+
/// A fatal error occured and the handshake should be terminated.
3812+
pub const FAILURE: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_failure);
3813+
3814+
/// The operation could not be completed and should be retried later.
3815+
pub const RETRY: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_retry);
3816+
}
3817+
37223818
use crate::ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server};
37233819

37243820
use crate::ffi::{DTLS_method, TLS_client_method, TLS_method, TLS_server_method};

boring/src/ssl/test/mod.rs

+11-13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::x509::store::X509StoreBuilder;
3434
use crate::x509::verify::X509CheckFlags;
3535
use crate::x509::{X509Name, X509StoreContext, X509VerifyResult, X509};
3636

37+
mod private_key_method;
3738
mod server;
3839

3940
static ROOT_CERT: &[u8] = include_bytes!("../../../test/root-ca.pem");
@@ -55,9 +56,7 @@ fn verify_untrusted() {
5556
#[test]
5657
fn verify_trusted() {
5758
let server = Server::builder().build();
58-
59-
let mut client = server.client();
60-
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
59+
let client = server.client_with_root_ca();
6160

6261
client.connect();
6362
}
@@ -109,9 +108,8 @@ fn verify_untrusted_callback_override_bad() {
109108
#[test]
110109
fn verify_trusted_callback_override_ok() {
111110
let server = Server::builder().build();
111+
let mut client = server.client_with_root_ca();
112112

113-
let mut client = server.client();
114-
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
115113
client
116114
.ctx()
117115
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
@@ -125,11 +123,12 @@ fn verify_trusted_callback_override_ok() {
125123
#[test]
126124
fn verify_trusted_callback_override_bad() {
127125
let mut server = Server::builder();
126+
128127
server.should_error();
128+
129129
let server = server.build();
130+
let mut client = server.client_with_root_ca();
130131

131-
let mut client = server.client();
132-
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
133132
client
134133
.ctx()
135134
.set_verify_callback(SslVerifyMode::PEER, |_, _| false);
@@ -155,9 +154,8 @@ fn verify_callback_load_certs() {
155154
#[test]
156155
fn verify_trusted_get_error_ok() {
157156
let server = Server::builder().build();
157+
let mut client = server.client_with_root_ca();
158158

159-
let mut client = server.client();
160-
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
161159
client
162160
.ctx()
163161
.set_verify_callback(SslVerifyMode::PEER, |_, x509| {
@@ -697,9 +695,8 @@ fn add_extra_chain_cert() {
697695
#[test]
698696
fn verify_valid_hostname() {
699697
let server = Server::builder().build();
698+
let mut client = server.client_with_root_ca();
700699

701-
let mut client = server.client();
702-
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
703700
client.ctx().set_verify(SslVerifyMode::PEER);
704701

705702
let mut client = client.build().builder();
@@ -714,11 +711,12 @@ fn verify_valid_hostname() {
714711
#[test]
715712
fn verify_invalid_hostname() {
716713
let mut server = Server::builder();
714+
717715
server.should_error();
716+
718717
let server = server.build();
718+
let mut client = server.client_with_root_ca();
719719

720-
let mut client = server.client();
721-
client.ctx().set_ca_file("test/root-ca.pem").unwrap();
722720
client.ctx().set_verify(SslVerifyMode::PEER);
723721

724722
let mut client = client.build().builder();

0 commit comments

Comments
 (0)