Skip to main content

s2n_tls/
config.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4#[cfg(feature = "unstable-cert_authorities")]
5use crate::cert_authorities::CertificateRequestCallback;
6#[cfg(feature = "unstable-events")]
7use crate::events::{EventSubscriber, HandshakeEvent};
8#[cfg(feature = "unstable-renegotiate")]
9use crate::renegotiate::RenegotiateCallback;
10use crate::{
11    callbacks::*,
12    cert_chain::CertificateChain,
13    enums::*,
14    error::{Error, ErrorType, Fallible},
15    security,
16};
17use core::{convert::TryInto, ptr::NonNull};
18use s2n_tls_sys::*;
19use std::{
20    ffi::{c_void, CString},
21    path::Path,
22    pin::Pin,
23    sync::atomic::{AtomicUsize, Ordering},
24    task::Poll,
25    time::{Duration, SystemTime},
26};
27
28/// Corresponds to [`s2n_config`].
29#[derive(Debug, PartialEq)]
30pub struct Config(NonNull<s2n_config>);
31
32/// # Safety
33///
34/// Safety: s2n_config objects can be sent across threads
35unsafe impl Send for Config {}
36
37/// # Safety
38///
39/// Safety: All C methods that mutate the s2n_config are wrapped
40/// in Rust methods that require a mutable reference.
41unsafe impl Sync for Config {}
42
43impl Config {
44    /// Returns a Config object with pre-defined defaults.
45    ///
46    /// Use the [`Builder`] if custom configuration is desired.
47    ///
48    /// # Warning
49    ///
50    /// The newly created Config will use the default security policy.
51    /// Consider changing this depending on your security and compatibility requirements
52    /// by using [`Builder`] and [`Builder::set_security_policy`].
53    /// See the s2n-tls usage guide:
54    /// <https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html>
55    pub fn new() -> Self {
56        Self::default()
57    }
58
59    /// Returns a Builder which can be used to configure the Config
60    pub fn builder() -> Builder {
61        Builder::default()
62    }
63
64    /// # Safety
65    ///
66    /// This config _MUST_ have been initialized with a [`Builder`].
67    /// Additionally, this does NOT increment the config reference count,
68    /// so consider cloning the result if the source pointer is still
69    /// valid and usable afterwards.
70    pub(crate) unsafe fn from_raw(config: NonNull<s2n_config>) -> Self {
71        let config = Self(config);
72
73        // Check if the context can be retrieved.
74        // If it can't, this is not a valid config.
75        config.context();
76
77        config
78    }
79
80    pub(crate) fn as_mut_ptr(&mut self) -> *mut s2n_config {
81        self.0.as_ptr()
82    }
83
84    /// Retrieve a reference to the [`Context`] stored on the config.
85    ///
86    /// Corresponds to [`s2n_config_get_ctx`].
87    pub(crate) fn context(&self) -> &Context {
88        let mut ctx = core::ptr::null_mut();
89        unsafe {
90            s2n_config_get_ctx(self.0.as_ptr(), &mut ctx)
91                .into_result()
92                .unwrap();
93            &*(ctx as *const Context)
94        }
95    }
96
97    /// Retrieve a mutable reference to the [`Context`] stored on the config.
98    ///
99    /// Corresponds to [`s2n_config_get_ctx`].
100    ///
101    /// SAFETY: There must only ever by mutable reference to `Context` alive at
102    ///         any time. Configs can be shared across threads, so this method is
103    ///         likely not correct for your usecase.
104    pub(crate) unsafe fn context_mut(&mut self) -> &mut Context {
105        let mut ctx = core::ptr::null_mut();
106        s2n_config_get_ctx(self.as_mut_ptr(), &mut ctx)
107            .into_result()
108            .unwrap();
109        &mut *(ctx as *mut Context)
110    }
111
112    #[cfg(test)]
113    /// Get the refcount associated with the config
114    pub fn test_get_refcount(&self) -> Result<usize, Error> {
115        let context = self.context();
116        Ok(context.refcount.load(Ordering::SeqCst))
117    }
118}
119
120impl Default for Config {
121    fn default() -> Self {
122        Builder::new().build().unwrap()
123    }
124}
125
126impl Clone for Config {
127    fn clone(&self) -> Self {
128        let context = self.context();
129
130        // Safety
131        //
132        // Using a relaxed ordering is alright here, as knowledge of the
133        // original reference prevents other threads from erroneously deleting
134        // the object.
135        // https://github.com/rust-lang/rust/blob/e012a191d768adeda1ee36a99ef8b92d51920154/library/alloc/src/sync.rs#L1329
136        let _count = context.refcount.fetch_add(1, Ordering::Relaxed);
137        Self(self.0)
138    }
139}
140
141impl Drop for Config {
142    /// Corresponds to [`s2n_config_free`].
143    fn drop(&mut self) {
144        let context = self.context();
145        let count = context.refcount.fetch_sub(1, Ordering::Release);
146        debug_assert!(count > 0, "refcount should not drop below 1 instance");
147
148        // only free the config if this is the last instance
149        if count != 1 {
150            return;
151        }
152
153        // Safety
154        //
155        // The use of Ordering and fence mirrors the `Arc` implementation in
156        // the standard library.
157        //
158        // This fence is needed to prevent reordering of use of the data and
159        // deletion of the data.  Because it is marked `Release`, the decreasing
160        // of the reference count synchronizes with this `Acquire` fence. This
161        // means that use of the data happens before decreasing the reference
162        // count, which happens before this fence, which happens before the
163        // deletion of the data.
164        // https://github.com/rust-lang/rust/blob/e012a191d768adeda1ee36a99ef8b92d51920154/library/alloc/src/sync.rs#L1637
165        std::sync::atomic::fence(Ordering::Acquire);
166
167        // This is the last instance so free the context.
168        let context = unsafe {
169            // SAFETY: The reference count is verified to be 1, so this is the
170            // last instance of the config, and the only reference to the context.
171            Box::from_raw(self.context_mut())
172        };
173        drop(context);
174
175        let _ = unsafe { s2n_config_free(self.0.as_ptr()).into_result() };
176    }
177}
178
179pub struct Builder {
180    pub(crate) config: Config,
181    load_system_certs: bool,
182    enable_ocsp: bool,
183}
184
185impl Builder {
186    /// # Warning
187    ///
188    /// The newly created Builder will create Configs that use the default security policy.
189    /// Consider changing this depending on your security and compatibility requirements
190    /// by calling [`Builder::set_security_policy`].
191    /// See the s2n-tls usage guide:
192    /// <https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html>
193    ///
194    /// Corresponds to [`s2n_config_new_minimal`],
195    /// but also calls [`s2n_config_set_client_hello_cb_mode`] to set the client
196    /// hello callback to non-blocking mode.
197    pub fn new() -> Self {
198        crate::init::init();
199        let config = unsafe { s2n_config_new_minimal().into_result() }.unwrap();
200
201        let context = Box::<Context>::default();
202        let context = Box::into_raw(context) as *mut c_void;
203
204        unsafe {
205            s2n_config_set_ctx(config.as_ptr(), context)
206                .into_result()
207                .unwrap();
208
209            // The client hello callback originally did not support async operations,
210            // so defaults to blocking mode for backwards compatibility with old integrations.
211            // But these bindings use a polling model, so assume non-blocking mode.
212            s2n_config_set_client_hello_cb_mode(
213                config.as_ptr(),
214                s2n_client_hello_cb_mode::NONBLOCKING,
215            )
216            .into_result()
217            .unwrap();
218        }
219
220        Self {
221            config: Config(config),
222            load_system_certs: true,
223            enable_ocsp: false,
224        }
225    }
226
227    /// Corresponds to [`s2n_config_set_alert_behavior`].
228    pub fn set_alert_behavior(&mut self, value: AlertBehavior) -> Result<&mut Self, Error> {
229        unsafe { s2n_config_set_alert_behavior(self.as_mut_ptr(), value.into()).into_result() }?;
230        Ok(self)
231    }
232
233    /// Corresponds to [`s2n_config_set_cipher_preferences`].
234    pub fn set_security_policy(&mut self, policy: &security::Policy) -> Result<&mut Self, Error> {
235        unsafe {
236            s2n_config_set_cipher_preferences(self.as_mut_ptr(), policy.as_cstr().as_ptr())
237                .into_result()
238        }?;
239        Ok(self)
240    }
241
242    /// sets the application protocol preferences on an s2n_config object.
243    ///
244    /// protocols is a list in order of preference, with most preferred protocol first,
245    /// and of length protocol_count. When acting as a client the protocol list is
246    /// included in the Client Hello message as the ALPN extension. As a server, the
247    /// list is used to negotiate a mutual application protocol with the client. After
248    /// the negotiation for the connection has completed, the agreed upon protocol can
249    /// be retrieved with s2n_get_application_protocol
250    ///
251    /// Corresponds to [`s2n_config_set_protocol_preferences`].
252    pub fn set_application_protocol_preference<P: IntoIterator<Item = I>, I: AsRef<[u8]>>(
253        &mut self,
254        protocols: P,
255    ) -> Result<&mut Self, Error> {
256        // reset the list
257        unsafe {
258            s2n_config_set_protocol_preferences(self.as_mut_ptr(), core::ptr::null(), 0)
259                .into_result()
260        }?;
261
262        for protocol in protocols {
263            self.append_application_protocol_preference(protocol.as_ref())?;
264        }
265
266        Ok(self)
267    }
268
269    /// Corresponds to [`s2n_config_append_protocol_preference`].
270    pub fn append_application_protocol_preference(
271        &mut self,
272        protocol: &[u8],
273    ) -> Result<&mut Self, Error> {
274        unsafe {
275            s2n_config_append_protocol_preference(
276                self.as_mut_ptr(),
277                protocol.as_ptr(),
278                protocol
279                    .len()
280                    .try_into()
281                    .map_err(|_| Error::INVALID_INPUT)?,
282            )
283            .into_result()
284        }?;
285        Ok(self)
286    }
287
288    /// Turns off x509 verification
289    ///
290    /// # Safety
291    /// This functionality will weaken the security of the connections. As such, it should only
292    /// be used in development environments where obtaining a valid certificate would not be possible.
293    ///
294    /// Corresponds to [`s2n_config_disable_x509_verification`].
295    pub unsafe fn disable_x509_verification(&mut self) -> Result<&mut Self, Error> {
296        s2n_config_disable_x509_verification(self.as_mut_ptr()).into_result()?;
297        Ok(self)
298    }
299
300    /// Turns off x509 intent verification
301    ///
302    /// # Safety
303    /// This API disables the x509 intent verification functionality. It should only
304    /// be used when updating an incompatible certificate would not be possible.
305    ///
306    /// Corresponds to [`s2n_config_disable_x509_intent_verification`].
307    pub fn disable_x509_intent_verification(&mut self) -> Result<&mut Self, Error> {
308        unsafe { s2n_config_disable_x509_intent_verification(self.as_mut_ptr()).into_result() }?;
309        Ok(self)
310    }
311
312    /// Corresponds to [`s2n_config_add_dhparams`].
313    pub fn add_dhparams(&mut self, pem: &[u8]) -> Result<&mut Self, Error> {
314        let cstring = CString::new(pem).map_err(|_| Error::INVALID_INPUT)?;
315        unsafe { s2n_config_add_dhparams(self.as_mut_ptr(), cstring.as_ptr()).into_result() }?;
316        Ok(self)
317    }
318
319    /// Associate a `certificate` and corresponding `private_key` with a config.
320    /// Using this method, at most one certificate per auth type (ECDSA, RSA, RSA-PSS)
321    /// can be loaded.
322    ///
323    /// For more advanced cert use cases such as sharing certs across configs or
324    /// serving different certs based on the client SNI, see [Builder::load_chain].
325    ///
326    /// Corresponds to [`s2n_config_add_cert_chain_and_key`].
327    pub fn load_pem(&mut self, certificate: &[u8], private_key: &[u8]) -> Result<&mut Self, Error> {
328        let certificate = CString::new(certificate).map_err(|_| Error::INVALID_INPUT)?;
329        let private_key = CString::new(private_key).map_err(|_| Error::INVALID_INPUT)?;
330        unsafe {
331            s2n_config_add_cert_chain_and_key(
332                self.as_mut_ptr(),
333                certificate.as_ptr(),
334                private_key.as_ptr(),
335            )
336            .into_result()
337        }?;
338        Ok(self)
339    }
340
341    /// Corresponds to [`s2n_config_add_cert_chain_and_key_to_store`].
342    pub fn load_chain(&mut self, chain: CertificateChain<'static>) -> Result<&mut Self, Error> {
343        // Out of an abundance of caution, we hold a reference to the CertificateChain
344        // regardless of whether add_to_store fails or succeeds. We have limited
345        // visibility into the failure modes, so this behavior ensures that _if_
346        // the C library held the reference despite the failure, it would continue
347        // to be valid memory.
348        let result = unsafe {
349            s2n_config_add_cert_chain_and_key_to_store(
350                self.as_mut_ptr(),
351                // SAFETY: audit of add_to_store shows that the certificate chain
352                // is not mutated. https://github.com/aws/s2n-tls/issues/4140
353                chain.as_ptr() as *mut _,
354            )
355            .into_result()
356        };
357        let context = unsafe {
358            // SAFETY: usage of context_mut is safe in the builder, because the
359            // Builder owns the only reference to the config.
360            self.config.context_mut()
361        };
362        context.application_owned_certs.push(chain);
363        result?;
364
365        Ok(self)
366    }
367
368    /// Corresponds to [`s2n_config_set_cert_chain_and_key_defaults`].
369    pub fn set_default_chains<T: IntoIterator<Item = CertificateChain<'static>>>(
370        &mut self,
371        chains: T,
372    ) -> Result<&mut Self, Error> {
373        // Must be equal to S2N_CERT_TYPE_COUNT in s2n_certificate.h.
374        const CHAINS_MAX_COUNT: usize = 4;
375
376        let mut chain_arrays: [Option<CertificateChain<'static>>; CHAINS_MAX_COUNT] =
377            [None, None, None, None];
378        let mut pointer_array = [std::ptr::null_mut(); CHAINS_MAX_COUNT];
379        let mut cert_chain_count = 0;
380
381        for chain in chains.into_iter() {
382            if cert_chain_count >= CHAINS_MAX_COUNT {
383                return Err(Error::bindings(
384                    ErrorType::UsageError,
385                    "InvalidInput",
386                    "A single default can be specified for each supported
387                    cert type, but more than 4 certs were supplied",
388                ));
389            }
390
391            // SAFETY: manual inspection of set_defaults shows that certificates
392            // are not mutated. https://github.com/aws/s2n-tls/issues/4140
393            pointer_array[cert_chain_count] = chain.as_ptr() as *mut _;
394            chain_arrays[cert_chain_count] = Some(chain);
395
396            cert_chain_count += 1;
397        }
398
399        let collected_chains = chain_arrays.into_iter().take(cert_chain_count).flatten();
400
401        let context = unsafe {
402            // SAFETY: usage of context_mut is safe in the builder, because the
403            // Builder owns the only reference to the config.
404            self.config.context_mut()
405        };
406        context.application_owned_certs.extend(collected_chains);
407
408        unsafe {
409            s2n_config_set_cert_chain_and_key_defaults(
410                self.as_mut_ptr(),
411                pointer_array.as_mut_ptr(),
412                cert_chain_count as u32,
413            )
414            .into_result()
415        }?;
416
417        Ok(self)
418    }
419
420    /// Corresponds to [`s2n_config_add_cert_chain`].
421    pub fn load_public_pem(&mut self, certificate: &[u8]) -> Result<&mut Self, Error> {
422        let size: u32 = certificate
423            .len()
424            .try_into()
425            .map_err(|_| Error::INVALID_INPUT)?;
426        let certificate = certificate.as_ptr() as *mut u8;
427        unsafe { s2n_config_add_cert_chain(self.as_mut_ptr(), certificate, size) }.into_result()?;
428        Ok(self)
429    }
430
431    /// Corresponds to [`s2n_config_add_pem_to_trust_store`].
432    pub fn trust_pem(&mut self, certificate: &[u8]) -> Result<&mut Self, Error> {
433        let certificate = CString::new(certificate).map_err(|_| Error::INVALID_INPUT)?;
434        unsafe {
435            s2n_config_add_pem_to_trust_store(self.as_mut_ptr(), certificate.as_ptr()).into_result()
436        }?;
437        Ok(self)
438    }
439
440    /// Adds to the trust store from a CA file or directory containing trusted certificates.
441    ///
442    /// Corresponds to [`s2n_config_set_verification_ca_location`], except it
443    /// calls [`s2n_config_set_status_request_type`] with NONE to avoid
444    /// automatically enabling OCSP stapling.
445    pub fn trust_location(
446        &mut self,
447        file: Option<&Path>,
448        dir: Option<&Path>,
449    ) -> Result<&mut Self, Error> {
450        fn to_cstr(input: Option<&Path>) -> Result<Option<CString>, Error> {
451            Ok(match input {
452                Some(input) => {
453                    let string = input.to_str().ok_or(Error::INVALID_INPUT)?;
454                    let cstring = CString::new(string).map_err(|_| Error::INVALID_INPUT)?;
455                    Some(cstring)
456                }
457                None => None,
458            })
459        }
460
461        let file_cstr = to_cstr(file)?;
462        let file_ptr = file_cstr
463            .as_ref()
464            .map(|f| f.as_ptr())
465            .unwrap_or(core::ptr::null());
466
467        let dir_cstr = to_cstr(dir)?;
468        let dir_ptr = dir_cstr
469            .as_ref()
470            .map(|f| f.as_ptr())
471            .unwrap_or(core::ptr::null());
472
473        unsafe {
474            s2n_config_set_verification_ca_location(self.as_mut_ptr(), file_ptr, dir_ptr)
475                .into_result()
476        }?;
477
478        // If OCSP has not been explicitly requested, turn off OCSP. This is to prevent this function from
479        // automatically enabling `OCSP` due to the legacy behavior of `s2n_config_set_verification_ca_location`
480        if !self.enable_ocsp {
481            unsafe {
482                s2n_config_set_status_request_type(self.as_mut_ptr(), s2n_status_request_type::NONE)
483                    .into_result()?
484            };
485        }
486
487        Ok(self)
488    }
489
490    /// Sets whether or not default system certificates will be loaded into the trust store.
491    ///
492    /// Set to false for increased performance if system certificates are not needed during
493    /// certificate validation.
494    ///
495    /// Corresponds to [`s2n_config_load_system_certs`].
496    pub fn with_system_certs(&mut self, load_system_certs: bool) -> Result<&mut Self, Error> {
497        self.load_system_certs = load_system_certs;
498        Ok(self)
499    }
500
501    /// Corresponds to [`s2n_config_wipe_trust_store`].
502    pub fn wipe_trust_store(&mut self) -> Result<&mut Self, Error> {
503        unsafe { s2n_config_wipe_trust_store(self.as_mut_ptr()).into_result()? };
504        Ok(self)
505    }
506
507    /// Sets whether or not a client certificate should be required to complete the TLS connection.
508    ///
509    /// See the [Usage Guide](https://github.com/aws/s2n-tls/blob/main/docs/USAGE-GUIDE.md#client-auth-related-calls) for more details.
510    ///
511    /// Corresponds to [`s2n_config_set_client_auth_type`].
512    pub fn set_client_auth_type(&mut self, auth_type: ClientAuthType) -> Result<&mut Self, Error> {
513        unsafe {
514            s2n_config_set_client_auth_type(self.as_mut_ptr(), auth_type.into()).into_result()
515        }?;
516        Ok(self)
517    }
518
519    /// Clients will request OCSP stapling from the server.
520    ///
521    /// Corresponds to [`s2n_config_set_status_request_type`].
522    pub fn enable_ocsp(&mut self) -> Result<&mut Self, Error> {
523        unsafe {
524            s2n_config_set_status_request_type(self.as_mut_ptr(), s2n_status_request_type::OCSP)
525                .into_result()
526        }?;
527        self.enable_ocsp = true;
528        Ok(self)
529    }
530
531    /// Sets the OCSP data for the default certificate chain associated with the Config.
532    ///
533    /// Servers will send the data in response to OCSP stapling requests from clients.
534    ///
535    /// Corresponds to [`s2n_config_set_extension_data`] with OCSP_STAPLING.
536    //
537    // NOTE: this modifies a certificate chain, NOT the Config itself. This is currently safe
538    // because the certificate chain is set with s2n_config_add_cert_chain_and_key, which
539    // creates a new certificate chain only accessible by the given config. It will
540    // NOT be safe when we add support for the newer s2n_config_add_cert_chain_and_key_to_store API,
541    // which allows certificate chains to be shared across configs.
542    // In that case, we'll need additional guard rails either in these bindings or in the underlying C.
543    pub fn set_ocsp_data(&mut self, data: &[u8]) -> Result<&mut Self, Error> {
544        let size: u32 = data.len().try_into().map_err(|_| Error::INVALID_INPUT)?;
545        unsafe {
546            s2n_config_set_extension_data(
547                self.as_mut_ptr(),
548                s2n_tls_extension_type::OCSP_STAPLING,
549                data.as_ptr(),
550                size,
551            )
552            .into_result()
553        }?;
554        self.enable_ocsp()
555    }
556
557    /// Sets the callback to use for verifying that a hostname from an X.509 certificate is
558    /// trusted.
559    ///
560    /// The callback may be called more than once during certificate validation as each SAN on
561    /// the certificate will be checked.
562    ///
563    /// Corresponds to [`s2n_config_set_verify_host_callback`].
564    pub fn set_verify_host_callback<T: 'static + VerifyHostNameCallback>(
565        &mut self,
566        handler: T,
567    ) -> Result<&mut Self, Error> {
568        unsafe extern "C" fn verify_host_cb_fn(
569            host_name: *const ::libc::c_char,
570            host_name_len: usize,
571            context: *mut ::libc::c_void,
572        ) -> u8 {
573            let context = &mut *(context as *mut Context);
574            let handler = context.verify_host_callback.as_mut().unwrap();
575            verify_host(host_name, host_name_len, handler)
576        }
577
578        let context = unsafe {
579            // SAFETY: usage of context_mut is safe in the builder, because the
580            // Builder owns the only reference to the config.
581            self.config.context_mut()
582        };
583        context.verify_host_callback = Some(Box::new(handler));
584        unsafe {
585            s2n_config_set_verify_host_callback(
586                self.as_mut_ptr(),
587                Some(verify_host_cb_fn),
588                self.config.context() as *const Context as *mut c_void,
589            )
590            .into_result()?;
591        }
592        Ok(self)
593    }
594
595    /// # Safety
596    /// THIS SHOULD BE USED FOR DEBUGGING PURPOSES ONLY!
597    /// The `context` pointer must live at least as long as the config
598    ///
599    /// Corresponds to [`s2n_config_set_key_log_cb`].
600    pub unsafe fn set_key_log_callback(
601        &mut self,
602        callback: s2n_key_log_fn,
603        context: *mut core::ffi::c_void,
604    ) -> Result<&mut Self, Error> {
605        s2n_config_set_key_log_cb(self.as_mut_ptr(), callback, context).into_result()?;
606        Ok(self)
607    }
608
609    /// Corresponds to [`s2n_config_set_max_cert_chain_depth`].
610    pub fn set_max_cert_chain_depth(&mut self, depth: u16) -> Result<&mut Self, Error> {
611        unsafe { s2n_config_set_max_cert_chain_depth(self.as_mut_ptr(), depth).into_result() }?;
612        Ok(self)
613    }
614
615    /// Corresponds to [`s2n_config_set_send_buffer_size`].
616    pub fn set_send_buffer_size(&mut self, size: u32) -> Result<&mut Self, Error> {
617        unsafe { s2n_config_set_send_buffer_size(self.as_mut_ptr(), size).into_result() }?;
618        Ok(self)
619    }
620
621    /// Corresponds to [`s2n_config_add_custom_x509_extension`].
622    #[cfg(feature = "unstable-custom_x509_extensions")]
623    pub fn add_custom_x509_extension(&mut self, extension_oid: &str) -> Result<&mut Self, Error> {
624        let extension_oid_len: u32 = extension_oid
625            .len()
626            .try_into()
627            .map_err(|_| Error::INVALID_INPUT)?;
628        let extension_oid = extension_oid.as_ptr() as *mut u8;
629        unsafe {
630            s2n_config_add_custom_x509_extension(
631                self.as_mut_ptr(),
632                extension_oid,
633                extension_oid_len,
634            )
635            .into_result()
636        }?;
637        Ok(self)
638    }
639
640    /// Set a callback function to perform custom cert validation synchronously.
641    ///
642    /// Corresponds to [`s2n_config_set_cert_validation_cb`], but the rust callback
643    /// can only perform in synchronous mode.
644    #[cfg(feature = "unstable-crl")]
645    pub fn set_cert_validation_callback_sync<T: 'static + CertValidationCallbackSync>(
646        &mut self,
647        handler: T,
648    ) -> Result<&mut Self, Error> {
649        unsafe extern "C" fn cert_validation_cb(
650            conn_ptr: *mut s2n_connection,
651            validation_info: *mut s2n_cert_validation_info,
652            _context: *mut core::ffi::c_void,
653        ) -> libc::c_int {
654            let mut info = CertValidationInfo::from_ptr(validation_info);
655            with_context(conn_ptr, |conn, context| {
656                let callback = context.cert_validation_callback_sync.as_ref();
657                callback.map(|callback| {
658                    let accepted = callback.handle_validation(conn, &mut info).unwrap();
659                    match accepted {
660                        true => info.accept().unwrap(),
661                        false => info.reject().unwrap(),
662                    }
663                })
664            });
665            CallbackResult::Success.into()
666        }
667
668        let handler = Box::new(handler);
669        let context = unsafe {
670            // SAFETY: usage of context_mut is safe in the builder, because while
671            // it is being built, the Builder is the only reference to the config.
672            self.config.context_mut()
673        };
674        context.cert_validation_callback_sync = Some(handler);
675
676        unsafe {
677            s2n_config_set_cert_validation_cb(
678                self.as_mut_ptr(),
679                Some(cert_validation_cb),
680                core::ptr::null_mut(),
681            )
682            .into_result()?;
683        }
684
685        Ok(self)
686    }
687
688    /// Set a custom callback function which is run after parsing the client hello.
689    ///
690    /// Corresponds to [`s2n_config_set_client_hello_cb`].
691    pub fn set_client_hello_callback<T: 'static + ClientHelloCallback>(
692        &mut self,
693        handler: T,
694    ) -> Result<&mut Self, Error> {
695        unsafe extern "C" fn client_hello_cb(
696            connection_ptr: *mut s2n_connection,
697            _context: *mut core::ffi::c_void,
698        ) -> libc::c_int {
699            with_context(connection_ptr, |conn, context| {
700                let callback = context.client_hello_callback.as_ref();
701                let future = callback
702                    .map(|c| c.on_client_hello(conn))
703                    .unwrap_or(Ok(None));
704                AsyncCallback::trigger_client_hello_cb(future, conn)
705            })
706            .into()
707        }
708
709        let handler = Box::new(handler);
710        let context = unsafe {
711            // SAFETY: usage of context_mut is safe in the builder, because while
712            // it is being built, the Builder is the only reference to the config.
713            self.config.context_mut()
714        };
715        context.client_hello_callback = Some(handler);
716
717        unsafe {
718            s2n_config_set_client_hello_cb(
719                self.as_mut_ptr(),
720                Some(client_hello_cb),
721                core::ptr::null_mut(),
722            )
723            .into_result()?;
724        }
725
726        Ok(self)
727    }
728
729    /// Corresponds to [`s2n_config_set_subscriber`] and [`s2n_config_set_handshake_event`].
730    #[cfg(feature = "unstable-events")]
731    pub fn set_event_subscriber<T: 'static + EventSubscriber>(
732        &mut self,
733        subscriber: T,
734    ) -> Result<&mut Self, Error> {
735        unsafe extern "C" fn on_handshake_event(
736            conn_ptr: *mut s2n_tls_sys::s2n_connection,
737            _subscriber: *mut c_void,
738            event: *mut s2n_tls_sys::s2n_event_handshake,
739        ) {
740            with_context(conn_ptr, |conn, context| {
741                let callback = context.event_subscriber.as_ref();
742                if let Some(callback) = callback {
743                    callback.on_handshake_event(conn, &HandshakeEvent::new(&*event));
744                }
745            });
746        }
747
748        let handler = Box::new(subscriber);
749        let context = unsafe {
750            // SAFETY: usage of context_mut is safe in the builder, because while
751            // it is being built, the Builder is the only reference to the config.
752            self.config.context_mut()
753        };
754        context.event_subscriber = Some(handler);
755
756        unsafe {
757            s2n_config_set_subscriber(
758                self.as_mut_ptr(),
759                self.config.context_mut() as *mut Context as *mut c_void,
760            )
761        };
762
763        unsafe {
764            s2n_config_set_handshake_event(self.as_mut_ptr(), Some(on_handshake_event))
765                .into_result()
766        }?;
767        Ok(self)
768    }
769
770    pub fn set_connection_initializer<T: 'static + ConnectionInitializer>(
771        &mut self,
772        handler: T,
773    ) -> Result<&mut Self, Error> {
774        // Store callback in config context
775        let handler = Box::new(handler);
776        let context = unsafe {
777            // SAFETY: usage of context_mut is safe in the builder, because while
778            // it is being built, the Builder is the only reference to the config.
779            self.config.context_mut()
780        };
781        context.connection_initializer = Some(handler);
782        Ok(self)
783    }
784
785    /// Sets a custom callback which provides access to session tickets when they arrive
786    ///
787    /// Corresponds to [`s2n_config_set_session_ticket_cb`].
788    pub fn set_session_ticket_callback<T: 'static + SessionTicketCallback>(
789        &mut self,
790        handler: T,
791    ) -> Result<&mut Self, Error> {
792        // enable session tickets automatically
793        self.enable_session_tickets(true)?;
794
795        // Define C callback function that can be set on the s2n_config struct
796        unsafe extern "C" fn session_ticket_cb(
797            conn_ptr: *mut s2n_connection,
798            _context: *mut ::libc::c_void,
799            session_ticket: *mut s2n_session_ticket,
800        ) -> libc::c_int {
801            let session_ticket = SessionTicket::from_ptr(&*session_ticket);
802            with_context(conn_ptr, |conn, context| {
803                let callback = context.session_ticket_callback.as_ref();
804                callback.map(|c| c.on_session_ticket(conn, session_ticket))
805            });
806            CallbackResult::Success.into()
807        }
808
809        // Store callback in context
810        let handler = Box::new(handler);
811        let context = unsafe {
812            // SAFETY: usage of context_mut is safe in the builder, because while
813            // it is being built, the Builder is the only reference to the config.
814            self.config.context_mut()
815        };
816        context.session_ticket_callback = Some(handler);
817
818        unsafe {
819            s2n_config_set_session_ticket_cb(
820                self.as_mut_ptr(),
821                Some(session_ticket_cb),
822                self.config.context() as *const Context as *mut c_void,
823            )
824            .into_result()
825        }?;
826        Ok(self)
827    }
828
829    /// Set a callback function triggered by operations requiring the private key.
830    ///
831    /// See https://github.com/aws/s2n-tls/blob/main/docs/USAGE-GUIDE.md#private-key-operation-related-calls
832    ///
833    /// Corresponds to [`s2n_config_set_async_pkey_callback`].
834    pub fn set_private_key_callback<T: 'static + PrivateKeyCallback>(
835        &mut self,
836        handler: T,
837    ) -> Result<&mut Self, Error> {
838        unsafe extern "C" fn private_key_cb(
839            conn_ptr: *mut s2n_connection,
840            op_ptr: *mut s2n_async_pkey_op,
841        ) -> libc::c_int {
842            with_context(conn_ptr, |conn, context| {
843                let state = PrivateKeyOperation::try_from_cb(conn, op_ptr);
844                let callback = context.private_key_callback.as_ref();
845                let future_result = state.and_then(|state| {
846                    callback.map_or(Ok(None), |callback| callback.handle_operation(conn, state))
847                });
848                AsyncCallback::trigger(future_result, conn)
849            })
850            .into()
851        }
852
853        let handler = Box::new(handler);
854        let context = unsafe {
855            // SAFETY: usage of context_mut is safe in the builder, because while
856            // it is being built, the Builder is the only reference to the config.
857            self.config.context_mut()
858        };
859        context.private_key_callback = Some(handler);
860
861        unsafe {
862            s2n_config_set_async_pkey_callback(self.as_mut_ptr(), Some(private_key_cb))
863                .into_result()?;
864        }
865        Ok(self)
866    }
867
868    /// Set a callback function that will be used to get the system time.
869    ///
870    /// The wall clock time is the best-guess at the real time, measured since the epoch.
871    /// Unlike monotonic time, it CAN move backwards.
872    /// It is used by s2n-tls for timestamps.
873    ///
874    /// Corresponds to [`s2n_config_set_wall_clock`].
875    pub fn set_wall_clock<T: 'static + WallClock>(
876        &mut self,
877        handler: T,
878    ) -> Result<&mut Self, Error> {
879        unsafe extern "C" fn clock_cb(
880            context: *mut ::libc::c_void,
881            time_in_nanos: *mut u64,
882        ) -> libc::c_int {
883            let context = &mut *(context as *mut Context);
884            if let Some(handler) = context.wall_clock.as_mut() {
885                if let Ok(nanos) = handler.get_time_since_epoch().as_nanos().try_into() {
886                    *time_in_nanos = nanos;
887                    return CallbackResult::Success.into();
888                }
889            }
890            CallbackResult::Failure.into()
891        }
892
893        let handler = Box::new(handler);
894        let context = unsafe {
895            // SAFETY: usage of context_mut is safe in the builder, because while
896            // it is being built, the Builder is the only reference to the config.
897            self.config.context_mut()
898        };
899        context.wall_clock = Some(handler);
900        unsafe {
901            s2n_config_set_wall_clock(
902                self.as_mut_ptr(),
903                Some(clock_cb),
904                self.config.context() as *const _ as *mut c_void,
905            )
906            .into_result()?;
907        }
908        Ok(self)
909    }
910
911    /// Set a callback function that will be used to get the monotonic time.
912    ///
913    /// The monotonic time is the time since an arbitrary, unspecified point.
914    /// Unlike wall clock time, it MUST never move backwards.
915    /// It is used by s2n-tls for timers.
916    ///
917    /// Corresponds to [`s2n_config_set_monotonic_clock`].
918    pub fn set_monotonic_clock<T: 'static + MonotonicClock>(
919        &mut self,
920        handler: T,
921    ) -> Result<&mut Self, Error> {
922        unsafe extern "C" fn clock_cb(
923            context: *mut ::libc::c_void,
924            time_in_nanos: *mut u64,
925        ) -> libc::c_int {
926            let context = &mut *(context as *mut Context);
927            if let Some(handler) = context.monotonic_clock.as_mut() {
928                if let Ok(nanos) = handler.get_time().as_nanos().try_into() {
929                    *time_in_nanos = nanos;
930                    return CallbackResult::Success.into();
931                }
932            }
933            CallbackResult::Failure.into()
934        }
935
936        let handler = Box::new(handler);
937        let context = unsafe {
938            // SAFETY: usage of context_mut is safe in the builder, because while
939            // it is being built, the Builder is the only reference to the config.
940            self.config.context_mut()
941        };
942        context.monotonic_clock = Some(handler);
943        unsafe {
944            s2n_config_set_monotonic_clock(
945                self.as_mut_ptr(),
946                Some(clock_cb),
947                self.config.context() as *const _ as *mut c_void,
948            )
949            .into_result()?;
950        }
951        Ok(self)
952    }
953
954    /// Enable negotiating session tickets in a TLS connection
955    ///
956    /// Corresponds to [`s2n_config_set_session_tickets_onoff`].
957    pub fn enable_session_tickets(&mut self, enable: bool) -> Result<&mut Self, Error> {
958        unsafe {
959            s2n_config_set_session_tickets_onoff(self.as_mut_ptr(), enable.into()).into_result()
960        }?;
961        Ok(self)
962    }
963
964    /// Adds a key which will be used to encrypt and decrypt session tickets. The intro_time parameter is time since
965    /// the Unix epoch (Midnight, January 1st, 1970). The key must be at least 16 bytes.
966    ///
967    /// Corresponds to [`s2n_config_add_ticket_crypto_key`], but also
968    /// automatically calls [Builder::enable_session_tickets].
969    pub fn add_session_ticket_key(
970        &mut self,
971        key_name: &[u8],
972        key: &[u8],
973        intro_time: SystemTime,
974    ) -> Result<&mut Self, Error> {
975        let key_name_len: u32 = key_name
976            .len()
977            .try_into()
978            .map_err(|_| Error::INVALID_INPUT)?;
979        let key_len: u32 = key.len().try_into().map_err(|_| Error::INVALID_INPUT)?;
980        let intro_time = intro_time
981            .duration_since(std::time::UNIX_EPOCH)
982            .map_err(|_| Error::INVALID_INPUT)?;
983        // Ticket keys should be at least 128 bits in strength
984        // https://www.rfc-editor.org/rfc/rfc5077#section-5.5
985        if key_len < 16 {
986            return Err(Error::INVALID_INPUT);
987        }
988        self.enable_session_tickets(true)?;
989        unsafe {
990            s2n_config_add_ticket_crypto_key(
991                self.as_mut_ptr(),
992                key_name.as_ptr(),
993                key_name_len,
994                // s2n-tls doesn't mutate key, it's just mut for easier use with stuffers and blobs
995                key.as_ptr() as *mut u8,
996                key_len,
997                intro_time.as_secs(),
998            )
999            .into_result()
1000        }?;
1001        Ok(self)
1002    }
1003
1004    // Sets how long a session ticket key will be able to be used for both encryption
1005    // and decryption of tickets
1006    ///
1007    /// Corresponds to [`s2n_config_set_ticket_encrypt_decrypt_key_lifetime`].
1008    pub fn set_ticket_key_encrypt_decrypt_lifetime(
1009        &mut self,
1010        lifetime: Duration,
1011    ) -> Result<&mut Self, Error> {
1012        unsafe {
1013            s2n_config_set_ticket_encrypt_decrypt_key_lifetime(
1014                self.as_mut_ptr(),
1015                lifetime.as_secs(),
1016            )
1017            .into_result()
1018        }?;
1019        Ok(self)
1020    }
1021
1022    // Sets how long a session ticket key will be able to be used for only decryption
1023    ///
1024    /// Corresponds to [`s2n_config_set_ticket_decrypt_key_lifetime`].
1025    pub fn set_ticket_key_decrypt_lifetime(
1026        &mut self,
1027        lifetime: Duration,
1028    ) -> Result<&mut Self, Error> {
1029        unsafe {
1030            s2n_config_set_ticket_decrypt_key_lifetime(self.as_mut_ptr(), lifetime.as_secs())
1031                .into_result()
1032        }?;
1033        Ok(self)
1034    }
1035
1036    /// Sets the expected connection serialization version. Must be set
1037    /// before serializing the connection.
1038    ///
1039    /// Corresponds to [`s2n_config_set_serialization_version`].
1040    pub fn set_serialization_version(
1041        &mut self,
1042        version: SerializationVersion,
1043    ) -> Result<&mut Self, Error> {
1044        unsafe {
1045            s2n_config_set_serialization_version(self.as_mut_ptr(), version.into()).into_result()
1046        }?;
1047        Ok(self)
1048    }
1049
1050    /// Sets a configurable blinding delay instead of the default
1051    ///
1052    /// Corresponds to [`s2n_config_set_max_blinding_delay`].
1053    pub fn set_max_blinding_delay(&mut self, seconds: u32) -> Result<&mut Self, Error> {
1054        unsafe { s2n_config_set_max_blinding_delay(self.as_mut_ptr(), seconds).into_result() }?;
1055        Ok(self)
1056    }
1057
1058    /// Requires that session tickets are only used when forward secrecy is possible
1059    ///
1060    /// Corresponds to [`s2n_config_require_ticket_forward_secrecy`].
1061    pub fn require_ticket_forward_secrecy(&mut self, enable: bool) -> Result<&mut Self, Error> {
1062        unsafe {
1063            s2n_config_require_ticket_forward_secrecy(self.as_mut_ptr(), enable).into_result()
1064        }?;
1065        Ok(self)
1066    }
1067
1068    pub fn build(mut self) -> Result<Config, Error> {
1069        if self.load_system_certs {
1070            unsafe {
1071                s2n_config_load_system_certs(self.as_mut_ptr()).into_result()?;
1072            }
1073        }
1074
1075        Ok(self.config)
1076    }
1077
1078    pub(crate) fn as_mut_ptr(&mut self) -> *mut s2n_config {
1079        self.config.as_mut_ptr()
1080    }
1081
1082    /// Returns the underlying `s2n_tls_sys::s2n_config` pointer associated with the
1083    /// `config::Builder`.
1084    ///
1085    /// #### Warning:
1086    /// This API is unstable, and may be removed in a future s2n-tls release. Applications should
1087    /// use the higher level s2n-tls bindings rather than calling the low-level `s2n_tls_sys` APIs
1088    /// directly.
1089    #[cfg(s2n_tls_external_build)]
1090    pub fn unstable_as_ptr(&mut self) -> *mut s2n_config {
1091        self.as_mut_ptr()
1092    }
1093
1094    /// Load all acceptable certificate authorities from the currently configured trust store.
1095    ///
1096    /// Corresponds to [`s2n_config_set_cert_authorities_from_trust_store`].
1097    pub fn set_certificate_authorities_from_trust_store(&mut self) -> Result<(), Error> {
1098        // SAFETY: valid builder geting passed in.
1099        unsafe {
1100            s2n_config_set_cert_authorities_from_trust_store(self.as_mut_ptr()).into_result()?;
1101        }
1102
1103        Ok(())
1104    }
1105}
1106
1107#[cfg(feature = "quic")]
1108impl Builder {
1109    /// Corresponds to [`s2n_config_enable_quic`].
1110    pub fn enable_quic(&mut self) -> Result<&mut Self, Error> {
1111        unsafe { s2n_tls_sys::s2n_config_enable_quic(self.as_mut_ptr()).into_result() }?;
1112        Ok(self)
1113    }
1114}
1115
1116/// # Warning
1117///
1118/// The newly created Builder uses the default security policy.
1119/// Consider changing this depending on your security and compatibility requirements
1120/// by using [`Builder::new`] instead and calling [`Builder::set_security_policy`].
1121/// See the s2n-tls usage guide:
1122/// <https://aws.github.io/s2n-tls/usage-guide/ch06-security-policies.html>
1123impl Default for Builder {
1124    fn default() -> Self {
1125        Self::new()
1126    }
1127}
1128
1129pub(crate) struct Context {
1130    refcount: AtomicUsize,
1131    /// This is a container for reference counts.
1132    ///
1133    /// In the bindings, application owned certificate chains are reference counted.
1134    /// The C library is not aware of the reference counts, so a naive implementation
1135    /// would result in certs being prematurely freed because the "reference"
1136    /// held by the C library wouldn't be accounted for.
1137    ///
1138    /// Storing the CertificateChains in this Vec ensures that reference counts
1139    /// behave as expected when stored in an s2n-tls config.
1140    application_owned_certs: Vec<CertificateChain<'static>>,
1141    pub(crate) client_hello_callback: Option<Box<dyn ClientHelloCallback>>,
1142    pub(crate) private_key_callback: Option<Box<dyn PrivateKeyCallback>>,
1143    pub(crate) verify_host_callback: Option<Box<dyn VerifyHostNameCallback>>,
1144    pub(crate) session_ticket_callback: Option<Box<dyn SessionTicketCallback>>,
1145    pub(crate) connection_initializer: Option<Box<dyn ConnectionInitializer>>,
1146    pub(crate) wall_clock: Option<Box<dyn WallClock>>,
1147    pub(crate) monotonic_clock: Option<Box<dyn MonotonicClock>>,
1148    #[cfg(feature = "unstable-renegotiate")]
1149    pub(crate) renegotiate: Option<Box<dyn RenegotiateCallback>>,
1150    #[cfg(feature = "unstable-cert_authorities")]
1151    pub(crate) cert_authorities: Option<Box<dyn CertificateRequestCallback>>,
1152    #[cfg(feature = "unstable-crl")]
1153    pub(crate) cert_validation_callback_sync: Option<Box<dyn CertValidationCallbackSync>>,
1154    #[cfg(feature = "unstable-events")]
1155    pub(crate) event_subscriber: Option<Box<dyn crate::events::EventSubscriber>>,
1156}
1157
1158impl Default for Context {
1159    fn default() -> Self {
1160        // The AtomicUsize is used to manually track the reference count of the Config.
1161        // This mechanism is used to track when the Config object should be freed.
1162        let refcount = AtomicUsize::new(1);
1163
1164        Self {
1165            refcount,
1166            application_owned_certs: Vec::new(),
1167            client_hello_callback: None,
1168            private_key_callback: None,
1169            verify_host_callback: None,
1170            session_ticket_callback: None,
1171            connection_initializer: None,
1172            wall_clock: None,
1173            monotonic_clock: None,
1174            #[cfg(feature = "unstable-renegotiate")]
1175            renegotiate: None,
1176            #[cfg(feature = "unstable-cert_authorities")]
1177            cert_authorities: None,
1178            #[cfg(feature = "unstable-crl")]
1179            cert_validation_callback_sync: None,
1180            #[cfg(feature = "unstable-events")]
1181            event_subscriber: None,
1182        }
1183    }
1184}
1185
1186/// A trait executed asynchronously before a new connection negotiates TLS.
1187///
1188/// Used for dynamic configuration of a specific connection.
1189///
1190/// # Safety: This trait is polled to completion at the beginning of the
1191/// [connection::poll_negotiate](`crate::connection::poll_negotiate()`) function.
1192/// Therefore, negotiation of the TLS connection will not begin until the Future has completed.
1193pub trait ConnectionInitializer: 'static + Send + Sync {
1194    /// The application can return an `Ok(None)` to resolve the callback
1195    /// synchronously or return an `Ok(Some(ConnectionFuture))` if it wants to
1196    /// run some asynchronous task before resolving the callback.
1197    fn initialize_connection(
1198        &self,
1199        connection: &mut crate::connection::Connection,
1200    ) -> ConnectionFutureResult;
1201}
1202
1203impl<A: ConnectionInitializer, B: ConnectionInitializer> ConnectionInitializer for (A, B) {
1204    fn initialize_connection(
1205        &self,
1206        connection: &mut crate::connection::Connection,
1207    ) -> ConnectionFutureResult {
1208        let a = self.0.initialize_connection(connection)?;
1209        let b = self.1.initialize_connection(connection)?;
1210        match (a, b) {
1211            (None, None) => Ok(None),
1212            (None, Some(fut)) => Ok(Some(fut)),
1213            (Some(fut), None) => Ok(Some(fut)),
1214            (Some(fut_a), Some(fut_b)) => Ok(Some(Box::pin(ConcurrentConnectionFuture::new([
1215                fut_a, fut_b,
1216            ])))),
1217        }
1218    }
1219}
1220
1221struct ConcurrentConnectionFuture<const N: usize> {
1222    futures: [Option<Pin<Box<dyn ConnectionFuture>>>; N],
1223}
1224
1225impl<const N: usize> ConcurrentConnectionFuture<N> {
1226    fn new(futures: [Pin<Box<dyn ConnectionFuture>>; N]) -> Self {
1227        let futures = futures.map(Some);
1228        Self { futures }
1229    }
1230}
1231
1232impl<const N: usize> ConnectionFuture for ConcurrentConnectionFuture<N> {
1233    fn poll(
1234        mut self: std::pin::Pin<&mut Self>,
1235        connection: &mut crate::connection::Connection,
1236        ctx: &mut core::task::Context,
1237    ) -> std::task::Poll<Result<(), Error>> {
1238        let mut is_pending = false;
1239        for container in self.futures.iter_mut() {
1240            if let Some(future) = container.as_mut() {
1241                match future.as_mut().poll(connection, ctx) {
1242                    Poll::Ready(result) => {
1243                        result?;
1244                        *container = None;
1245                    }
1246                    Poll::Pending => is_pending = true,
1247                }
1248            }
1249        }
1250        if is_pending {
1251            Poll::Pending
1252        } else {
1253            Poll::Ready(Ok(()))
1254        }
1255    }
1256}
1257
1258#[cfg(test)]
1259mod tests {
1260    use super::*;
1261    use crate::testing::*;
1262
1263    // ensure the config context is send and sync
1264    #[test]
1265    fn context_send_sync_test() {
1266        fn assert_send_sync<T: 'static + Send + Sync>() {}
1267        assert_send_sync::<Context>();
1268    }
1269
1270    /// Test that `config::Builder::unstable_as_ptr()` can be used to call an s2n_tls_sys API.
1271    #[cfg(s2n_tls_external_build)]
1272    #[test]
1273    fn test_config_unstable_as_ptr() -> Result<(), Error> {
1274        let mut builder = Config::builder();
1275
1276        let auth_types = [
1277            ClientAuthType::None,
1278            ClientAuthType::Optional,
1279            ClientAuthType::Required,
1280        ];
1281        for auth_type in auth_types {
1282            builder.set_client_auth_type(auth_type)?;
1283
1284            let mut retrieved_auth_type = s2n_cert_auth_type::NONE;
1285            unsafe {
1286                s2n_config_get_client_auth_type(
1287                    builder.unstable_as_ptr(),
1288                    &mut retrieved_auth_type,
1289                );
1290            }
1291
1292            assert_eq!(retrieved_auth_type, auth_type.into());
1293        }
1294
1295        Ok(())
1296    }
1297
1298    #[test]
1299    fn disable_intent_verification() -> Result<(), Error> {
1300        let invalid_certs = CertKeyPair::from_path(
1301            "intent/cert_chains/ku_key_cert_sign_leaf/",
1302            "cert-chain",
1303            "leaf-key",
1304            "root-cert",
1305        );
1306
1307        for disable_intent in [true, false] {
1308            let config = {
1309                let mut config = Builder::new();
1310                config.set_security_policy(&security::DEFAULT_TLS13)?;
1311                config.set_verify_host_callback(InsecureAcceptAllCertificatesHandler {})?;
1312
1313                if disable_intent {
1314                    config.disable_x509_intent_verification()?;
1315                }
1316
1317                config.load_pem(invalid_certs.cert(), invalid_certs.key())?;
1318                config.trust_pem(invalid_certs.ca_cert())?;
1319                config.build()?
1320            };
1321            let mut pair = TestPair::from_config(&config);
1322
1323            if disable_intent {
1324                pair.handshake()?;
1325            } else {
1326                let s2n_err = pair.handshake().unwrap_err();
1327                assert_eq!(s2n_err.name(), "S2N_ERR_CERT_INTENT_INVALID");
1328            }
1329        }
1330
1331        Ok(())
1332    }
1333
1334    #[cfg(all(
1335        // The `add_custom_x509_extension` API is only exposed when its unstable feature is enabled.
1336        feature = "unstable-custom_x509_extensions",
1337        // The `add_custom_x509_extension` API is only supported with AWS-LC, so
1338        // this test is disabled for the external build, which may link to other libcryptos.
1339        not(s2n_tls_external_build),
1340        // The `add_custom_x509_extension` API is currently unsupported with AWS-LC-FIPS.
1341        not(feature = "fips")
1342    ))]
1343    #[test]
1344    fn custom_critical_extensions() -> Result<(), Error> {
1345        let certs = CertKeyPair::from_path(
1346            "custom_oids/",
1347            "single_oid_cert_chain",
1348            "single_oid_key",
1349            "ca-cert",
1350        );
1351        let single_oid = "1.3.187.25240.2";
1352
1353        for add_oid in [true, false] {
1354            let config = {
1355                let mut config = Builder::new();
1356                config.set_security_policy(&security::DEFAULT_TLS13)?;
1357                config.set_verify_host_callback(InsecureAcceptAllCertificatesHandler {})?;
1358
1359                if add_oid {
1360                    config.add_custom_x509_extension(single_oid)?;
1361                }
1362
1363                config.load_pem(certs.cert(), certs.key())?;
1364                config.trust_pem(certs.cert())?;
1365                config.build()?
1366            };
1367            let mut pair = TestPair::from_config(&config);
1368
1369            if add_oid {
1370                pair.handshake()?;
1371            } else {
1372                let s2n_err = pair.handshake().unwrap_err();
1373                assert_eq!(s2n_err.name(), "S2N_ERR_CERT_UNHANDLED_CRITICAL_EXTENSION");
1374            }
1375        }
1376
1377        Ok(())
1378    }
1379}