No description
Find a file
damfle 0661dac73a
All checks were successful
CI / Lint (push) Successful in 48s
CI / Test (push) Successful in 25s
CI / Build (push) Successful in 24s
CI / Create Tag (push) Successful in 6s
min: move estd
2026-02-11 11:35:59 +01:00
.forgejo/workflows min: try to pin lint to another version 2026-02-10 17:36:39 +01:00
src min: format 2026-02-10 17:52:11 +01:00
tests min: format 2026-02-10 17:52:11 +01:00
.gitignore init: initial commit 2026-02-07 07:16:02 +01:00
build.rs init: initial commit 2026-02-07 07:16:02 +01:00
Cargo.toml min: move estd 2026-02-11 11:35:59 +01:00
LICENSE init: initial commit 2026-02-07 07:16:02 +01:00
README.md min: trigger build 2026-02-10 17:29:54 +01:00

estd-tls

A unified TLS/DTLS wrapper for Rust that supports OpenSSL, LibreSSL, and WolfSSL with a single idiomatic API. Using estd philosophies and patterns.

Features

  • Unified API: Same Rust API regardless of underlying SSL library
  • Feature-Based Selection: Choose your SSL backend at compile time
    • openssl (default)
    • libressl
    • wolfssl
  • TCP TLS & UDP DTLS: Encrypted streams over both transports
  • Mutual TLS (mTLS): Client certificate authentication
  • Crypto Primitives: Sign, verify, encrypt, decrypt
  • Key Generation: RSA and EC key pairs
  • Certificate Generation: Selfsigned certs, CSRs, CA signing
  • Async Support: Every blocking operation has an async counterpart via estd::sync::go
  • Built on estd: Uses estd patterns, logging, env, fs, strings, sync
  • No External Dependencies: Only estd and system SSL libraries

Quick Start

use estd_tls::{Connector, ConnectorConfig};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let connector = Connector::new();
    let mut stream = connector.connect("example.com:443")?;

    use std::io::{Read, Write};
    stream.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")?;

    let mut response = Vec::new();
    stream.read_to_end(&mut response)?;

    println!("Version: {}", stream.version()?);
    println!("Cipher: {}", stream.cipher()?);
    Ok(())
}

Modules

tcp — TLS over TCP

use estd_tls::tcp::Connector;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let connector = Connector::new();              // or ::with_config(config)
    let stream = connector.connect("host:443")?;   // -> TlsStream
    Ok(())
}

udp — DTLS over UDP

use estd_tls::udp::Connector;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let connector = Connector::new();
    let stream = connector.connect("host:4433")?;  // -> DtlsStream
    stream.send(b"hello")?;
    Ok(())
}

mtls — Mutual TLS

use estd_tls::{ConnectorConfig, mtls};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = ConnectorConfig::new()
        .cert_path(Some("/path/to/client.pem".into()))
        .key_path(Some("/path/to/client-key.pem".into()));

    let connector = mtls::Connector::new(config)?;
    let stream = connector.connect("host:443")?;
    Ok(())
}

crypto — Sign / Verify / Encrypt / Decrypt

use estd_tls::crypto::{self, SignatureAlgorithm, CipherAlgorithm};

let private_key = b"stub-private-key";
let public_key  = b"stub-public-key";
let data        = b"hello world";

// Sign & verify
let sig = crypto::sign(private_key, data, SignatureAlgorithm::RsaSha256).unwrap();
let ok  = crypto::verify(public_key, data, &sig, SignatureAlgorithm::RsaSha256).unwrap();

// Encrypt & decrypt
let key = [0u8; 32];
let ct = crypto::encrypt(&key, b"secret", CipherAlgorithm::Aes256Gcm).unwrap();
let pt = crypto::decrypt(&key, &ct, CipherAlgorithm::Aes256Gcm).unwrap();

key — Key Generation

use estd_tls::key::{self, KeyType, EcCurve};

let rsa = key::generate(&KeyType::Rsa(4096)).unwrap();
let ec  = key::generate(&KeyType::Ec(EcCurve::P256)).unwrap();
let pub_key = key::public_key_from(&rsa.private_key).unwrap();

cert — Certificate Generation

use estd_tls::cert::{self, Subject};
use estd_tls::key::{self as keygen, KeyType};

let kp = keygen::generate(&KeyType::Rsa(2048)).unwrap();
let subject = Subject::new("example.com").organisation("Acme").country("FR");

let self_cert = cert::self_signed(&kp.private_key, &subject, 365).unwrap();
let csr       = cert::create_csr(&kp.private_key, &subject).unwrap();
let signed    = cert::sign_csr(&kp.private_key, &self_cert, &csr, 365).unwrap();

async_tls — Async Wrappers

Every blocking operation has an async counterpart that runs on a background goroutine (via estd::sync::go):

use estd_tls::{async_tls, ConnectorConfig};
use estd_tls::key::{KeyType, EcCurve};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let handle = async_tls::connect_tcp(ConnectorConfig::new(), "host:443".into());
    let stream = handle.join().unwrap()?;

    let handle = async_tls::generate_key(KeyType::Ec(EcCurve::P384));
    let kp = handle.join().unwrap()?;
    Ok(())
}

config — ConnectorConfig

Builder pattern, implements estd::patterns::Builder<tcp::Connector>:

use estd_tls::ConnectorConfig;

let config = ConnectorConfig::new()
    .verify(true)
    .ca_path(Some("/path/to/ca.pem".to_string()))
    .server_name(Some("example.com".to_string()))
    .alpn(vec!["h2".into(), "http/1.1".into()]);

Or from environment variables (TLS_VERIFY, TLS_CA_PATH, TLS_CERT_PATH, TLS_KEY_PATH):

use estd_tls::ConnectorConfig;

let config = ConnectorConfig::from_env();

License

ISC