1use core::ffi::{c_char, c_int, c_void, CStr};
2use core::marker::PhantomData;
3use core::ptr::NonNull;
4
5use embedded_io::{Error, ErrorKind};
6
7use super::sys::*;
8use super::{
9 mbedtls_calloc, mbedtls_free, mbedtls_rng, Certificate, MBox, PrivateKey, Tls, TlsReference,
10 TlsVersion,
11};
12
13pub use asynch::*;
14
15mod asynch;
16pub mod blocking;
17
18pub(crate) struct ServerName {
23 ptr: NonNull<u8>,
24}
25
26impl ServerName {
27 fn from_cstr(name: &CStr) -> Option<Self> {
30 Self::from_bytes_with_nul(name.to_bytes_with_nul())
31 }
32
33 fn from_bytes_with_nul(bytes: &[u8]) -> Option<Self> {
34 let ptr =
38 NonNull::new(unsafe { mbedtls_calloc(bytes.len(), size_of::<u8>()) }.cast::<u8>())?;
39
40 unsafe {
43 core::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.as_ptr(), bytes.len());
44 }
45
46 Some(Self { ptr })
47 }
48
49 fn as_bytes(&self) -> &[u8] {
50 unsafe { CStr::from_ptr(self.ptr.as_ptr().cast::<c_char>()) }.to_bytes_with_nul()
53 }
54}
55
56impl Drop for ServerName {
57 fn drop(&mut self) {
58 unsafe {
61 mbedtls_free(self.ptr.as_ptr() as *mut c_void);
62 }
63 }
64}
65
66pub struct SavedSession {
74 pub(crate) mbedtls_session: MBox<mbedtls_ssl_session>,
75 pub(crate) server_name: Option<ServerName>,
78}
79
80pub(crate) fn check_saved_session_server_name(
88 saved: &Option<ServerName>,
89 current: *const c_char,
90) -> Result<(), SessionError> {
91 let current_bytes: Option<&[u8]> = if current.is_null() {
92 None
93 } else {
94 Some(unsafe { CStr::from_ptr(current) }.to_bytes_with_nul())
98 };
99 let saved_bytes = saved.as_ref().map(|n| n.as_bytes());
100
101 if current_bytes == saved_bytes {
102 Ok(())
103 } else {
104 Err(SessionError::MbedTls(MbedtlsError::new(
105 MBEDTLS_ERR_SSL_BAD_INPUT_DATA,
106 )))
107 }
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112#[cfg_attr(feature = "defmt", derive(defmt::Format))]
113pub enum AuthMode {
114 None,
116 Optional,
119 Required,
121 Unset,
123}
124
125impl AuthMode {
126 fn mbedtls_authmode(&self) -> i32 {
127 (match self {
128 AuthMode::None => MBEDTLS_SSL_VERIFY_NONE,
129 AuthMode::Optional => MBEDTLS_SSL_VERIFY_OPTIONAL,
130 AuthMode::Required => MBEDTLS_SSL_VERIFY_REQUIRED,
131 AuthMode::Unset => MBEDTLS_SSL_VERIFY_UNSET,
132 }) as i32
133 }
134}
135
136#[derive(Debug, Clone)]
139#[cfg_attr(feature = "defmt", derive(defmt::Format))]
140pub struct Credentials<'a> {
141 pub certificate: Certificate<'a>,
143 pub private_key: PrivateKey,
145}
146
147#[derive(Debug, Clone)]
149#[cfg_attr(feature = "defmt", derive(defmt::Format))]
150pub struct ClientSessionConfig<'a> {
151 pub ca_chain: Option<Certificate<'a>>,
157 pub creds: Option<Credentials<'a>>,
159 pub server_name: Option<&'a CStr>,
162 pub auth_mode: AuthMode,
165 pub min_version: TlsVersion,
167 pub alpn_protocols: Option<&'a [&'a CStr]>,
169}
170
171impl<'a> Default for ClientSessionConfig<'a> {
172 fn default() -> Self {
173 Self::new()
174 }
175}
176
177impl<'a> ClientSessionConfig<'a> {
178 pub const fn new() -> Self {
179 Self {
180 ca_chain: None,
181 creds: None,
182 server_name: None,
183 auth_mode: AuthMode::Required,
184 min_version: TlsVersion::Tls1_2,
185 alpn_protocols: None,
186 }
187 }
188}
189
190#[derive(Debug, Clone)]
191#[cfg_attr(feature = "defmt", derive(defmt::Format))]
192pub struct ServerSessionConfig<'a> {
193 pub ca_chain: Option<Certificate<'a>>,
199 pub creds: Credentials<'a>,
201 pub auth_mode: AuthMode,
204 pub min_version: TlsVersion,
206 pub alpn_protocols: Option<&'a [&'a CStr]>,
208}
209
210impl<'a> ServerSessionConfig<'a> {
211 pub const fn new(creds: Credentials<'a>) -> Self {
212 Self {
213 ca_chain: None,
214 creds,
215 auth_mode: AuthMode::None,
216 min_version: TlsVersion::Tls1_2,
217 alpn_protocols: None,
218 }
219 }
220}
221
222#[derive(Debug, Clone)]
224#[cfg_attr(feature = "defmt", derive(defmt::Format))]
225pub enum SessionConfig<'a> {
226 Client(ClientSessionConfig<'a>),
227 Server(ServerSessionConfig<'a>),
228}
229
230impl<'a> SessionConfig<'a> {
231 fn ca_chain(&self) -> Option<&Certificate<'a>> {
232 match self {
233 SessionConfig::Client(ClientSessionConfig { ca_chain, .. }) => ca_chain.as_ref(),
234 SessionConfig::Server(ServerSessionConfig { ca_chain, .. }) => ca_chain.as_ref(),
235 }
236 }
237
238 fn creds(&self) -> Option<&Credentials<'a>> {
239 match self {
240 SessionConfig::Client(ClientSessionConfig { creds, .. }) => creds.as_ref(),
241 SessionConfig::Server(ServerSessionConfig { creds, .. }) => Some(creds),
242 }
243 }
244
245 fn auth_mode(&self) -> AuthMode {
246 match self {
247 SessionConfig::Client(ClientSessionConfig { auth_mode, .. }) => *auth_mode,
248 SessionConfig::Server(ServerSessionConfig { auth_mode, .. }) => *auth_mode,
249 }
250 }
251
252 fn min_version(&self) -> TlsVersion {
253 match self {
254 SessionConfig::Client(ClientSessionConfig { min_version, .. }) => *min_version,
255 SessionConfig::Server(ServerSessionConfig { min_version, .. }) => *min_version,
256 }
257 }
258
259 fn alpn_protocols(&self) -> Option<&'a [&'a CStr]> {
260 match self {
261 SessionConfig::Client(ClientSessionConfig { alpn_protocols, .. }) => *alpn_protocols,
262 SessionConfig::Server(ServerSessionConfig { alpn_protocols, .. }) => *alpn_protocols,
263 }
264 }
265
266 fn raw_mode(&self) -> c_int {
267 match self {
268 Self::Client { .. } => MBEDTLS_SSL_IS_CLIENT as c_int,
269 Self::Server { .. } => MBEDTLS_SSL_IS_SERVER as c_int,
270 }
271 }
272}
273
274#[derive(Debug)]
279#[cfg_attr(feature = "defmt", derive(defmt::Format))]
280struct ALPNArray<'a>(NonNull<*const c_char>, PhantomData<&'a CStr>);
281
282impl<'a> ALPNArray<'a> {
283 pub fn from_slice(slice: &'a [&'a CStr]) -> Option<Self> {
284 NonNull::new(
285 unsafe { mbedtls_calloc(slice.len() + 1, size_of::<*const c_char>()) }
286 .cast::<*const c_char>(),
287 )
288 .map(|ptr| {
289 let output = unsafe { core::slice::from_raw_parts_mut(ptr.as_ptr(), slice.len()) };
292 for (index, element) in slice.iter().enumerate() {
293 output[index] = element.as_ptr()
294 }
295 Self(ptr, PhantomData)
296 })
297 }
298
299 pub fn as_ptr(&self) -> *mut *const c_char {
300 self.0.as_ptr()
301 }
302}
303
304impl<'a> Drop for ALPNArray<'a> {
305 fn drop(&mut self) {
306 unsafe {
307 mbedtls_free(self.0.as_ptr() as *mut c_void);
308 }
309 }
310}
311
312struct SessionState<'a> {
314 ssl_context: MBox<mbedtls_ssl_context>,
316 _drbg: MBox<mbedtls_ctr_drbg_context>,
321 _ssl_config: MBox<mbedtls_ssl_config>,
326 _ca_chain: Option<Certificate<'a>>,
331 _creds: Option<Credentials<'a>>,
336 _alpn_ptrs: Option<ALPNArray<'a>>,
341}
342
343impl<'a> SessionState<'a> {
344 fn new(conf: &SessionConfig<'a>) -> Result<Self, MbedtlsError> {
346 merr!(unsafe { psa_crypto_init() })?;
347
348 let mut ssl_config = MBox::new().ok_or(MbedtlsError::new(MBEDTLS_ERR_SSL_ALLOC_FAILED))?;
349
350 merr!(unsafe {
351 mbedtls_ssl_config_defaults(
352 &mut *ssl_config,
353 conf.raw_mode(),
354 MBEDTLS_SSL_TRANSPORT_STREAM as i32,
355 MBEDTLS_SSL_PRESET_DEFAULT as i32,
356 )
357 })?;
358
359 ssl_config.private_min_tls_version = conf.min_version().mbed_tls_version();
362
363 Tls::hook_debug_logs(&mut ssl_config);
364
365 unsafe {
366 mbedtls_ssl_conf_authmode(&mut *ssl_config, conf.auth_mode().mbedtls_authmode());
367 }
368
369 if let Some(creds) = conf.creds() {
370 merr!(unsafe {
371 mbedtls_ssl_conf_own_cert(
372 &mut *ssl_config,
373 &*creds.certificate.crt as *const _ as *mut _,
374 &*creds.private_key.0 as *const _ as *mut _,
375 )
376 })?;
377 }
378
379 if let Some(ca_chain) = conf.ca_chain() {
380 unsafe {
381 mbedtls_ssl_conf_ca_chain(
382 &mut *ssl_config,
383 &*ca_chain.crt as *const _ as *mut _,
384 core::ptr::null_mut(),
385 );
386 }
387 }
388
389 let alpn = if let Some(alpn_protocols) = conf.alpn_protocols() {
390 let alpn = ALPNArray::from_slice(alpn_protocols)
391 .ok_or(MbedtlsError::new(MBEDTLS_ERR_SSL_ALLOC_FAILED))?;
392 merr!(unsafe { mbedtls_ssl_conf_alpn_protocols(&mut *ssl_config, alpn.as_ptr()) })?;
393 Some(alpn)
394 } else {
395 None
396 };
397
398 let mut drbg_context =
399 MBox::new().ok_or(MbedtlsError::new(MBEDTLS_ERR_SSL_ALLOC_FAILED))?;
400
401 unsafe {
403 mbedtls_ssl_conf_rng(
404 &mut *ssl_config,
405 Some(mbedtls_rng),
406 &mut *drbg_context as *mut _ as *mut c_void,
407 );
408 }
409
410 let mut ssl_context = MBox::new().ok_or(MbedtlsError::new(MBEDTLS_ERR_SSL_ALLOC_FAILED))?;
411
412 merr!(unsafe { mbedtls_ssl_setup(&mut *ssl_context, &*ssl_config) })?;
413
414 if let SessionConfig::Client(conf) = conf {
415 if let Some(name) = conf.server_name {
416 merr!(unsafe { mbedtls_ssl_set_hostname(&mut *ssl_context, name.as_ptr()) })?;
417 }
418 }
419
420 Ok(Self {
421 ssl_context,
422 _drbg: drbg_context,
423 _ssl_config: ssl_config,
424 _ca_chain: conf.ca_chain().cloned(),
425 _creds: conf.creds().cloned(),
426 _alpn_ptrs: alpn,
427 })
428 }
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433pub enum SessionError {
434 MbedTls(MbedtlsError),
436 Io(ErrorKind),
438}
439
440impl SessionError {
441 pub fn from_io<E: Error>(err: E) -> Self {
443 Self::Io(err.kind())
444 }
445}
446
447impl From<MbedtlsError> for SessionError {
448 fn from(e: MbedtlsError) -> Self {
449 Self::MbedTls(e)
450 }
451}
452
453impl From<ErrorKind> for SessionError {
454 fn from(e: ErrorKind) -> Self {
455 Self::Io(e)
456 }
457}
458
459impl core::fmt::Display for SessionError {
460 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
461 match self {
462 Self::MbedTls(e) => write!(f, "{}", e),
463 Self::Io(e) => write!(f, "IO({:?})", e),
464 }
465 }
466}
467
468#[cfg(feature = "defmt")]
469impl defmt::Format for SessionError {
470 fn format(&self, f: defmt::Formatter<'_>) {
471 match self {
472 Self::MbedTls(e) => defmt::write!(f, "{}", e),
473 Self::Io(e) => defmt::write!(f, "IO({:?})", debug2format!(e)),
474 }
475 }
476}
477
478impl core::error::Error for SessionError {}
479
480impl embedded_io::Error for SessionError {
481 fn kind(&self) -> embedded_io::ErrorKind {
482 match self {
483 Self::Io(e) => *e,
484 _ => embedded_io::ErrorKind::Other,
485 }
486 }
487}