Skip to main content

native_ossl/
ssl.rs

1//! TLS — `SSL_CTX`, `SSL`, and `SSL_SESSION` wrappers.
2//!
3//! # Types
4//!
5//! | Type           | Owned / Shared | Description                               |
6//! |----------------|---------------|-------------------------------------------|
7//! | [`SslCtx`]     | Shared (Clone) | TLS context — configuration, certs, keys  |
8//! | [`Ssl`]        | Exclusive      | Per-connection TLS object                 |
9//! | [`SslSession`] | Shared (Clone) | Resumable TLS session handle              |
10//!
11//! # Protocol version
12//!
13//! `SSL_CTX_set_min_proto_version` / `SSL_CTX_set_max_proto_version` are C macros
14//! that expand to `SSL_CTX_ctrl(ctx, 123 / 124, version, NULL)`.  This module
15//! calls `SSL_CTX_ctrl` directly since bindgen cannot expose C macros as functions.
16//!
17//! # SNI hostname
18//!
19//! `SSL_set_tlsext_host_name` is a C macro expanding to
20//! `SSL_ctrl(s, 55, 0, name)`.  Use [`Ssl::set_hostname`] to set the SNI extension.
21//!
22//! # BIO ownership
23//!
24//! `SSL_set_bio` transfers ownership of the supplied `BIO*` pointers to the `SSL`
25//! object.  [`Ssl::set_bio_duplex`] accepts a single [`crate::bio::Bio`] for the
26//! common case where the same BIO serves as both read and write channel (e.g. the
27//! output of `BIO_new_bio_pair`).
28
29use crate::bio::Bio;
30use crate::error::ErrorStack;
31use crate::pkey::{HasPrivate, Pkey};
32use crate::x509::X509;
33use native_ossl_sys as sys;
34use std::ffi::CStr;
35
36// ── TLS version ───────────────────────────────────────────────────────────────
37
38/// TLS protocol version selector.
39///
40/// Passed to [`SslCtx::set_min_proto_version`] and [`SslCtx::set_max_proto_version`].
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum TlsVersion {
43    /// TLS 1.2 (`0x0303`)
44    Tls12 = 0x0303,
45    /// TLS 1.3 (`0x0304`)
46    Tls13 = 0x0304,
47}
48
49// ── Verify mode ───────────────────────────────────────────────────────────────
50
51/// Certificate verification mode flags.
52///
53/// Combine with bitwise OR using [`SslVerifyMode::or`].
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub struct SslVerifyMode(i32);
56
57impl SslVerifyMode {
58    /// Do not verify the peer certificate (`SSL_VERIFY_NONE`).
59    pub const NONE: Self = SslVerifyMode(0x00);
60    /// Verify the peer certificate (`SSL_VERIFY_PEER`).
61    pub const PEER: Self = SslVerifyMode(0x01);
62    /// Fail if the peer does not present a certificate (`SSL_VERIFY_FAIL_IF_NO_PEER_CERT`).
63    pub const FAIL_IF_NO_PEER_CERT: Self = SslVerifyMode(0x02);
64
65    /// Combine two mode values with bitwise OR.
66    #[must_use]
67    pub fn or(self, other: Self) -> Self {
68        SslVerifyMode(self.0 | other.0)
69    }
70}
71
72// ── SSL I/O error ─────────────────────────────────────────────────────────────
73
74/// Error returned by non-blocking SSL I/O operations.
75///
76/// `WantRead` / `WantWrite` indicate that the operation should be retried
77/// after the underlying BIO becomes ready.  For in-memory BIO pairs, retrying
78/// immediately after driving the peer is sufficient.
79#[derive(Debug)]
80pub enum SslIoError {
81    /// Retry after data arrives on the read BIO (`SSL_ERROR_WANT_READ`).
82    WantRead,
83    /// Retry after the write BIO drains (`SSL_ERROR_WANT_WRITE`).
84    WantWrite,
85    /// The peer closed the connection cleanly (`SSL_ERROR_ZERO_RETURN`).
86    ZeroReturn,
87    /// Underlying I/O error; see [`ErrorStack`] for details (`SSL_ERROR_SYSCALL`).
88    Syscall(ErrorStack),
89    /// OpenSSL protocol error (`SSL_ERROR_SSL`).
90    Ssl(ErrorStack),
91    /// Unexpected error code returned by `SSL_get_error`.
92    Other(i32),
93}
94
95impl std::fmt::Display for SslIoError {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match self {
98            Self::WantRead => write!(f, "SSL want read"),
99            Self::WantWrite => write!(f, "SSL want write"),
100            Self::ZeroReturn => write!(f, "SSL zero return (peer closed)"),
101            Self::Syscall(e) => write!(f, "SSL syscall error: {e}"),
102            Self::Ssl(e) => write!(f, "SSL error: {e}"),
103            Self::Other(code) => write!(f, "SSL error code {code}"),
104        }
105    }
106}
107
108impl std::error::Error for SslIoError {}
109
110// ── ShutdownResult ────────────────────────────────────────────────────────────
111
112/// Result of [`Ssl::shutdown`].
113#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub enum ShutdownResult {
115    /// First shutdown stage sent; call `shutdown` again to complete.
116    Sent,
117    /// Bidirectional shutdown complete.
118    Complete,
119}
120
121// ── SslSession ────────────────────────────────────────────────────────────────
122
123/// A TLS session handle (`SSL_SESSION*`).
124///
125/// Cloneable via `SSL_SESSION_up_ref`.  Pass to [`Ssl::set_session`] to enable
126/// session resumption.
127pub struct SslSession {
128    ptr: *mut sys::SSL_SESSION,
129}
130
131// SAFETY: `SSL_SESSION` is reference-counted.
132unsafe impl Send for SslSession {}
133unsafe impl Sync for SslSession {}
134
135impl Clone for SslSession {
136    fn clone(&self) -> Self {
137        unsafe { sys::SSL_SESSION_up_ref(self.ptr) };
138        SslSession { ptr: self.ptr }
139    }
140}
141
142impl Drop for SslSession {
143    fn drop(&mut self) {
144        unsafe { sys::SSL_SESSION_free(self.ptr) };
145    }
146}
147
148// ── SslCtx ────────────────────────────────────────────────────────────────────
149
150/// TLS context (`SSL_CTX*`).
151///
152/// Holds shared configuration such as certificates, private keys, and verify
153/// settings.  Multiple [`Ssl`] objects can be created from the same `SslCtx`.
154///
155/// Cloneable via `SSL_CTX_up_ref`; wrapping in `Arc<SslCtx>` is safe.
156pub struct SslCtx {
157    ptr: *mut sys::SSL_CTX,
158}
159
160// SAFETY: `SSL_CTX` is reference-counted.
161unsafe impl Send for SslCtx {}
162unsafe impl Sync for SslCtx {}
163
164impl Clone for SslCtx {
165    fn clone(&self) -> Self {
166        unsafe { sys::SSL_CTX_up_ref(self.ptr) };
167        SslCtx { ptr: self.ptr }
168    }
169}
170
171impl Drop for SslCtx {
172    fn drop(&mut self) {
173        unsafe { sys::SSL_CTX_free(self.ptr) };
174    }
175}
176
177impl SslCtx {
178    /// Create a new TLS context accepting any role (client or server).
179    ///
180    /// Uses the generic `TLS_method()`.  Call [`SslCtx::new_client`] or
181    /// [`SslCtx::new_server`] for role-specific method selection.
182    ///
183    /// # Errors
184    ///
185    /// Returns `Err` if `SSL_CTX_new` fails.
186    pub fn new() -> Result<Self, ErrorStack> {
187        let method = unsafe { sys::TLS_method() };
188        let ptr = unsafe { sys::SSL_CTX_new(method) };
189        if ptr.is_null() {
190            return Err(ErrorStack::drain());
191        }
192        Ok(SslCtx { ptr })
193    }
194
195    /// Create a new TLS context optimised for client connections (`TLS_client_method`).
196    ///
197    /// # Errors
198    pub fn new_client() -> Result<Self, ErrorStack> {
199        let method = unsafe { sys::TLS_client_method() };
200        let ptr = unsafe { sys::SSL_CTX_new(method) };
201        if ptr.is_null() {
202            return Err(ErrorStack::drain());
203        }
204        Ok(SslCtx { ptr })
205    }
206
207    /// Create a new TLS context optimised for server connections (`TLS_server_method`).
208    ///
209    /// # Errors
210    pub fn new_server() -> Result<Self, ErrorStack> {
211        let method = unsafe { sys::TLS_server_method() };
212        let ptr = unsafe { sys::SSL_CTX_new(method) };
213        if ptr.is_null() {
214            return Err(ErrorStack::drain());
215        }
216        Ok(SslCtx { ptr })
217    }
218
219    /// Set the minimum acceptable TLS protocol version.
220    ///
221    /// Internally calls `SSL_CTX_ctrl(ctx, 123 /*SSL_CTRL_SET_MIN_PROTO_VERSION*/, version, NULL)`.
222    ///
223    /// # Errors
224    pub fn set_min_proto_version(&self, ver: TlsVersion) -> Result<(), ErrorStack> {
225        let rc = unsafe { sys::SSL_CTX_ctrl(self.ptr, 123, ver as i64, std::ptr::null_mut()) };
226        if rc != 1 {
227            return Err(ErrorStack::drain());
228        }
229        Ok(())
230    }
231
232    /// Set the maximum acceptable TLS protocol version.
233    ///
234    /// Internally calls `SSL_CTX_ctrl(ctx, 124 /*SSL_CTRL_SET_MAX_PROTO_VERSION*/, version, NULL)`.
235    ///
236    /// # Errors
237    pub fn set_max_proto_version(&self, ver: TlsVersion) -> Result<(), ErrorStack> {
238        let rc = unsafe { sys::SSL_CTX_ctrl(self.ptr, 124, ver as i64, std::ptr::null_mut()) };
239        if rc != 1 {
240            return Err(ErrorStack::drain());
241        }
242        Ok(())
243    }
244
245    /// Set the peer certificate verification mode.
246    ///
247    /// Wraps `SSL_CTX_set_verify(ctx, mode, NULL)`.
248    pub fn set_verify(&self, mode: SslVerifyMode) {
249        unsafe { sys::SSL_CTX_set_verify(self.ptr, mode.0, None) };
250    }
251
252    /// Set the allowed cipher list (TLS 1.2 and below).
253    ///
254    /// `list` uses OpenSSL cipher string syntax (e.g. `c"HIGH:!aNULL:!MD5"`).
255    ///
256    /// # Errors
257    pub fn set_cipher_list(&self, list: &CStr) -> Result<(), ErrorStack> {
258        let rc = unsafe { sys::SSL_CTX_set_cipher_list(self.ptr, list.as_ptr()) };
259        if rc != 1 {
260            return Err(ErrorStack::drain());
261        }
262        Ok(())
263    }
264
265    /// Set the allowed TLS 1.3 ciphersuites.
266    ///
267    /// `list` uses OpenSSL ciphersuite syntax (e.g. `c"TLS_AES_256_GCM_SHA384"`).
268    ///
269    /// # Errors
270    pub fn set_ciphersuites(&self, list: &CStr) -> Result<(), ErrorStack> {
271        let rc = unsafe { sys::SSL_CTX_set_ciphersuites(self.ptr, list.as_ptr()) };
272        if rc != 1 {
273            return Err(ErrorStack::drain());
274        }
275        Ok(())
276    }
277
278    /// Load a certificate into the context.
279    ///
280    /// For a server, this is the certificate that will be presented to clients.
281    ///
282    /// # Errors
283    pub fn use_certificate(&self, cert: &X509) -> Result<(), ErrorStack> {
284        let rc = unsafe { sys::SSL_CTX_use_certificate(self.ptr, cert.as_ptr()) };
285        if rc != 1 {
286            return Err(ErrorStack::drain());
287        }
288        Ok(())
289    }
290
291    /// Load a private key into the context.
292    ///
293    /// The key must correspond to the certificate loaded via [`SslCtx::use_certificate`].
294    ///
295    /// # Errors
296    pub fn use_private_key<T: HasPrivate>(&self, key: &Pkey<T>) -> Result<(), ErrorStack> {
297        let rc = unsafe { sys::SSL_CTX_use_PrivateKey(self.ptr, key.as_ptr()) };
298        if rc != 1 {
299            return Err(ErrorStack::drain());
300        }
301        Ok(())
302    }
303
304    /// Verify that the loaded certificate and private key are consistent.
305    ///
306    /// # Errors
307    ///
308    /// Returns `Err` if the key/certificate pair is invalid or not loaded.
309    pub fn check_private_key(&self) -> Result<(), ErrorStack> {
310        let rc = unsafe { sys::SSL_CTX_check_private_key(self.ptr) };
311        if rc != 1 {
312            return Err(ErrorStack::drain());
313        }
314        Ok(())
315    }
316
317    /// Load the system default CA certificate store for verification.
318    ///
319    /// # Errors
320    pub fn set_default_verify_paths(&self) -> Result<(), ErrorStack> {
321        let rc = unsafe { sys::SSL_CTX_set_default_verify_paths(self.ptr) };
322        if rc != 1 {
323            return Err(ErrorStack::drain());
324        }
325        Ok(())
326    }
327
328    /// Disable TLS session caching on this context.
329    pub fn disable_session_cache(&self) {
330        // SSL_CTRL_SET_SESS_CACHE_MODE = 44, SSL_SESS_CACHE_OFF = 0
331        unsafe { sys::SSL_CTX_ctrl(self.ptr, 44, 0, std::ptr::null_mut()) };
332    }
333
334    /// Create a new [`Ssl`] connection object from this context.
335    ///
336    /// # Errors
337    ///
338    /// Returns `Err` if `SSL_new` fails.
339    pub fn new_ssl(&self) -> Result<Ssl, ErrorStack> {
340        let ptr = unsafe { sys::SSL_new(self.ptr) };
341        if ptr.is_null() {
342            return Err(ErrorStack::drain());
343        }
344        Ok(Ssl { ptr })
345    }
346}
347
348// ── Ssl ───────────────────────────────────────────────────────────────────────
349
350/// Per-connection TLS object (`SSL*`).
351///
352/// Has exclusive ownership over its state; no `Clone`.  BIOs passed to
353/// [`Ssl::set_bio_duplex`] or [`Ssl::set_bio`] are owned by the `Ssl` thereafter.
354pub struct Ssl {
355    ptr: *mut sys::SSL,
356}
357
358// SAFETY: `SSL*` is not thread-safe for concurrent access, but `Ssl` has
359// exclusive ownership, so `Send` is safe for moving between threads.
360unsafe impl Send for Ssl {}
361
362impl Drop for Ssl {
363    fn drop(&mut self) {
364        unsafe { sys::SSL_free(self.ptr) };
365    }
366}
367
368impl Ssl {
369    /// Set a single duplex BIO for both reading and writing.
370    ///
371    /// Transfers ownership of `bio` to the `SSL` object; do not use `bio`
372    /// afterwards.  Suitable for `BIO_new_bio_pair` endpoints.
373    ///
374    /// When `rbio == wbio` (same pointer), OpenSSL only increments the
375    /// reference count once, so the single reference in `bio` is correct.
376    pub fn set_bio_duplex(&mut self, bio: Bio) {
377        let ptr = bio.as_ptr();
378        // Prevent our Drop from calling BIO_free — SSL now owns this BIO.
379        std::mem::forget(bio);
380        // Passing the same pointer for rbio and wbio: OpenSSL increments
381        // the ref count only once when rbio == wbio.
382        unsafe { sys::SSL_set_bio(self.ptr, ptr, ptr) };
383    }
384
385    /// Set separate read and write BIOs.
386    ///
387    /// Transfers ownership of both `rbio` and `wbio` to the `SSL` object.
388    pub fn set_bio(&mut self, rbio: Bio, wbio: Bio) {
389        let rbio_ptr = rbio.as_ptr();
390        let wbio_ptr = wbio.as_ptr();
391        // Prevent our Drop from calling BIO_free on each — SSL now owns them.
392        std::mem::forget(rbio);
393        std::mem::forget(wbio);
394        unsafe { sys::SSL_set_bio(self.ptr, rbio_ptr, wbio_ptr) };
395    }
396
397    /// Set the SNI hostname extension sent during the TLS handshake.
398    ///
399    /// Call before [`Self::connect`] on client connections to enable SNI.
400    /// `hostname` must be a NUL-terminated ASCII/UTF-8 hostname.
401    ///
402    /// `SSL_set_tlsext_host_name` is a C macro expanding to
403    /// `SSL_ctrl(s, 55 /*SSL_CTRL_SET_TLSEXT_HOSTNAME*/, 0 /*TLSEXT_NAMETYPE_host_name*/, name)`.
404    ///
405    /// # Errors
406    ///
407    /// Returns `Err` if the control call fails.
408    pub fn set_hostname(&mut self, hostname: &CStr) -> Result<(), ErrorStack> {
409        let rc = unsafe {
410            sys::SSL_ctrl(
411                self.ptr,
412                55, // SSL_CTRL_SET_TLSEXT_HOSTNAME
413                0,  // TLSEXT_NAMETYPE_host_name
414                // OpenSSL declares parg as *mut c_void but uses it as const here.
415                hostname.as_ptr() as *mut std::os::raw::c_void,
416            )
417        };
418        if rc != 1 {
419            return Err(ErrorStack::drain());
420        }
421        Ok(())
422    }
423
424    /// Set this SSL object to operate in client (connect) mode.
425    ///
426    /// Required before calling [`Self::do_handshake`] if neither [`Self::connect`] nor
427    /// [`Self::accept`] will be used.
428    pub fn set_connect_state(&mut self) {
429        unsafe { sys::SSL_set_connect_state(self.ptr) };
430    }
431
432    /// Set this SSL object to operate in server (accept) mode.
433    pub fn set_accept_state(&mut self) {
434        unsafe { sys::SSL_set_accept_state(self.ptr) };
435    }
436
437    /// Initiate a client-side TLS handshake (`SSL_connect`).
438    ///
439    /// Returns `Ok(())` on success, [`SslIoError::WantRead`] / [`SslIoError::WantWrite`]
440    /// when the operation must be retried after more data is available.
441    ///
442    /// # Errors
443    pub fn connect(&mut self) -> Result<(), SslIoError> {
444        let rc = unsafe { sys::SSL_connect(self.ptr) };
445        if rc == 1 {
446            return Ok(());
447        }
448        Err(self.ssl_io_error(rc))
449    }
450
451    /// Accept an incoming TLS connection (`SSL_accept`).
452    ///
453    /// Returns `Ok(())` on success, [`SslIoError::WantRead`] / [`SslIoError::WantWrite`]
454    /// on non-blocking retry.
455    ///
456    /// # Errors
457    pub fn accept(&mut self) -> Result<(), SslIoError> {
458        let rc = unsafe { sys::SSL_accept(self.ptr) };
459        if rc == 1 {
460            return Ok(());
461        }
462        Err(self.ssl_io_error(rc))
463    }
464
465    /// Drive the TLS handshake in either role (`SSL_do_handshake`).
466    ///
467    /// The role must have been set via [`Self::set_connect_state`] or [`Self::set_accept_state`]
468    /// (or implicitly by [`Self::connect`] / [`Self::accept`]).
469    ///
470    /// # Errors
471    pub fn do_handshake(&mut self) -> Result<(), SslIoError> {
472        let rc = unsafe { sys::SSL_do_handshake(self.ptr) };
473        if rc == 1 {
474            return Ok(());
475        }
476        Err(self.ssl_io_error(rc))
477    }
478
479    /// Read decrypted application data (`SSL_read_ex`).
480    ///
481    /// Returns the number of bytes written into `buf` on success.
482    ///
483    /// # Errors
484    pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, SslIoError> {
485        let mut readbytes: usize = 0;
486        let rc = unsafe {
487            sys::SSL_read_ex(
488                self.ptr,
489                buf.as_mut_ptr().cast(),
490                buf.len(),
491                std::ptr::addr_of_mut!(readbytes),
492            )
493        };
494        if rc == 1 {
495            return Ok(readbytes);
496        }
497        Err(self.ssl_io_error(rc))
498    }
499
500    /// Write application data (`SSL_write_ex`).
501    ///
502    /// Returns the number of bytes consumed from `buf` on success.
503    ///
504    /// # Errors
505    pub fn write(&mut self, buf: &[u8]) -> Result<usize, SslIoError> {
506        let mut written: usize = 0;
507        let rc = unsafe {
508            sys::SSL_write_ex(
509                self.ptr,
510                buf.as_ptr().cast(),
511                buf.len(),
512                std::ptr::addr_of_mut!(written),
513            )
514        };
515        if rc == 1 {
516            return Ok(written);
517        }
518        Err(self.ssl_io_error(rc))
519    }
520
521    /// Send a TLS close-notify alert (`SSL_shutdown`).
522    ///
523    /// Returns [`ShutdownResult::Sent`] after the first shutdown stage and
524    /// [`ShutdownResult::Complete`] after a bidirectional shutdown.  Call
525    /// twice on a non-blocking connection to complete the exchange.
526    ///
527    /// # Errors
528    ///
529    /// Returns `Err` on a fatal error during shutdown.
530    pub fn shutdown(&mut self) -> Result<ShutdownResult, ErrorStack> {
531        let rc = unsafe { sys::SSL_shutdown(self.ptr) };
532        match rc {
533            1 => Ok(ShutdownResult::Complete),
534            0 => Ok(ShutdownResult::Sent),
535            _ => Err(ErrorStack::drain()),
536        }
537    }
538
539    /// Return the peer's certificate, or `None` if unavailable.
540    ///
541    /// The returned certificate has its reference count incremented, so it
542    /// outlives `self`.
543    #[must_use]
544    pub fn peer_certificate(&self) -> Option<X509> {
545        let ptr = unsafe { sys::SSL_get0_peer_certificate(self.ptr) };
546        if ptr.is_null() {
547            return None;
548        }
549        // get0 → no ownership; increment ref count to produce an owned X509.
550        unsafe { sys::X509_up_ref(ptr) };
551        Some(unsafe { X509::from_ptr(ptr) })
552    }
553
554    /// Get an owned reference to the current session (`SSL_get1_session`).
555    ///
556    /// Returns `None` if no session is established.  The session can be passed
557    /// to [`Self::set_session`] on a new `Ssl` for resumption.
558    #[must_use]
559    pub fn get1_session(&self) -> Option<SslSession> {
560        let ptr = unsafe { sys::SSL_get1_session(self.ptr) };
561        if ptr.is_null() {
562            None
563        } else {
564            Some(SslSession { ptr })
565        }
566    }
567
568    /// Set a previously obtained session for resumption (`SSL_set_session`).
569    ///
570    /// Call before the handshake.
571    ///
572    /// # Errors
573    pub fn set_session(&mut self, session: &SslSession) -> Result<(), ErrorStack> {
574        let rc = unsafe { sys::SSL_set_session(self.ptr, session.ptr) };
575        if rc != 1 {
576            return Err(ErrorStack::drain());
577        }
578        Ok(())
579    }
580
581    /// Translate a non-positive SSL I/O return code into an [`SslIoError`].
582    fn ssl_io_error(&self, ret: i32) -> SslIoError {
583        let err = unsafe { sys::SSL_get_error(self.ptr, ret) };
584        match err {
585            2 => SslIoError::WantRead,
586            3 => SslIoError::WantWrite,
587            5 => SslIoError::Syscall(ErrorStack::drain()),
588            6 => SslIoError::ZeroReturn,
589            _ => {
590                let stack = ErrorStack::drain();
591                if stack.errors().next().is_none() {
592                    SslIoError::Other(err)
593                } else {
594                    SslIoError::Ssl(stack)
595                }
596            }
597        }
598    }
599}
600
601// ── Tests ─────────────────────────────────────────────────────────────────────
602
603#[cfg(test)]
604mod tests {
605    use super::*;
606    use crate::pkey::{KeygenCtx, Pkey, Private, Public};
607    use crate::x509::{X509Builder, X509NameOwned};
608
609    // ── Helpers ───────────────────────────────────────────────────────────────
610
611    /// Generate a fresh Ed25519 key pair.
612    fn make_ed25519_key() -> (Pkey<Private>, Pkey<Public>) {
613        let mut kgen = KeygenCtx::new(c"ED25519").unwrap();
614        let priv_key = kgen.generate().unwrap();
615        let pub_key = Pkey::<Public>::from(priv_key.clone());
616        (priv_key, pub_key)
617    }
618
619    /// Build a self-signed Ed25519 certificate valid for 1 day.
620    fn make_self_signed_cert(priv_key: &Pkey<Private>, pub_key: &Pkey<Public>) -> X509 {
621        let mut name = X509NameOwned::new().unwrap();
622        name.add_entry_by_txt(c"CN", b"test").unwrap();
623
624        X509Builder::new()
625            .unwrap()
626            .set_version(2)
627            .unwrap()
628            .set_serial_number(1)
629            .unwrap()
630            .set_not_before_offset(0)
631            .unwrap()
632            .set_not_after_offset(86400)
633            .unwrap()
634            .set_subject_name(&name)
635            .unwrap()
636            .set_issuer_name(&name)
637            .unwrap()
638            .set_public_key(pub_key)
639            .unwrap()
640            .sign(priv_key, None)
641            .unwrap()
642            .build()
643    }
644
645    /// Drive a pair of SSL objects to a completed handshake using an in-memory
646    /// BIO pair.  Returns `(client, server)` after the handshake.
647    ///
648    /// `BIO_new_bio_pair` creates two linked BIOs:
649    ///   - data written to bio1 → readable from bio2  (client→server)
650    ///   - data written to bio2 → readable from bio1  (server→client)
651    fn do_handshake_pair(mut client: Ssl, mut server: Ssl) -> Result<(Ssl, Ssl), SslIoError> {
652        let mut client_bio: *mut sys::BIO = std::ptr::null_mut();
653        let mut server_bio: *mut sys::BIO = std::ptr::null_mut();
654        let rc = unsafe {
655            sys::BIO_new_bio_pair(
656                std::ptr::addr_of_mut!(client_bio),
657                0,
658                std::ptr::addr_of_mut!(server_bio),
659                0,
660            )
661        };
662        assert_eq!(rc, 1, "BIO_new_bio_pair failed");
663
664        // Transfer ownership to the SSL objects.  Since rbio == wbio for each,
665        // OpenSSL only takes one ref, matching the single ref from BIO_new_bio_pair.
666        let client_bio_obj = unsafe { Bio::from_ptr_owned(client_bio) };
667        let server_bio_obj = unsafe { Bio::from_ptr_owned(server_bio) };
668        client.set_bio_duplex(client_bio_obj);
669        server.set_bio_duplex(server_bio_obj);
670
671        // Alternate between client and server until both complete (up to 20 steps).
672        let mut client_done = false;
673        let mut server_done = false;
674
675        for _ in 0..20 {
676            if !client_done {
677                match client.connect() {
678                    Ok(()) => client_done = true,
679                    Err(SslIoError::WantRead | SslIoError::WantWrite) => {}
680                    Err(e) => return Err(e),
681                }
682            }
683            if !server_done {
684                match server.accept() {
685                    Ok(()) => server_done = true,
686                    Err(SslIoError::WantRead | SslIoError::WantWrite) => {}
687                    Err(e) => return Err(e),
688                }
689            }
690            if client_done && server_done {
691                return Ok((client, server));
692            }
693        }
694        Err(SslIoError::Other(-1))
695    }
696
697    // ── SslCtx construction ───────────────────────────────────────────────────
698
699    #[test]
700    fn ctx_new_variants() {
701        SslCtx::new().unwrap();
702        SslCtx::new_client().unwrap();
703        SslCtx::new_server().unwrap();
704    }
705
706    #[test]
707    fn ctx_clone() {
708        let ctx = SslCtx::new().unwrap();
709        let _clone = ctx.clone();
710    }
711
712    #[test]
713    fn ctx_proto_version() {
714        let ctx = SslCtx::new().unwrap();
715        ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
716        ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
717    }
718
719    #[test]
720    fn ctx_verify_mode() {
721        let ctx = SslCtx::new().unwrap();
722        ctx.set_verify(SslVerifyMode::NONE);
723        ctx.set_verify(SslVerifyMode::PEER);
724        ctx.set_verify(SslVerifyMode::PEER.or(SslVerifyMode::FAIL_IF_NO_PEER_CERT));
725    }
726
727    #[test]
728    fn ctx_cipher_list() {
729        let ctx = SslCtx::new().unwrap();
730        ctx.set_cipher_list(c"HIGH:!aNULL").unwrap();
731    }
732
733    // ── Certificate / key loading ─────────────────────────────────────────────
734
735    #[test]
736    fn ctx_load_cert_and_key() {
737        let (priv_key, pub_key) = make_ed25519_key();
738        let cert = make_self_signed_cert(&priv_key, &pub_key);
739
740        let ctx = SslCtx::new_server().unwrap();
741        ctx.use_certificate(&cert).unwrap();
742        ctx.use_private_key(&priv_key).unwrap();
743        ctx.check_private_key().unwrap();
744    }
745
746    // ── In-memory TLS handshake ───────────────────────────────────────────────
747
748    #[test]
749    fn tls13_handshake_ed25519() {
750        let (priv_key, pub_key) = make_ed25519_key();
751        let cert = make_self_signed_cert(&priv_key, &pub_key);
752
753        // Server context: present our self-signed cert.
754        let server_ctx = SslCtx::new_server().unwrap();
755        server_ctx.set_min_proto_version(TlsVersion::Tls13).unwrap();
756        server_ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
757        server_ctx.use_certificate(&cert).unwrap();
758        server_ctx.use_private_key(&priv_key).unwrap();
759        server_ctx.check_private_key().unwrap();
760        server_ctx.disable_session_cache();
761
762        // Client context: do not verify the server cert (self-signed test cert).
763        let client_ctx = SslCtx::new_client().unwrap();
764        client_ctx.set_min_proto_version(TlsVersion::Tls13).unwrap();
765        client_ctx.set_max_proto_version(TlsVersion::Tls13).unwrap();
766        client_ctx.set_verify(SslVerifyMode::NONE);
767        client_ctx.disable_session_cache();
768
769        let client_ssl = client_ctx.new_ssl().unwrap();
770        let server_ssl = server_ctx.new_ssl().unwrap();
771
772        let (mut client, mut server) =
773            do_handshake_pair(client_ssl, server_ssl).expect("TLS 1.3 handshake failed");
774
775        // Exchange a small message.
776        let msg = b"hello TLS 1.3";
777        let n = client.write(msg).unwrap();
778        assert_eq!(n, msg.len());
779
780        let mut buf = [0u8; 64];
781        let n = server.read(&mut buf).unwrap();
782        assert_eq!(&buf[..n], msg);
783
784        // Server replies.
785        let reply = b"world";
786        server.write(reply).unwrap();
787        let n = client.read(&mut buf).unwrap();
788        assert_eq!(&buf[..n], reply);
789    }
790
791    #[test]
792    fn tls12_handshake() {
793        let (priv_key, pub_key) = make_ed25519_key();
794        let cert = make_self_signed_cert(&priv_key, &pub_key);
795
796        let server_ctx = SslCtx::new_server().unwrap();
797        server_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
798        server_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
799        server_ctx.use_certificate(&cert).unwrap();
800        server_ctx.use_private_key(&priv_key).unwrap();
801        server_ctx.check_private_key().unwrap();
802        server_ctx.disable_session_cache();
803
804        let client_ctx = SslCtx::new_client().unwrap();
805        client_ctx.set_min_proto_version(TlsVersion::Tls12).unwrap();
806        client_ctx.set_max_proto_version(TlsVersion::Tls12).unwrap();
807        client_ctx.set_verify(SslVerifyMode::NONE);
808        client_ctx.disable_session_cache();
809
810        let client_ssl = client_ctx.new_ssl().unwrap();
811        let server_ssl = server_ctx.new_ssl().unwrap();
812
813        let (mut client, mut server) =
814            do_handshake_pair(client_ssl, server_ssl).expect("TLS 1.2 handshake failed");
815
816        // Verify data exchange works after handshake.
817        client.write(b"tls12").unwrap();
818        let mut buf = [0u8; 16];
819        let n = server.read(&mut buf).unwrap();
820        assert_eq!(&buf[..n], b"tls12");
821    }
822
823    #[test]
824    fn peer_certificate_after_handshake() {
825        let (priv_key, pub_key) = make_ed25519_key();
826        let cert = make_self_signed_cert(&priv_key, &pub_key);
827
828        // Re-encode cert to DER for comparison.
829        let cert_der = cert.to_der().unwrap();
830
831        let server_ctx = SslCtx::new_server().unwrap();
832        server_ctx.use_certificate(&cert).unwrap();
833        server_ctx.use_private_key(&priv_key).unwrap();
834        server_ctx.disable_session_cache();
835
836        // Client requests peer cert verification (peer cert = server cert).
837        let client_ctx = SslCtx::new_client().unwrap();
838        client_ctx.set_verify(SslVerifyMode::NONE);
839        client_ctx.disable_session_cache();
840
841        let (client, _server) =
842            do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
843                .unwrap();
844
845        // The client should have the server's certificate.
846        let peer_cert = client.peer_certificate().expect("no peer certificate");
847        let peer_der = peer_cert.to_der().unwrap();
848        assert_eq!(peer_der, cert_der, "peer cert DER mismatch");
849    }
850
851    #[test]
852    fn shutdown_sequence() {
853        let (priv_key, pub_key) = make_ed25519_key();
854        let cert = make_self_signed_cert(&priv_key, &pub_key);
855
856        let server_ctx = SslCtx::new_server().unwrap();
857        server_ctx.use_certificate(&cert).unwrap();
858        server_ctx.use_private_key(&priv_key).unwrap();
859        server_ctx.disable_session_cache();
860
861        let client_ctx = SslCtx::new_client().unwrap();
862        client_ctx.set_verify(SslVerifyMode::NONE);
863        client_ctx.disable_session_cache();
864
865        let (mut client, mut server) =
866            do_handshake_pair(client_ctx.new_ssl().unwrap(), server_ctx.new_ssl().unwrap())
867                .unwrap();
868
869        // Client sends close_notify → Sent.
870        let r = client.shutdown().unwrap();
871        assert_eq!(r, ShutdownResult::Sent);
872
873        // Server receives close_notify; its first shutdown call returns Sent.
874        let r = server.shutdown().unwrap();
875        assert_eq!(r, ShutdownResult::Sent);
876
877        // Client receives server's close_notify → Complete.
878        let r = client.shutdown().unwrap();
879        assert_eq!(r, ShutdownResult::Complete);
880    }
881
882    #[test]
883    fn verify_mode_bits() {
884        let both = SslVerifyMode::PEER.or(SslVerifyMode::FAIL_IF_NO_PEER_CERT);
885        assert_eq!(both.0, 0x03);
886    }
887}