Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS & DTLS fixes #445

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions micro-rdk/src/esp32/certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ use esp_idf_svc::sys::{
mbedtls_pk_type_t_MBEDTLS_PK_ECKEY, mbedtls_pk_write_key_der, mbedtls_x509write_cert,
mbedtls_x509write_crt_der, mbedtls_x509write_crt_free, mbedtls_x509write_crt_init,
mbedtls_x509write_crt_set_issuer_key, mbedtls_x509write_crt_set_issuer_name,
mbedtls_x509write_crt_set_md_alg, mbedtls_x509write_crt_set_subject_key,
mbedtls_x509write_crt_set_subject_name, mbedtls_x509write_crt_set_validity,
mbedtls_x509write_crt_set_version, MBEDTLS_ERR_PK_BAD_INPUT_DATA, MBEDTLS_X509_CRT_VERSION_3,
SHA_TYPE_SHA2_256,
mbedtls_x509write_crt_set_md_alg, mbedtls_x509write_crt_set_serial_raw,
mbedtls_x509write_crt_set_subject_key, mbedtls_x509write_crt_set_subject_name,
mbedtls_x509write_crt_set_validity, mbedtls_x509write_crt_set_version,
MBEDTLS_ERR_PK_BAD_INPUT_DATA, MBEDTLS_X509_CRT_VERSION_3, SHA_TYPE_SHA2_256,
};

use crate::common::webrtc::certificate::{Certificate, Fingerprint};
Expand Down Expand Up @@ -173,7 +173,9 @@ impl GeneratedWebRtcCertificateBuilder {
}?;

// TODO(RSDK-10196): The `pk_ctx` field doesn't exist in ESP-IDF 5. Maybe it should be `private_kp_ctx`?
let ecp_keypair = self.kp_context.pk_ctx;
// we should use the mbedtls_ecp_keypair *mbedtls_pk_ec(const mbedtls_pk_context pk) but it's defined as static inline
// to access it we would need to change bindgen invocation to export it
let ecp_keypair = self.kp_context.private_pk_ctx;

unsafe {
MbedTLSError::to_unit_result(mbedtls_ecp_gen_key(
Expand Down Expand Up @@ -235,6 +237,15 @@ impl GeneratedWebRtcCertificateBuilder {
))
}?;

let mut serial_number = [0x0_u8; 1];
unsafe {
MbedTLSError::to_unit_result(mbedtls_x509write_crt_set_serial_raw(
&mut self.crt_context as *mut mbedtls_x509write_cert,
serial_number.as_mut_ptr(),
serial_number.len(),
))
}?;

let mut work_buffer = vec![0_u8; 2048];

let ret = unsafe {
Expand Down
30 changes: 20 additions & 10 deletions micro-rdk/src/esp32/dtls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::{
io::{self, Read, Write},
marker::PhantomData,
pin::Pin,
ptr::NonNull,
rc::Rc,
task::{Context, Poll},
time::{Duration, Instant},
Expand All @@ -29,19 +30,22 @@ use crate::esp32::esp_idf_svc::sys::{
mbedtls_ssl_config_init, mbedtls_ssl_context, mbedtls_ssl_free, mbedtls_ssl_handshake,
mbedtls_ssl_init, mbedtls_ssl_read, mbedtls_ssl_set_bio, mbedtls_ssl_set_timer_cb,
mbedtls_ssl_setup, mbedtls_ssl_write, mbedtls_x509_crt, mbedtls_x509_crt_free,
mbedtls_x509_crt_init, mbedtls_x509_crt_parse_der, MBEDTLS_ERR_NET_RECV_FAILED,
MBEDTLS_ERR_NET_SEND_FAILED, MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY, MBEDTLS_ERR_SSL_TIMEOUT,
MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_ERR_SSL_WANT_WRITE, MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_PRESET_DEFAULT, MBEDTLS_SSL_TRANSPORT_DATAGRAM,
mbedtls_x509_crt_init, mbedtls_x509_crt_parse_der, MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY,
MBEDTLS_ERR_SSL_TIMEOUT, MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_ERR_SSL_WANT_WRITE,
MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_PRESET_DEFAULT, MBEDTLS_SSL_TRANSPORT_DATAGRAM,
};
use async_io::Timer;
use core::ffi::CStr;
use esp_idf_svc::sys::esp_tls_get_ssl_context;
use futures_lite::{AsyncRead, AsyncWrite, Future};
use log::{log, Level};
use thiserror::Error;

use super::tcp::Esp32TLSContext;

const MBEDTLS_ERR_NET_SEND_FAILED: i32 = -0x4E;
const MBEDTLS_ERR_NET_RECV_FAILED: i32 = -0x4C;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had to manually add them, not sure if esp-rs will update their code to include these symbols

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did file an issue upstream with them: https://github.com/viamrobotics/micro-rdk/pull/445/files, but at the time we weren't prioritizing ESP-IDF 5 and following up fell off my todo list. Do you want me to re-engage with them and see if they are willing to make a fix?

Also, do you have any thoughts on the package they indicated: https://github.com/esp-rs/esp-mbedtls?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either way, can we add a comment here explaining why we are doing this, and maybe referencing the upstream issue?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you link the issue URL?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esp-mbedtls doesn't look mature but if someone wants to try happy to let them

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry, somehow I posted the link to this review. I meant to post a link to esp-rs/esp-idf-sys#361.


extern "C" {
fn mbedtls_debug_set_threshold(level: c_int);
}
Expand All @@ -62,7 +66,6 @@ unsafe extern "C" fn mbedtls_net_write<S: AsyncWrite>(
if state.error.as_ref().unwrap().kind() == std::io::ErrorKind::WouldBlock {
return MBEDTLS_ERR_SSL_WANT_WRITE;
}
// TODO(RSDK-10189): This constant is not exported in ESP-IDF 5
MBEDTLS_ERR_NET_SEND_FAILED
}
}
Expand All @@ -87,7 +90,7 @@ unsafe extern "C" fn mbedtls_net_read<S: AsyncRead>(
if state.error.as_ref().unwrap().kind() == std::io::ErrorKind::WouldBlock {
return MBEDTLS_ERR_SSL_WANT_READ;
}
// TODO(RSDK-10189): This constant is not exported in ESP-IDF 5

MBEDTLS_ERR_NET_RECV_FAILED
}
}
Expand Down Expand Up @@ -228,7 +231,7 @@ extern "C" fn mbedtls_timing_dtls_set_delay<S>(
} else if let Some(ssl_ctx) = ctx.ssl_ctx {
let (_, cx) = unsafe {
// TODO(RSDK-10196): The `p_bio` field doesn't exist in ESP-IDF 5. Maybe it should be `private_p_bio`?
let state = SSLStreamInner::<S>::from_raw((*ssl_ctx).p_bio);
let state = SSLStreamInner::<S>::from_raw((*ssl_ctx).private_p_bio);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouldn't do that. We could use a user_data field in the ssl context struct alternatively (it is private as well) however it's accessor functions are inline and therefore not exported by bindgen

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this hinting that we are somehow misusing mbedtls, that we suddenly need access to "private" state? Or do you think this is more a flaw in the rust bindings, in that we can't make use of the user_data field?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bindgen doesn't export inlined functions. MbedTLS discourage accessing private field directly I think they just want some stable API

state.as_parts()
};

Expand All @@ -251,7 +254,7 @@ extern "C" fn mbedtls_timing_get_delay<S>(data: *mut c_void) -> c_int {
if let Some(ssl_ctx) = ctx.ssl_ctx {
let (_, cx) = unsafe {
// TODO(RSDK-10196): The `p_bio` field doesn't exist in ESP-IDF 5. Maybe it should be `private_p_bio`?
let state = SSLStreamInner::<S>::from_raw((*ssl_ctx).p_bio);
let state = SSLStreamInner::<S>::from_raw((*ssl_ctx).private_p_bio);
state.as_parts()
};
ctx.poll_delay(cx);
Expand Down Expand Up @@ -332,6 +335,8 @@ impl DtlsSSLContext {
certificate.get_der_keypair().len(),
std::ptr::null(),
0,
Some(mbedtls_entropy_func),
self.dtls_entropy.as_mut() as *mut mbedtls_entropy_context as *mut _,
)
};
if ret != 0 {
Expand Down Expand Up @@ -420,7 +425,6 @@ impl DtlsSSLContext {
if ret != 0 {
return Err(SSLError::SSLConfigFailure(ret));
}

unsafe {
self.timer_ctx.ssl_ctx = Some(self.ssl_ctx.as_mut());
mbedtls_ssl_set_timer_cb(
Expand Down Expand Up @@ -511,7 +515,13 @@ impl SSLContext {
match self {
SSLContext::DtlsSSLContext(context) => context.ssl_ctx.as_mut(),
// TODO(RSDK-10198): The `ssl` field here no longer works in ESP-IDF 5
SSLContext::Esp32TLSContext(context) => unsafe { &mut (*(**context)).ssl }, // potentially needs read_unaligned
SSLContext::Esp32TLSContext(context) => unsafe {
NonNull::<mbedtls_ssl_context>::new(
esp_tls_get_ssl_context(**context) as *mut mbedtls_ssl_context
)
.expect("ssl context pointer is null")
.as_ptr()
},
}
}
}
Expand Down
47 changes: 15 additions & 32 deletions micro-rdk/src/esp32/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use async_io::Async;

use esp_idf_svc::sys::{
esp, esp_crt_bundle_attach, esp_tls_cfg, esp_tls_cfg_server, esp_tls_conn_destroy,
esp_tls_init, esp_tls_role_ESP_TLS_CLIENT, esp_tls_role_ESP_TLS_SERVER, esp_tls_t,
mbedtls_ssl_conf_read_timeout, EspError,
esp_tls_get_ssl_context, esp_tls_init, esp_tls_server_session_create, esp_tls_t,
mbedtls_ssl_conf_read_timeout, mbedtls_ssl_config, mbedtls_ssl_context, EspError,
};
use futures_lite::FutureExt;
use futures_lite::{ready, AsyncRead, AsyncWrite, Future};
Expand Down Expand Up @@ -98,11 +98,12 @@ impl Esp32ServerConfig {
},
serverkey_password: std::ptr::null(),
serverkey_password_len: 0_u32,
..Default::default()
});
Self { cfg, alpn_proto }
}
fn get_cfg_ptr(&self) -> *const esp_tls_cfg_server {
&*self.cfg as *const _
fn get_cfg_ptr_mut(&mut self) -> *mut esp_tls_cfg_server {
&mut *self.cfg as *mut _
}
}

Expand Down Expand Up @@ -143,13 +144,13 @@ impl Esp32ClientConfig {
use_global_ca_store: false,
skip_common_name: false,
keep_alive_cfg: std::ptr::null_mut(),
psk_hint_key: std::ptr::null(),
crt_bundle_attach: Some(esp_crt_bundle_attach),
ds_data: std::ptr::null_mut(),
if_name: std::ptr::null_mut(),
is_plain_tcp: false,
timeout_ms: 50000,
common_name: std::ptr::null(),
..Default::default()
});
Self { cfg, alpn_proto }
}
Expand Down Expand Up @@ -204,26 +205,17 @@ impl<IO> Esp32Accept<IO>
where
IO: AsyncRead + AsyncWrite + Unpin + AsRawFd,
{
fn new(stream: IO, cfg: Esp32ServerConfig) -> Result<Self, std::io::Error> {
fn new(stream: IO, mut cfg: Esp32ServerConfig) -> Result<Self, std::io::Error> {
let tls_context = Esp32TLSContext::new()?;
unsafe {
std::ptr::write_unaligned(
// TODO(RSDK-10201): The field `role` is not found when using ESP-IDF 5
std::ptr::addr_of_mut!((*(*tls_context)).role),
esp_tls_role_ESP_TLS_SERVER,
)
};
// TODO(RSDK-10201): The field `sockfd` is not found when using ESP-IDF 5
unsafe { std::ptr::write_unaligned(std::ptr::addr_of_mut!((*(*tls_context)).sockfd), -1) };
unsafe {
esp!(esp_create_mbedtls_handle(
std::ptr::null_mut(),
0,
cfg.get_cfg_ptr() as *const c_void,
esp!(esp_tls_server_session_create(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to see the write_unaligned's are no longer needed.

cfg.get_cfg_ptr_mut(),
stream.as_raw_fd(),
*tls_context
))
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?
};
}
.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;

let io = AsyncSSLStream::new(SSLContext::Esp32TLSContext(tls_context), stream).unwrap();
Ok(Self {
cfg: Arc::new(cfg),
Expand Down Expand Up @@ -330,15 +322,6 @@ where
fn new(stream: IO, cfg: Esp32ClientConfig, addr: &Uri) -> Result<Self, std::io::Error> {
let tls_context = Esp32TLSContext::new()?;

unsafe {
std::ptr::write_unaligned(
// TODO(RSDK-10201): The field `role` is not found when using ESP-IDF 5
std::ptr::addr_of_mut!((*(*tls_context)).role),
esp_tls_role_ESP_TLS_CLIENT,
)
};
// TODO(RSDK-10201): The field `sockfd` is not found when using ESP-IDF 5
unsafe { std::ptr::write_unaligned(std::ptr::addr_of_mut!((*(*tls_context)).sockfd), -1) };
let host = CString::new(addr.host().unwrap()).unwrap();
unsafe {
esp!(esp_create_mbedtls_handle(
Expand All @@ -352,8 +335,8 @@ where

unsafe {
mbedtls_ssl_conf_read_timeout(
Copy link
Member Author

@npmenard npmenard Mar 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edit : it's doable but I hate it. I want to remove it unless there is a strong opinion against

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you like it? what's the implication of us not having it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like answers to Matt's questions too. Are you proposing removing setting the SSL read timeout entirely, rather than accessing private_conf (about which I share your distaste)? Are we sure there isn't some other mechanism to do this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the ssl read timeout was useful to detect states where a connection was dropped but for some reason our stack assumed it was still alive. if this particular bug resurface then we should re introduce it

// TODO(RSDK-10201): The field `conf` is not found when using ESP-IDF 5
std::ptr::addr_of_mut!((*(*tls_context)).conf),
(*(esp_tls_get_ssl_context(*tls_context) as *mut mbedtls_ssl_context)).private_conf
as *mut mbedtls_ssl_config,
30 * 1000,
);
}
Expand Down