diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9679066..b63c0bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: - uses: dtolnay/rust-toolchain@v1 with: target: riscv32imc-unknown-none-elf - toolchain: nightly-2024-12-01 + toolchain: nightly components: rust-src,rustfmt - uses: esp-rs/xtensa-toolchain@v1.5 with: diff --git a/Cargo.toml b/Cargo.toml index 2769707..602d6bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["bjoernQ "] edition = "2021" license = "MIT OR Apache-2.0" -rust-version = "1.82" +rust-version = "1.84" [profile.release] debug = true @@ -24,39 +24,32 @@ opt-level = "z" opt-level = 3 [dependencies] -esp-hal = { version = "0.22.0", optional = true } -esp-backtrace = { version = "0.14.0", optional = true, features = [ +esp-hal = { version = "0.23.0", optional = true } +esp-backtrace = { version = "0.15.0", optional = true, features = [ "panic-handler", "println", "exception-handler", ] } -esp-println = { version = "0.12.0", optional = true, features = ["log"] } -esp-hal-embassy = { version = "0.5.0", optional = true } +esp-println = { version = "0.13.0", optional = true, features = ["log"] } +esp-hal-embassy = { version = "0.6.0", optional = true } -embassy-time = { version = "0.3.0", optional = true } -embassy-executor = { version = "0.6", package = "embassy-executor", features = [ +embassy-time = { version = "0.4.0", optional = true } +embassy-executor = { version = "0.7", package = "embassy-executor", features = [ "nightly", - "integrated-timers", ], optional = true } -embassy-net = { version = "0.5.0", features = [ +embassy-net = { version = "0.6.0", features = [ "tcp", "udp", "dhcpv4", "medium-ethernet", ], optional = true } -esp-wifi = { version = "0.11.0", optional = true, features = ["sys-logs", "utils", "wifi"] } -blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack", rev = "1c581661d78e0cf0f17b936297179b993fb149d7" } -smoltcp11 = { package = "smoltcp", version = "0.11.0", optional = true, default-features = false, features = [ - "proto-ipv4", - "socket-tcp", - "socket-icmp", - "socket-udp", - "medium-ethernet", - "proto-dhcpv4", - "socket-raw", - "socket-dhcpv4", +esp-wifi = { version = "0.12.0", optional = true, features = [ + "sys-logs", + "utils", + "wifi", ] } +blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack", rev = "b3ecefc222d8806edd221f266999ca339c52d34e" } smoltcp = { version = "0.12.0", optional = true, default-features = false, features = [ "proto-ipv4", "socket-tcp", @@ -76,17 +69,15 @@ static_cell = { version = "2.1", features = ["nightly"] } esp-mbedtls = { path = "./esp-mbedtls" } -edge-http = { version = "0.4.0", optional = true } -edge-nal = { version = "0.4.0", optional = true } -edge-nal-embassy = { version = "0.4.0", optional = true } +edge-http = { version = "0.5.0", optional = true } +edge-nal = { version = "0.5.0", optional = true } +edge-nal-embassy = { version = "0.5.0", optional = true } cfg-if = "1.0.0" -esp-alloc = { version = "0.5.0", optional = true} +esp-alloc = { version = "0.6.0", optional = true } enumset = { version = "1", default-features = false } -tinyrlibc = { version = "0.5", optional = true, default-features = false } - [target.'cfg(target_os = "espidf")'.dependencies] -esp-idf-svc = { version = "0.50", features = ["binstart"] } +esp-idf-svc = { version = "0.51", features = ["binstart"] } [[example]] name = "crypto_self_test" @@ -99,25 +90,35 @@ name = "crypto_self_test_std" name = "async_client" required-features = ["examples-async"] -[[example]] -name = "async_client_mTLS" -required-features = ["examples-async"] - [[example]] name = "async_server" required-features = ["examples-async"] -[[example]] -name = "async_server_mTLS" -required-features = ["examples-async"] - [[example]] name = "edge_server" required-features = ["examples-async", "edge-http"] [features] -examples = ["esp-hal", "esp-backtrace", "esp-println", "esp-wifi", "smoltcp", "smoltcp11", "esp-alloc"] -examples-async = ["examples", "esp-hal-embassy", "embassy-time", "embassy-executor", "embassy-net", "edge-http", "edge-nal", "edge-nal-embassy", "esp-mbedtls/async", "esp-mbedtls/edge-nal"] +examples = [ + "esp-hal", + "esp-backtrace", + "esp-println", + "esp-wifi", + "smoltcp", + "esp-alloc", +] +examples-async = [ + "examples", + "esp-hal-embassy", + "embassy-time", + "embassy-executor", + "embassy-net", + "edge-http", + "edge-nal", + "edge-nal-embassy", + "esp-mbedtls/async", + "esp-mbedtls/edge-nal", +] examples-std = ["critical-section/std"] esp32 = [ @@ -135,7 +136,6 @@ esp32c3 = [ "esp-println?/esp32c3", "esp-wifi?/esp32c3", "esp-mbedtls/esp32c3", - "tinyrlibc/memchr", ] esp32s2 = [ "esp-hal?/esp32s2", @@ -154,5 +154,13 @@ esp32s3 = [ "esp-mbedtls/esp32s3", ] +# Enable mTLS for the running example. See example documentation for further details. +# Applies to: +# - async_client +# - async_server +# - sync_client +# - sync_server +mtls = [] + [build-dependencies] embuild = { version = "0.33", features = ["espidf"] } diff --git a/README.md b/README.md index 49fbd72..7180db9 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,10 @@ It comes with mbedtls precompiled to avoid the need for a complete C toolchain. This should work together with `esp-wifi`. It currently won't work without. However it's not well tested yet besides the included examples. -See the examples for how to use it. A key thing is to [set a bigger heap size](https://github.com/esp-rs/esp-wifi/blob/main/esp-wifi/docs/tuning.md) for esp-wifi since more heap memory is needed to get this working. - In general this is heavy in terms of heap memory used and code size. If you can, you should prefer using something like `embedded-tls`. For now it's missing advanced configuration options which will be added step-by-step. -Currently this won't work on ESP32-S2 - getting it to work will require tweaking the memory usage a lot! - The examples use one hard-coded address of `www.google.com` which might not always work. ### Certificates @@ -30,10 +26,9 @@ Examples are available for: - esp32 - esp32c3 +- esp32s2 - esp32s3 -Limited support is also available for `esp32s2` but it won't compile for async. - To run examples, you need to specify the architecture as a feature, the example name, the target and the toolchain. You also need to set `SSID` and `PASSWORD` as your environment variables @@ -58,20 +53,21 @@ Here's a table of the architectures with their corresponding target for quick re | ------------ | --------------------------- | ------------------ | | esp32 | xtensa-esp32-none-elf | esp | | esp32c3 | riscv32imc-unknown-none-elf | nightly | +| esp32s2 | xtensa-esp32s2-none-elf | esp | | esp32s3 | xtensa-esp32s3-none-elf | esp | -Heres's a list of all the examples with their description: - -| Example | Description | -| :---------------- | ------------------------------------------------------------ | -| async_client | Example of a HTTPS connection using the async client. | -| async_client_mTLS | Example of a HTTPS connection using the async client, with certificate authentication. This sends client certificates to a server, and the response indicates informations about the certificates. | -| sync_client | Example of a HTTPS connection using the sync client. | -| sync_client_mTLS | Example of a HTTPS connection using the sync client, with certificate authentication. This sends client certificates to a server, and the response indicates informations about the certificates. | -| async_server | Example of a simple async server with HTTPS support. This uses self-signed certificates, so you will need to enable an exception in your browser. | -| async_server_mTLS | Example of a simple async server with HTTPS support, with client authentication. You will need to pass client certificates in your request in order to have a successful connection. Refer to the documentation inside the example. | -| sync_server | Example of a simple sync server with HTTPS support. This uses self-signed certificates, so you will need to enable an exception in your browser. | -| sync_server_mTLS | Example of a simple sync server with HTTPS support, with client authentication. You will need to pass client certificates in your request in order to have a successful connection. Refer to the documentation inside the example. | +Heres's a list of all the examples with their description, and the required features to enable them: + +| Example | Features | Description | +| :----------------------- | -------- | ------------------------------------------------------------ | +| async_client | - | Example of a HTTPS connection using the async client. | +| async_client (with mTLS) | mtls | Example of a HTTPS connection using the async client, with certificate authentication. This sends client certificates to a server, and the response indicates informations about the certificates. | +| sync_client | - | Example of a HTTPS connection using the sync client. | +| sync_client (with mTLS) | mtls | Example of a HTTPS connection using the sync client, with certificate authentication. This sends client certificates to a server, and the response indicates informations about the certificates. | +| async_server | - | Example of a simple async server with HTTPS support. This uses self-signed certificates, so you will need to enable an exception in your browser. | +| async_server (with mTLS) | mtls | Example of a simple async server with HTTPS support, with client authentication. You will need to pass client certificates in your request in order to have a successful connection. Refer to the documentation inside the example. | +| sync_server | - | Example of a simple sync server with HTTPS support. This uses self-signed certificates, so you will need to enable an exception in your browser. | +| sync_server (with mTLS) | mtls | Example of a simple sync server with HTTPS support, with client authentication. You will need to pass client certificates in your request in order to have a successful connection. Refer to the documentation inside the example. | This needs `espflash` version 2.x. If you are using version 1.x you need to remove the `flash` command from the runner in `.cargo/config.toml` diff --git a/esp-mbedtls-sys/Cargo.toml b/esp-mbedtls-sys/Cargo.toml index 0c4a89c..2ae6c1e 100644 --- a/esp-mbedtls-sys/Cargo.toml +++ b/esp-mbedtls-sys/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" links = "mbedtls" license = "MIT OR Apache-2.0" -rust-version = "1.82" +rust-version = "1.84" [build-dependencies] anyhow = "1.0.68" @@ -19,7 +19,7 @@ embuild = "0.33" # For malloc/free # TODO: Replace with `esp-alloc` once `esp-alloc` starts to provide `malloc` and `free` in future # ... or switch to our own `mbedtls_malloc/free` -esp-wifi = { version = "0.11.0", default-features = false, optional = true } +esp-wifi = { version = "0.12.0", default-features = false, optional = true } # ESP-IDF: The mbedtls lib distributed with ESP-IDF is used [target.'cfg(target_os = "espidf")'.dependencies] diff --git a/esp-mbedtls/Cargo.toml b/esp-mbedtls/Cargo.toml index 2e52d47..0dcc3fb 100644 --- a/esp-mbedtls/Cargo.toml +++ b/esp-mbedtls/Cargo.toml @@ -3,7 +3,7 @@ name = "esp-mbedtls" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" -rust-version = "1.82" +rust-version = "1.84" [lib] harness = false @@ -14,23 +14,27 @@ log = { version = "0.4.17", default-features = false } enumset = { version = "1", default-features = false } embedded-io = { version = "0.6.1" } embedded-io-async = { version = "0.6.0", optional = true } -esp-hal = { version = "0.22.0", optional = true } +esp-hal = { version = "0.23.0", optional = true } # For malloc/free # TODO: Replace with `esp-alloc` once `esp-alloc` starts to provide `malloc` and `free` in future # ... or switch to our own `mbedtls_malloc/free` -esp-wifi = { version = "0.11.0", default-features = false, optional = true } +esp-wifi = { version = "0.12.0", default-features = false, optional = true } cfg-if = "1.0.0" -edge-nal = { version = "0.4.0", optional = true } +edge-nal = { version = "0.5.0", optional = true } critical-section = "1.1.3" crypto-bigint = { version = "0.5.3", optional = true, default-features = false, features = ["extra-sizes"] } +nb = { version = "1.1.0", optional = true } [features] default = ["edge-nal"] async = ["dep:embedded-io-async"] -esp32 = ["esp-hal/esp32", "esp-wifi/esp32", "esp-mbedtls-sys/esp32", "crypto-bigint"] -esp32c3 = ["esp-hal/esp32c3", "esp-wifi/esp32c3", "esp-mbedtls-sys/esp32c3", "crypto-bigint"] -esp32s2 = ["esp-hal/esp32s2", "esp-wifi/esp32s2", "esp-mbedtls-sys/esp32s2", "crypto-bigint"] -esp32s3 = ["esp-hal/esp32s3", "esp-wifi/esp32s3", "esp-mbedtls-sys/esp32s3", "crypto-bigint"] +esp32 = ["esp-hal/esp32", "esp-wifi/esp32", "esp-mbedtls-sys/esp32"] +esp32c3 = ["esp-hal/esp32c3", "esp-wifi/esp32c3", "esp-mbedtls-sys/esp32c3"] +esp32s2 = ["esp-hal/esp32s2", "esp-wifi/esp32s2", "esp-mbedtls-sys/esp32s2"] +esp32s3 = ["esp-hal/esp32s3", "esp-wifi/esp32s3", "esp-mbedtls-sys/esp32s3"] # Implement the traits defined in the latest HEAD of `edge-nal` edge-nal = ["dep:edge-nal", "async"] + +# Enable dependencies related to esp-hal (baremetal) +esp-hal = ["dep:esp-hal", "crypto-bigint", "nb"] diff --git a/esp-mbedtls/src/esp_hal/bignum.rs b/esp-mbedtls/src/esp_hal/bignum.rs index 7f0d9b2..dcd7f43 100644 --- a/esp-mbedtls/src/esp_hal/bignum.rs +++ b/esp-mbedtls/src/esp_hal/bignum.rs @@ -2,7 +2,6 @@ use core::ffi::c_int; -use esp_hal::prelude::nb; use esp_hal::rsa::{operand_sizes, RsaModularExponentiation}; use crypto_bigint::*; diff --git a/esp-mbedtls/src/esp_hal/sha/mod.rs b/esp-mbedtls/src/esp_hal/sha/mod.rs index 07dc6ef..277ae26 100644 --- a/esp-mbedtls/src/esp_hal/sha/mod.rs +++ b/esp-mbedtls/src/esp_hal/sha/mod.rs @@ -1,4 +1,3 @@ -use esp_hal::prelude::nb; use esp_hal::sha::{Context, ShaDigest}; mod sha1; diff --git a/esp-mbedtls/src/esp_hal/sha/sha1.rs b/esp-mbedtls/src/esp_hal/sha/sha1.rs index ea1ceaf..f49688e 100644 --- a/esp-mbedtls/src/esp_hal/sha/sha1.rs +++ b/esp-mbedtls/src/esp_hal/sha/sha1.rs @@ -4,7 +4,7 @@ use esp_hal::sha::Sha1; use crate::esp_hal::SHARED_SHA; -use super::{nb, Context, ShaDigest}; +use super::{Context, ShaDigest}; #[allow(non_camel_case_types)] #[repr(C)] diff --git a/esp-mbedtls/src/esp_hal/sha/sha256.rs b/esp-mbedtls/src/esp_hal/sha/sha256.rs index 797a8f7..722520b 100644 --- a/esp-mbedtls/src/esp_hal/sha/sha256.rs +++ b/esp-mbedtls/src/esp_hal/sha/sha256.rs @@ -4,7 +4,7 @@ use esp_hal::sha::{Sha224, Sha256}; use crate::esp_hal::SHARED_SHA; -use super::{nb, Context, ShaDigest}; +use super::{Context, ShaDigest}; #[allow(non_camel_case_types)] #[repr(C)] diff --git a/esp-mbedtls/src/esp_hal/sha/sha512.rs b/esp-mbedtls/src/esp_hal/sha/sha512.rs index dd270ab..42c6d0e 100644 --- a/esp-mbedtls/src/esp_hal/sha/sha512.rs +++ b/esp-mbedtls/src/esp_hal/sha/sha512.rs @@ -4,7 +4,7 @@ use esp_hal::sha::{Sha384, Sha512}; use crate::esp_hal::SHARED_SHA; -use super::{nb, Context, ShaDigest}; +use super::{Context, ShaDigest}; #[allow(non_camel_case_types)] #[repr(C)] diff --git a/esp-mbedtls/src/lib.rs b/esp-mbedtls/src/lib.rs index 4198db3..f19d983 100644 --- a/esp-mbedtls/src/lib.rs +++ b/esp-mbedtls/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] use core::cell::Cell; -use core::ffi::{c_int, c_uchar, c_ulong, c_void, CStr}; +use core::ffi::{c_char, c_int, c_uchar, c_ulong, c_void, CStr}; use core::fmt; use core::marker::PhantomData; use core::mem::size_of; @@ -290,13 +290,13 @@ pub struct Certificates<'a> { pub password: Option<&'a str>, } -impl<'a> Default for Certificates<'a> { +impl Default for Certificates<'_> { fn default() -> Self { Self::new() } } -impl<'a> Certificates<'a> { +impl Certificates<'_> { /// Create a new instance of [Certificates] with no certificates whatsoever pub const fn new() -> Self { Self { @@ -573,7 +573,7 @@ static TLS_CREATED: Mutex> = Mutex::new(Cell::new(false)); /// Only one such instance can be active at any point in time. pub struct Tls<'d>(PhantomData<&'d mut ()>); -impl<'d> Tls<'d> { +impl Tls<'_> { /// Create a new instance of the `Tls` type. /// /// Note that there could be only one active `Tls` instance at any point in time, @@ -705,7 +705,7 @@ impl<'a, T> Session<'a, T> { ) -> Result { let (drbg_context, ssl_context, ssl_config, crt, client_crt, private_key) = certificates.init_ssl(mode, min_version)?; - return Ok(Self { + Ok(Self { stream, drbg_context, ssl_context, @@ -715,11 +715,11 @@ impl<'a, T> Session<'a, T> { private_key, state: SessionState::Initial, _tls_ref: tls_ref, - }); + }) } } -impl<'a, T> Session<'a, T> +impl Session<'_, T> where T: Read + Write, { @@ -869,7 +869,7 @@ where unsafe extern "C" fn send(ctx: *mut c_void, buf: *const c_uchar, len: usize) -> c_int { let session = ctx as *mut Session; let stream = &mut (*session).stream; - let slice = core::ptr::slice_from_raw_parts(buf as *const u8, len as usize); + let slice = core::ptr::slice_from_raw_parts(buf, len); let res = stream.write(&*slice); match res { @@ -887,8 +887,8 @@ where unsafe extern "C" fn receive(ctx: *mut c_void, buf: *mut c_uchar, len: usize) -> c_int { let session = ctx as *mut Session; let stream = &mut (*session).stream; - let mut buffer = core::slice::from_raw_parts_mut(buf as *mut u8, len as usize); - let res = stream.read(&mut buffer); + let buffer = core::slice::from_raw_parts_mut(buf, len); + let res = stream.read(buffer); match res { Ok(len) => { @@ -1021,7 +1021,7 @@ pub mod asynch { ) -> Result { let (drbg_context, ssl_context, ssl_config, crt, client_crt, private_key) = certificates.init_ssl(mode, min_version)?; - return Ok(Self { + Ok(Self { stream, drbg_context, ssl_context, @@ -1033,7 +1033,7 @@ pub mod asynch { read_byte: None, write_byte: None, _token: tls_ref, - }); + }) } } @@ -1058,7 +1058,7 @@ pub mod asynch { } } - impl<'a, T> Session<'a, T> + impl Session<'_, T> where T: embedded_io_async::Read + embedded_io_async::Write, { @@ -1185,8 +1185,7 @@ pub mod asynch { // and then re-tries the call, the outstanding byte will not be written so it needs to be re-tried here. self.flush_write().await?; - let outcome = - core::future::poll_fn(|cx| PollCtx::poll(self, cx, |ssl| f(ssl))).await?; + let outcome = core::future::poll_fn(|cx| PollCtx::poll(self, cx, &mut f)).await?; match outcome { PollOutcome::Success(res) => { @@ -1542,12 +1541,12 @@ pub mod asynch { unsafe extern "C" fn dbg_print( _arg: *mut c_void, lvl: i32, - file: *const i8, + file: *const c_char, line: i32, - msg: *const i8, + msg: *const c_char, ) { - let file = CStr::from_ptr(file as *const i8); - let msg = CStr::from_ptr(msg as *const i8); + let file = CStr::from_ptr(file); + let msg = CStr::from_ptr(msg); let file = file.to_str().unwrap_or("???").trim(); let msg = msg.to_str().unwrap_or("???").trim(); @@ -1569,9 +1568,7 @@ extern "C" fn rand() -> crate::c_ulong { unsafe extern "C" fn rng(_param: *mut c_void, buffer: *mut c_uchar, len: usize) -> c_int { for i in 0..len { - buffer - .offset(i as isize) - .write_volatile((random() & 0xff) as u8); + buffer.add(i).write_volatile((random() & 0xff) as u8); } 0 diff --git a/examples/async_client.rs b/examples/async_client.rs index 2deec9a..310cf26 100644 --- a/examples/async_client.rs +++ b/examples/async_client.rs @@ -1,18 +1,15 @@ //! Example for a client connection to a server. -//! This example connects to Google.com and then prints out the result +//! This example connects to either `Google.com` or `certauth.cryptomix.com` (mTLS) and then prints out the result. +//! +//! # mTLS +//! Use the mTLS feature to enable client authentication and send client certificates when doing a +//! request. Note that this will connect to `certauth.cryptomix.com` instead of `google.com` #![no_std] #![no_main] #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_assoc_type)] -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; +use core::ffi::CStr; #[doc(hidden)] pub use esp_hal as hal; @@ -32,7 +29,7 @@ use esp_wifi::wifi::{ WifiState, }; use esp_wifi::{init, EspWifiController}; -use hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; +use hal::{clock::CpuClock, rng::Rng, timer::timg::TimerGroup}; // Patch until https://github.com/embassy-rs/static-cell/issues/16 is fixed macro_rules! mk_static { @@ -47,6 +44,19 @@ macro_rules! mk_static { const SSID: &str = env!("SSID"); const PASSWORD: &str = env!("PASSWORD"); +// Setup configuration based on mTLS feature. +cfg_if::cfg_if! { + if #[cfg(feature = "mtls")] { + const REMOTE_IP: Ipv4Address = Ipv4Address::new(62, 210, 201, 125); // certauth.cryptomix.com + const SERVERNAME: &CStr = c"certauth.cryptomix.com"; + const REQUEST: &[u8] = b"GET /json/ HTTP/1.0\r\nHost: certauth.cryptomix.com\r\n\r\n"; + } else { + const REMOTE_IP: Ipv4Address = Ipv4Address::new(142, 250, 185, 68); // google.com + const SERVERNAME: &CStr = c"www.google.com"; + const REQUEST: &[u8] = b"GET /notfound HTTP/1.0\r\nHost: www.google.com\r\n\r\n"; + } +} + #[esp_hal_embassy::main] async fn main(spawner: Spawner) -> ! { init_logger(log::LevelFilter::Info); @@ -80,8 +90,8 @@ async fn main(spawner: Spawner) -> ! { let timg1 = TimerGroup::new(peripherals.TIMG1); esp_hal_embassy::init(timg1.timer0); } else { - use esp_hal::timer::systimer::{SystemTimer, Target}; - let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + use esp_hal::timer::systimer::SystemTimer; + let systimer = SystemTimer::new(peripherals.SYSTIMER); esp_hal_embassy::init(systimer.alarm0); } } @@ -124,7 +134,7 @@ async fn main(spawner: Spawner) -> ! { socket.set_timeout(Some(Duration::from_secs(10))); - let remote_endpoint = (Ipv4Address::new(142, 250, 185, 68), 443); // www.google.com + let remote_endpoint = (REMOTE_IP, 443); println!("connecting..."); let r = socket.connect(remote_endpoint).await; if let Err(e) = r { @@ -133,6 +143,30 @@ async fn main(spawner: Spawner) -> ! { loop {} } + cfg_if::cfg_if! { + if #[cfg(feature = "mtls")] { + let certificates = Certificates { + ca_chain: X509::pem( + concat!(include_str!("./certs/certauth.cryptomix.com.pem"), "\0").as_bytes(), + ) + .ok(), + certificate: X509::pem(concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes()) + .ok(), + private_key: X509::pem(concat!(include_str!("./certs/private_key.pem"), "\0").as_bytes()) + .ok(), + password: None, + }; + } else { + let certificates = Certificates { + ca_chain: X509::pem( + concat!(include_str!("./certs/www.google.com.pem"), "\0").as_bytes(), + ) + .ok(), + ..Default::default() + }; + } + } + let mut tls = Tls::new(peripherals.SHA) .unwrap() .with_hardware_rsa(peripherals.RSA); @@ -142,16 +176,10 @@ async fn main(spawner: Spawner) -> ! { let mut session = Session::new( &mut socket, Mode::Client { - servername: c"www.google.com", + servername: SERVERNAME, }, TlsVersion::Tls1_3, - Certificates { - ca_chain: X509::pem( - concat!(include_str!("./certs/www.google.com.pem"), "\0").as_bytes(), - ) - .ok(), - ..Default::default() - }, + certificates, tls.reference(), ) .unwrap(); @@ -164,9 +192,7 @@ async fn main(spawner: Spawner) -> ! { use embedded_io_async::Write; - let r = session - .write_all(b"GET /notfound HTTP/1.0\r\nHost: www.google.com\r\n\r\n") - .await; + let r = session.write_all(REQUEST).await; if let Err(e) = r { println!("write error: {:?}", e); #[allow(clippy::empty_loop)] diff --git a/examples/async_client_mTLS.rs b/examples/async_client_mTLS.rs deleted file mode 100644 index fc6e9ca..0000000 --- a/examples/async_client_mTLS.rs +++ /dev/null @@ -1,238 +0,0 @@ -//! Example for a client connection using certificate authentication (mTLS) -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] -#![feature(impl_trait_in_assoc_type)] -#![allow(non_snake_case)] - -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - -#[doc(hidden)] -pub use esp_hal as hal; - -use embassy_executor::Spawner; - -use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, Ipv4Address, Runner, StackResources}; - -use embassy_time::{Duration, Timer}; -use esp_backtrace as _; -use esp_mbedtls::{asynch::Session, Mode, TlsVersion}; -use esp_mbedtls::{Certificates, Tls, X509}; -use esp_println::logger::init_logger; -use esp_println::{print, println}; -use esp_wifi::wifi::{ - ClientConfiguration, Configuration, WifiController, WifiDevice, WifiEvent, WifiStaDevice, - WifiState, -}; -use esp_wifi::{init, EspWifiController}; -use hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; - -// Patch until https://github.com/embassy-rs/static-cell/issues/16 is fixed -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - -const SSID: &str = env!("SSID"); -const PASSWORD: &str = env!("PASSWORD"); - -#[esp_hal_embassy::main] -async fn main(spawner: Spawner) -> ! { - init_logger(log::LevelFilter::Info); - - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); - - esp_alloc::heap_allocator!(115 * 1024); - - let timg0 = TimerGroup::new(peripherals.TIMG0); - - let init = &*mk_static!( - EspWifiController<'_>, - init( - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap() - ); - - let wifi = peripherals.WIFI; - let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(init, wifi, WifiStaDevice).unwrap(); - - cfg_if::cfg_if! { - if #[cfg(feature = "esp32")] { - let timg1 = TimerGroup::new(peripherals.TIMG1); - esp_hal_embassy::init(timg1.timer0); - } else { - use esp_hal::timer::systimer::{SystemTimer, Target}; - let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); - esp_hal_embassy::init(systimer.alarm0); - } - } - - let config = Config::dhcpv4(Default::default()); - - let seed = 1234; // very random, very secure seed - - // Init network stack - let (stack, runner) = embassy_net::new( - wifi_interface, - config, - mk_static!(StackResources<3>, StackResources::<3>::new()), - seed, - ); - - spawner.spawn(connection(controller)).ok(); - spawner.spawn(net_task(runner)).ok(); - - let mut rx_buffer = [0; 4096]; - let mut tx_buffer = [0; 4096]; - - loop { - if stack.is_link_up() { - break; - } - Timer::after(Duration::from_millis(500)).await; - } - - println!("Waiting to get IP address..."); - loop { - if let Some(config) = stack.config_v4() { - println!("Got IP: {}", config.address); - break; - } - Timer::after(Duration::from_millis(500)).await; - } - - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - - socket.set_timeout(Some(Duration::from_secs(10))); - - let remote_endpoint = (Ipv4Address::new(62, 210, 201, 125), 443); // certauth.cryptomix.com - println!("connecting..."); - let r = socket.connect(remote_endpoint).await; - if let Err(e) = r { - println!("connect error: {:?}", e); - #[allow(clippy::empty_loop)] - loop {} - } - - let certificates = Certificates { - ca_chain: X509::pem( - concat!(include_str!("./certs/certauth.cryptomix.com.pem"), "\0").as_bytes(), - ) - .ok(), - certificate: X509::pem(concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes()) - .ok(), - private_key: X509::pem(concat!(include_str!("./certs/private_key.pem"), "\0").as_bytes()) - .ok(), - password: None, - }; - - let mut tls = Tls::new(peripherals.SHA) - .unwrap() - .with_hardware_rsa(peripherals.RSA); - - tls.set_debug(0); - - let mut session = Session::new( - &mut socket, - Mode::Client { - servername: c"certauth.cryptomix.com", - }, - TlsVersion::Tls1_3, - certificates, - tls.reference(), - ) - .unwrap(); - - println!("Start tls connect"); - session.connect().await.unwrap(); - - println!("connected!"); - let mut buf = [0; 1024]; - - use embedded_io_async::Write; - - let r = session - .write_all(b"GET /json/ HTTP/1.0\r\nHost: certauth.cryptomix.com\r\n\r\n") - .await; - if let Err(e) = r { - println!("write error: {:?}", e); - #[allow(clippy::empty_loop)] - loop {} - } - - loop { - let n = match session.read(&mut buf).await { - Ok(n) => n, - Err(esp_mbedtls::TlsError::Eof) => { - break; - } - Err(e) => { - println!("read error: {:?}", e); - break; - } - }; - print!("{}", core::str::from_utf8(&buf[..n]).unwrap()); - } - println!("Done"); - - #[allow(clippy::empty_loop)] - loop {} -} - -#[embassy_executor::task] -async fn connection(mut controller: WifiController<'static>) { - println!("start connection task"); - println!("Device capabilities: {:?}", controller.capabilities()); - loop { - if matches!(esp_wifi::wifi::wifi_state(), WifiState::StaConnected) { - // wait until we're no longer connected - controller.wait_for_event(WifiEvent::StaDisconnected).await; - Timer::after(Duration::from_millis(5000)).await - } - if !matches!(controller.is_started(), Ok(true)) { - let client_config = Configuration::Client(ClientConfiguration { - ssid: SSID.try_into().unwrap(), - password: PASSWORD.try_into().unwrap(), - ..Default::default() - }); - controller.set_configuration(&client_config).unwrap(); - println!("Starting wifi"); - controller.start_async().await.unwrap(); - println!("Wifi started!"); - } - println!("About to connect..."); - - match controller.connect_async().await { - Ok(_) => println!("Wifi connected!"), - Err(e) => { - println!("Failed to connect to wifi: {e:?}"); - Timer::after(Duration::from_millis(5000)).await - } - } - } -} - -#[embassy_executor::task] -async fn net_task(mut runner: Runner<'static, WifiDevice<'static, WifiStaDevice>>) { - runner.run().await -} diff --git a/examples/async_server.rs b/examples/async_server.rs index d2d237c..f2b33d0 100644 --- a/examples/async_server.rs +++ b/examples/async_server.rs @@ -3,20 +3,33 @@ //! //! This example uses self-signed certificate. Your browser may display an error. //! You have to enable the exception to then proceed, of if using curl, use the flag `-k`. +//! +//! # mTLS +//! Running this example with the feature `mtls` will make the server request a client +//! certificate for the connection. If you send a request, without passing +//! certificates, you will get an error. Theses certificates below are generated +//! to work is the configured CA: +//! +//! certificate.pem +//! ```text +#![doc = include_str!("./certs/certificate.pem")] +//! ``` +//! +//! private_key.pem +//! ```text +#![doc = include_str!("./certs/private_key.pem")] +//! ``` +//! +//! Test with curl: +//! ```bash +//! curl https:/// --cert certificate.pem --key private_key.pem -k +//! ``` +//! #![no_std] #![no_main] #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_assoc_type)] -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - #[doc(hidden)] pub use esp_hal as hal; @@ -36,7 +49,7 @@ use esp_wifi::wifi::{ WifiState, }; use esp_wifi::{init, EspWifiController}; -use hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; +use hal::{clock::CpuClock, rng::Rng, timer::timg::TimerGroup}; // Patch until https://github.com/embassy-rs/static-cell/issues/16 is fixed macro_rules! mk_static { @@ -84,8 +97,8 @@ async fn main(spawner: Spawner) -> ! { let timg1 = TimerGroup::new(peripherals.TIMG1); esp_hal_embassy::init(timg1.timer0); } else { - use esp_hal::timer::systimer::{SystemTimer, Target}; - let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + use esp_hal::timer::systimer::SystemTimer; + let systimer = SystemTimer::new(peripherals.SYSTIMER); esp_hal_embassy::init(systimer.alarm0); } } @@ -160,6 +173,10 @@ async fn main(spawner: Spawner) -> ! { Mode::Server, TlsVersion::Tls1_2, Certificates { + // Provide a ca_chain if you want to enable mTLS for the server. + #[cfg(feature = "mtls")] + ca_chain: X509::pem(concat!(include_str!("./certs/ca_cert.pem"), "\0").as_bytes()) + .ok(), // Use self-signed certificates certificate: X509::pem( concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes(), @@ -221,6 +238,9 @@ async fn main(spawner: Spawner) -> ! { Timer::after(Duration::from_millis(1000)).await; } + Err(TlsError::NoClientCertificate) => { + println!("Error: No client certificates given. Please provide client certificates during your request"); + } Err(TlsError::MbedTlsError(-30592)) => { println!("Fatal message: Please enable the exception for a self-signed certificate in your browser"); } diff --git a/examples/async_server_mTLS.rs b/examples/async_server_mTLS.rs deleted file mode 100644 index 9e46f5c..0000000 --- a/examples/async_server_mTLS.rs +++ /dev/null @@ -1,298 +0,0 @@ -//! Example for an async server. -//! Contains a basic server implementation to test mbedtls in server mode. -//! -//! This example is configured to use mTLS. If you send a request, without passing -//! certificates, you will get an error. Theses certificates below are generated -//! to work is the configured CA: -//! -//! certificate.pem -//! ```text -#![doc = include_str!("./certs/certificate.pem")] -//! ``` -//! -//! private_key.pem -//! ```text -#![doc = include_str!("./certs/private_key.pem")] -//! ``` -//! -//! Test with curl: -//! ```bash -//! curl https:/// --cert certificate.pem --key private_key.pem -k -//! ``` -#![no_std] -#![no_main] -#![feature(type_alias_impl_trait)] -#![feature(impl_trait_in_assoc_type)] -#![allow(non_snake_case)] - -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - -#[doc(hidden)] -pub use esp_hal as hal; - -use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, IpListenEndpoint, Runner, StackResources}; - -use embassy_executor::Spawner; -use embassy_time::{Duration, Timer}; -use esp_alloc as _; -use esp_backtrace as _; -use esp_mbedtls::{asynch::Session, Certificates, Mode, TlsVersion}; -use esp_mbedtls::{Tls, TlsError, X509}; -use esp_println::logger::init_logger; -use esp_println::{print, println}; -use esp_wifi::wifi::{ - ClientConfiguration, Configuration, WifiController, WifiDevice, WifiEvent, WifiStaDevice, - WifiState, -}; -use esp_wifi::{init, EspWifiController}; -use hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; - -// Patch until https://github.com/embassy-rs/static-cell/issues/16 is fixed -macro_rules! mk_static { - ($t:ty,$val:expr) => {{ - static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); - #[deny(unused_attributes)] - let x = STATIC_CELL.uninit().write(($val)); - x - }}; -} - -const SSID: &str = env!("SSID"); -const PASSWORD: &str = env!("PASSWORD"); - -#[esp_hal_embassy::main] -async fn main(spawner: Spawner) -> ! { - init_logger(log::LevelFilter::Info); - - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); - - esp_alloc::heap_allocator!(115 * 1024); - - let timg0 = TimerGroup::new(peripherals.TIMG0); - - let init = &*mk_static!( - EspWifiController<'_>, - init( - timg0.timer0, - Rng::new(peripherals.RNG), - peripherals.RADIO_CLK, - ) - .unwrap() - ); - - let wifi = peripherals.WIFI; - let (wifi_interface, controller) = - esp_wifi::wifi::new_with_mode(init, wifi, WifiStaDevice).unwrap(); - - cfg_if::cfg_if! { - if #[cfg(feature = "esp32")] { - let timg1 = TimerGroup::new(peripherals.TIMG1); - esp_hal_embassy::init(timg1.timer0); - } else { - use esp_hal::timer::systimer::{SystemTimer, Target}; - let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); - esp_hal_embassy::init(systimer.alarm0); - } - } - - let config = Config::dhcpv4(Default::default()); - - let seed = 1234; // very random, very secure seed - - // Init network stack - let (stack, runner) = embassy_net::new( - wifi_interface, - config, - mk_static!(StackResources<3>, StackResources::<3>::new()), - seed, - ); - - spawner.spawn(connection(controller)).ok(); - spawner.spawn(net_task(runner)).ok(); - - let mut tls = Tls::new(peripherals.SHA) - .unwrap() - .with_hardware_rsa(peripherals.RSA); - - tls.set_debug(0); - - let mut rx_buffer = [0; 4096]; - let mut tx_buffer = [0; 4096]; - - loop { - if stack.is_link_up() { - break; - } - Timer::after(Duration::from_millis(500)).await; - } - - println!("Waiting to get IP address..."); - loop { - if let Some(config) = stack.config_v4() { - println!("Got IP: {}", config.address); - println!( - "Point your browser to https://{}/", - config.address.address() - ); - break; - } - Timer::after(Duration::from_millis(500)).await; - } - - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - loop { - println!("Waiting for connection..."); - let r = socket - .accept(IpListenEndpoint { - addr: None, - port: 443, - }) - .await; - println!("Connected..."); - - if let Err(e) = r { - println!("connect error: {:?}", e); - continue; - } - - use embedded_io_async::Write; - - let mut buffer = [0u8; 1024]; - let mut pos = 0; - let mut session = Session::new( - &mut socket, - Mode::Server, - TlsVersion::Tls1_2, - Certificates { - ca_chain: X509::pem(concat!(include_str!("./certs/ca_cert.pem"), "\0").as_bytes()) - .ok(), - // Use self-signed certificates - certificate: X509::pem( - concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes(), - ) - .ok(), - private_key: X509::pem( - concat!(include_str!("./certs/private_key.pem"), "\0").as_bytes(), - ) - .ok(), - ..Default::default() - }, - tls.reference(), - ) - .unwrap(); - - println!("Start tls connect"); - match session.connect().await { - Ok(()) => { - log::info!("Got session"); - loop { - match session.read(&mut buffer).await { - Ok(0) => { - println!("read EOF"); - break; - } - Ok(len) => { - let to_print = - unsafe { core::str::from_utf8_unchecked(&buffer[..(pos + len)]) }; - - if to_print.contains("\r\n\r\n") { - print!("{}", to_print); - println!(); - break; - } - - pos += len; - } - Err(e) => { - println!("read error: {:?}", e); - break; - } - }; - } - - let r = session - .write_all( - b"HTTP/1.0 200 OK\r\n\r\n\ - \ - \ -

Hello Rust! Hello esp-mbedtls!

\ - \ - \r\n\ - ", - ) - .await; - if let Err(e) = r { - println!("write error: {:?}", e); - } - - Timer::after(Duration::from_millis(1000)).await; - } - Err(TlsError::NoClientCertificate) => { - println!("Error: No client certificates given. Please provide client certificates during your request"); - } - Err(TlsError::MbedTlsError(-30592)) => { - println!("Fatal message: Please enable the exception for a self-signed certificate in your browser"); - } - Err(error) => { - panic!("{:?}", error); - } - } - drop(session); - println!("Closing socket"); - socket.close(); - Timer::after(Duration::from_millis(1000)).await; - - socket.abort(); - } -} - -#[embassy_executor::task] -async fn connection(mut controller: WifiController<'static>) { - println!("start connection task"); - println!("Device capabilities: {:?}", controller.capabilities()); - loop { - if matches!(esp_wifi::wifi::wifi_state(), WifiState::StaConnected) { - // wait until we're no longer connected - controller.wait_for_event(WifiEvent::StaDisconnected).await; - Timer::after(Duration::from_millis(5000)).await - } - if !matches!(controller.is_started(), Ok(true)) { - let client_config = Configuration::Client(ClientConfiguration { - ssid: SSID.try_into().unwrap(), - password: PASSWORD.try_into().unwrap(), - ..Default::default() - }); - controller.set_configuration(&client_config).unwrap(); - println!("Starting wifi"); - controller.start_async().await.unwrap(); - println!("Wifi started!"); - } - println!("About to connect..."); - - match controller.connect_async().await { - Ok(_) => println!("Wifi connected!"), - Err(e) => { - println!("Failed to connect to wifi: {e:?}"); - Timer::after(Duration::from_millis(5000)).await - } - } - } -} - -#[embassy_executor::task] -async fn net_task(mut runner: Runner<'static, WifiDevice<'static, WifiStaDevice>>) { - runner.run().await -} diff --git a/examples/crypto_self_test.rs b/examples/crypto_self_test.rs index f74ce76..a8f94a9 100644 --- a/examples/crypto_self_test.rs +++ b/examples/crypto_self_test.rs @@ -2,15 +2,6 @@ #![no_std] #![no_main] -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - #[doc(hidden)] pub use esp_hal as hal; @@ -22,7 +13,7 @@ use esp_println::{logger::init_logger, println}; /// Only used for ROM functions #[allow(unused_imports)] use esp_wifi::init; -use hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; +use hal::{clock::CpuClock, main, rng::Rng, timer::timg::TimerGroup}; pub fn cycles() -> u64 { #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] @@ -32,11 +23,12 @@ pub fn cycles() -> u64 { #[cfg(not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")))] { - esp_hal::timer::systimer::SystemTimer::now() + use esp_hal::timer::systimer::{SystemTimer, Unit}; + SystemTimer::unit_value(Unit::Unit0) } } -#[entry] +#[main] fn main() -> ! { init_logger(log::LevelFilter::Info); diff --git a/examples/edge_server.rs b/examples/edge_server.rs index f1f709f..703114b 100644 --- a/examples/edge_server.rs +++ b/examples/edge_server.rs @@ -10,15 +10,6 @@ #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_assoc_type)] -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - use core::net::{IpAddr, Ipv4Addr, SocketAddr}; #[doc(hidden)] @@ -45,7 +36,7 @@ use esp_wifi::wifi::{ WifiState, }; use esp_wifi::{init, EspWifiController}; -use hal::{prelude::*, rng::Rng, timer::timg::TimerGroup}; +use hal::{clock::CpuClock, rng::Rng, timer::timg::TimerGroup}; // Patch until https://github.com/embassy-rs/static-cell/issues/16 is fixed macro_rules! mk_static { @@ -108,8 +99,8 @@ async fn main(spawner: Spawner) -> ! { let timg1 = TimerGroup::new(peripherals.TIMG1); esp_hal_embassy::init(timg1.timer0); } else { - use esp_hal::timer::systimer::{SystemTimer, Target}; - let systimer = SystemTimer::new(peripherals.SYSTIMER).split::(); + use esp_hal::timer::systimer::SystemTimer; + let systimer = SystemTimer::new(peripherals.SYSTIMER); esp_hal_embassy::init(systimer.alarm0); } } diff --git a/examples/sync_client.rs b/examples/sync_client.rs index 374de57..76cbf3f 100644 --- a/examples/sync_client.rs +++ b/examples/sync_client.rs @@ -1,16 +1,13 @@ //! Example for a client connection to a server. -//! This example connects to Google.com and then prints out the result +//! This example connects to either `Google.com` or `certauth.cryptomix.com` (mTLS) and then prints out the result. +//! +//! # mTLS +//! Use the mTLS feature to enable client authentication and send client certificates when doing a +//! request. Note that this will connect to `certauth.cryptomix.com` instead of `google.com` #![no_std] #![no_main] -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; +use core::ffi::CStr; #[doc(hidden)] pub use esp_hal as hal; @@ -26,8 +23,8 @@ use esp_wifi::{ init, wifi::{utils::create_network_interface, ClientConfiguration, Configuration, WifiStaDevice}, }; -use hal::{prelude::*, rng::Rng, time, timer::timg::TimerGroup}; -use smoltcp11::{ +use hal::{clock::CpuClock, main, rng::Rng, time, timer::timg::TimerGroup}; +use smoltcp::{ iface::{SocketSet, SocketStorage}, wire::IpAddress, }; @@ -35,7 +32,20 @@ use smoltcp11::{ const SSID: &str = env!("SSID"); const PASSWORD: &str = env!("PASSWORD"); -#[entry] +// Setup configuration based on mTLS feature. +cfg_if::cfg_if! { + if #[cfg(feature = "mtls")] { + const REMOTE_IP: IpAddress = IpAddress::v4(62, 210, 201, 125); // certauth.cryptomix.com + const SERVERNAME: &CStr = c"certauth.cryptomix.com"; + const REQUEST: &[u8] = b"GET /json/ HTTP/1.0\r\nHost: certauth.cryptomix.com\r\n\r\n"; + } else { + const REMOTE_IP: IpAddress = IpAddress::v4(142, 250, 185, 68); // google.com + const SERVERNAME: &CStr = c"www.google.com"; + const REQUEST: &[u8] = b"GET /notfound HTTP/1.0\r\nHost: www.google.com\r\n\r\n"; + } +} + +#[main] fn main() -> ! { init_logger(log::LevelFilter::Info); let peripherals = esp_hal::init({ @@ -110,9 +120,31 @@ fn main() -> ! { socket.work(); - socket - .open(IpAddress::v4(142, 250, 185, 68), 443) // google.com - .unwrap(); + socket.open(REMOTE_IP, 443).unwrap(); + + cfg_if::cfg_if! { + if #[cfg(feature = "mtls")] { + let certificates = Certificates { + ca_chain: X509::pem( + concat!(include_str!("./certs/certauth.cryptomix.com.pem"), "\0").as_bytes(), + ) + .ok(), + certificate: X509::pem(concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes()) + .ok(), + private_key: X509::pem(concat!(include_str!("./certs/private_key.pem"), "\0").as_bytes()) + .ok(), + password: None, + }; + } else { + let certificates = Certificates { + ca_chain: X509::pem( + concat!(include_str!("./certs/www.google.com.pem"), "\0").as_bytes(), + ) + .ok(), + ..Default::default() + }; + } + } let mut tls = Tls::new(peripherals.SHA) .unwrap() @@ -123,16 +155,10 @@ fn main() -> ! { let mut session = Session::new( &mut socket, Mode::Client { - servername: c"www.google.com", + servername: SERVERNAME, }, TlsVersion::Tls1_3, - Certificates { - ca_chain: X509::pem( - concat!(include_str!("./certs/www.google.com.pem"), "\0").as_bytes(), - ) - .ok(), - ..Default::default() - }, + certificates, tls.reference(), ) .unwrap(); @@ -141,9 +167,7 @@ fn main() -> ! { session.connect().unwrap(); println!("Write to connection"); - session - .write(b"GET /notfound HTTP/1.0\r\nHost: www.google.com\r\n\r\n") - .unwrap(); + session.write(REQUEST).unwrap(); println!("Read from connection"); let mut buffer = [0u8; 4096]; diff --git a/examples/sync_client_mTLS.rs b/examples/sync_client_mTLS.rs deleted file mode 100644 index 8928884..0000000 --- a/examples/sync_client_mTLS.rs +++ /dev/null @@ -1,172 +0,0 @@ -//! Example for a client connection using certificate authentication (mTLS) -#![no_std] -#![no_main] - -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - -#[doc(hidden)] -pub use esp_hal as hal; - -use blocking_network_stack::Stack; - -use esp_alloc as _; -use esp_backtrace as _; -use esp_mbedtls::{Certificates, Session}; -use esp_mbedtls::{Mode, Tls, TlsVersion, X509}; -use esp_println::{logger::init_logger, print, println}; -use esp_wifi::{ - init, - wifi::{utils::create_network_interface, ClientConfiguration, Configuration, WifiStaDevice}, -}; -use hal::{prelude::*, rng::Rng, time, timer::timg::TimerGroup}; -use smoltcp11::{ - iface::{SocketSet, SocketStorage}, - wire::IpAddress, -}; - -const SSID: &str = env!("SSID"); -const PASSWORD: &str = env!("PASSWORD"); - -#[entry] -fn main() -> ! { - init_logger(log::LevelFilter::Info); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); - - esp_alloc::heap_allocator!(115 * 1024); - - let timg0 = TimerGroup::new(peripherals.TIMG0); - - let mut rng = Rng::new(peripherals.RNG); - - let init = init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); - - let wifi = peripherals.WIFI; - - let (iface, device, mut controller) = - create_network_interface(&init, wifi, WifiStaDevice).unwrap(); - - let mut socket_set_entries: [SocketStorage; 3] = Default::default(); - let sockets = SocketSet::new(&mut socket_set_entries[..]); - - let now = || time::now().duration_since_epoch().to_millis(); - let wifi_stack = Stack::new(iface, device, sockets, now, rng.random()); - - println!("Call wifi_connect"); - let client_config = Configuration::Client(ClientConfiguration { - ssid: SSID.try_into().unwrap(), - password: PASSWORD.try_into().unwrap(), - ..Default::default() - }); - controller.set_configuration(&client_config).unwrap(); - controller.start().unwrap(); - controller.connect().unwrap(); - - println!("Wait to get connected"); - loop { - let res = controller.is_connected(); - match res { - Ok(connected) => { - if connected { - break; - } - } - Err(err) => { - println!("{:?}", err); - #[allow(clippy::empty_loop)] - loop {} - } - } - } - - // wait for getting an ip address - println!("Wait to get an ip address"); - loop { - wifi_stack.work(); - - if wifi_stack.is_iface_up() { - println!("Got ip {:?}", wifi_stack.get_ip_info()); - break; - } - } - - println!("We are connected!"); - - println!("Making HTTP request"); - let mut rx_buffer = [0u8; 1536]; - let mut tx_buffer = [0u8; 1536]; - let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); - - socket.work(); - - socket - .open(IpAddress::v4(62, 210, 201, 125), 443) // certauth.cryptomix.com - .unwrap(); - - let certificates = Certificates { - ca_chain: X509::pem( - concat!(include_str!("./certs/certauth.cryptomix.com.pem"), "\0").as_bytes(), - ) - .ok(), - certificate: X509::pem(concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes()) - .ok(), - private_key: X509::pem(concat!(include_str!("./certs/private_key.pem"), "\0").as_bytes()) - .ok(), - password: None, - }; - - let mut tls = Tls::new(peripherals.SHA) - .unwrap() - .with_hardware_rsa(peripherals.RSA); - - tls.set_debug(0); - - let mut session = Session::new( - &mut socket, - Mode::Client { - servername: c"certauth.cryptomix.com", - }, - TlsVersion::Tls1_3, - certificates, - tls.reference(), - ) - .unwrap(); - - println!("Start tls connect"); - session.connect().unwrap(); - - println!("Write to connection"); - session - .write(b"GET /json/ HTTP/1.0\r\nHost: certauth.cryptomix.com\r\n\r\n") - .unwrap(); - - println!("Read from connection"); - let mut buffer = [0u8; 4096]; - loop { - match session.read(&mut buffer) { - Ok(len) => { - print!("{}", unsafe { - core::str::from_utf8_unchecked(&buffer[..len]) - }); - } - Err(_) => { - println!(); - break; - } - } - } - println!("Done"); - - #[allow(clippy::empty_loop)] - loop {} -} diff --git a/examples/sync_server.rs b/examples/sync_server.rs index 916748d..e0a5c0e 100644 --- a/examples/sync_server.rs +++ b/examples/sync_server.rs @@ -3,18 +3,31 @@ //! //! This example uses self-signed certificate. Your browser may display an error. //! You have to enable the exception to then proceed, of if using curl, use the flag `-k`. +//! +//! # mTLS +//! Running this example with the feature `mtls` will make the server request a client +//! certificate for the connection. If you send a request, without passing +//! certificates, you will get an error. Theses certificates below are generated +//! to work is the configured CA: +//! +//! certificate.pem +//! ```text +#![doc = include_str!("./certs/certificate.pem")] +//! ``` +//! +//! private_key.pem +//! ```text +#![doc = include_str!("./certs/private_key.pem")] +//! ``` +//! +//! Test with curl: +//! ```bash +//! curl https:/// --cert certificate.pem --key private_key.pem -k +//! ``` +//! #![no_std] #![no_main] -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - #[doc(hidden)] pub use esp_hal as hal; @@ -29,13 +42,13 @@ use esp_wifi::{ init, wifi::{utils::create_network_interface, ClientConfiguration, Configuration, WifiStaDevice}, }; -use hal::{prelude::*, rng::Rng, time, timer::timg::TimerGroup}; -use smoltcp11::iface::{SocketSet, SocketStorage}; +use hal::{clock::CpuClock, main, rng::Rng, time, timer::timg::TimerGroup}; +use smoltcp::iface::{SocketSet, SocketStorage}; const SSID: &str = env!("SSID"); const PASSWORD: &str = env!("PASSWORD"); -#[entry] +#[main] fn main() -> ! { init_logger(log::LevelFilter::Info); let peripherals = esp_hal::init({ @@ -139,6 +152,12 @@ fn main() -> ! { Mode::Server, TlsVersion::Tls1_2, Certificates { + // Provide a ca_chain if you want to enable mTLS for the server. + #[cfg(feature = "mtls")] + ca_chain: X509::pem( + concat!(include_str!("./certs/ca_cert.pem"), "\0").as_bytes(), + ) + .ok(), // Use self-signed certificates certificate: X509::pem( concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes(), @@ -189,6 +208,9 @@ fn main() -> ! { .unwrap(); } } + Err(TlsError::NoClientCertificate) => { + println!("Error: No client certificates given. Please provide client certificates during your request"); + } Err(TlsError::MbedTlsError(-30592)) => { println!("Fatal message: Please enable the exception for a self-signed certificate in your browser"); } diff --git a/examples/sync_server_mTLS.rs b/examples/sync_server_mTLS.rs deleted file mode 100644 index db52fe3..0000000 --- a/examples/sync_server_mTLS.rs +++ /dev/null @@ -1,237 +0,0 @@ -//! Example for a sync server. -//! Contains a basic server implementation to test mbedtls in server mode. -//! -//! This example is configured to use mTLS. If you send a request, without passing -//! certificates, you will get an error. Theses certificates below are generated -//! to work is the configured CA: -//! -//! certificate.pem -//! ```text -#![doc = include_str!("./certs/certificate.pem")] -//! ``` -//! -//! private_key.pem -//! ```text -#![doc = include_str!("./certs/private_key.pem")] -//! ``` -//! -//! Test with curl: -//! ```bash -//! curl https:/// --cert certificate.pem --key private_key.pem -k -//! ``` -#![no_std] -#![no_main] - -// See https://github.com/esp-rs/esp-mbedtls/pull/62#issuecomment-2560830139 -// -// This is by the way a generic way to polyfill the libc functions used by `mbedtls`: -// - If your (baremetal) platform does not provide one or more of these, just -// add a dependency on `tinyrlibc` in your binary crate with features for all missing functions -// and then put such a `use` statement in your main file -#[cfg(feature = "esp32c3")] -use tinyrlibc as _; - -#[doc(hidden)] -pub use esp_hal as hal; - -use blocking_network_stack::Stack; - -use embedded_io::*; -use esp_backtrace as _; -use esp_mbedtls::{Certificates, Session}; -use esp_mbedtls::{Mode, Tls, TlsError, TlsVersion, X509}; -use esp_println::{logger::init_logger, print, println}; -use esp_wifi::{ - init, - wifi::{utils::create_network_interface, ClientConfiguration, Configuration, WifiStaDevice}, -}; -use hal::{prelude::*, rng::Rng, time, timer::timg::TimerGroup}; -use smoltcp11::iface::{SocketSet, SocketStorage}; - -const SSID: &str = env!("SSID"); -const PASSWORD: &str = env!("PASSWORD"); - -#[entry] -fn main() -> ! { - init_logger(log::LevelFilter::Info); - let peripherals = esp_hal::init({ - let mut config = esp_hal::Config::default(); - config.cpu_clock = CpuClock::max(); - config - }); - - esp_alloc::heap_allocator!(115 * 1024); - - let timg0 = TimerGroup::new(peripherals.TIMG0); - - let mut rng = Rng::new(peripherals.RNG); - - let init = init(timg0.timer0, rng, peripherals.RADIO_CLK).unwrap(); - - let wifi = peripherals.WIFI; - - let (iface, device, mut controller) = - create_network_interface(&init, wifi, WifiStaDevice).unwrap(); - - let mut socket_set_entries: [SocketStorage; 3] = Default::default(); - let sockets = SocketSet::new(&mut socket_set_entries[..]); - - let now = || time::now().duration_since_epoch().to_millis(); - let wifi_stack = Stack::new(iface, device, sockets, now, rng.random()); - - println!("Call wifi_connect"); - let client_config = Configuration::Client(ClientConfiguration { - ssid: SSID.try_into().unwrap(), - password: PASSWORD.try_into().unwrap(), - ..Default::default() - }); - controller.set_configuration(&client_config).unwrap(); - controller.start().unwrap(); - controller.connect().unwrap(); - - println!("Wait to get connected"); - loop { - let res = controller.is_connected(); - match res { - Ok(connected) => { - if connected { - break; - } - } - Err(err) => { - println!("{:?}", err); - #[allow(clippy::empty_loop)] - loop {} - } - } - } - - // wait for getting an ip address - println!("Wait to get an ip address"); - loop { - wifi_stack.work(); - - if wifi_stack.is_iface_up() { - println!("Got ip {:?}", wifi_stack.get_ip_info()); - break; - } - } - - println!("We are connected!"); - - println!( - "Point your browser to https://{:?}/", - wifi_stack.get_ip_info().unwrap().ip - ); - let mut rx_buffer = [0u8; 1536]; - let mut tx_buffer = [0u8; 1536]; - let mut socket = wifi_stack.get_socket(&mut rx_buffer, &mut tx_buffer); - - socket.listen(443).unwrap(); - - let mut tls = Tls::new(peripherals.SHA) - .unwrap() - .with_hardware_rsa(peripherals.RSA); - - tls.set_debug(0); - - loop { - socket.work(); - - if !socket.is_open() { - socket.listen(443).unwrap(); - } - - if socket.is_connected() { - println!("New connection"); - - let mut time_out = false; - let wait_end = now() + 20 * 1000; - let mut buffer = [0u8; 1024]; - let mut pos = 0; - - let mut session = Session::new( - &mut socket, - Mode::Server, - TlsVersion::Tls1_2, - Certificates { - ca_chain: X509::pem( - concat!(include_str!("./certs/ca_cert.pem"), "\0").as_bytes(), - ) - .ok(), - // Use self-signed certificates - certificate: X509::pem( - concat!(include_str!("./certs/certificate.pem"), "\0").as_bytes(), - ) - .ok(), - private_key: X509::pem( - concat!(include_str!("./certs/private_key.pem"), "\0").as_bytes(), - ) - .ok(), - ..Default::default() - }, - tls.reference(), - ) - .unwrap(); - - match session.connect() { - Ok(_) => { - while let Ok(len) = session.read(&mut buffer[pos..]) { - let to_print = - unsafe { core::str::from_utf8_unchecked(&buffer[..(pos + len)]) }; - - if to_print.contains("\r\n\r\n") { - print!("{}", to_print); - println!(); - break; - } - - pos += len; - - if now() > wait_end { - println!("Timed out"); - time_out = true; - break; - } - } - - if !time_out { - session - .write_all( - b"HTTP/1.0 200 OK\r\n\r\n\ - \ - \ -

Hello Rust! Hello esp-mbedtls!

\ - \ - \r\n\ - ", - ) - .unwrap(); - } - } - Err(TlsError::NoClientCertificate) => { - println!("Error: No client certificates given. Please provide client certificates during your request"); - } - Err(TlsError::MbedTlsError(-30592)) => { - println!("Fatal message: Please enable the exception for a self-signed certificate in your browser"); - } - Err(error) => { - panic!("{:?}", error); - } - } - - drop(session); - socket.close(); - - println!("Done\n"); - println!(); - } - - // This seems to delay after a connection. Removed to allow instant connections - // - // let wait_end = current_millis() + 5 * 1000; - // while current_millis() < wait_end { - // socket.work(); - // } - } -} diff --git a/justfile b/justfile index 104ee0f..3e379d6 100644 --- a/justfile +++ b/justfile @@ -1,19 +1,19 @@ export SSID := "Dummy" export PASSWORD := "Dummy" -all: (check "esp32" "esp") (check "esp32s3" "esp") (check "esp32c3" "nightly-2024-12-01") - cd esp-mbedtls && cargo +nightly-2024-12-01 fmt --all -- --check +all: (check "esp32" "esp") (check "esp32s3" "esp") (check "esp32c3" "nightly") + cd esp-mbedtls && cargo +nightly fmt --all -- --check [private] check arch toolchain: cargo +{{ toolchain }} b{{ arch }} --example sync_client --features="examples" - cargo +{{ toolchain }} b{{ arch }} --example sync_client_mTLS --features="examples" + cargo +{{ toolchain }} b{{ arch }} --example sync_client --features="examples, mtls" cargo +{{ toolchain }} b{{ arch }} --example async_client --features="examples-async" - cargo +{{ toolchain }} b{{ arch }} --example async_client_mTLS --features="examples-async" + cargo +{{ toolchain }} b{{ arch }} --example async_client --features="examples-async, mtls" cargo +{{ toolchain }} b{{ arch }} --example sync_server --features="examples" - cargo +{{ toolchain }} b{{ arch }} --example sync_server_mTLS --features="examples" + cargo +{{ toolchain }} b{{ arch }} --example sync_server --features="examples, mtls" cargo +{{ toolchain }} b{{ arch }} --example async_server --features="examples-async" - cargo +{{ toolchain }} b{{ arch }} --example async_server_mTLS --features="examples-async" + cargo +{{ toolchain }} b{{ arch }} --example async_server --features="examples-async, mtls" cargo +{{ toolchain }} b{{ arch }} --example edge_server --features="examples-async" cargo +{{ toolchain }} b{{ arch }} --example crypto_self_test --features="examples" cargo +{{ toolchain }} b --example crypto_self_test_std --features="examples-std" --target x86_64-unknown-linux-gnu -Z build-std=std,panic_abort