Skip to content

Commit 87fb27e

Browse files
committed
Implement SslContextBuilder::set_private_key_method
1 parent 43c57d0 commit 87fb27e

5 files changed

Lines changed: 503 additions & 32 deletions

File tree

boring/src/ssl/callbacks.rs

Lines changed: 98 additions & 13 deletions
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, PrivateKeyError, PrivateKeyMethod, SelectCertError, SniError, Ssl,
5+
SslAlert, SslContext, SslContextRef, SslRef, SslSession, SslSessionRef, SslSignatureAlgorithm,
6+
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, PrivateKeyError>,
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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,31 @@ impl SslContextBuilder {
13821382
}
13831383
}
13841384

1385+
/// Configures a custom private key method on the context.
1386+
///
1387+
/// See [`PrivateKeyMethod`] for more details.
1388+
///
1389+
/// This corresponds to [`SSL_CTX_set_private_key_method`]
1390+
///
1391+
/// [`SSL_CTX_set_private_key_method`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_private_key_method
1392+
pub fn set_private_key_method<M>(&mut self, method: M)
1393+
where
1394+
M: PrivateKeyMethod,
1395+
{
1396+
unsafe {
1397+
self.set_ex_data(SslContext::cached_ex_index::<M>(), method);
1398+
1399+
ffi::SSL_CTX_set_private_key_method(
1400+
self.as_ptr(),
1401+
&ffi::SSL_PRIVATE_KEY_METHOD {
1402+
sign: Some(callbacks::raw_sign::<M>),
1403+
decrypt: Some(callbacks::raw_decrypt::<M>),
1404+
complete: Some(callbacks::raw_complete::<M>),
1405+
},
1406+
)
1407+
}
1408+
}
1409+
13851410
/// Checks for consistency between the private key and certificate.
13861411
///
13871412
/// This corresponds to [`SSL_CTX_check_private_key`].
@@ -3649,6 +3674,76 @@ bitflags! {
36493674
}
36503675
}
36513676

3677+
/// Describes private key hooks. This is used to off-load signing operations to
3678+
/// a custom, potentially asynchronous, backend. Metadata about the key such as
3679+
/// the type and size are parsed out of the certificate.
3680+
///
3681+
/// Corresponds to [`ssl_private_key_method_st`].
3682+
///
3683+
/// [`ssl_private_key_method_st`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
3684+
pub trait PrivateKeyMethod: Send + Sync + 'static {
3685+
/// Signs the message `input` using the specified signature algorithm.
3686+
///
3687+
/// On success, it returns `Ok(written)` where `written` is the number of
3688+
/// bytes written into `output`. On failure, it returns
3689+
/// `Err(PrivateKeyError::FAILURE)`. If the operation has not completed,
3690+
/// it returns `Err(PrivateKeyError::RETRY)`.
3691+
///
3692+
/// The caller should arrange for the high-level operation on `ssl` to be
3693+
/// retried when the operation is completed. This will result in a call to
3694+
/// [`Self::complete`].
3695+
fn sign(
3696+
&self,
3697+
ssl: &mut SslRef,
3698+
input: &[u8],
3699+
signature_algorithm: SslSignatureAlgorithm,
3700+
output: &mut [u8],
3701+
) -> Result<usize, PrivateKeyError>;
3702+
3703+
/// Decrypts `input`.
3704+
///
3705+
/// On success, it returns `Ok(written)` where `written` is the number of
3706+
/// bytes written into `output`. On failure, it returns
3707+
/// `Err(PrivateKeyError::FAILURE)`. If the operation has not completed,
3708+
/// it returns `Err(PrivateKeyError::RETRY)`.
3709+
///
3710+
/// The caller should arrange for the high-level operation on `ssl` to be
3711+
/// retried when the operation is completed. This will result in a call to
3712+
/// [`Self::complete`].
3713+
///
3714+
/// This method only works with RSA keys and should perform a raw RSA
3715+
/// decryption operation with no padding.
3716+
// NOTE(nox): What does it mean that it is an error?
3717+
fn decrypt(
3718+
&self,
3719+
ssl: &mut SslRef,
3720+
input: &[u8],
3721+
output: &mut [u8],
3722+
) -> Result<usize, PrivateKeyError>;
3723+
3724+
/// Completes a pending operation.
3725+
///
3726+
/// On success, it returns `Ok(written)` where `written` is the number of
3727+
/// bytes written into `output`. On failure, it returns
3728+
/// `Err(PrivateKeyError::FAILURE)`. If the operation has not completed,
3729+
/// it returns `Err(PrivateKeyError::RETRY)`.
3730+
///
3731+
/// This method may be called arbitrarily many times before completion.
3732+
fn complete(&self, ssl: &mut SslRef, output: &mut [u8]) -> Result<usize, PrivateKeyError>;
3733+
}
3734+
3735+
/// An error returned from a private key method.
3736+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3737+
pub struct PrivateKeyError(ffi::ssl_private_key_result_t);
3738+
3739+
impl PrivateKeyError {
3740+
/// A fatal error occured and the handshake should be terminated.
3741+
pub const FAILURE: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_failure);
3742+
3743+
/// The operation could not be completed and should be retried later.
3744+
pub const RETRY: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_retry);
3745+
}
3746+
36523747
use crate::ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server};
36533748

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

boring/src/ssl/test/mod.rs

Lines changed: 11 additions & 13 deletions
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)