What Is subtle‑tls and How It Powers WebAssembly TLS in webtor‑rs

subtle‑tls is a lightweight TLS 1.3 client library for WebAssembly that delegates cryptographic operations to the browser's SubtleCrypto API, enabling secure HTTPS and Snowflake transport in webtor‑rs without native dependencies.

The igor53627/webtor‑rs repository implements a fully functional Tor client in Rust that compiles to WebAssembly for browser environments. Because standard TLS libraries like ring cannot target WASM, the project includes subtle‑tls as a specialized alternative that bridges the Tor protocol stack with browser-native cryptography.

What Is subtle‑tls?

subtle‑tls is a TLS 1.3 implementation (with automatic TLS 1.2 fallback) designed specifically for wasm32 targets. Unlike conventional Rust TLS crates that depend on native cryptographic libraries, it routes all crypto operations through the browser's SubtleCrypto API using js‑sys and web‑sys bindings.

Key capabilities include:

  • TLS 1.3 primary with TLS 1.2 fallback: The client negotiates TLS 1.3 automatically and falls back to TLS 1.2 only when the modern handshake fails.
  • Hybrid key exchange: Uses P‑256 ECDH via SubtleCrypto and X25519 via a pure‑Rust implementation.
  • Modern cipher suites: AES‑128‑GCM and AES‑256‑GCM through SubtleCrypto, with ChaCha20‑Poly1305 available as a pure‑Rust fallback.
  • Embedded certificate validation: Ships with a minimal Let’s Encrypt root store (~3.5 KB) including hostname verification.
  • Async stream interface: Exposes TlsStream<S> that implements AsyncRead and AsyncWrite for integration with async Rust ecosystems.

All code resides under #[cfg(target_arch = "wasm32")] guards, ensuring it compiles only into WebAssembly bundles.

How webtor‑rs Uses subtle‑tls

In the webtor‑rs codebase, subtle‑tls serves as the exclusive TLS implementation when targeting WASM. It secures two critical communication paths:

HTTPS Requests Over Tor Circuits

When TorClient::request in webtor/src/http.rs detects an https:// URL, it constructs a TlsConfig specifying TLS 1.3 as the primary version with TLS 1.2 as fallback. The code invokes TlsConnector::connect to wrap the raw Tor circuit stream, producing a TlsStream that encrypts the HTTP traffic.

let config = TlsConfig {
    skip_verification: false,
    alpn_protocols: vec!["http/1.1".to_string()],
    version: TlsVersion::Tls13,
};
let connector = TlsConnector::with_config(config);

match connector.connect(stream, &host).await {
    Ok(mut tls_stream) => {
        execute_http_request_wasm(&mut tls_stream, &request_bytes).await?;
    }
    Err(tls13_err) => {
        // Fallback to TLS 1.2
        let config_tls12 = TlsConfig {
            skip_verification: false,
            alpn_protocols: vec!["http/1.1".to_string()],
            version: TlsVersion::Tls12,
        };
        let connector_tls12 = TlsConnector::with_config(config_tls12);
        let mut tls_stream = connector_tls12.connect_tls12(stream_tls12, &host).await?;
        execute_http_request_wasm_tls12(&mut tls_stream, &request_bytes).await?;
    }
}

The execute_http_request_wasm helper manages async read/write operations on the encrypted stream. See the TLS implementation block in webtor/src/http.rs (lines 199–236).

Snowflake WebSocket Transport

For the Snowflake bridge—available only in browser builds—the transport stack builds a pipeline from WebSocket through Turbo, KCP, and SMUX layers. In webtor/src/snowflake_ws.rs, the code applies a TlsConnector with skip_verification = true to the SMUX stream because Snowflake uses self‑signed certificates.

let tls_config = TlsConfig {
    skip_verification: true, // Snowflake uses self-signed certs
    alpn_protocols: vec![],
    ..Default::default()
};
let connector = TlsConnector::with_config(tls_config);
let tls_stream = connector.connect(smux, "www.example.com").await?;

This yields a TlsStream that encrypts Snowflake traffic before it enters the Tor network. The TLS wrapping occurs at lines 140–152 of webtor/src/snowflake_ws.rs.

Why SubtleCrypto Instead of Native TLS?

Standard Rust TLS libraries rely on ring or other native cryptographic implementations that cannot compile to WebAssembly. Additionally, browsers lack support for std::time::Instant and block direct access to OS‑level cryptographic facilities.

subtle‑tls solves these constraints by delegating to the browser's SubtleCrypto API. This approach provides hardware‑accelerated, standards‑compliant cryptography across all modern browsers without requiring WASM-bindgen shims for system libraries.

Key Source Files

Understanding the integration requires examining these specific locations in the igor53627/webtor‑rs repository:

  • subtle‑tls/src/lib.rs: Defines the public API including TlsConnector, TlsConfig, and TlsStream.
  • webtor/src/http.rs: Contains the HTTPS request logic that selects subtle‑tls for WASM targets (lines 199–236).
  • webtor/src/snowflake_ws.rs: Implements the Snowflake bridge stack with TLS wrapping after SMUX (lines 140–152).
  • subtle‑tls/README.md: Documents the architecture, testing procedures, and design rationale.

Summary

  • subtle‑tls is a WebAssembly‑specific TLS 1.3/1.2 client that replaces native crypto with browser SubtleCrypto APIs.
  • It enables HTTPS requests in webtor/src/http.rs by wrapping Tor circuit streams with TlsConnector::connect and providing automatic version fallback.
  • It secures the Snowflake bridge in webtor/src/snowflake_ws.rs by applying TLS to SMUX streams with certificate verification disabled for self‑signed Snowflake certs.
  • The library is conditionally compiled under #[cfg(target_arch = "wasm32")] and integrates with the async ecosystem via AsyncRead/AsyncWrite traits.

Frequently Asked Questions

What makes subtle‑tls different from rustls or native‑tls?

subtle‑tls is built exclusively for WebAssembly environments. While rustls depends on ring for cryptographic primitives and native‑tls binds to OpenSSL or Schannel, subtle‑tls delegates all operations to the browser's SubtleCrypto API. This eliminates native dependencies that cannot compile to WASM while providing hardware acceleration through the browser.

Does subtle‑tls support modern TLS 1.3 features?

Yes. subtle‑tls prioritizes TLS 1.3 negotiation and automatically falls back to TLS 1.2 only when the remote server fails the modern handshake. It supports TLS 1.3 cipher suites including AES‑128‑GCM and AES‑256‑GCM via SubtleCrypto, plus X25519 and ChaCha20‑Poly1305 through pure‑Rust fallbacks.

Why does the Snowflake transport skip certificate verification?

The Snowflake bridge in webtor/src/snowflake_ws.rs uses skip_verification: true because Snowflake servers employ self‑signed certificates that would fail standard validation. This is a deliberate security trade‑off for the specific Snowflake transport path, while standard HTTPS requests in webtor/src/http.rs enforce full certificate validation against the embedded Let’s Encrypt root store.

Can subtle‑tls be used outside of webtor‑rs?

Yes. Although designed for the webtor‑rs project, subtle‑tls is a standalone crate within the repository that can be imported into any Rust project targeting wasm32. It provides a standard TlsConnector interface compatible with any stream implementing AsyncRead and AsyncWrite, making it suitable for general‑purpose WASM TLS needs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →