# Configuring TLS and Key Exchange in iroh Connections

> Learn to configure TLS and key exchange in iroh connections using rustls. Customize crypto providers and explore post-quantum options for secure peer-to-peer and external service communication. Maximize security with iroh.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-06-18

---

**iroh uses rustls as its TLS/QUIC stack, exposing configuration through `TlsConfig` for peer-to-peer connections and `CaTlsConfig` for external services, with customizable crypto providers that control key-exchange algorithms including post-quantum options.**

iroh is a peer-to-peer networking library built on QUIC that relies on rustls for transport layer security. When configuring TLS and key exchange in iroh connections, you work with two distinct configuration layers: one for end-to-end encrypted peer connections using raw public keys, and another for verifying external services like relays and DNS. The library supports multiple crypto providers including **ring** and **aws-lc-rs**, allowing precise control over cipher suites and key-exchange groups.

## Understanding iroh's TLS Architecture

iroh separates TLS concerns into two primary structures based on connection type.

### CaTlsConfig for External Services

The `CaTlsConfig` structure in [`iroh-relay/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs) handles certificate verification for non-iroh connections such as relays, PKARR, and DNS-over-HTTPS. This configuration determines how the client validates server certificates when connecting to infrastructure services. By default, iroh uses `EmbeddedWebPki` which bundles Mozilla's root certificate store, but you can switch to system roots or custom verification logic.

### TlsConfig for End-to-End Connections

For direct peer-to-peer connections, iroh uses `TlsConfig` located in [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs). This structure holds the **crypto provider** and **raw-public-key** verifier used to authenticate iroh nodes. Unlike traditional TLS with certificate authorities, iroh uses raw public keys for peer authentication, implemented through the `ResolveRawPublicKeyCert` resolver and custom verifiers (`ServerCertificateVerifier` / `ClientCertificateVerifier`).

The `make_client_config` and `make_server_config` methods in [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs) build rustls configurations that use `builder_with_provider` to attach your selected crypto provider, along with protocol versions and the custom certificate resolver.

### Crypto Provider and Key-Exchange Groups

The `crypto_provider` supplies all cryptographic primitives including cipher suites, HKDF implementations, and key-exchange groups. As defined in [`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs), iroh can compile with either the **ring** provider (`tls-ring` feature) or **aws-lc-rs** (`tls-aws-lc-rs` feature). The provider's `kx_groups` field determines which algorithms are offered during the TLS handshake.

## Selecting Key-Exchange Algorithms

Key-exchange groups are configured by mutating the crypto provider before passing it to the endpoint builder.

### Default Crypto Providers

By default, the `ring` provider offers classical groups like `X25519` and `SECP256R1`. The `aws-lc-rs` provider includes these plus the post-quantum group **X25519MLKEM768**. You select the provider through builder presets or manual configuration in [`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs).

### Preferring Post-Quantum Key Exchange

To prioritize post-quantum security while maintaining compatibility, reorder the `kx_groups` vector to place `X25519MLKEM768` first, followed by classical fallbacks. This configuration, demonstrated in [`iroh/examples/prefer-pq-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/prefer-pq-key-exchange.rs), allows connections with PQ-capable peers while falling back to classical algorithms otherwise.

```rust
let mut provider = aws_lc_rs::default_provider();
provider.kx_groups = vec![
    kx_group::X25519MLKEM768, // PQ first
    kx_group::X25519,
    kx_group::SECP256R1,
];
let provider = Arc::new(provider);

```

### Enforcing Post-Quantum Only

For environments requiring strict post-quantum security, replace the entire `kx_groups` list with only the PQ group. As shown in [`iroh/examples/pq-only-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/pq-only-key-exchange.rs), this causes handshakes to fail if the remote peer does not support `X25519MLKEM768`.

```rust
fn pq_only_provider() -> Arc<rustls::crypto::CryptoProvider> {
    let mut p = aws_lc_rs::default_provider();
    p.kx_groups = vec![aws_lc_rs::kx_group::X25519MLKEM768];
    Arc::new(p)
}

```

## Configuring TLS for External Services

When connecting to relays or DNS services, use `CaTlsConfig` to control certificate verification. The `client_config` method in [`iroh-relay/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs) creates a rustls `ClientConfig` using your specified crypto provider and verification mode.

```rust
use iroh_relay::tls::{CaTlsConfig, default_provider};

let tls_cfg = CaTlsConfig::system(); // Use OS root store
let client_cfg = tls_cfg.client_config(default_provider())?;

```

Available modes include `EmbeddedWebPki` (Mozilla roots), `System` (OS store), `CustomRoots` (explicit certificates), and `InsecureSkipVerify` (testing only).

## Complete Implementation Examples

### Preferred Post-Quantum with Fallback

This example from [`iroh/examples/prefer-pq-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/prefer-pq-key-exchange.rs) builds an endpoint with PQ-preferred key exchange:

```rust
use std::sync::Arc;
use iroh::endpoint::{Endpoint, presets::N0};
use rustls::crypto::aws_lc_rs::{self, kx_group};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut provider = aws_lc_rs::default_provider();
    provider.kx_groups = vec![
        kx_group::X25519MLKEM768,
        kx_group::X25519,
        kx_group::SECP256R1,
    ];
    let provider = Arc::new(provider);

    let endpoint = Endpoint::builder(N0)
        .crypto_provider(provider)
        .alpns(vec![b"example".to_vec()])
        .bind()
        .await?;

    Ok(())
}

```

### Post-Quantum Only Mode

To enforce strict PQ requirements as in [`iroh/examples/pq-only-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/pq-only-key-exchange.rs):

```rust
use std::sync::Arc;
use iroh::{RelayMode, endpoint::{Endpoint, presets::Empty}};
use rustls::crypto::aws_lc_rs;

fn pq_only_provider() -> Arc<rustls::crypto::CryptoProvider> {
    let mut p = aws_lc_rs::default_provider();
    p.kx_groups = vec![aws_lc_rs::kx_group::X25519MLKEM768];
    Arc::new(p)
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let provider = pq_only_provider();
    let server = Endpoint::builder(Empty)
        .crypto_provider(provider)
        .relay_mode(RelayMode::Disabled)
        .bind()
        .await?;
    Ok(())
}

```

### Custom Root Certificates

For air-gapped or enterprise environments requiring specific trust anchors:

```rust
use iroh_relay::tls::CaTlsConfig;

let tls_cfg = CaTlsConfig::custom_roots(vec![]); // Load your root certs

```

## Summary

- **iroh uses rustls** for all TLS operations, separating external service verification (`CaTlsConfig` in [`iroh-relay/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs)) from peer-to-peer authentication (`TlsConfig` in [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs)).
- **Crypto providers** control key-exchange algorithms through the `kx_groups` field, with `aws-lc-rs` offering post-quantum `X25519MLKEM768`.
- **Configure key exchange** by mutating the provider's `kx_groups` vector before passing it to `Endpoint::builder()`.
- **External services** use `CaTlsConfig` with options for Mozilla roots, system roots, or custom verification.
- **Source files** implementing this logic include [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs), [`iroh-relay/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs), and [`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs).

## Frequently Asked Questions

### What crypto providers does iroh support?

iroh supports two rustls crypto providers: **ring** (via the `tls-ring` feature) and **aws-lc-rs** (via `tls-aws-lc-rs`). The `ring` provider offers classical key-exchange groups like X25519 and SECP256R1, while `aws-lc-rs` adds support for the post-quantum group X25519MLKEM768. You select the provider through endpoint builder presets or by manually constructing the crypto provider in [`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs).

### How do I enable post-quantum key exchange?

Enable post-quantum key exchange by using the `aws-lc-rs` provider and including `X25519MLKEM768` in the `kx_groups` vector. According to [`iroh/examples/prefer-pq-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/prefer-pq-key-exchange.rs), place `kx_group::X25519MLKEM768` first in the list to prefer PQ while keeping classical fallbacks, or use [`iroh/examples/pq-only-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/pq-only-key-exchange.rs) to enforce PQ-only mode by making it the only group in the list.

### What's the difference between TlsConfig and CaTlsConfig?

`TlsConfig` in [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs) handles rustls configuration for direct iroh peer-to-peer connections using raw public keys for authentication. `CaTlsConfig` in [`iroh-relay/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/tls.rs) manages certificate verification for external services like relays, PKARR, and DNS-over-HTTPS using traditional certificate authorities. The former uses `make_client_config` and `make_server_config` with custom raw-public-key verifiers, while the latter provides `client_config` for standard TLS verification.

### Can I use system certificates for relay connections?

Yes, configure `CaTlsConfig::system()` to use the operating system's root certificate store instead of the embedded Mozilla roots. Call `client_config()` on the resulting `CaTlsConfig` instance with your crypto provider to create a rustls `ClientConfig` suitable for connecting to iroh relays and other external services over standard TLS.