Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -61,3 +61,4 @@ defmt = [
"nourl/defmt",
"heapless/defmt",
]
mbedtls-rs = ["dep:mbedtls-rs"]
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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`.

Expand All @@ -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`
Expand All @@ -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"] }
```
<!-- TODO: Update this when esp-mbedtls switches to the unified hal -->
<!-- TODO: Update this when mbedtls-rs switches to the unified hal -->

#### Example
```rust,ignore
Expand Down
81 changes: 46 additions & 35 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<TlsConfig<'a>>,
}

/// 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.
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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")]
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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!(),
};
}
Expand All @@ -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!(),
}
}
Expand All @@ -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!(),
}
}
Expand All @@ -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!(),
}
}
Expand Down
14 changes: 7 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -83,12 +83,12 @@ impl From<embedded_tls::TlsError> 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<esp_mbedtls::TlsError> for Error {
fn from(e: esp_mbedtls::TlsError) -> Error {
#[cfg(feature = "mbedtls-rs")]
impl From<mbedtls_rs::TlsError> for Error {
fn from(e: mbedtls_rs::TlsError) -> Error {
Error::Tls(e)
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,8 @@ pub enum Method {
CONNECT,
/// TRACE
TRACE,
/// REPORT
REPORT,
}

impl Method {
Expand All @@ -287,6 +289,7 @@ impl Method {
Method::OPTIONS => "OPTIONS",
Method::CONNECT => "CONNECT",
Method::TRACE => "TRACE",
Method::REPORT => "REPORT",
}
}
}
Expand Down
Loading