ove/
net_tls.rs

1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! TLS/SSL session wrapper (mbedTLS).
8//!
9//! [`Session`] wraps the oveRTOS TLS handle with automatic cleanup.
10//! After creating a session and completing the handshake over an existing
11//! [`crate::net::TcpStream`], all I/O goes through [`Session::send`] and
12//! [`Session::recv`].
13//!
14//! Works in both heap and zero-heap modes.
15
16use core::fmt;
17
18use crate::bindings;
19use crate::error::{Error, Result};
20use crate::net::TcpStream;
21
22// ---------------------------------------------------------------------------
23// TlsConfig
24// ---------------------------------------------------------------------------
25
26/// TLS session configuration.
27///
28/// `hostname` must be a null-terminated byte slice when provided (used for
29/// SNI and certificate verification).
30pub struct TlsConfig<'a> {
31    /// PEM or DER CA certificate for server verification (`None` to skip).
32    pub ca_cert: Option<&'a [u8]>,
33    /// Expected server hostname for SNI/verify (null-terminated, or `None`).
34    pub hostname: Option<&'a [u8]>,
35}
36
37// ---------------------------------------------------------------------------
38// Session
39// ---------------------------------------------------------------------------
40
41/// TLS session with RAII cleanup.
42///
43/// Wraps `ove_tls_t` and frees resources on drop.
44pub struct Session {
45    handle: bindings::ove_tls_t,
46}
47
48impl Session {
49    /// Create a new TLS session via heap allocation (only in heap mode).
50    #[cfg(not(zero_heap))]
51    pub fn new() -> Result<Self> {
52        let mut handle: bindings::ove_tls_t = core::ptr::null_mut();
53        let rc = unsafe { bindings::ove_tls_create(&mut handle) };
54        Error::from_code(rc)?;
55        Ok(Self { handle })
56    }
57
58    /// Create from caller-provided static storage.
59    ///
60    /// # Safety
61    /// Caller must ensure `storage` outlives the `Session` and is not
62    /// shared with another primitive.
63    #[cfg(zero_heap)]
64    pub unsafe fn from_static(
65        storage: *mut bindings::ove_tls_storage_t,
66    ) -> Result<Self> {
67        let mut handle: bindings::ove_tls_t = core::ptr::null_mut();
68        let rc = unsafe { bindings::ove_tls_init(&mut handle, storage) };
69        Error::from_code(rc)?;
70        Ok(Self { handle })
71    }
72
73    /// Perform the TLS handshake over an established TCP connection.
74    ///
75    /// After a successful handshake, use [`send`](Session::send) and
76    /// [`recv`](Session::recv) for encrypted I/O.
77    ///
78    /// # Errors
79    /// Returns an error if the handshake fails (certificate mismatch,
80    /// protocol error, etc.).
81    pub fn handshake(&self, sock: &TcpStream, cfg: &TlsConfig) -> Result<()> {
82        let mut c: bindings::ove_tls_config_t = unsafe { core::mem::zeroed() };
83
84        if let Some(cert) = cfg.ca_cert {
85            c.ca_cert = cert.as_ptr();
86            c.ca_cert_len = cert.len();
87        }
88        if let Some(host) = cfg.hostname {
89            c.hostname = host.as_ptr() as *const _;
90        }
91
92        let rc = unsafe {
93            bindings::ove_tls_handshake(self.handle, sock.handle(), &c)
94        };
95        Error::from_code(rc)
96    }
97
98    /// Send data over the encrypted session.
99    ///
100    /// Returns the number of bytes actually sent.
101    ///
102    /// # Errors
103    /// Returns an error if the send fails.
104    pub fn send(&self, data: &[u8]) -> Result<usize> {
105        let mut sent: usize = 0;
106        let rc = unsafe {
107            bindings::ove_tls_send(
108                self.handle,
109                data.as_ptr() as *const _,
110                data.len(),
111                &mut sent,
112            )
113        };
114        Error::from_code(rc)?;
115        Ok(sent)
116    }
117
118    /// Receive data from the encrypted session.
119    ///
120    /// Returns the number of bytes received into `buf`.
121    ///
122    /// # Errors
123    /// Returns an error if the receive fails or the peer closed the
124    /// connection.
125    pub fn recv(&self, buf: &mut [u8]) -> Result<usize> {
126        let mut received: usize = 0;
127        let rc = unsafe {
128            bindings::ove_tls_recv(
129                self.handle,
130                buf.as_mut_ptr() as *mut _,
131                buf.len(),
132                &mut received,
133            )
134        };
135        Error::from_code(rc)?;
136        Ok(received)
137    }
138
139    /// Shut down the TLS session (sends `close_notify`).
140    ///
141    /// The underlying socket is NOT closed -- the caller must close it
142    /// separately.
143    pub fn close(&self) {
144        unsafe { bindings::ove_tls_close(self.handle) }
145    }
146}
147
148impl fmt::Debug for Session {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        f.debug_struct("Session")
151            .field("handle", &format_args!("{:p}", self.handle))
152            .finish()
153    }
154}
155
156impl Drop for Session {
157    fn drop(&mut self) {
158        if self.handle.is_null() { return; }
159        #[cfg(not(zero_heap))]
160        unsafe { bindings::ove_tls_destroy(self.handle) }
161        #[cfg(zero_heap)]
162        unsafe { bindings::ove_tls_deinit(self.handle) }
163    }
164}
165
166// SAFETY: Wraps a ove handle. Send/recv are thread-safe RTOS calls.
167// Create/destroy are single-threaded (lifecycle guarantee).
168unsafe impl Send for Session {}