Skip to main content

wolfssl/
context.rs

1use crate::{
2    callback::IOCallbacks,
3    error::{Error, Result},
4    ssl::{Session, SessionConfig},
5    CurveGroup, Method, NewSessionError, RootCertificate, Secret, SslVerifyMode,
6};
7use std::os::raw::c_int;
8use std::ptr::NonNull;
9use thiserror::Error;
10
11/// Produces a [`Context`] once built.
12#[derive(Debug)]
13pub struct ContextBuilder {
14    ctx: NonNull<wolfssl_sys::WOLFSSL_CTX>,
15    method: Method,
16}
17
18/// Error creating a [`ContextBuilder`] object.
19#[derive(Error, Debug)]
20pub enum NewContextBuilderError {
21    /// Failed to initialize WolfSSL
22    #[error("Failed to initialize WolfSSL: {0}")]
23    InitFailed(Error),
24
25    /// Failed to turn `Method` into a `wolfssl_sys::WOLFSSL_METHOD`
26    #[error("Failed to obtain WOLFSSL_METHOD")]
27    MethodFailed,
28
29    /// `wolfSSL_CTX_new` failed
30    #[error("Failed to allocate WolfSSL Context")]
31    CreateFailed,
32}
33
34impl ContextBuilder {
35    /// Invokes [`wolfSSL_CTX_new`][0]
36    ///
37    /// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_new
38    pub fn new(method: Method) -> std::result::Result<Self, NewContextBuilderError> {
39        crate::wolf_init().map_err(NewContextBuilderError::InitFailed)?;
40
41        let method_fn = method
42            .into_method_ptr()
43            .ok_or(NewContextBuilderError::MethodFailed)?;
44
45        // SAFETY: [`wolfSSL_CTX_new`][0] is documented to get pointer to a valid `WOLFSSL_METHOD` structure which is created using one of the `wolfSSLvXX_XXXX_method()`.
46        // `Method::into_method_ptr` function returns a pointer `wolfSSLvXX_XXXX_method()`
47        //
48        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_new
49        let ctx = unsafe { wolfssl_sys::wolfSSL_CTX_new(method_fn.as_ptr()) };
50        let ctx = NonNull::new(ctx).ok_or(NewContextBuilderError::CreateFailed)?;
51
52        Ok(Self { ctx, method })
53    }
54
55    /// When `cond` is True call fallible `func` on `Self`
56    pub fn try_when<F>(self, cond: bool, func: F) -> Result<Self>
57    where
58        F: FnOnce(Self) -> Result<Self>,
59    {
60        if cond {
61            func(self)
62        } else {
63            Ok(self)
64        }
65    }
66
67    /// When `maybe` is Some(_) call fallible `func` on `Self` and the contained value
68    pub fn try_when_some<F, T>(self, maybe: Option<T>, func: F) -> Result<Self>
69    where
70        F: FnOnce(Self, T) -> Result<Self>,
71    {
72        if let Some(t) = maybe {
73            func(self, t)
74        } else {
75            Ok(self)
76        }
77    }
78
79    /// Wraps [`wolfSSL_CTX_load_verify_buffer`][0] and [`wolfSSL_CTX_load_verify_locations`][1]
80    ///
81    /// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_load_verify_buffer
82    /// [1]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_load_verify_locations
83    pub fn with_root_certificate(self, root: RootCertificate) -> Result<Self> {
84        use wolfssl_sys::{
85            wolfSSL_CTX_load_verify_buffer, wolfSSL_CTX_load_verify_locations,
86            WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM,
87        };
88
89        let result = match root {
90            // SAFETY: [`wolfSSL_CTX_load_verify_buffer`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
91            // The pointer given as the `in` argument must point to a region of `sz` bytes.
92            // The values passed here are valid since they are derived from the same byte slice.
93            //
94            // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_load_verify_buffer
95            // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaa5a28f0ac25d9abeb72fcee81bbf647b
96            RootCertificate::Asn1Buffer(buf) => unsafe {
97                wolfSSL_CTX_load_verify_buffer(
98                    self.ctx.as_ptr(),
99                    buf.as_ptr(),
100                    buf.len() as std::os::raw::c_long,
101                    WOLFSSL_FILETYPE_ASN1 as c_int,
102                )
103            },
104            // SAFETY: [`wolfSSL_CTX_load_verify_buffer`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
105            // The pointer given as the `in` argument must point to a region of `sz` bytes.
106            // The values passed here are valid since they are derived from the same byte slice.
107            //
108            // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_load_verify_buffer
109            // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaa5a28f0ac25d9abeb72fcee81bbf647b
110            RootCertificate::PemBuffer(buf) => unsafe {
111                wolfSSL_CTX_load_verify_buffer(
112                    self.ctx.as_ptr(),
113                    buf.as_ptr(),
114                    buf.len() as std::os::raw::c_long,
115                    WOLFSSL_FILETYPE_PEM as c_int,
116                )
117            },
118            RootCertificate::PemFileOrDirectory(path) => {
119                let is_dir = path.is_dir();
120                let path = path.to_str().ok_or_else(|| {
121                    Error::fatal(wolfssl_sys::wolfSSL_ErrorCodes_WOLFSSL_BAD_PATH)
122                })?;
123                let path = std::ffi::CString::new(path)
124                    .map_err(|_| Error::fatal(wolfssl_sys::wolfSSL_ErrorCodes_WOLFSSL_BAD_PATH))?;
125                if is_dir {
126                    // SAFETY: [`wolfSSL_CTX_load_verify_locations`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
127                    // If not NULL, then the pointer passed as the path argument must be a valid NULL-terminated C-style string,
128                    // which is guaranteed by the use of `std::ffi::CString::as_c_str()` here.
129                    //
130                    // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_load_verify_locations
131                    // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaf592c652b5d7a599ee511a394dfc488e
132                    unsafe {
133                        wolfSSL_CTX_load_verify_locations(
134                            self.ctx.as_ptr(),
135                            std::ptr::null(),
136                            path.as_c_str().as_ptr(),
137                        )
138                    }
139                } else {
140                    // SAFETY: [`wolfSSL_CTX_load_verify_locations`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
141                    // If not NULL, then the pointer passed as the path argument must be a valid NULL-terminated C-style string,
142                    // which is guaranteed by the use of `std::ffi::CString::as_c_str()` here.
143                    //
144                    // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_load_verify_locations
145                    // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaf592c652b5d7a599ee511a394dfc488e
146                    unsafe {
147                        wolfSSL_CTX_load_verify_locations(
148                            self.ctx.as_ptr(),
149                            path.as_c_str().as_ptr(),
150                            std::ptr::null(),
151                        )
152                    }
153                }
154            }
155        };
156
157        if result == wolfssl_sys::WOLFSSL_SUCCESS as c_int {
158            Ok(self)
159        } else {
160            Err(Error::fatal(result))
161        }
162    }
163
164    /// Wraps [`wolfSSL_CTX_set_cipher_list`][0]
165    ///
166    /// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/ssl_8h.html#function-wolfssl_ctx_set_cipher_list
167    pub fn with_cipher_list(self, cipher_list: &str) -> Result<Self> {
168        let cipher_list = std::ffi::CString::new(cipher_list)
169            .map_err(|_| Error::fatal(wolfssl_sys::WOLFSSL_FAILURE as c_int))?;
170
171        // SAFETY: [`wolfSSL_CTX_set_cipher_list`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()` and
172        // `list` parameter which should be a null terminated C string pointer which is guaranteed by
173        // the use of `std::ffi::CString::as_c_str()` here.
174        //
175        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/ssl_8h.html#function-wolfssl_ctx_set_cipher_list
176        // [1]: https://www.wolfssl.com/doxygen/group__Setup.html#gafa55814f56bd7a36f4035d71b2b31832
177        let result = unsafe {
178            wolfssl_sys::wolfSSL_CTX_set_cipher_list(
179                self.ctx.as_ptr(),
180                cipher_list.as_c_str().as_ptr(),
181            )
182        };
183
184        if result == wolfssl_sys::WOLFSSL_SUCCESS as c_int {
185            Ok(self)
186        } else {
187            Err(Error::fatal(result))
188        }
189    }
190
191    /// Wraps [`wolfSSL_CTX_set_groups`][0]
192    ///
193    /// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_set_groups
194    pub fn with_groups(self, groups: &[CurveGroup]) -> Result<Self> {
195        let mut ffi_curves = groups.iter().map(|g| g.as_ffi() as i32).collect::<Vec<_>>();
196
197        // SAFETY: [`wolfSSL_CTX_set_groups`][0] ([also][1]) requires
198        // a valid `ctx` pointer from `wolfSSL_CTX_new()` and `groups`
199        // parameter which should be a pointer to int with length
200        // corresponding to the `count` argument which is guaranteed
201        // by our use of a `Vec` here.
202        //
203        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_set_groups
204        // [1]: https://www.wolfssl.com/doxygen/group__Setup.html#ga5bab039f79486d3ac31be72bc5f4e1e8
205        let result = unsafe {
206            wolfssl_sys::wolfSSL_CTX_set_groups(
207                self.ctx.as_ptr(),
208                ffi_curves.as_mut_ptr(),
209                ffi_curves.len() as i32,
210            )
211        };
212
213        if result == wolfssl_sys::WOLFSSL_SUCCESS as c_int {
214            Ok(self)
215        } else {
216            Err(Error::fatal(result))
217        }
218    }
219
220    /// Wraps [`wolfSSL_CTX_use_certificate_file`][0] and [`wolfSSL_CTX_use_certificate_buffer`][1]
221    ///
222    /// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_certificate_file
223    /// [1]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_certificate_buffer
224    pub fn with_certificate(self, secret: Secret) -> Result<Self> {
225        use wolfssl_sys::{
226            wolfSSL_CTX_use_certificate_buffer, wolfSSL_CTX_use_certificate_file,
227            WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM,
228        };
229
230        let result = match secret {
231            // SAFETY: [`wolfSSL_CTX_use_certificate_buffer`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
232            // The pointer given as the `in` argument must point to a region of `sz` bytes.
233            // The values passed here are valid since they are derived from the same byte slice.
234            //
235            // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_certificate_buffer
236            // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gae424b3a63756ab805de5c43b67f4df4f
237            Secret::Asn1Buffer(buf) => unsafe {
238                wolfSSL_CTX_use_certificate_buffer(
239                    self.ctx.as_ptr(),
240                    buf.as_ptr(),
241                    buf.len() as std::os::raw::c_long,
242                    WOLFSSL_FILETYPE_ASN1 as c_int,
243                )
244            },
245            Secret::Asn1File(path) => {
246                let path = path.to_str().ok_or_else(|| {
247                    Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR)
248                })?;
249                let file = std::ffi::CString::new(path)
250                    .map_err(|_| Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR))?;
251                // SAFETY: [`wolfSSL_CTX_use_certificate_file`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
252                // The pointer passed as the path argument must be a valid NULL-terminated C-style string,
253                // which is guaranteed by the use of `std::ffi::CString::as_c_str()` here.
254                //
255                // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_certificate_file
256                // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#ga5a31292b75b4caa4462a3305d2615beb
257                unsafe {
258                    wolfSSL_CTX_use_certificate_file(
259                        self.ctx.as_ptr(),
260                        file.as_c_str().as_ptr(),
261                        WOLFSSL_FILETYPE_ASN1 as c_int,
262                    )
263                }
264            }
265            // SAFETY: [`wolfSSL_CTX_use_certificate_buffer`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
266            // The pointer given as the `in` argument must point to a region of `sz` bytes.
267            // The values passed here are valid since they are derived from the same byte slice.
268            //
269            // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_certificate_buffer
270            // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gae424b3a63756ab805de5c43b67f4df4f
271            Secret::PemBuffer(buf) => unsafe {
272                wolfSSL_CTX_use_certificate_buffer(
273                    self.ctx.as_ptr(),
274                    buf.as_ptr(),
275                    buf.len() as std::os::raw::c_long,
276                    WOLFSSL_FILETYPE_PEM as c_int,
277                )
278            },
279            Secret::PemFile(path) => {
280                let path = path.to_str().ok_or_else(|| {
281                    Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR)
282                })?;
283                let file = std::ffi::CString::new(path)
284                    .map_err(|_| Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR))?;
285                // SAFETY: [`wolfSSL_CTX_use_certificate_file`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
286                // The pointer passed as the path argument must be a valid NULL-terminated C-style string,
287                // which is guaranteed by the use of `std::ffi::CString::as_c_str()` here.
288                //
289                // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_certificate_file
290                // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#ga5a31292b75b4caa4462a3305d2615beb
291                unsafe {
292                    wolfSSL_CTX_use_certificate_file(
293                        self.ctx.as_ptr(),
294                        file.as_c_str().as_ptr(),
295                        WOLFSSL_FILETYPE_PEM as c_int,
296                    )
297                }
298            }
299        };
300
301        if result == wolfssl_sys::WOLFSSL_SUCCESS as c_int {
302            Ok(self)
303        } else {
304            Err(Error::fatal(result))
305        }
306    }
307
308    /// Wraps [`wolfSSL_CTX_use_PrivateKey_file`][0] and [`wolfSSL_CTX_use_PrivateKey_buffer`][1]
309    ///
310    /// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_privatekey_file
311    /// [1]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_privatekey_buffer
312    pub fn with_private_key(self, secret: Secret) -> Result<Self> {
313        use wolfssl_sys::{
314            wolfSSL_CTX_use_PrivateKey_buffer, wolfSSL_CTX_use_PrivateKey_file,
315            WOLFSSL_FILETYPE_ASN1, WOLFSSL_FILETYPE_PEM,
316        };
317
318        let result = match secret {
319            // SAFETY: [`wolfSSL_CTX_use_PrivateKey_buffer`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
320            // The pointer given as the `in` argument must point to a region of `sz` bytes.
321            // The values passed here are valid since they are derived from the same byte slice.
322            //
323            // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_privatekey_buffer
324            // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaf88bd3ade7faefb028679f48ef64a237
325            Secret::Asn1Buffer(buf) => unsafe {
326                wolfSSL_CTX_use_PrivateKey_buffer(
327                    self.ctx.as_ptr(),
328                    buf.as_ptr(),
329                    buf.len() as std::os::raw::c_long,
330                    WOLFSSL_FILETYPE_ASN1 as c_int,
331                )
332            },
333            Secret::Asn1File(path) => {
334                let path = path.to_str().ok_or_else(|| {
335                    Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR)
336                })?;
337                let file = std::ffi::CString::new(path)
338                    .map_err(|_| Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR))?;
339                // SAFETY: [`wolfSSL_CTX_use_PrivateKey_file`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
340                // The pointer passed as the path argument must be a valid NULL-terminated C-style string,
341                // which is guaranteed by the use of `std::ffi::CString::as_c_str()` here.
342                //
343                // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_privatekey_file
344                // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gab80ef18b3232ebd19acab106b52feeb0
345                unsafe {
346                    wolfSSL_CTX_use_PrivateKey_file(
347                        self.ctx.as_ptr(),
348                        file.as_c_str().as_ptr(),
349                        WOLFSSL_FILETYPE_ASN1 as c_int,
350                    )
351                }
352            }
353            // SAFETY: [`wolfSSL_CTX_use_PrivateKey_buffer`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
354            // The pointer given as the `in` argument must point to a region of `sz` bytes.
355            // The values passed here are valid since they are derived from the same byte slice.
356            //
357            // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_privatekey_buffer
358            // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaf88bd3ade7faefb028679f48ef64a237
359            Secret::PemBuffer(buf) => unsafe {
360                wolfSSL_CTX_use_PrivateKey_buffer(
361                    self.ctx.as_ptr(),
362                    buf.as_ptr(),
363                    buf.len() as std::os::raw::c_long,
364                    WOLFSSL_FILETYPE_PEM as c_int,
365                )
366            },
367            Secret::PemFile(path) => {
368                let path = path.to_str().ok_or_else(|| {
369                    Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR)
370                })?;
371                let file = std::ffi::CString::new(path)
372                    .map_err(|_| Error::fatal(wolfssl_sys::wolfCrypt_ErrorCodes_BAD_PATH_ERROR))?;
373                // SAFETY: [`wolfSSL_CTX_use_PrivateKey_file`][0] ([also][1]) requires a valid `ctx` pointer from `wolfSSL_CTX_new()`.
374                // The pointer passed as the path argument must be a valid NULL-terminated C-style string,
375                // which is guaranteed by the use of `std::ffi::CString::as_c_str()` here.
376                //
377                // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_use_privatekey_file
378                // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gab80ef18b3232ebd19acab106b52feeb0
379                unsafe {
380                    wolfSSL_CTX_use_PrivateKey_file(
381                        self.ctx.as_ptr(),
382                        file.as_c_str().as_ptr(),
383                        WOLFSSL_FILETYPE_PEM as c_int,
384                    )
385                }
386            }
387        };
388
389        if result == wolfssl_sys::WOLFSSL_SUCCESS as c_int {
390            Ok(self)
391        } else {
392            Err(Error::fatal(result))
393        }
394    }
395
396    /// Wraps `wolfSSL_CTX_UseSecureRenegotiation`
397    ///
398    /// NOTE: No official documentation available for this api from wolfssl
399    pub fn with_secure_renegotiation(self) -> Result<Self> {
400        // SAFETY: [`wolfSSL_CTX_UseSecureRenegotiation`][1] does not have proper documentation.
401        // Based on the implementation, the only requirement is the context which is passed to this api has to be a valid `WOLFSSL_CTX`
402        let result = unsafe { wolfssl_sys::wolfSSL_CTX_UseSecureRenegotiation(self.ctx.as_ptr()) };
403        if result == wolfssl_sys::WOLFSSL_SUCCESS as c_int {
404            Ok(self)
405        } else {
406            Err(Error::fatal(result))
407        }
408    }
409
410    /// Wraps `wolfSSL_CTX_set_verify`[0]([also][1])
411    // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_set_verify
412    // [1]: https://www.wolfssl.com/doxygen/group__Setup.html#ga26c623e093cf15f81cdfc3bb26682089
413    pub fn with_verify_method(self, mode: SslVerifyMode) -> Self {
414        // SAFETY: [`wolfSSL_CTX_set_verify`][0] ([also][1]) requires a valid `ctx` pointer
415        // from `wolfSSL_CTX_new()`.
416        // Third parameter `verify_callback` if valid, will be called when verification fails.
417        // But we send `None` since we do not use this additional functionality
418        //
419        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_set_verify
420        // [1]: https://www.wolfssl.com/doxygen/group__Setup.html#ga26c623e093cf15f81cdfc3bb26682089
421        unsafe { wolfssl_sys::wolfSSL_CTX_set_verify(self.ctx.as_ptr(), mode.into(), None) };
422        self
423    }
424
425    /// Wraps `wolfSSL_CTX_load_system_CA_certs`[0]([also][1])
426    // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__CertsKeys.html#function-wolfssl_ctx_load_system_ca_certs
427    // [1]: https://www.wolfssl.com/doxygen/group__CertsKeys.html#gaa66006fab6369002eda43cb4f83c857d
428    #[cfg(feature = "system_ca_certs")]
429    pub fn with_system_ca_certs(self) -> Self {
430        // SAFETY: [`wolfSSL_CTX_load_system_CA_certs`][0] ([also][1]) requires a valid `ctx` pointer
431        // from `wolfSSL_CTX_new()`.
432        unsafe { wolfssl_sys::wolfSSL_CTX_load_system_CA_certs(self.ctx.as_ptr()) };
433        self
434    }
435
436    /// Finalizes a `WolfContext`.
437    pub fn build(self) -> Context {
438        Context {
439            method: self.method,
440            ctx: ContextPointer(self.ctx),
441        }
442    }
443}
444
445// Wrap a valid pointer to a [`wolfssl_sys::WOLFSSL_CONTEXT`] such that we can
446// add traits such as `Send`.
447pub(crate) struct ContextPointer(NonNull<wolfssl_sys::WOLFSSL_CTX>);
448
449impl std::ops::Deref for ContextPointer {
450    type Target = NonNull<wolfssl_sys::WOLFSSL_CTX>;
451
452    fn deref(&self) -> &Self::Target {
453        &self.0
454    }
455}
456
457// SAFETY: Per [Library Design][] under "Thread Safety"
458//
459// > Besides sharing WOLFSSL pointers, users must also take care to
460// > completely initialize an WOLFSSL_CTX before passing the structure to
461// > wolfSSL_new(). The same WOLFSSL_CTX can create multiple WOLFSSL
462// > structs but the WOLFSSL_CTX is only read during wolfSSL_new()
463// > creation and any future (or simultaneous changes) to the WOLFSSL_CTX
464// > will not be reflected once the WOLFSSL object is created.
465//
466// > Again, multiple threads should synchronize writing access to a
467// > WOLFSSL_CTX and it is advised that a single thread initialize the
468// > WOLFSSL_CTX to avoid the synchronization and update problem
469// > described above.
470//
471// This is consistent with the requirements for `Send`.
472//
473// The required syncronization when setting up the context is handled
474// by the fact that [`ContextBuilder`] is not `Send`. In addition
475// neither [`ContextPointer`] nor [`Context`] have any methods which
476// offer writeable access.
477//
478// [Library Design]: https://www.wolfssl.com/documentation/manuals/wolfssl/chapter09.html
479unsafe impl Send for ContextPointer {}
480
481// SAFETY: Per documentation quoted for `Send` above: once built the
482// underlying `WOLFSSL_CONTEXT` is considered read-only. Our
483// `ContextBuilder` enforces that the `Context` is completely built
484// before a `Context` can be obtained and there are no mutable APIs on
485// `Context` object once it is built.
486unsafe impl Sync for ContextPointer {}
487
488// Wrap a valid pointer to a [`wolfssl_sys::WOLFSSL`] such that we can
489// add traits such as `Send`.
490pub(crate) struct WolfsslPointer(NonNull<wolfssl_sys::WOLFSSL>);
491
492impl WolfsslPointer {
493    pub(crate) fn as_ptr(&mut self) -> *mut wolfssl_sys::WOLFSSL {
494        self.0.as_ptr()
495    }
496}
497
498// SAFETY: Per [Library Design][] under "Thread Safety"
499//
500// > A client may share an WOLFSSL object across multiple threads but
501// > access must be synchronized, i.e., trying to read/write at the same
502// > time from two different threads with the same SSL pointer is not
503// > supported.
504//
505// This is consistent with the requirements for `Send`. The required
506// syncronization is handled by requiring `&mut self` in all relevant
507// methods.
508//
509// [Library Design]: https://www.wolfssl.com/documentation/manuals/wolfssl/chapter09.html
510unsafe impl Send for WolfsslPointer {}
511
512/// A wrapper around a `WOLFSSL_CTX`.
513pub struct Context {
514    method: Method,
515    ctx: ContextPointer,
516}
517
518impl Context {
519    /// Returns the Context's [`Method`].
520    pub fn method(&self) -> Method {
521        self.method
522    }
523
524    /// Creates a new SSL session using this underlying context.
525    pub fn new_session<IOCB: IOCallbacks>(
526        &self,
527        config: SessionConfig<IOCB>,
528    ) -> std::result::Result<Session<IOCB>, NewSessionError> {
529        // SAFETY: [`wolfSSL_new`][0] ([also][1]) needs a valid `wolfssl_sys::WOLFSSL_CTX` pointer as per documentation
530        //
531        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_new
532        // [1]: https://www.wolfssl.com/doxygen/group__Setup.html#gaa37dc22775da8f6a3b5c149d5dfd6e1c
533        let ptr = unsafe { wolfssl_sys::wolfSSL_new(self.ctx.as_ptr()) };
534
535        let ssl = WolfsslPointer(NonNull::new(ptr).ok_or(NewSessionError::CreateFailed)?);
536
537        Session::new_from_wolfssl_pointer(ssl, config)
538    }
539}
540
541impl Drop for Context {
542    /// Invokes [`wolfSSL_CTX_free`][0]
543    ///
544    /// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_free
545    fn drop(&mut self) {
546        // SAFETY: [`wolfSSL_CTX_free`][0] ([also][1]) takes pointer to `WOLFSSL_CTX` and frees it if the reference count becomes 0.
547        // Documentation is not clear about when this reference count will be incremented. From implementation, it is
548        // incremented in [`wolfSSL_set_SSL_CTX`][2] and [`wolfSSL_CTX_up_ref`][3], and we dont use these apis
549        //
550        // [0]: https://www.wolfssl.com/doxygen/group__Setup.html#gabe86939065276c9271a17d799860535d
551        // [1]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_free
552        // [2]: https://github.com/wolfSSL/wolfssl/blob/v5.6.3-stable/src/ssl.c#L31235
553        // [3]: https://github.com/wolfSSL/wolfssl/blob/v5.6.3-stable/src/ssl.c#L1357
554        unsafe { wolfssl_sys::wolfSSL_CTX_free(self.ctx.as_ptr()) }
555    }
556}
557
558#[cfg(test)]
559mod tests {
560    use super::*;
561    use test_case::test_case;
562
563    #[test_case(Method::DtlsClient)]
564    #[test_case(Method::DtlsClientV1_2)]
565    #[test_case(Method::DtlsServer)]
566    #[test_case(Method::DtlsServerV1_2)]
567    #[test_case(Method::TlsClient)]
568    #[test_case(Method::TlsClientV1_2)]
569    #[test_case(Method::TlsClientV1_3)]
570    #[test_case(Method::TlsServer)]
571    #[test_case(Method::TlsServerV1_2)]
572    #[test_case(Method::TlsServerV1_3)]
573    fn wolfssl_context_new(method: Method) {
574        crate::wolf_init().unwrap();
575        let _ = method.into_method_ptr().unwrap();
576    }
577
578    #[test]
579    fn new() {
580        ContextBuilder::new(Method::DtlsClient).unwrap();
581    }
582
583    #[test_case(true, true => true)]
584    #[test_case(true, false => panics "Fatal(Other { what:")]
585    #[test_case(false, false => false)]
586    #[test_case(false, true => false)]
587    fn try_when(whether: bool, ok: bool) -> bool {
588        let mut called = false;
589        let _ = ContextBuilder::new(Method::TlsClient)
590            .unwrap()
591            .try_when(whether, |b| {
592                called = true;
593                if ok {
594                    Ok(b)
595                } else {
596                    Err(Error::fatal(wolfssl_sys::WOLFSSL_FAILURE as c_int))
597                }
598            })
599            .unwrap();
600        called
601    }
602
603    #[test_case(Some(true) => true)]
604    #[test_case(Some(false) => panics "Fatal(Other { what:")]
605    #[test_case(None => false)]
606    fn try_some(whether: Option<bool>) -> bool {
607        let mut called = false;
608        let _ = ContextBuilder::new(Method::TlsClient)
609            .unwrap()
610            .try_when_some(whether, |b, ok| {
611                called = true;
612                if ok {
613                    Ok(b)
614                } else {
615                    Err(Error::fatal(wolfssl_sys::WOLFSSL_FAILURE as c_int))
616                }
617            })
618            .unwrap();
619        called
620    }
621
622    #[test]
623    fn root_certificate_buffer() {
624        const CA_CERT: &[u8] = &include!(concat!(
625            env!("CARGO_MANIFEST_DIR"),
626            "/tests/data/ca_cert_der_2048"
627        ));
628
629        let cert = RootCertificate::Asn1Buffer(CA_CERT);
630
631        let _ = ContextBuilder::new(Method::TlsClient)
632            .unwrap()
633            .with_root_certificate(cert)
634            .unwrap();
635    }
636
637    #[test]
638    fn set_cipher_list() {
639        let _ = ContextBuilder::new(Method::DtlsClient)
640            .unwrap()
641            // This string might need to change depending on the flags
642            // we built wolfssl with.
643            .with_cipher_list("TLS13-CHACHA20-POLY1305-SHA256")
644            .unwrap();
645    }
646
647    #[test]
648    fn set_certificate_buffer() {
649        const SERVER_CERT: &[u8] = &include!(concat!(
650            env!("CARGO_MANIFEST_DIR"),
651            "/tests/data/server_cert_der_2048"
652        ));
653
654        let cert = Secret::Asn1Buffer(SERVER_CERT);
655
656        let _ = ContextBuilder::new(Method::TlsClient)
657            .unwrap()
658            .with_certificate(cert)
659            .unwrap();
660    }
661
662    #[test]
663    fn set_private_key_buffer() {
664        const SERVER_KEY: &[u8] = &include!(concat!(
665            env!("CARGO_MANIFEST_DIR"),
666            "/tests/data/server_key_der_2048"
667        ));
668
669        let key = Secret::Asn1Buffer(SERVER_KEY);
670
671        let _ = ContextBuilder::new(Method::TlsClient)
672            .unwrap()
673            .with_private_key(key)
674            .unwrap();
675    }
676
677    #[test]
678    fn set_secure_renegotiation() {
679        let _ = ContextBuilder::new(Method::TlsClient)
680            .unwrap()
681            .with_secure_renegotiation()
682            .unwrap();
683    }
684
685    #[test_case(SslVerifyMode::SslVerifyNone)]
686    #[test_case(SslVerifyMode::SslVerifyPeer)]
687    #[test_case(SslVerifyMode::SslVerifyFailIfNoPeerCert)]
688    #[test_case(SslVerifyMode::SslVerifyFailExceptPsk)]
689    fn set_verify_method(mode: SslVerifyMode) {
690        ContextBuilder::new(Method::TlsClient)
691            .unwrap()
692            .with_verify_method(mode);
693    }
694
695    #[test]
696    fn register_io_callbacks() {
697        let _ = ContextBuilder::new(Method::TlsClient).unwrap().build();
698    }
699}