# How to Configure Custom TLS Crypto Providers (ring vs aws-lc-rs) in iroh

> Learn to configure custom TLS crypto providers ring or aws-lc-rs in iroh. Easily switch between backends via Cargo.toml or runtime configuration for enhanced security and performance.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: tutorial
- Published: 2026-06-19

---

**Enable either the `tls-ring` or `tls-aws-lc-rs` feature in your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml) to automatically configure the cryptographic backend, or call `Builder::crypto_provider()` with an explicit `Arc<rustls::crypto::CryptoProvider>` to override the defaults at runtime.**

The `iroh` crate uses **rustls** for its TLS implementation and allows you to configure custom TLS crypto providers to meet specific compliance or performance requirements. You can configure TLS crypto providers in iroh through Cargo feature flags for automatic selection, or programmatically via the `Builder` API when you need explicit control over the cryptographic implementation.

## Understanding TLS Crypto Providers in iroh

`iroh` delegates all cryptographic operations to rustls via the `CryptoProvider` trait. The library ships with two optional, mutually exclusive providers that you can enable via feature flags:

| Feature flag | Provider | Crate |
|--------------|----------|-------|
| `tls-ring` | **ring** | `rustls-crypto-ring` |
| `tls-aws-lc-rs` | **aws-lc-rs** | `rustls-crypto-aws-lc-rs` |

Only one provider can be active at runtime. If you enable both features, the **`Minimal`** and **`N0`** presets automatically prefer `ring` over `aws-lc-rs` according to the logic defined in [`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs).

## Configure TLS Crypto Providers via Feature Flags

The simplest way to configure TLS crypto providers in iroh is through Cargo features. When you enable a feature, the built-in presets automatically initialize the corresponding provider.

The selection logic in [`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs) uses conditional compilation to set the provider:

```rust
#[cfg(feature = "tls-ring")]
{
    builder = builder.crypto_provider(
        Arc::new(rustls::crypto::ring::default_provider()));
}

#[cfg(all(feature = "tls-aws-lc-rs", not(feature = "tls-ring")))]
{
    builder = builder.crypto_provider(
        Arc::new(rustls::crypto::aws_lc_rs::default_provider()));
}

```

To use this automatic configuration, add one of the following to your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml):

```toml

# For the Ring provider

iroh = { version = "0.12", features = ["tls-ring"] }

# For the aws-lc-rs provider

iroh = { version = "0.12", features = ["tls-aws-lc-rs"] }

```

Then use the preset when building your endpoint:

```rust
use iroh::{Endpoint, endpoint::presets};

let endpoint = Endpoint::builder(presets::N0).bind().await?;

```

## Explicit Provider Configuration with the Builder API

For full control over the TLS crypto provider, bypass the feature-flag logic and set the provider explicitly using `Builder::crypto_provider()`. This method is defined at line 761 in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs):

```rust
pub fn crypto_provider(mut self,
    crypto_provider: Arc<rustls::crypto::CryptoProvider>) -> Self {
    self.crypto_provider = Some(crypto_provider);
    self
}

```

This approach is useful when you need to:
- Use a custom `CryptoProvider` implementation
- Switch providers at runtime based on configuration
- Avoid compiling unused cryptographic code

### Runtime Provider Selection

Because the builder stores the provider as an `Arc<rustls::crypto::CryptoProvider>`, you can dynamically select or even swap providers without recompiling:

```rust
use std::sync::Arc;
use iroh::endpoint::Builder;

// Explicitly choose aws-lc-rs at runtime
let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());

let endpoint = Builder::empty()
    .crypto_provider(provider)
    .preset(iroh::endpoint::presets::Minimal)
    .bind().await?;

```

## Practical Configuration Examples

### Example 1: Feature-Flag Driven Configuration

Enable the provider via Cargo and let the preset handle initialization:

```toml
[dependencies]
iroh = { version = "0.12", features = ["tls-ring"] }

```

```rust
use iroh::{Endpoint, endpoint::presets};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Automatically uses the Ring provider based on the feature flag
    let endpoint = Endpoint::builder(presets::N0).bind().await?;
    Ok(())
}

```

### Example 2: Explicit Provider Without Feature Flags

Disable default features and provide the crypto provider explicitly:

```toml
[dependencies]
iroh = { version = "0.12", default-features = false }
rustls = { version = "0.23", features = ["dangerous_configuration"] }
rustls-crypto-aws-lc-rs = "0.23"

```

```rust
use std::sync::Arc;
use iroh::endpoint::Builder;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
    
    let endpoint = Builder::empty()
        .crypto_provider(provider)
        .preset(iroh::endpoint::presets::Minimal)
        .bind().await?;
    
    Ok(())
}

```

### Example 3: Post-Quantum Key Exchange Example

The repository includes a practical example in [`iroh/examples/pq-only-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/pq-only-key-exchange.rs) demonstrating explicit provider configuration:

```rust
#[cfg(feature = "tls-aws-lc-rs")]
async fn make_endpoint() -> anyhow::Result<iroh::Endpoint> {
    let provider = Arc::new(rustls::crypto::aws_lc_rs::default_provider());
    iroh::Endpoint::builder(iroh::endpoint::presets::N0)
        .crypto_provider(provider)
        .bind().await
}

```

## Key Source Files

The implementation of TLS crypto provider configuration is distributed across these files:

- **[`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs)** – Contains the `Minimal` preset logic that selects providers based on feature flags.
- **[`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)** (line 761) – Defines `Builder::crypto_provider()`, the API for explicit provider injection.
- **[`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs)** – Consumes the configured `CryptoProvider` when constructing rustls `ClientConfig` and `ServerConfig`.
- **[`iroh/build.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/build.rs)** – Defines the `with_crypto_provider` configuration flag that gates TLS provider code compilation.
- **[`iroh/examples/pq-only-key-exchange.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/pq-only-key-exchange.rs)** – Demonstrates explicit provider selection in a real-world scenario.

## Summary

- **Feature flags** provide automatic configuration: enable `tls-ring` or `tls-aws-lc-rs` in [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml) and use `presets::N0` or `presets::Minimal`.
- **Builder API** offers explicit control via `Builder::crypto_provider()` in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), accepting any `Arc<rustls::crypto::CryptoProvider>`.
- **Mutual exclusivity** is enforced at compile time; if both features are enabled, `ring` takes precedence in the default presets.
- **Runtime flexibility** allows dynamic provider selection because the builder stores the provider as a trait object.

## Frequently Asked Questions

### Can I use both ring and aws-lc-rs providers simultaneously in the same application?

No, iroh only supports a single TLS crypto provider at runtime. The `Builder` stores exactly one `Arc<rustls::crypto::CryptoProvider>`, and the rustls `ClientConfig`/`ServerConfig` initialized in [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs) uses that single provider for all cryptographic operations. You must choose one provider when constructing the `Endpoint`.

### Which TLS crypto provider should I choose for production?

**Ring** is the default choice and offers broad compatibility with existing rustls deployments. **aws-lc-rs** provides FIPS-compliant cryptography and may offer better performance on certain hardware, but requires the `tls-aws-lc-rs` feature flag. According to the implementation in [`iroh/src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/presets.rs), if you enable both features, ring is preferred, so you must explicitly set the provider via `Builder::crypto_provider()` if you want aws-lc-rs when both are compiled.

### How do I verify which crypto provider is active in my iroh application?

The simplest method is to check your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml) dependencies and the feature flags enabled. If you configure the provider explicitly via `Builder::crypto_provider()`, the provider you pass is the one used. You can also verify at runtime by inspecting the `CryptoProvider` type, though iroh does not expose a direct getter for the configured provider after the `Endpoint` is built.

### Can I use a custom crypto provider instead of ring or aws-lc-rs?

Yes. Any type implementing `rustls::crypto::CryptoProvider` can be wrapped in an `Arc` and passed to `Builder::crypto_provider()`. This works because the builder in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) stores the provider as `Arc<rustls::crypto::CryptoProvider>`, which is a trait object. You do not need to enable either `tls-ring` or `tls-aws-lc-rs` features if you provide a fully custom implementation.