# How webtor-rs Handles TLS Connections in the Browser: Wasm-Compatible Encryption

> Discover how webtor-rs manages TLS connections in the browser, leveraging native browser capabilities and WASM-compatible encryption for secure data transfer.

- Repository: [igor53627/webtor-rs](https://github.com/igor53627/webtor-rs)
- Tags: internals
- Published: 2026-03-04

---

**webtor-rs delegates TLS encryption to the browser's native networking stack via the subtle-tls crate, using conditional compilation to exclude native TLS implementations when targeting wasm32.**

webtor-rs is a Rust implementation of Tor networking designed to compile to WebAssembly for browser environments. When running in the browser, the library cannot rely on native TLS libraries due to WebAssembly sandbox constraints, so it leverages the **subtle-tls** crate to perform TLS 1.2 and 1.3 handshakes through the Web Crypto API.

## Why Native TLS Fails in WebAssembly

Standard Rust TLS implementations depend on system-level cryptography libraries and `std::time::Instant`, which are unavailable in browser contexts. The webtor-rs codebase explicitly excludes native TLS code from WebAssembly builds using conditional compilation attributes.

In [`webtor/src/tls.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/tls.rs), the entire native TLS implementation is gated behind `#[cfg(not(target_arch = "wasm32"))]`. This ensures that when compiling for `wasm32-unknown-unknown`, the compiler skips this file entirely, preventing linker errors and runtime incompatibilities.

## The Wasm-Specific TLS Architecture

### Conditional Compilation Strategy

The codebase uses Rust's conditional compilation to maintain dual TLS implementations. For native targets, [`webtor/src/tls.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/tls.rs) provides platform-specific encryption. For WebAssembly targets, the library routes all TLS operations through [`webtor/src/http.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/http.rs), which implements browser-compatible encryption layers.

### The WasmTlsStream Abstraction

At the heart of the browser TLS implementation lies the `WasmTlsStream` trait, defined in [`webtor/src/http.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/http.rs) at lines 91-98:

```rust
trait WasmTlsStream {
    async fn tls_write(&mut self, buf: &[u8]) -> std::io::Result<usize>;
    async fn tls_flush(&mut self) -> std::io::Result<()>;
    async fn tls_read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>;
}

```

This trait abstracts the asynchronous read, write, and flush operations required by TLS streams, providing a uniform interface regardless of the underlying TLS version.

### subtle-tls Integration

The same file provides concrete implementations of `WasmTlsStream` for the subtle-tls crate's stream types. For TLS 1.3 connections, lines 102-113 implement the trait for `subtle_tls::TlsStream<S>`:

```rust
// Implementation for subtle_tls::TlsStream<S> (TLS 1.3)
impl<S> WasmTlsStream for subtle_tls::TlsStream<S>
where
    S: AsyncRead + AsyncWrite + Unpin,
{
    async fn tls_write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        self.write(buf).await
    }
    
    async fn tls_flush(&mut self) -> std::io::Result<()> {
        self.flush().await
    }
    
    async fn tls_read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        self.read(buf).await
    }
}

```

For legacy TLS 1.2 support, lines 118-130 provide an identical implementation for `subtle_tls::TlsStream12<S>`. Both implementations forward directly to the underlying async methods of the subtle-tls stream, which handles the cryptographic operations using the browser's Web Crypto API.

## Executing HTTPS Requests in the Browser

### The Request Flow

When an HTTPS request originates from a WebAssembly build, the `HttpClient::request` method creates a TLS connection using `subtle_tls::TlsConnector::connect`. The resulting stream passes to `execute_http_request_wasm`, defined at lines 336-378 in [`webtor/src/http.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/http.rs):

```rust
async fn execute_http_request_wasm<S: WasmTlsStream>(
    stream: &mut S,
    request: &Request,
) -> Result<Response, Error> {
    // Write request headers and body
    let data = format_request(request);
    stream.tls_write(data.as_bytes()).await?;
    stream.tls_flush().await?;
    
    // Read response with size limits
    let mut buf = vec![0u8; 8192];
    let n = stream.tls_read(&mut buf).await?;
    parse_response(&buf[..n])
}

```

For TLS 1.2 connections, the code path diverges at line 244 to `execute_http_request_wasm_tls12`, maintaining separate handling for legacy protocol versions while using the same `WasmTlsStream` abstraction.

### Integration with Browser Primitives

Under the hood, subtle-tls establishes the TLS session over browser-native transports. The crate performs the handshake using the Web Crypto API for cryptographic operations, while the actual network traffic flows through the browser's `fetch` or `WebSocket` implementations. This approach avoids the `std::time::Instant` limitation prevalent in WebAssembly environments.

## Practical Implementation Examples

Creating an HTTPS request from a wasm-compiled webtor client requires no special syntax—the library handles TLS transparently:

```rust
let client = HttpClient::new(/* Tor config */).await?;
let response = client.get("https://example.com").await?;
println!("Status: {}", response.status);
println!("Body ({} bytes)", response.body.len());

```

The underlying process executes these steps:

1. Resolve the host and open a TCP-like connection via browser `WebSocket` or `fetch` primitives
2. Call `subtle_tls::TlsConnector::connect` to establish a TLS 1.3 (or 1.2) session using browser cryptography
3. Wrap the resulting `subtle_tls::TlsStream` as a `WasmTlsStream`
4. Use `execute_http_request_wasm` to transmit the HTTP request and read the response

For advanced use cases requiring manual stream control:

```rust
let connector = subtle_tls::TlsConnector::new().await?;
let mut tls_stream = connector
    .connect("example.com".parse().unwrap(), WebSocket::new("wss://example.com").await?)
    .await?;

tls_stream.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n").await?;
tls_stream.flush().await?;
let mut buf = vec![0u8; 8192];
let n = tls_stream.read(&mut buf).await?;
println!("{}", std::str::from_utf8(&buf[..n])?);

```

This example mirrors the internal steps of `execute_http_request_wasm` while exposing the raw `subtle_tls` API for custom protocols.

## Key Source Files and Their Roles

Understanding the browser TLS implementation requires familiarity with these specific files in the igor53627/webtor-rs repository:

- **[`webtor/src/http.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/http.rs)** – Defines the `WasmTlsStream` trait, implements it for subtle-tls types, and drives TLS-enabled HTTP requests in wasm environments
- **[`webtor/src/tls.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/tls.rs)** – Contains the native-only TLS implementation, excluded from browser builds via conditional compilation
- **[`subtle-tls/src/lib.rs`](https://github.com/igor53627/webtor-rs/blob/main/subtle-tls/src/lib.rs)** – Core crate providing `TlsConnector`, `TlsStream`, and Web-Crypto-based TLS for WebAssembly
- **[`subtle-tls/tests/tls12_browser_tests.rs`](https://github.com/igor53627/webtor-rs/blob/main/subtle-tls/tests/tls12_browser_tests.rs)** – Test suite confirming subtle-tls functionality in browser environments
- **[`webtor/src/snowflake_ws.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/snowflake_ws.rs)** – Demonstrates additional subtle-tls usage for WebSocket-based Snowflake bridges

## Summary

- **webtor-rs uses conditional compilation** to exclude native TLS code in wasm32 targets, routing browser builds through [`webtor/src/http.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/http.rs) instead of [`webtor/src/tls.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/tls.rs)
- **The `WasmTlsStream` trait** abstracts TLS operations for browser environments, with implementations for both TLS 1.3 and TLS 1.2 via the subtle-tls crate
- **HTTPS requests execute through `execute_http_request_wasm`** and `execute_http_request_wasm_tls12`, which handle request serialization and response parsing over encrypted streams
- **Cryptographic operations rely on the Web Crypto API** rather than native system libraries, ensuring compatibility with WebAssembly sandbox constraints
- **The subtle-tls crate performs handshakes** while browser networking primitives (`fetch`/`WebSocket`) handle the underlying transport layer

## Frequently Asked Questions

### What prevents webtor-rs from using rustls or native-tls in the browser?

WebAssembly running in browsers lacks access to system-level sockets and native threading primitives. Additionally, `std::time::Instant` is unavailable in wasm32-unknown-unknown targets, breaking most native TLS implementations. The webtor-rs codebase explicitly excludes these via `#[cfg(not(target_arch = "wasm32"))]` attributes in [`webtor/src/tls.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/tls.rs).

### How does subtle-tls differ from traditional Rust TLS libraries?

**subtle-tls** builds TLS 1.2 and 1.3 sessions on top of the browser's Web Crypto API rather than interfacing with operating system cryptographic libraries. It delegates network I/O to browser `fetch` and `WebSocket` APIs while maintaining the standard async read/write interface exposed by the `WasmTlsStream` trait in [`webtor/src/http.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/http.rs).

### Can webtor-rs negotiate both TLS 1.2 and 1.3 in browser environments?

Yes. The codebase provides separate implementations of `WasmTlsStream` for both `subtle_tls::TlsStream` (TLS 1.3) at lines 102-113 and `subtle_tls::TlsStream12` (TLS 1.2) at lines 118-130 of [`webtor/src/http.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/http.rs). The `HttpClient` automatically selects the appropriate version during the handshake process.

### Where does the actual TLS handshake occur when using webtor-rs in a browser?

The handshake occurs within the **subtle-tls** crate's `TlsConnector::connect` method, which utilizes the Web Crypto API for cryptographic operations. However, the underlying TCP-like connection and network transport are handled by the browser's native `fetch` or `WebSocket` implementation, as seen in [`webtor/src/snowflake_ws.rs`](https://github.com/igor53627/webtor-rs/blob/main/webtor/src/snowflake_ws.rs) and the HTTP client code paths.