quinn_boring/
bffi_ext.rs

1use crate::error::{br, br_zero_is_success, BoringResult};
2use boring::error::ErrorStack;
3use boring::pkey::{HasPrivate, PKey};
4use boring::ssl::{Ssl, SslContext, SslContextRef, SslSession};
5use boring::x509::store::X509StoreBuilderRef;
6use boring::x509::X509;
7use boring_sys as bffi;
8use bytes::{Buf, BufMut};
9use foreign_types_shared::{ForeignType, ForeignTypeRef};
10use std::ffi::{c_char, c_int, c_uint, c_void, CStr};
11use std::fmt::{Display, Formatter};
12use std::result::Result as StdResult;
13use std::{ffi, fmt, mem, ptr, slice};
14
15/// Provides additional methods to [SslContext] needed for QUIC.
16pub trait QuicSslContext {
17    fn set_options(&mut self, options: u32) -> u32;
18    fn verify_peer(&mut self, verify: bool);
19    fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult;
20    fn set_session_cache_mode(&mut self, mode: c_int) -> c_int;
21    fn set_new_session_callback(
22        &mut self,
23        cb: Option<
24            unsafe extern "C" fn(ssl: *mut bffi::SSL, session: *mut bffi::SSL_SESSION) -> c_int,
25        >,
26    );
27    fn set_info_callback(
28        &mut self,
29        cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, type_: c_int, value: c_int)>,
30    );
31    fn set_keylog_callback(
32        &mut self,
33        cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, line: *const c_char)>,
34    );
35    fn set_certificate(&mut self, cert: X509) -> BoringResult;
36    fn load_certificate_from_pem_file(&mut self, path: &str) -> BoringResult;
37    fn add_to_cert_chain(&mut self, cert: X509) -> BoringResult;
38    fn load_cert_chain_from_pem_file(&mut self, path: &str) -> BoringResult;
39    fn set_private_key<T: HasPrivate>(&mut self, key: PKey<T>) -> BoringResult;
40    fn load_private_key_from_pem_file(&mut self, path: &str) -> BoringResult;
41    fn check_private_key(&self) -> BoringResult;
42    fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef;
43
44    fn enable_early_data(&mut self, enable: bool);
45    fn set_alpn_protos(&mut self, protos: &[u8]) -> BoringResult;
46    fn set_alpn_select_cb(
47        &mut self,
48        cb: Option<
49            unsafe extern "C" fn(
50                ssl: *mut bffi::SSL,
51                out: *mut *const u8,
52                out_len: *mut u8,
53                in_: *const u8,
54                in_len: c_uint,
55                arg: *mut c_void,
56            ) -> c_int,
57        >,
58    );
59    fn set_server_name_cb(
60        &mut self,
61        cb: Option<
62            unsafe extern "C" fn(
63                ssl: *mut bffi::SSL,
64                out_alert: *mut c_int,
65                arg: *mut c_void,
66            ) -> c_int,
67        >,
68    );
69    fn set_select_certificate_cb(
70        &mut self,
71        cb: Option<
72            unsafe extern "C" fn(
73                arg1: *const bffi::SSL_CLIENT_HELLO,
74            ) -> bffi::ssl_select_cert_result_t,
75        >,
76    );
77}
78
79impl QuicSslContext for SslContext {
80    fn set_options(&mut self, options: u32) -> u32 {
81        unsafe { bffi::SSL_CTX_set_options(self.as_ptr(), options) }
82    }
83
84    fn verify_peer(&mut self, verify: bool) {
85        let mode = if verify {
86            bffi::SSL_VERIFY_PEER | bffi::SSL_VERIFY_FAIL_IF_NO_PEER_CERT
87        } else {
88            bffi::SSL_VERIFY_NONE
89        };
90
91        unsafe { bffi::SSL_CTX_set_verify(self.as_ptr(), mode, None) }
92    }
93
94    fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult {
95        unsafe { br(bffi::SSL_CTX_set_quic_method(self.as_ptr(), method)) }
96    }
97
98    fn set_session_cache_mode(&mut self, mode: c_int) -> c_int {
99        unsafe { bffi::SSL_CTX_set_session_cache_mode(self.as_ptr(), mode) }
100    }
101
102    fn set_new_session_callback(
103        &mut self,
104        cb: Option<
105            unsafe extern "C" fn(ssl: *mut bffi::SSL, session: *mut bffi::SSL_SESSION) -> c_int,
106        >,
107    ) {
108        unsafe {
109            bffi::SSL_CTX_sess_set_new_cb(self.as_ptr(), cb);
110        }
111    }
112
113    fn set_info_callback(
114        &mut self,
115        cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, type_: c_int, value: c_int)>,
116    ) {
117        unsafe { bffi::SSL_CTX_set_info_callback(self.as_ptr(), cb) }
118    }
119
120    fn set_keylog_callback(
121        &mut self,
122        cb: Option<unsafe extern "C" fn(ssl: *const bffi::SSL, line: *const c_char)>,
123    ) {
124        unsafe { bffi::SSL_CTX_set_keylog_callback(self.as_ptr(), cb) }
125    }
126
127    fn set_certificate(&mut self, cert: X509) -> BoringResult {
128        unsafe {
129            br(bffi::SSL_CTX_use_certificate(self.as_ptr(), cert.as_ptr()))?;
130            mem::forget(cert);
131            Ok(())
132        }
133    }
134
135    fn load_certificate_from_pem_file(&mut self, path: &str) -> BoringResult {
136        let path = ffi::CString::new(path).unwrap();
137        unsafe {
138            br(bffi::SSL_CTX_use_certificate_file(
139                self.as_ptr(),
140                path.as_ptr(),
141                bffi::SSL_FILETYPE_PEM,
142            ))
143        }
144    }
145
146    fn add_to_cert_chain(&mut self, cert: X509) -> BoringResult {
147        unsafe {
148            br(bffi::SSL_CTX_add_extra_chain_cert(self.as_ptr(), cert.as_ptr()) as c_int)?;
149            mem::forget(cert);
150            Ok(())
151        }
152    }
153
154    fn load_cert_chain_from_pem_file(&mut self, path: &str) -> BoringResult {
155        let path = ffi::CString::new(path).unwrap();
156        unsafe {
157            br(bffi::SSL_CTX_use_certificate_chain_file(
158                self.as_ptr(),
159                path.as_ptr(),
160            ))
161        }
162    }
163
164    fn set_private_key<T: HasPrivate>(&mut self, key: PKey<T>) -> BoringResult {
165        unsafe {
166            br(bffi::SSL_CTX_use_PrivateKey(self.as_ptr(), key.as_ptr()))?;
167            mem::forget(key);
168            Ok(())
169        }
170    }
171
172    fn load_private_key_from_pem_file(&mut self, path: &str) -> BoringResult {
173        let path = ffi::CString::new(path).unwrap();
174
175        unsafe {
176            br(bffi::SSL_CTX_use_PrivateKey_file(
177                self.as_ptr(),
178                path.as_ptr(),
179                bffi::SSL_FILETYPE_PEM,
180            ))
181        }
182    }
183
184    fn check_private_key(&self) -> BoringResult {
185        unsafe { br(bffi::SSL_CTX_check_private_key(self.as_ptr())) }
186    }
187
188    fn cert_store_mut(&mut self) -> &mut X509StoreBuilderRef {
189        unsafe { X509StoreBuilderRef::from_ptr_mut(bffi::SSL_CTX_get_cert_store(self.as_ptr())) }
190    }
191
192    fn enable_early_data(&mut self, enable: bool) {
193        unsafe { bffi::SSL_CTX_set_early_data_enabled(self.as_ptr(), enable.into()) }
194    }
195
196    fn set_alpn_protos(&mut self, protos: &[u8]) -> BoringResult {
197        unsafe {
198            br_zero_is_success(bffi::SSL_CTX_set_alpn_protos(
199                self.as_ptr(),
200                protos.as_ptr(),
201                protos.len() as _,
202            ))
203        }
204    }
205
206    fn set_alpn_select_cb(
207        &mut self,
208        cb: Option<
209            unsafe extern "C" fn(
210                *mut bffi::SSL,
211                *mut *const u8,
212                *mut u8,
213                *const u8,
214                c_uint,
215                *mut c_void,
216            ) -> c_int,
217        >,
218    ) {
219        unsafe { bffi::SSL_CTX_set_alpn_select_cb(self.as_ptr(), cb, ptr::null_mut()) }
220    }
221
222    fn set_server_name_cb(
223        &mut self,
224        cb: Option<
225            unsafe extern "C" fn(
226                ssl: *mut bffi::SSL,
227                out_alert: *mut c_int,
228                arg: *mut c_void,
229            ) -> c_int,
230        >,
231    ) {
232        // The function always returns 1.
233        unsafe {
234            let _ = bffi::SSL_CTX_set_tlsext_servername_callback(self.as_ptr(), cb);
235        }
236    }
237
238    fn set_select_certificate_cb(
239        &mut self,
240        cb: Option<
241            unsafe extern "C" fn(
242                arg1: *const bffi::SSL_CLIENT_HELLO,
243            ) -> bffi::ssl_select_cert_result_t,
244        >,
245    ) {
246        unsafe { bffi::SSL_CTX_set_select_certificate_cb(self.as_ptr(), cb) }
247    }
248}
249
250/// Provides additional methods to [Ssl] needed for QUIC.
251pub trait QuicSsl {
252    fn set_connect_state(&mut self);
253    fn set_accept_state(&mut self);
254    fn state_string(&self) -> &'static str;
255    fn set_quic_transport_params(&mut self, params: &[u8]) -> BoringResult;
256    fn get_peer_quic_transport_params(&self) -> Option<&[u8]>;
257    fn get_error(&self, raw: c_int) -> SslError;
258    fn is_handshaking(&self) -> bool;
259    fn do_handshake(&mut self) -> SslError;
260    fn provide_quic_data(&mut self, level: Level, data: &[u8]) -> SslError;
261    fn quic_max_handshake_flight_len(&self, level: Level) -> usize;
262    fn quic_read_level(&self) -> Level;
263    fn quic_write_level(&self) -> Level;
264    fn process_post_handshake(&mut self) -> SslError;
265    fn set_verify_hostname(&mut self, domain: &str) -> BoringResult;
266    fn export_keyring_material(
267        &self,
268        output: &mut [u8],
269        label: &[u8],
270        context: &[u8],
271    ) -> BoringResult;
272
273    fn in_early_data(&self) -> bool;
274    fn early_data_accepted(&self) -> bool;
275    fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult;
276    fn set_quic_early_data_context(&mut self, value: &[u8]) -> BoringResult;
277    fn get_early_data_reason(&self) -> bffi::ssl_early_data_reason_t;
278    fn early_data_reason_string(reason: bffi::ssl_early_data_reason_t) -> &'static str;
279    fn reset_early_rejected_data(&mut self);
280    fn set_quic_use_legacy_codepoint(&mut self, use_legacy: bool);
281}
282
283impl QuicSsl for Ssl {
284    fn set_connect_state(&mut self) {
285        unsafe { bffi::SSL_set_connect_state(self.as_ptr()) }
286    }
287
288    fn set_accept_state(&mut self) {
289        unsafe { bffi::SSL_set_accept_state(self.as_ptr()) }
290    }
291
292    fn state_string(&self) -> &'static str {
293        unsafe {
294            CStr::from_ptr(bffi::SSL_state_string_long(self.as_ptr()))
295                .to_str()
296                .unwrap()
297        }
298    }
299
300    fn set_quic_transport_params(&mut self, params: &[u8]) -> BoringResult {
301        unsafe {
302            br(bffi::SSL_set_quic_transport_params(
303                self.as_ptr(),
304                params.as_ptr(),
305                params.len(),
306            ))
307        }
308    }
309
310    fn get_peer_quic_transport_params(&self) -> Option<&[u8]> {
311        let mut ptr: *const u8 = ptr::null();
312        let mut len: usize = 0;
313
314        unsafe {
315            bffi::SSL_get_peer_quic_transport_params(self.as_ptr(), &mut ptr, &mut len);
316
317            if len == 0 {
318                None
319            } else {
320                Some(slice::from_raw_parts(ptr, len))
321            }
322        }
323    }
324
325    #[inline]
326    fn get_error(&self, raw: c_int) -> SslError {
327        unsafe { SslError(bffi::SSL_get_error(self.as_ptr(), raw)) }
328    }
329
330    #[inline]
331    fn is_handshaking(&self) -> bool {
332        unsafe { bffi::SSL_in_init(self.as_ptr()) == 1 }
333    }
334
335    #[inline]
336    fn do_handshake(&mut self) -> SslError {
337        self.get_error(unsafe { bffi::SSL_do_handshake(self.as_ptr()) })
338    }
339
340    #[inline]
341    fn provide_quic_data(&mut self, level: Level, plaintext: &[u8]) -> SslError {
342        unsafe {
343            self.get_error(bffi::SSL_provide_quic_data(
344                self.as_ptr(),
345                level.into(),
346                plaintext.as_ptr(),
347                plaintext.len(),
348            ))
349        }
350    }
351
352    #[inline]
353    fn quic_max_handshake_flight_len(&self, level: Level) -> usize {
354        unsafe { bffi::SSL_quic_max_handshake_flight_len(self.as_ptr(), level.into()) }
355    }
356
357    #[inline]
358    fn quic_read_level(&self) -> Level {
359        unsafe { bffi::SSL_quic_read_level(self.as_ptr()).into() }
360    }
361
362    #[inline]
363    fn quic_write_level(&self) -> Level {
364        unsafe { bffi::SSL_quic_write_level(self.as_ptr()).into() }
365    }
366
367    #[inline]
368    fn process_post_handshake(&mut self) -> SslError {
369        self.get_error(unsafe { bffi::SSL_process_quic_post_handshake(self.as_ptr()) })
370    }
371
372    fn set_verify_hostname(&mut self, domain: &str) -> BoringResult {
373        let param = self.param_mut();
374        param.set_hostflags(boring::x509::verify::X509CheckFlags::NO_PARTIAL_WILDCARDS);
375        match domain.parse() {
376            Ok(ip) => param.set_ip(ip)?,
377            Err(_) => param.set_host(domain)?,
378        }
379        Ok(())
380    }
381
382    #[inline]
383    fn export_keyring_material(
384        &self,
385        output: &mut [u8],
386        label: &[u8],
387        context: &[u8],
388    ) -> BoringResult {
389        unsafe {
390            br(bffi::SSL_export_keying_material(
391                self.as_ptr(),
392                output.as_mut_ptr(),
393                output.len(),
394                label.as_ptr() as *const c_char,
395                label.len(),
396                context.as_ptr(),
397                context.len(),
398                context.is_empty() as _,
399            ))
400        }
401    }
402
403    #[inline]
404    fn in_early_data(&self) -> bool {
405        unsafe { bffi::SSL_in_early_data(self.as_ptr()) == 1 }
406    }
407
408    #[inline]
409    fn early_data_accepted(&self) -> bool {
410        unsafe { bffi::SSL_early_data_accepted(self.as_ptr()) == 1 }
411    }
412
413    fn set_quic_method(&mut self, method: &bffi::SSL_QUIC_METHOD) -> BoringResult {
414        unsafe { br(bffi::SSL_set_quic_method(self.as_ptr(), method)) }
415    }
416
417    fn set_quic_early_data_context(&mut self, value: &[u8]) -> BoringResult {
418        unsafe {
419            br(bffi::SSL_set_quic_early_data_context(
420                self.as_ptr(),
421                value.as_ptr(),
422                value.len(),
423            ))
424        }
425    }
426
427    fn get_early_data_reason(&self) -> bffi::ssl_early_data_reason_t {
428        unsafe { bffi::SSL_get_early_data_reason(self.as_ptr()) }
429    }
430
431    fn early_data_reason_string(reason: bffi::ssl_early_data_reason_t) -> &'static str {
432        unsafe {
433            bffi::SSL_early_data_reason_string(reason)
434                .as_ref()
435                .map_or("unknown", |reason| CStr::from_ptr(reason).to_str().unwrap())
436        }
437    }
438
439    #[inline]
440    fn reset_early_rejected_data(&mut self) {
441        unsafe { bffi::SSL_reset_early_data_reject(self.as_ptr()) }
442    }
443
444    fn set_quic_use_legacy_codepoint(&mut self, use_legacy: bool) {
445        unsafe { bffi::SSL_set_quic_use_legacy_codepoint(self.as_ptr(), use_legacy as _) }
446    }
447}
448
449pub trait QuicSslSession {
450    fn early_data_capable(&self) -> bool;
451    fn copy_without_early_data(&mut self) -> SslSession;
452    fn encode<W: BufMut>(&self, out: &mut W) -> BoringResult;
453    fn decode<R: Buf>(ctx: &SslContextRef, r: &mut R) -> StdResult<SslSession, ErrorStack>;
454}
455
456impl QuicSslSession for SslSession {
457    fn early_data_capable(&self) -> bool {
458        unsafe { bffi::SSL_SESSION_early_data_capable(self.as_ptr()) == 1 }
459    }
460
461    fn copy_without_early_data(&mut self) -> SslSession {
462        unsafe { SslSession::from_ptr(bffi::SSL_SESSION_copy_without_early_data(self.as_ptr())) }
463    }
464
465    fn encode<W: BufMut>(&self, out: &mut W) -> BoringResult {
466        unsafe {
467            let mut buf: *mut u8 = ptr::null_mut();
468            let mut len = 0usize;
469            br(bffi::SSL_SESSION_to_bytes(
470                self.as_ptr(),
471                &mut buf,
472                &mut len,
473            ))?;
474            out.put_slice(slice::from_raw_parts(buf, len));
475            bffi::OPENSSL_free(buf as _);
476            Ok(())
477        }
478    }
479
480    fn decode<R: Buf>(ctx: &SslContextRef, r: &mut R) -> StdResult<SslSession, ErrorStack> {
481        unsafe {
482            let in_len = r.remaining();
483            let in_ = r.chunk();
484            bffi::SSL_SESSION_from_bytes(in_.as_ptr(), in_len, ctx.as_ptr())
485                .as_mut()
486                .map_or_else(
487                    || Err(ErrorStack::get()),
488                    |session| Ok(SslSession::from_ptr(session)),
489                )
490        }
491    }
492}
493
494#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
495pub enum Level {
496    Initial = 0,
497    EarlyData = 1,
498    Handshake = 2,
499    Application = 3,
500}
501
502impl Level {
503    pub const NUM_LEVELS: usize = 4;
504
505    pub fn next(&self) -> Self {
506        match self {
507            Level::Initial => Level::Handshake,
508            Level::EarlyData => Level::Handshake,
509            _ => Level::Application,
510        }
511    }
512}
513
514impl From<bffi::ssl_encryption_level_t> for Level {
515    fn from(value: bffi::ssl_encryption_level_t) -> Self {
516        match value {
517            bffi::ssl_encryption_level_t::ssl_encryption_initial => Self::Initial,
518            bffi::ssl_encryption_level_t::ssl_encryption_early_data => Self::EarlyData,
519            bffi::ssl_encryption_level_t::ssl_encryption_handshake => Self::Handshake,
520            bffi::ssl_encryption_level_t::ssl_encryption_application => Self::Application,
521            _ => unreachable!(),
522        }
523    }
524}
525
526impl From<Level> for bffi::ssl_encryption_level_t {
527    fn from(value: Level) -> Self {
528        match value {
529            Level::Initial => bffi::ssl_encryption_level_t::ssl_encryption_initial,
530            Level::EarlyData => bffi::ssl_encryption_level_t::ssl_encryption_early_data,
531            Level::Handshake => bffi::ssl_encryption_level_t::ssl_encryption_handshake,
532            Level::Application => bffi::ssl_encryption_level_t::ssl_encryption_application,
533        }
534    }
535}
536
537#[derive(Copy, Clone)]
538pub struct SslError(c_int);
539
540impl SslError {
541    #[inline]
542    pub fn value(&self) -> c_int {
543        self.0
544    }
545
546    #[inline]
547    pub fn is_none(&self) -> bool {
548        self.0 == bffi::SSL_ERROR_NONE
549    }
550
551    #[inline]
552    pub fn get_description(&self) -> &'static str {
553        unsafe {
554            CStr::from_ptr(bffi::SSL_error_description(self.0))
555                .to_str()
556                .unwrap()
557        }
558    }
559}
560
561impl Display for SslError {
562    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
563        write!(f, "SSL_ERROR[{}]: {}", self.0, self.get_description())
564    }
565}