Skip to main content

Crate GatoPSKTLS

Crate GatoPSKTLS 

Source
Expand description

§GatoPSKTLS

Crates.io Docs.rs License

GatoPSKTLS is a no_std, no-allocator TLS 1.3 implementation for embedded Rust — focused on the PSK (Pre-Shared Key) handshake on both ends of the connection. It is a fork of drogue-iot/embedded-tls (Apache-2.0) extended with a complete server-mode PSK_KE handshake driver.

The motivating use case is a TLS 1.3 MQTT broker running on a microcontroller (e.g. ESP32-C3, RP2040) talking PSK to firmware peers — the original embedded-tls provides an excellent client; GatoPSKTLS adds the matching server.

§Features

  • TLS 1.3 PSK_KE serverprocess_client_hello + process_client_finished bytes-in/bytes-out functions, plus TlsServerSession and TlsServerConfig helpers. AEAD application data via encrypt_application_data / decrypt_application_data.
  • TLS 1.3 client (inherited from upstream) — TlsConnection, TlsConfig, async + blocking variants, optional X.509 verification.
  • External PSK with the binder verified against the partial-transcript hash exactly as RFC 8446 §4.2.11.2 prescribes (constant-time HMAC compare).
  • no_std friendly. Only one mandatory cipher suite is needed: TLS_AES_128_GCM_SHA256.
  • Validated against openssl s_client 3.0.13 and mbedtls 3.6.5 ssl_client2 on host, and on real ESP32-C3 hardware via mbedtls.

§Status

Server-mode is PSK_KE only (no DHE). Suitable when forward secrecy is not required at the session level (e.g. provisioned external PSK). PSK_DHE_KE is on the roadmap.

The current scope:

AreaStatus
PSK_KE handshake
External PSK identity
Single cipher suite (TLS_AES_128_GCM_SHA256)
Application data AEAD
PSK_DHE_KE (forward secrecy)❌ roadmap
HelloRetryRequest
Client certificates / mTLS❌ (server side)
Resumption tickets / 0-RTT
Post-handshake KeyUpdate
Graceful close_notify alert❌ roadmap

These restrictions reflect the embedded MQTT-broker target. The client side inherits the broader feature set from upstream.

§Quick start — server

use GatoPSKTLS::server::{TlsServerConfig, TlsServerSession};

// 1. Configure: external PSK identity + secret, plus a fresh server random
//    drawn from a CSPRNG.
let mut server_random = [0u8; 32];
rng.fill_bytes(&mut server_random);
let config = TlsServerConfig {
    psk: (b"my-device-id", &shared_psk),
    server_random,
};

// 2. Read the client's first record from the transport (5-byte TLSPlaintext
//    record header + body).
let ch_record = read_one_record(&mut socket).await?;
assert_eq!(ch_record[0], 0x16); // ContentType::Handshake
let ch_handshake = &ch_record[5..];

// 3. Process the ClientHello, build the server's first flight (ServerHello
//    + EncryptedExtensions + Finished — the latter two AEAD-wrapped under the
//    server handshake traffic secret).
let mut session = TlsServerSession::new();
let mut out = [0u8; 1024];
let flight = session.process_client_hello(ch_handshake, &config, &mut out)?;
socket.write_all(flight).await?;

// 4. Drain any ChangeCipherSpec dummies, then process the encrypted client
//    Finished record.
let cf_record = drain_ccs_then_read(&mut socket).await?;
session.process_client_finished(&cf_record)?;

// 5. Application data exchange.
let mut tx = [0u8; 1024];
let echo = session.encrypt_app_data(b"hello", &mut tx)?;
socket.write_all(echo).await?;

A fully wired async TCP example using tokio lives in the companion picobrokerTLS workspace under tools/host_tls_psk_server/ — that repo also hosts the ESP32-C3 firmware that integrates this crate with an MQTT broker.

§Cargo features

The lib defaults to ["std", "log", "tokio"]. For embedded targets disable defaults:

GatoPSKTLS = { version = "0.1", default-features = false }
FeatureEffect
stdEnable embedded-io[-async]/std. Default on.
logRoute internal warnings/traces through the log crate.
defmtRoute internal warnings/traces through defmt.
tokioEnable embedded-io-adapters/tokio-1 for desktop testing.
webpkiClient-side WebPKI cert verification (uses rustls-webpki).
rustpkiClient-side cert handling via the der crate (no rustls-webpki).
rsaClient cert + RSA signing.
ed25519Client cert + Ed25519 signing.
p384Client cert + ECDSA-P-384 signing.

§Interop matrix

The server-mode handshake has been verified against:

PeerConfigurationWhere
openssl s_client 3.0.13-tls1_3 -psk -allow_no_dhe_kexWSL Ubuntu
mbedtls ssl_client2 3.6.5tls13_kex_modes=pskWindows
mbedtls ssl_client2 3.6.5tls13_kex_modes=pskESP32-C3 over Wi-Fi

§Tests

Lib-level tests (host; on Windows MSVC the openssl dev-dependency requires WSL to build):

cargo test --lib
# 26 tests pass — includes the full PSK_KE handshake self-loop, the
# bytes-in/bytes-out round-trip, and per-primitive RFC 8448 vectors.

§License

Apache License 2.0 — see LICENSE and NOTICE.

This crate is a fork of drogue-iot/embedded-tls; the upstream contributors’ copyright is preserved. Modifications are documented in CHANGELOG.md.

§Example

use embedded_tls::*;
use embedded_io_adapters::tokio_1::FromTokio;
use rand::rngs::OsRng;
use tokio::net::TcpStream;

#[tokio::main]
async fn main() {
    let stream = TcpStream::connect("google.com:443")
        .await
        .expect("error creating TCP connection");

    println!("TCP connection opened");
    let mut read_record_buffer = [0; 16384];
    let mut write_record_buffer = [0; 16384];
    let config = TlsConfig::new().with_server_name("google.com").enable_rsa_signatures();
    let mut tls = TlsConnection::new(
        FromTokio::new(stream),
        &mut read_record_buffer,
        &mut write_record_buffer,
    );

    // Allows disabling cert verification, in case you are using PSK and don't need it, or are just testing.
    // otherwise, use embedded_tls::webpki::CertVerifier, which only works on std for now.
    tls.open(TlsContext::new(
        &config,
        UnsecureProvider::new::<Aes128GcmSha256>(OsRng),
    ))
    .await
    .expect("error establishing TLS connection");

    println!("TLS session opened");
}

Re-exports§

pub use flush_policy::*;

Modules§

alert
blocking
flush_policy
Flush policy for TLS sockets.
read_buffer
server
Server-mode TLS 1.3 (PSK_KE) handshake driver — bytes-in/bytes-out.

Structs§

Aes128GcmSha256
Aes256GcmSha384
CertificateRef
CertificateVerify
CertificateVerifyRef
NoClock
NoSign
NoVerify
TlsConfig
TlsConnection
Type representing an async TLS connection. An instance of this type can be used to establish a TLS connection, write and read encrypted data over this connection, and closing to free up the underlying resources.
TlsContext
TlsReader
TlsWriter
UnsecureProvider

Enums§

Certificate
CertificateEntryRef
MaxFragmentLength
Maximum plaintext fragment length
SignatureScheme
TlsError

Constants§

TLS_RECORD_OVERHEAD

Traits§

CryptoProvider
CryptoRng
A marker trait used to indicate that an RngCore or BlockRngCore implementation is supposed to be cryptographically secure.
CryptoRngCore
An extension trait that is automatically implemented for any type implementing RngCore and CryptoRng.
TlsCipherSuite
Represents a TLS 1.3 cipher suite
TlsClock
TlsVerifier
A TLS 1.3 verifier.

Type Aliases§

Sha256
SHA-256 hasher.
Sha384
SHA-384 hasher.