diff --git a/Cargo.toml b/Cargo.toml index 9d9d04d..c0575b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ p256 = { version = "0.13", default-features = false, features = [ embedded-tls = { git = "https://github.com/drogue-iot/embedded-tls.git", default-features = false, features = ["rustpki"], optional = true } rand_chacha = { version = "0.3", default-features = false } nourl = "0.1.2" -esp-mbedtls = { version = "0.1", git = "https://github.com/esp-rs/esp-mbedtls.git", optional = true } +mbedtls-rs = { version = "0.1", git = "https://github.com/esp-rs/mbedtls-rs.git", optional = true } [dev-dependencies] hyper = { version = "0.14.23", features = ["full"] } @@ -61,3 +61,4 @@ defmt = [ "nourl/defmt", "heapless/defmt", ] +mbedtls-rs = ["dep:mbedtls-rs"] diff --git a/README.md b/README.md index f003118..5712461 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ traits from the `embedded-io` crate. No alloc or std lib required! It offers two sets of APIs: * A low-level `request` API which allows you to construct HTTP requests and write them to a `embedded-io` transport. -* A higher level `client` API which uses the `embedded-nal-async` (+ optional `embedded-tls` / `esp-mbedtls`) crates to establish TCP + TLS connections. +* A higher level `client` API which uses the `embedded-nal-async` (+ optional `embedded-tls` / `mbedtls-rs`) crates to establish TCP + TLS connections. ## example @@ -30,7 +30,7 @@ let response = client .unwrap(); ``` -The client is still lacking many features, but can perform basic HTTP GET/PUT/POST/DELETE requests with payloads. However, not all content types and status codes are implemented, and are added on a need basis. For TLS, it uses either `embedded-tls` or `esp-mbedtls` as the transport. +The client is still lacking many features, but can perform basic HTTP GET/PUT/POST/DELETE requests with payloads. However, not all content types and status codes are implemented, and are added on a need basis. For TLS, it uses either `embedded-tls` or `mbedtls-rs` as the transport. NOTE: TLS verification is not supported in no_std environments for `embedded-tls`. @@ -39,19 +39,19 @@ In addition to common headers like `.content_type()` on requests, broader `.head If you are missing a feature or would like an improvement, please raise an issue or a PR. ## TLS 1.2*, 1.3 and Supported Cipher Suites -`reqwless` uses `embedded-tls` or `esp-mbedtls` to establish secure TLS connections for `https://..` urls. +`reqwless` uses `embedded-tls` or `mbedtls-rs` to establish secure TLS connections for `https://..` urls. -*TLS 1.2 is only supported with `esp-mbedtls` +*TLS 1.2 is only supported with `mbedtls-rs` :warning: Note that both features cannot be used together and will cause a compilation error. -:warning: The released version of `reqwless` does not support `esp-mbedtls`. The reason for this is that `esp-mbedtls` is not yet published to crates.io. One should specify `reqwless` as a git dependency to use `esp-mbedtls`. +:warning: The released version of `reqwless` does not support `mbedtls-rs`. The reason for this is that `mbedtls-rs` is not yet published to crates.io. One should specify `reqwless` as a git dependency to use `mbedtls-rs`. -### esp-mbedtls +### mbedtls-rs **Can only be used on esp32 boards** -`esp-mbedtls` supports TLS 1.2 and 1.3. It uses espressif's Rust wrapper over mbedtls, alongside optimizations such as hardware acceleration. +`mbedtls-rs` supports TLS 1.2 and 1.3. It uses espressif's Rust wrapper over mbedtls, alongside optimizations such as hardware acceleration. -To use, you need to enable the transitive dependency of `esp-mbedtls` for your SoC. +To use, you need to enable the transitive dependency of `mbedtls-rs` for your SoC. Currently, the supported SoCs are: - `esp32` @@ -62,10 +62,10 @@ Currently, the supported SoCs are: Cargo.toml: ```toml -reqwless = { version = "0.12.0", default-features = false, features = ["esp-mbedtls", "log"] } -esp-mbedtls = { git = "https://github.com/esp-rs/esp-mbedtls.git", features = ["esp32s3"] } +reqwless = { version = "0.12.0", default-features = false, features = ["mbedtls-rs", "log"] } +mbedtls-rs = { git = "https://github.com/esp-rs/mbedtls-rs.git", features = ["esp32s3"] } ``` - + #### Example ```rust,ignore diff --git a/src/client.rs b/src/client.rs index 1f9b084..85401ee 100644 --- a/src/client.rs +++ b/src/client.rs @@ -11,6 +11,8 @@ use embedded_io::Error as _; use embedded_io::ErrorType; use embedded_io_async::{Read, Write}; use embedded_nal_async::{Dns, TcpConnect}; + +extern crate alloc; #[cfg(feature = "embedded-tls")] use embedded_tls::{ Aes128GcmSha256, CryptoProvider, NoClock, SignatureScheme, TlsError, TlsVerifier, pki::CertVerifier, @@ -30,21 +32,21 @@ where { client: &'a T, dns: &'a D, - #[cfg(any(feature = "embedded-tls", feature = "esp-mbedtls"))] + #[cfg(any(feature = "embedded-tls", feature = "mbedtls-rs"))] tls: Option>, } /// Type for TLS configuration of HTTP client. -#[cfg(feature = "esp-mbedtls")] +#[cfg(feature = "mbedtls-rs")] pub struct TlsConfig<'a, const RX_SIZE: usize = 4096, const TX_SIZE: usize = 4096> { /// Minimum TLS version for the connection version: crate::TlsVersion, - /// Client certificates. See [esp_mbedtls::Certificates] - certificates: crate::Certificates<'a>, + /// Client certificates. See [mbedtls_rs::Certificates] + certificates: crate::Certificate<'a>, /// A reference to instance of the MbedTLS library. - tls_reference: esp_mbedtls::TlsReference<'a>, + tls_reference: mbedtls_rs::TlsReference<'a>, } /// Type for TLS configuration of HTTP client. @@ -114,11 +116,11 @@ impl<'a> TlsConfig<'a> { } } -#[cfg(feature = "esp-mbedtls")] +#[cfg(feature = "mbedtls-rs")] impl<'a, const RX_SIZE: usize, const TX_SIZE: usize> TlsConfig<'a, RX_SIZE, TX_SIZE> { pub fn new( version: crate::TlsVersion, - certificates: crate::Certificates<'a>, + certificates: crate::Certificate<'a>, tls_reference: crate::TlsReference<'a>, ) -> Self { Self { @@ -139,13 +141,13 @@ where Self { client, dns, - #[cfg(any(feature = "embedded-tls", feature = "esp-mbedtls"))] + #[cfg(any(feature = "embedded-tls", feature = "mbedtls-rs"))] tls: None, } } /// Create a new HTTP client for a given connection handle and a target host. - #[cfg(any(feature = "embedded-tls", feature = "esp-mbedtls"))] + #[cfg(any(feature = "embedded-tls", feature = "mbedtls-rs"))] pub fn new_with_tls(client: &'a T, dns: &'a D, tls: TlsConfig<'a>) -> Self { Self { client, @@ -174,21 +176,30 @@ where .map_err(|e| e.kind())?; if url.scheme() == UrlScheme::HTTPS { - #[cfg(feature = "esp-mbedtls")] + #[cfg(feature = "mbedtls-rs")] if let Some(tls) = self.tls.as_mut() { - let mut servername = host.as_bytes().to_vec(); - servername.push(0); - let mut session = esp_mbedtls::asynch::Session::new( - conn, - esp_mbedtls::Mode::Client { - servername: unsafe { core::ffi::CStr::from_bytes_with_nul_unchecked(&servername) }, - }, - tls.version, - tls.certificates, - tls.tls_reference, - )?; - - session.connect().await?; + use mbedtls_rs::ClientSessionConfig; + + // Build a stack-allocated null-terminated server name (max 253 chars per RFC1034 + nul) + let mut servername = heapless::CString::<255>::new(); + servername.extend_from_bytes(host.as_bytes()).unwrap(); + + // SAFETY: `servername` lives for the remainder of this scope. + // `SessionState::new` (called by `Session::new`) copies the hostname + // internally via `mbedtls_ssl_set_hostname` and does not store this + // reference. The raw pointer reborrow detaches the compiler lifetime + // so it can satisfy `ClientSessionConfig<'a>`. + let sname: &core::ffi::CStr = + unsafe { &*(servername.as_c_str() as *const core::ffi::CStr) }; + + let mut ses_conf = ClientSessionConfig::new(); + ses_conf.ca_chain = Option::from(tls.certificates.clone()); + ses_conf.min_version = tls.version; + ses_conf.server_name = Some(sname); + let conf = mbedtls_rs::SessionConfig::Client(ses_conf); + let mut session = mbedtls_rs::Session::new(tls.tls_reference, conn, &conf).unwrap(); + + session.connect().await.unwrap(); Ok(HttpConnection::Tls(session)) } else { Ok(HttpConnection::Plain(conn)) @@ -244,7 +255,7 @@ where } else { Ok(HttpConnection::Plain(conn)) } - #[cfg(all(not(feature = "embedded-tls"), not(feature = "esp-mbedtls")))] + #[cfg(all(not(feature = "embedded-tls"), not(feature = "mbedtls-rs")))] Err(Error::InvalidUrl(nourl::Error::UnsupportedScheme)) } else { #[cfg(feature = "embedded-tls")] @@ -298,11 +309,11 @@ where { Plain(C), PlainBuffered(BufferedWrite<'conn, C>), - #[cfg(feature = "esp-mbedtls")] - Tls(esp_mbedtls::asynch::Session<'conn, C>), + #[cfg(feature = "mbedtls-rs")] + Tls(mbedtls_rs::Session<'conn, C>), #[cfg(feature = "embedded-tls")] Tls(embedded_tls::TlsConnection<'conn, C, embedded_tls::Aes128GcmSha256>), - #[cfg(all(not(feature = "embedded-tls"), not(feature = "esp-mbedtls")))] + #[cfg(all(not(feature = "embedded-tls"), not(feature = "mbedtls-rs")))] Tls((&'conn mut (), core::convert::Infallible)), // Variant is impossible to create, but we need it to avoid "unused lifetime" warning } @@ -400,13 +411,13 @@ where writer.terminate().await.map_err(|e| e.kind())?; buffered.clear(); } - #[cfg(any(feature = "embedded-tls", feature = "esp-mbedtls"))] + #[cfg(any(feature = "embedded-tls", feature = "mbedtls-rs"))] HttpConnection::Tls(c) => { let mut writer = ChunkedBodyWriter::new(c); body.write(&mut writer).await?; writer.terminate().await.map_err(|e| e.kind())?; } - #[cfg(all(not(feature = "embedded-tls"), not(feature = "esp-mbedtls")))] + #[cfg(all(not(feature = "embedded-tls"), not(feature = "mbedtls-rs")))] HttpConnection::Tls(_) => unreachable!(), }; } @@ -431,9 +442,9 @@ where match self { Self::Plain(conn) => conn.read(buf).await.map_err(|e| e.kind()), Self::PlainBuffered(conn) => conn.read(buf).await.map_err(|e| e.kind()), - #[cfg(any(feature = "embedded-tls", feature = "esp-mbedtls"))] + #[cfg(any(feature = "embedded-tls", feature = "mbedtls-rs"))] Self::Tls(conn) => conn.read(buf).await.map_err(|e| e.kind()), - #[cfg(not(any(feature = "embedded-tls", feature = "esp-mbedtls")))] + #[cfg(not(any(feature = "embedded-tls", feature = "mbedtls-rs")))] _ => unreachable!(), } } @@ -447,9 +458,9 @@ where match self { Self::Plain(conn) => conn.write(buf).await.map_err(|e| e.kind()), Self::PlainBuffered(conn) => conn.write(buf).await.map_err(|e| e.kind()), - #[cfg(any(feature = "embedded-tls", feature = "esp-mbedtls"))] + #[cfg(any(feature = "embedded-tls", feature = "mbedtls-rs"))] Self::Tls(conn) => conn.write(buf).await.map_err(|e| e.kind()), - #[cfg(not(any(feature = "embedded-tls", feature = "esp-mbedtls")))] + #[cfg(not(any(feature = "embedded-tls", feature = "mbedtls-rs")))] _ => unreachable!(), } } @@ -458,9 +469,9 @@ where match self { Self::Plain(conn) => conn.flush().await.map_err(|e| e.kind()), Self::PlainBuffered(conn) => conn.flush().await.map_err(|e| e.kind()), - #[cfg(any(feature = "embedded-tls", feature = "esp-mbedtls"))] + #[cfg(any(feature = "embedded-tls", feature = "mbedtls-rs"))] Self::Tls(conn) => conn.flush().await.map_err(|e| e.kind()), - #[cfg(not(any(feature = "embedded-tls", feature = "esp-mbedtls")))] + #[cfg(not(any(feature = "embedded-tls", feature = "mbedtls-rs")))] _ => unreachable!(), } } diff --git a/src/lib.rs b/src/lib.rs index 017d392..a79cd35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,8 +30,8 @@ pub enum Error { #[cfg(feature = "embedded-tls")] Tls(embedded_tls::TlsError), /// Tls Error - #[cfg(feature = "esp-mbedtls")] - Tls(esp_mbedtls::TlsError), + #[cfg(feature = "mbedtls-rs")] + Tls(mbedtls_rs::TlsError), /// The provided buffer is too small BufferTooSmall, /// The request is already sent @@ -83,12 +83,12 @@ impl From for Error { } /// Re-export those members since they're used for [client::TlsConfig]. -#[cfg(feature = "esp-mbedtls")] -pub use esp_mbedtls::{Certificates, TlsReference, TlsVersion, X509}; +#[cfg(feature = "mbedtls-rs")] +pub use mbedtls_rs::{Certificate, TlsReference, TlsVersion, X509}; -#[cfg(feature = "esp-mbedtls")] -impl From for Error { - fn from(e: esp_mbedtls::TlsError) -> Error { +#[cfg(feature = "mbedtls-rs")] +impl From for Error { + fn from(e: mbedtls_rs::TlsError) -> Error { Error::Tls(e) } } diff --git a/src/request.rs b/src/request.rs index a699883..7fbbbd4 100644 --- a/src/request.rs +++ b/src/request.rs @@ -272,6 +272,8 @@ pub enum Method { CONNECT, /// TRACE TRACE, + /// REPORT + REPORT, } impl Method { @@ -287,6 +289,7 @@ impl Method { Method::OPTIONS => "OPTIONS", Method::CONNECT => "CONNECT", Method::TRACE => "TRACE", + Method::REPORT => "REPORT", } } }