boring_imp/ssl/
async_callbacks.rs

1use super::mut_only::MutOnly;
2use super::{
3    ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError, SelectCertError,
4    Ssl, SslAlert, SslContextBuilder, SslRef, SslSession, SslSignatureAlgorithm, SslVerifyError,
5    SslVerifyMode,
6};
7use crate::ex_data::Index;
8use once_cell::sync::Lazy;
9use std::convert::identity;
10use std::future::Future;
11use std::pin::Pin;
12use std::task::{ready, Context, Poll, Waker};
13
14/// The type of futures to pass to [`SslContextBuilderExt::set_async_select_certificate_callback`].
15pub type BoxSelectCertFuture = ExDataFuture<Result<BoxSelectCertFinish, AsyncSelectCertError>>;
16
17/// The type of callbacks returned by [`BoxSelectCertFuture`] methods.
18pub type BoxSelectCertFinish = Box<dyn FnOnce(ClientHello<'_>) -> Result<(), AsyncSelectCertError>>;
19
20/// The type of futures returned by [`AsyncPrivateKeyMethod`] methods.
21pub type BoxPrivateKeyMethodFuture =
22    ExDataFuture<Result<BoxPrivateKeyMethodFinish, AsyncPrivateKeyMethodError>>;
23
24/// The type of callbacks returned by [`BoxPrivateKeyMethodFuture`].
25pub type BoxPrivateKeyMethodFinish =
26    Box<dyn FnOnce(&mut SslRef, &mut [u8]) -> Result<usize, AsyncPrivateKeyMethodError>>;
27
28/// The type of futures to pass to [`SslContextBuilderExt::set_async_get_session_callback`].
29pub type BoxGetSessionFuture = ExDataFuture<Option<BoxGetSessionFinish>>;
30
31/// The type of callbacks returned by [`BoxSelectCertFuture`] methods.
32pub type BoxGetSessionFinish = Box<dyn FnOnce(&mut SslRef, &[u8]) -> Option<SslSession>>;
33
34/// The type of futures to pass to [`SslContextBuilderExt::set_async_custom_verify_callback`].
35pub type BoxCustomVerifyFuture = ExDataFuture<Result<BoxCustomVerifyFinish, SslAlert>>;
36
37/// The type of callbacks returned by [`BoxCustomVerifyFuture`] methods.
38pub type BoxCustomVerifyFinish = Box<dyn FnOnce(&mut SslRef) -> Result<(), SslAlert>>;
39
40/// Convenience alias for futures stored in [`Ssl`] ex data by [`SslContextBuilderExt`] methods.
41///
42/// Public for documentation purposes.
43pub type ExDataFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
44
45pub(crate) static TASK_WAKER_INDEX: Lazy<Index<Ssl, Option<Waker>>> =
46    Lazy::new(|| Ssl::new_ex_index().unwrap());
47pub(crate) static SELECT_CERT_FUTURE_INDEX: Lazy<Index<Ssl, MutOnly<Option<BoxSelectCertFuture>>>> =
48    Lazy::new(|| Ssl::new_ex_index().unwrap());
49pub(crate) static SELECT_PRIVATE_KEY_METHOD_FUTURE_INDEX: Lazy<
50    Index<Ssl, MutOnly<Option<BoxPrivateKeyMethodFuture>>>,
51> = Lazy::new(|| Ssl::new_ex_index().unwrap());
52pub(crate) static SELECT_GET_SESSION_FUTURE_INDEX: Lazy<
53    Index<Ssl, MutOnly<Option<BoxGetSessionFuture>>>,
54> = Lazy::new(|| Ssl::new_ex_index().unwrap());
55pub(crate) static SELECT_CUSTOM_VERIFY_FUTURE_INDEX: Lazy<
56    Index<Ssl, MutOnly<Option<BoxCustomVerifyFuture>>>,
57> = Lazy::new(|| Ssl::new_ex_index().unwrap());
58
59impl SslContextBuilder {
60    /// Sets a callback that is called before most [`ClientHello`] processing
61    /// and before the decision whether to resume a session is made. The
62    /// callback may inspect the [`ClientHello`] and configure the connection.
63    ///
64    /// This method uses a function that returns a future whose output is
65    /// itself a closure that will be passed [`ClientHello`] to configure
66    /// the connection based on the computations done in the future.
67    ///
68    /// A task waker must be set on `Ssl` values associated with the resulting
69    /// `SslContext` with [`SslRef::set_task_waker`].
70    ///
71    /// See [`SslContextBuilder::set_select_certificate_callback`] for the sync
72    /// setter of this callback.
73    pub fn set_async_select_certificate_callback<F>(&mut self, callback: F)
74    where
75        F: Fn(&mut ClientHello<'_>) -> Result<BoxSelectCertFuture, AsyncSelectCertError>
76            + Send
77            + Sync
78            + 'static,
79    {
80        self.set_select_certificate_callback(move |mut client_hello| {
81            let fut_poll_result = with_ex_data_future(
82                &mut client_hello,
83                *SELECT_CERT_FUTURE_INDEX,
84                ClientHello::ssl_mut,
85                &callback,
86                identity,
87            );
88
89            let fut_result = match fut_poll_result {
90                Poll::Ready(fut_result) => fut_result,
91                Poll::Pending => return Err(SelectCertError::RETRY),
92            };
93
94            let finish = fut_result.or(Err(SelectCertError::ERROR))?;
95
96            finish(client_hello).or(Err(SelectCertError::ERROR))
97        })
98    }
99
100    /// Configures a custom private key method on the context.
101    ///
102    /// A task waker must be set on `Ssl` values associated with the resulting
103    /// `SslContext` with [`SslRef::set_task_waker`].
104    ///
105    /// See [`AsyncPrivateKeyMethod`] for more details.
106    pub fn set_async_private_key_method(&mut self, method: impl AsyncPrivateKeyMethod) {
107        self.set_private_key_method(AsyncPrivateKeyMethodBridge(Box::new(method)));
108    }
109
110    /// Sets a callback that is called when a client proposed to resume a session
111    /// but it was not found in the internal cache.
112    ///
113    /// The callback is passed a reference to the session ID provided by the client.
114    /// It should return the session corresponding to that ID if available. This is
115    /// only used for servers, not clients.
116    ///
117    /// A task waker must be set on `Ssl` values associated with the resulting
118    /// `SslContext` with [`SslRef::set_task_waker`].
119    ///
120    /// See [`SslContextBuilder::set_get_session_callback`] for the sync setter
121    /// of this callback.
122    ///
123    /// # Safety
124    ///
125    /// The returned [`SslSession`] must not be associated with a different [`SslContext`].
126    pub unsafe fn set_async_get_session_callback<F>(&mut self, callback: F)
127    where
128        F: Fn(&mut SslRef, &[u8]) -> Option<BoxGetSessionFuture> + Send + Sync + 'static,
129    {
130        let async_callback = move |ssl: &mut SslRef, id: &[u8]| {
131            let fut_poll_result = with_ex_data_future(
132                &mut *ssl,
133                *SELECT_GET_SESSION_FUTURE_INDEX,
134                |ssl| ssl,
135                |ssl| callback(ssl, id).ok_or(()),
136                |option| option.ok_or(()),
137            );
138
139            match fut_poll_result {
140                Poll::Ready(Err(())) => Ok(None),
141                Poll::Ready(Ok(finish)) => Ok(finish(ssl, id)),
142                Poll::Pending => Err(GetSessionPendingError),
143            }
144        };
145
146        self.set_get_session_callback(async_callback)
147    }
148
149    /// Configures certificate verification.
150    ///
151    /// The callback should return `Ok(())` if the certificate is valid.
152    /// If the certificate is invalid, the callback should return `SslVerifyError::Invalid(alert)`.
153    /// Some useful alerts include [`SslAlert::CERTIFICATE_EXPIRED`], [`SslAlert::CERTIFICATE_REVOKED`],
154    /// [`SslAlert::UNKNOWN_CA`], [`SslAlert::BAD_CERTIFICATE`], [`SslAlert::CERTIFICATE_UNKNOWN`],
155    /// and [`SslAlert::INTERNAL_ERROR`]. See RFC 5246 section 7.2.2 for their precise meanings.
156    ///
157    /// A task waker must be set on `Ssl` values associated with the resulting
158    /// `SslContext` with [`SslRef::set_task_waker`].
159    ///
160    /// See [`SslContextBuilder::set_custom_verify_callback`] for the sync version of this method.
161    ///
162    /// # Panics
163    ///
164    /// This method panics if this `Ssl` is associated with a RPK context.
165    pub fn set_async_custom_verify_callback<F>(&mut self, mode: SslVerifyMode, callback: F)
166    where
167        F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
168    {
169        self.set_custom_verify_callback(mode, async_custom_verify_callback(callback))
170    }
171}
172
173impl SslRef {
174    pub fn set_async_custom_verify_callback<F>(&mut self, mode: SslVerifyMode, callback: F)
175    where
176        F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
177    {
178        self.set_custom_verify_callback(mode, async_custom_verify_callback(callback))
179    }
180
181    /// Sets the task waker to be used in async callbacks installed on this `Ssl`.
182    pub fn set_task_waker(&mut self, waker: Option<Waker>) {
183        self.replace_ex_data(*TASK_WAKER_INDEX, waker);
184    }
185}
186
187fn async_custom_verify_callback<F>(
188    callback: F,
189) -> impl Fn(&mut SslRef) -> Result<(), SslVerifyError>
190where
191    F: Fn(&mut SslRef) -> Result<BoxCustomVerifyFuture, SslAlert> + Send + Sync + 'static,
192{
193    move |ssl| {
194        let fut_poll_result = with_ex_data_future(
195            &mut *ssl,
196            *SELECT_CUSTOM_VERIFY_FUTURE_INDEX,
197            |ssl| ssl,
198            &callback,
199            identity,
200        );
201
202        match fut_poll_result {
203            Poll::Ready(Err(alert)) => Err(SslVerifyError::Invalid(alert)),
204            Poll::Ready(Ok(finish)) => Ok(finish(ssl).map_err(SslVerifyError::Invalid)?),
205            Poll::Pending => Err(SslVerifyError::Retry),
206        }
207    }
208}
209
210/// A fatal error to be returned from async select certificate callbacks.
211#[derive(Debug, Copy, Clone, PartialEq, Eq)]
212pub struct AsyncSelectCertError;
213
214/// Describes async private key hooks. This is used to off-load signing
215/// operations to a custom, potentially asynchronous, backend. Metadata about the
216/// key such as the type and size are parsed out of the certificate.
217///
218/// See [`PrivateKeyMethod`] for the sync version of those hooks.
219///
220/// [`ssl_private_key_method_st`]: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
221pub trait AsyncPrivateKeyMethod: Send + Sync + 'static {
222    /// Signs the message `input` using the specified signature algorithm.
223    ///
224    /// This method uses a function that returns a future whose output is
225    /// itself a closure that will be passed `ssl` and `output`
226    /// to finish writing the signature.
227    ///
228    /// See [`PrivateKeyMethod::sign`] for the sync version of this method.
229    fn sign(
230        &self,
231        ssl: &mut SslRef,
232        input: &[u8],
233        signature_algorithm: SslSignatureAlgorithm,
234        output: &mut [u8],
235    ) -> Result<BoxPrivateKeyMethodFuture, AsyncPrivateKeyMethodError>;
236
237    /// Decrypts `input`.
238    ///
239    /// This method uses a function that returns a future whose output is
240    /// itself a closure that will be passed `ssl` and `output`
241    /// to finish decrypting the input.
242    ///
243    /// See [`PrivateKeyMethod::decrypt`] for the sync version of this method.
244    fn decrypt(
245        &self,
246        ssl: &mut SslRef,
247        input: &[u8],
248        output: &mut [u8],
249    ) -> Result<BoxPrivateKeyMethodFuture, AsyncPrivateKeyMethodError>;
250}
251
252/// A fatal error to be returned from async private key methods.
253#[derive(Debug, Copy, Clone, PartialEq, Eq)]
254pub struct AsyncPrivateKeyMethodError;
255
256struct AsyncPrivateKeyMethodBridge(Box<dyn AsyncPrivateKeyMethod>);
257
258impl PrivateKeyMethod for AsyncPrivateKeyMethodBridge {
259    fn sign(
260        &self,
261        ssl: &mut SslRef,
262        input: &[u8],
263        signature_algorithm: SslSignatureAlgorithm,
264        output: &mut [u8],
265    ) -> Result<usize, PrivateKeyMethodError> {
266        with_private_key_method(ssl, output, |ssl, output| {
267            <dyn AsyncPrivateKeyMethod>::sign(&*self.0, ssl, input, signature_algorithm, output)
268        })
269    }
270
271    fn decrypt(
272        &self,
273        ssl: &mut SslRef,
274        input: &[u8],
275        output: &mut [u8],
276    ) -> Result<usize, PrivateKeyMethodError> {
277        with_private_key_method(ssl, output, |ssl, output| {
278            <dyn AsyncPrivateKeyMethod>::decrypt(&*self.0, ssl, input, output)
279        })
280    }
281
282    fn complete(
283        &self,
284        ssl: &mut SslRef,
285        output: &mut [u8],
286    ) -> Result<usize, PrivateKeyMethodError> {
287        with_private_key_method(ssl, output, |_, _| {
288            // This should never be reached, if it does, that's a bug on boring's side,
289            // which called `complete` without having been returned to with a pending
290            // future from `sign` or `decrypt`.
291
292            if cfg!(debug_assertions) {
293                panic!("BUG: boring called complete without a pending operation");
294            }
295
296            Err(AsyncPrivateKeyMethodError)
297        })
298    }
299}
300
301/// Creates and drives a private key method future.
302///
303/// This is a convenience function for the three methods of impl `PrivateKeyMethod``
304/// for `dyn AsyncPrivateKeyMethod`. It relies on [`with_ex_data_future`] to
305/// drive the future and then immediately calls the final [`BoxPrivateKeyMethodFinish`]
306/// when the future is ready.
307fn with_private_key_method(
308    ssl: &mut SslRef,
309    output: &mut [u8],
310    create_fut: impl FnOnce(
311        &mut SslRef,
312        &mut [u8],
313    ) -> Result<BoxPrivateKeyMethodFuture, AsyncPrivateKeyMethodError>,
314) -> Result<usize, PrivateKeyMethodError> {
315    let fut_poll_result = with_ex_data_future(
316        ssl,
317        *SELECT_PRIVATE_KEY_METHOD_FUTURE_INDEX,
318        |ssl| ssl,
319        |ssl| create_fut(ssl, output),
320        identity,
321    );
322
323    let fut_result = match fut_poll_result {
324        Poll::Ready(fut_result) => fut_result,
325        Poll::Pending => return Err(PrivateKeyMethodError::RETRY),
326    };
327
328    let finish = fut_result.or(Err(PrivateKeyMethodError::FAILURE))?;
329
330    finish(ssl, output).or(Err(PrivateKeyMethodError::FAILURE))
331}
332
333/// Creates and drives a future stored in `ssl_handle`'s `Ssl` at ex data index `index`.
334///
335/// This function won't even bother storing the future in `index` if the future
336/// created by `create_fut` returns `Poll::Ready(_)` on the first poll call.
337fn with_ex_data_future<H, R, T, E>(
338    ssl_handle: &mut H,
339    index: Index<Ssl, MutOnly<Option<ExDataFuture<R>>>>,
340    get_ssl_mut: impl Fn(&mut H) -> &mut SslRef,
341    create_fut: impl FnOnce(&mut H) -> Result<ExDataFuture<R>, E>,
342    into_result: impl Fn(R) -> Result<T, E>,
343) -> Poll<Result<T, E>> {
344    let ssl = get_ssl_mut(ssl_handle);
345    let waker = ssl
346        .ex_data(*TASK_WAKER_INDEX)
347        .cloned()
348        .flatten()
349        .expect("task waker should be set");
350
351    let mut ctx = Context::from_waker(&waker);
352
353    if let Some(data @ Some(_)) = ssl.ex_data_mut(index).map(MutOnly::get_mut) {
354        let fut_result = into_result(ready!(data.as_mut().unwrap().as_mut().poll(&mut ctx)));
355
356        *data = None;
357
358        Poll::Ready(fut_result)
359    } else {
360        let mut fut = create_fut(ssl_handle)?;
361
362        match fut.as_mut().poll(&mut ctx) {
363            Poll::Ready(fut_result) => Poll::Ready(into_result(fut_result)),
364            Poll::Pending => {
365                get_ssl_mut(ssl_handle).replace_ex_data(index, MutOnly::new(Some(fut)));
366
367                Poll::Pending
368            }
369        }
370    }
371}