Skip to main content

wolfssl/
lib.rs

1//! The `wolfssl` crate is designed to be a Rust layer built on top of
2//! the `wolfssl-sys` crate (a C passthrough crate).
3
4mod aes256;
5mod callback;
6mod chacha20_poly1305;
7mod context;
8mod debug;
9mod error;
10mod rng;
11mod ssl;
12
13pub use aes256::*;
14pub use callback::*;
15pub use chacha20_poly1305::*;
16pub use context::*;
17pub use rng::*;
18pub use ssl::*;
19
20pub use error::{Error, ErrorKind, Poll, Result};
21
22#[cfg(feature = "debug")]
23pub use debug::*;
24use wolfssl_sys::{
25    WOLFSSL_VERIFY_FAIL_EXCEPT_PSK_c_int as WOLFSSL_VERIFY_FAIL_EXCEPT_PSK,
26    WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT_c_int as WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
27    WOLFSSL_VERIFY_NONE_c_int as WOLFSSL_VERIFY_NONE,
28    WOLFSSL_VERIFY_PEER_c_int as WOLFSSL_VERIFY_PEER,
29};
30
31use std::{os::raw::c_int, ptr::NonNull};
32
33pub use wolfssl_sys::get_wolfssl_version_string;
34
35/// Record size is defined as `2^14 + 1`.
36///
37/// > ...the full encoded TLSInnerPlaintext MUST NOT exceed 2^14 + 1
38/// > octets
39/// - [source][0]
40///
41/// This value must also equal or exceed `<wolfssl/internal.h>`'s
42/// `MAX_RECORD_SIZE` (though I'm not sure how to assert that yet).
43///
44/// [0]: https://www.rfc-editor.org/rfc/rfc8446#section-5.4
45const TLS_MAX_RECORD_SIZE: usize = 2usize.pow(14) + 1;
46
47/// Wraps [`wolfSSL_Init`][0]
48///
49/// This must be called internally by any binding which uses a library function.
50///
51/// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__TLS.html#function-wolfssl_init
52fn wolf_init() -> Result<()> {
53    static ONCE: std::sync::OnceLock<Result<()>> = std::sync::OnceLock::new();
54
55    ONCE.get_or_init(|| {
56        // SAFETY: [`wolfSSL_Init`][0] ([also][1]) must be called once
57        // per application, this is enforced using the `ONCE:
58        // OnceLock` and by ensuring that all entry points into this
59        // crate call this method.
60        //
61        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__TLS.html#function-wolfssl_init
62        // [1]: https://www.wolfssl.com/doxygen/group__TLS.html#ga789ef74e34df659a62f06da2ea709737
63        match unsafe { wolfssl_sys::wolfSSL_Init() } {
64            wolfssl_sys::WOLFSSL_SUCCESS_c_int => Ok(()),
65            e => Err(Error::fatal(e)),
66        }
67    })
68    .clone()
69}
70
71/// Wraps [`wolfSSL_Debugging_ON`][0] and [`wolfSSL_Debugging_OFF`][1]
72///
73/// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Debug.html#function-wolfssl_debugging_on
74/// [1]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Debug.html#function-wolfssl_debugging_off
75#[cfg(feature = "debug")]
76pub fn enable_debugging(on: bool) {
77    wolf_init().expect("Unable to initialize wolfSSL");
78
79    if on {
80        // SAFETY: [`wolfSSL_Debugging_ON`][0] ([also][1]) requires `DEBUG_WOLFSSL` to be compiled in to succeed
81        // This function will be compiled only on enabling feature `debug`
82        //
83        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Debug.html#function-wolfssl_debugging_on
84        // [1]: https://www.wolfssl.com/doxygen/group__Debug.html#ga192a2501d23697c2b56ce26b1af0eb2c
85        match unsafe { wolfssl_sys::wolfSSL_Debugging_ON() } {
86            0 => {}
87            // This wrapper function is only enabled if we built wolfssl-sys with debugging on.
88            wolfssl_sys::wolfCrypt_ErrorCodes_NOT_COMPILED_IN => {
89                panic!("Inconsistent build, debug not enabled in wolfssl_sys")
90            }
91            e => unreachable!("wolfSSL_Debugging_ON: {e:?}"),
92        }
93    } else {
94        // SAFETY: [`wolfSSL_Debugging_OFF`][0] ([also][1]) has no safety concerns as per documentation
95        //
96        // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Debug.html#function-wolfssl_debugging_off
97        // [1]: https://www.wolfssl.com/doxygen/group__Debug.html#gafa8dab742182b891d80300fb195399ce
98        unsafe { wolfssl_sys::wolfSSL_Debugging_OFF() }
99    }
100}
101
102#[cfg(feature = "debug")]
103pub use wolfssl_sys::wolfSSL_Logging_cb as WolfsslLoggingCallback;
104
105/// Wraps [`wolfSSL_SetLoggingCb`][0]. You must call [`enable_debugging`] first to enable logging at runtime before setting the callback.
106///
107/// [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Logging.html#function-wolfssl_setloggingcb
108#[cfg(feature = "debug")]
109pub fn set_logging_callback(cb: WolfsslLoggingCallback) {
110    wolf_init().expect("Unable to initialize wolfSSL");
111
112    // SAFETY: [`wolfSSL_SetLoggingCb`][0] would return an error if a function pointer is not provided, or we failed to set logging callback.
113    // This function will be compiled only on enabling feature `debug`
114    //
115    // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Logging.html#function-wolfssl_setloggingcb
116    match unsafe { wolfssl_sys::wolfSSL_SetLoggingCb(cb) } {
117        0 => {}
118        wolfssl_sys::wolfCrypt_ErrorCodes_BAD_FUNC_ARG => {
119            panic!("Function pointer is not provided")
120        }
121        e => unreachable!("wolfSSL_SetLoggingCb: {e:?}"),
122    }
123}
124
125/// TLS/DTLS protocol versions
126#[derive(Debug, Copy, Clone, PartialEq)]
127pub enum ProtocolVersion {
128    /// SSL 2.0
129    SslV2,
130    /// SSL 3.0
131    SslV3,
132    /// TLS 1.0
133    TlsV1_0,
134    /// TLS 1.1
135    TlsV1_1,
136    /// TLS 1.2
137    TlsV1_2,
138    /// TLS 1.3
139    TlsV1_3,
140    /// DTLS 1.0
141    DtlsV1_0,
142    /// DTLS 1.2
143    DtlsV1_2,
144    /// DTLS 1.3
145    DtlsV1_3,
146    /// Unknown protocol version
147    Unknown,
148}
149
150impl ProtocolVersion {
151    /// Get a static string representation of the version.
152    pub fn as_str(&self) -> &'static str {
153        match self {
154            ProtocolVersion::SslV2 => "ssl_2",
155            ProtocolVersion::SslV3 => "ssl_3",
156            ProtocolVersion::TlsV1_0 => "tls_1_0",
157            ProtocolVersion::TlsV1_1 => "tls_1_1",
158            ProtocolVersion::TlsV1_2 => "tls_1_2",
159            ProtocolVersion::TlsV1_3 => "tls_1_3",
160            ProtocolVersion::DtlsV1_0 => "dtls_1_0",
161            ProtocolVersion::DtlsV1_2 => "dtls_1_2",
162            ProtocolVersion::DtlsV1_3 => "dtls_1_3",
163            ProtocolVersion::Unknown => "unknown",
164        }
165    }
166
167    /// Checks if the protocol version is compatible with TLS 1.3
168    fn is_tls_13(&self) -> bool {
169        matches!(self, Self::TlsV1_3)
170    }
171
172    /// Checks if the protocol version is compatible with DTLS 1.3
173    fn is_dtls_13(&self) -> bool {
174        matches!(self, Self::DtlsV1_3)
175    }
176}
177
178/// Corresponds to the various `wolf*_{client,server}_method()` APIs
179#[derive(Debug, Copy, Clone)]
180pub enum Method {
181    /// `wolfDTLS_client_method`
182    DtlsClient,
183    /// `wolfDTLSv1_2_client_method`
184    DtlsClientV1_2,
185    /// `wolfDTLSv1_3_client_method`
186    DtlsClientV1_3,
187    /// `wolfDTLS_server_method`
188    DtlsServer,
189    /// `wolfDTLSv1_2_server_method`
190    DtlsServerV1_2,
191    /// `wolfDTLSv1_3_server_method`
192    DtlsServerV1_3,
193    /// `wolfTLS_client_method`
194    TlsClient,
195    /// `wolfTLSv1_2_client_method`
196    TlsClientV1_2,
197    /// `wolfTLSv1_3_client_method`
198    TlsClientV1_3,
199    /// `wolfTLS_server_method`
200    TlsServer,
201    /// `wolfTLSv1_2_server_method`
202    TlsServerV1_2,
203    /// `wolfTLSv1_3_server_method`
204    TlsServerV1_3,
205}
206
207impl Method {
208    /// Converts a [`Self`] into a [`wolfssl_sys::WOLFSSL_METHOD`]
209    /// compatible with [`wolfssl_sys::wolfSSL_CTX_new`]
210    fn into_method_ptr(self) -> Option<NonNull<wolfssl_sys::WOLFSSL_METHOD>> {
211        let ptr = match self {
212            // SAFETY: Per documentation [`wolfDTLS_client_method][0] and its sibling methods allocate memory for `WOLFSSL_METHOD` and initialize with proper values.
213            // The documentation is unclear about when to free the memory.
214            // Based on implementation[2], the api [`wolfSSL_CTX_new`][1] will consume this memory and thus take care of freeing it
215            //
216            // [0]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfsslv3_client_method
217            // [1]: https://www.wolfssl.com/documentation/manuals/wolfssl/group__Setup.html#function-wolfssl_ctx_new
218            // [2]: https://github.com/wolfSSL/wolfssl/blob/v5.6.3-stable/src/internal.c#L2156
219            Self::DtlsClient => unsafe { wolfssl_sys::wolfDTLS_client_method() },
220            // SAFETY: as above
221            Self::DtlsClientV1_2 => unsafe { wolfssl_sys::wolfDTLSv1_2_client_method() },
222            // SAFETY: as above
223            Self::DtlsClientV1_3 => unsafe { wolfssl_sys::wolfDTLSv1_3_client_method() },
224            // SAFETY: as above
225            Self::DtlsServer => unsafe { wolfssl_sys::wolfDTLS_server_method() },
226            // SAFETY: as above
227            Self::DtlsServerV1_2 => unsafe { wolfssl_sys::wolfDTLSv1_2_server_method() },
228            // SAFETY: as above
229            Self::DtlsServerV1_3 => unsafe { wolfssl_sys::wolfDTLSv1_3_server_method() },
230            // SAFETY: as above
231            Self::TlsClient => unsafe { wolfssl_sys::wolfTLS_client_method() },
232            // SAFETY: as above
233            Self::TlsClientV1_2 => unsafe { wolfssl_sys::wolfTLSv1_2_client_method() },
234            // SAFETY: as above
235            Self::TlsClientV1_3 => unsafe { wolfssl_sys::wolfTLSv1_3_client_method() },
236            // SAFETY: as above
237            Self::TlsServer => unsafe { wolfssl_sys::wolfTLS_server_method() },
238            // SAFETY: as above
239            Self::TlsServerV1_2 => unsafe { wolfssl_sys::wolfTLSv1_2_server_method() },
240            // SAFETY: as above
241            Self::TlsServerV1_3 => unsafe { wolfssl_sys::wolfTLSv1_3_server_method() },
242        };
243
244        NonNull::new(ptr)
245    }
246}
247
248/// Corresponds to the various defined `WOLFSSL_*` curves
249#[derive(Debug, Copy, Clone)]
250pub enum CurveGroup {
251    /// `WOLFSSL_ECC_SECP256R1`
252    EccSecp256R1,
253
254    /// `WOLFSSL_ECC_X25519`
255    EccX25519,
256
257    /// `WOLFSSL_P256_KYBER_LEVEL1`
258    #[cfg(feature = "postquantum")]
259    P256KyberLevel1,
260    /// `WOLFSSL_P384_KYBER_LEVEL3`
261    #[cfg(feature = "postquantum")]
262    P384KyberLevel3,
263    /// `WOLFSSL_P521_KYBER_LEVEL5`
264    #[cfg(feature = "postquantum")]
265    P521KyberLevel5,
266
267    /// `WOLFSSL_SECP256R1MLKEM512`
268    #[cfg(feature = "postquantum")]
269    P256MLKEM512,
270    /// `WOLFSSL_SECP384R1MLKEM768`
271    #[cfg(feature = "postquantum")]
272    P384MLKEM768,
273    /// `WOLFSSL_SECP521R1MLKEM1024`
274    #[cfg(feature = "postquantum")]
275    P521MLKEM1024,
276
277    /// `WOLFSSL_X25519MLKEM768`
278    #[cfg(feature = "postquantum")]
279    X25519MLKEM768,
280}
281
282#[cfg(unix)]
283type CurveGroupType = std::os::raw::c_uint;
284#[cfg(windows)]
285type CurveGroupType = std::os::raw::c_int;
286
287impl CurveGroup {
288    fn as_ffi(&self) -> CurveGroupType {
289        use CurveGroup::*;
290        match self {
291            EccSecp256R1 => wolfssl_sys::WOLFSSL_ECC_SECP256R1,
292            EccX25519 => wolfssl_sys::WOLFSSL_ECC_X25519,
293            #[cfg(feature = "postquantum")]
294            P256KyberLevel1 => wolfssl_sys::WOLFSSL_P256_KYBER_LEVEL1,
295            #[cfg(feature = "postquantum")]
296            P384KyberLevel3 => wolfssl_sys::WOLFSSL_P384_KYBER_LEVEL3,
297            #[cfg(feature = "postquantum")]
298            P521KyberLevel5 => wolfssl_sys::WOLFSSL_P521_KYBER_LEVEL5,
299            #[cfg(feature = "postquantum")]
300            P256MLKEM512 => wolfssl_sys::WOLFSSL_SECP256R1MLKEM512,
301            #[cfg(feature = "postquantum")]
302            P384MLKEM768 => wolfssl_sys::WOLFSSL_SECP384R1MLKEM768,
303            #[cfg(feature = "postquantum")]
304            P521MLKEM1024 => wolfssl_sys::WOLFSSL_SECP521R1MLKEM1024,
305            #[cfg(feature = "postquantum")]
306            X25519MLKEM768 => wolfssl_sys::WOLFSSL_X25519MLKEM768,
307        }
308    }
309}
310
311/// Defines a CA certificate
312#[derive(Debug, Copy, Clone)]
313pub enum RootCertificate<'a> {
314    /// In-memory PEM buffer
315    PemBuffer(&'a [u8]),
316    /// In-memory ASN1 buffer
317    Asn1Buffer(&'a [u8]),
318    /// Path to a PEM file, or a directory of PEM files
319    PemFileOrDirectory(&'a std::path::Path),
320}
321
322/// Defines either a public or private key
323#[derive(Debug, Copy, Clone)]
324pub enum Secret<'a> {
325    /// In-memory ASN1 buffer
326    Asn1Buffer(&'a [u8]),
327    /// Path to ASN1 file
328    Asn1File(&'a std::path::Path),
329    /// In-memory PEM buffer
330    PemBuffer(&'a [u8]),
331    /// Path to PEM file
332    PemFile(&'a std::path::Path),
333}
334
335/// SSL Verification method
336/// Ref: `https://www.wolfssl.com/doxygen/group__Setup.html#gaf9198658e31dd291088be18262ef2354`
337#[derive(Debug, Default, Copy, Clone)]
338pub enum SslVerifyMode {
339    /// No verification done
340    SslVerifyNone,
341    /// Verify peers certificate
342    #[default]
343    SslVerifyPeer,
344    /// Verify client's certificate (applicable only for server)
345    SslVerifyFailIfNoPeerCert,
346    /// Verify client's certificate except PSK connection (applicable only for server)
347    SslVerifyFailExceptPsk,
348}
349
350impl From<SslVerifyMode> for c_int {
351    fn from(value: SslVerifyMode) -> Self {
352        match value {
353            SslVerifyMode::SslVerifyNone => WOLFSSL_VERIFY_NONE,
354            SslVerifyMode::SslVerifyPeer => WOLFSSL_VERIFY_PEER,
355            SslVerifyMode::SslVerifyFailIfNoPeerCert => WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
356            SslVerifyMode::SslVerifyFailExceptPsk => WOLFSSL_VERIFY_FAIL_EXCEPT_PSK,
357        }
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use super::*;
364    use test_case::test_case;
365
366    #[test]
367    fn wolf_init_test() {
368        wolf_init().unwrap();
369    }
370
371    #[test_case(ProtocolVersion::SslV2 => "ssl_2")]
372    #[test_case(ProtocolVersion::SslV3 => "ssl_3")]
373    #[test_case(ProtocolVersion::TlsV1_0 => "tls_1_0")]
374    #[test_case(ProtocolVersion::TlsV1_1 => "tls_1_1")]
375    #[test_case(ProtocolVersion::TlsV1_2 => "tls_1_2")]
376    #[test_case(ProtocolVersion::TlsV1_3 => "tls_1_3")]
377    #[test_case(ProtocolVersion::DtlsV1_0 => "dtls_1_0")]
378    #[test_case(ProtocolVersion::DtlsV1_2 => "dtls_1_2")]
379    #[test_case(ProtocolVersion::DtlsV1_3 => "dtls_1_3")]
380    #[test_case(ProtocolVersion::Unknown => "unknown")]
381    fn protocol_version_as_str(p: ProtocolVersion) -> &'static str {
382        p.as_str()
383    }
384
385    #[test_case(SslVerifyMode::SslVerifyNone => WOLFSSL_VERIFY_NONE)]
386    #[test_case(SslVerifyMode::SslVerifyPeer => WOLFSSL_VERIFY_PEER)]
387    #[test_case(SslVerifyMode::SslVerifyFailIfNoPeerCert => WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT)]
388    #[test_case(SslVerifyMode::SslVerifyFailExceptPsk => WOLFSSL_VERIFY_FAIL_EXCEPT_PSK)]
389    fn ssl_verify_mode(s: SslVerifyMode) -> c_int {
390        s.into()
391    }
392}