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}