Skip to content

Commit 9681a1d

Browse files
committed
Implement SslContextBuilder::set_private_key_method
1 parent f18e96a commit 9681a1d

File tree

2 files changed

+193
-13
lines changed

2 files changed

+193
-13
lines changed

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
@@ -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`].
@@ -3577,6 +3602,76 @@ bitflags! {
35773602
}
35783603
}
35793604

3605+
/// Describes private key hooks. This is used to off-load signing operations to
3606+
/// a custom, potentially asynchronous, backend. Metadata about the key such as
3607+
/// the type and size are parsed out of the certificate.
3608+
///
3609+
/// Corresponds to [`ssl_private_key_method_st`].
3610+
///
3611+
/// [`ssl_private_key_method_st`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
3612+
pub trait PrivateKeyMethod: Send + Sync + 'static {
3613+
/// Signs the message `input` using the specified signature algorithm.
3614+
///
3615+
/// On success, it returns `Ok(written)` where `written` is the number of
3616+
/// bytes written into `output`. On failure, it returns
3617+
/// `Err(PrivateKeyError::FAILURE)`. If the operation has not completed,
3618+
/// it returns `Err(PrivateKeyError::RETRY)`.
3619+
///
3620+
/// The caller should arrange for the high-level operation on `ssl` to be
3621+
/// retried when the operation is completed. This will result in a call to
3622+
/// [`Self::complete`].
3623+
fn sign(
3624+
&self,
3625+
ssl: &mut SslRef,
3626+
input: &[u8],
3627+
signature_algorithm: SslSignatureAlgorithm,
3628+
output: &mut [u8],
3629+
) -> Result<usize, PrivateKeyError>;
3630+
3631+
/// Decrypts `input`.
3632+
///
3633+
/// On success, it returns `Ok(written)` where `written` is the number of
3634+
/// bytes written into `output`. On failure, it returns
3635+
/// `Err(PrivateKeyError::FAILURE)`. If the operation has not completed,
3636+
/// it returns `Err(PrivateKeyError::RETRY)`.
3637+
///
3638+
/// The caller should arrange for the high-level operation on `ssl` to be
3639+
/// retried when the operation is completed. This will result in a call to
3640+
/// [`Self::complete`].
3641+
///
3642+
/// This method only works with RSA keys and should perform a raw RSA
3643+
/// decryption operation with no padding.
3644+
// NOTE(nox): What does it mean that it is an error?
3645+
fn decrypt(
3646+
&self,
3647+
ssl: &mut SslRef,
3648+
input: &[u8],
3649+
output: &mut [u8],
3650+
) -> Result<usize, PrivateKeyError>;
3651+
3652+
/// Completes a pending operation.
3653+
///
3654+
/// On success, it returns `Ok(written)` where `written` is the number of
3655+
/// bytes written into `output`. On failure, it returns
3656+
/// `Err(PrivateKeyError::FAILURE)`. If the operation has not completed,
3657+
/// it returns `Err(PrivateKeyError::RETRY)`.
3658+
///
3659+
/// This method may be called arbitrarily many times before completion.
3660+
fn complete(&self, ssl: &mut SslRef, output: &mut [u8]) -> Result<usize, PrivateKeyError>;
3661+
}
3662+
3663+
/// An error returned from a private key method.
3664+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
3665+
pub struct PrivateKeyError(ffi::ssl_private_key_result_t);
3666+
3667+
impl PrivateKeyError {
3668+
/// A fatal error occured and the handshake should be terminated.
3669+
pub const FAILURE: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_failure);
3670+
3671+
/// The operation could not be completed and should be retried later.
3672+
pub const RETRY: Self = Self(ffi::ssl_private_key_result_t::ssl_private_key_retry);
3673+
}
3674+
35803675
use crate::ffi::{SSL_CTX_up_ref, SSL_SESSION_get_master_key, SSL_SESSION_up_ref, SSL_is_server};
35813676

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

0 commit comments

Comments
 (0)