# How to Use Custom Crypto Providers in iroh: ring vs aws‑lc‑rs

> Learn how to use custom crypto providers ring or aws-lc-rs in iroh. Select your preferred TLS backend via Cargo features or Builder crypto provider.

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

---

**Enable the `tls-ring` or `tls-aws-lc-rs` Cargo feature in your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml) to select between ring and aws‑lc‑rs cryptographic backends, or manually inject a provider via `Builder::crypto_provider()` before binding the endpoint.**

`iroh` delegates its QUIC/TLS encryption to **rustls**, which relies on a pluggable **crypto provider** that implements `rustls::crypto::CryptoProvider`. By default, the `iroh` crate supports two provider implementations—ring and aws‑lc‑rs—and allows you to configure them either at compile time through Cargo features or programmatically at runtime. The choice of provider determines the specific cryptographic primitives used for all encrypted connections managed by an `Endpoint`.

## Understanding iroh's Crypto Provider Architecture

`iroh` abstracts its cryptographic dependencies through rustls’s provider trait. The library ships with support for two industry-standard implementations:

| Provider | Module Path | Cargo Feature | Default Constructor |
|----------|-------------|---------------|-------------------|
| **ring** | `rustls::crypto::ring` | `tls-ring` | `rustls::crypto::ring::default_provider()` |
| **aws‑lc‑rs** | `rustls::crypto::aws_lc_rs` | `tls-aws-lc-rs` | `rustls::crypto::aws_lc_rs::default_provider()` |

The active provider is stored in the `Builder::crypto_provider` field defined in [`src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint.rs) (lines 49‑50). When you invoke `bind()`, the implementation validates that a provider exists and returns `BindError::InvalidCryptoProvider` if the field is empty (lines 28‑31).

## Compile‑Time Configuration via Cargo Features

The simplest way to select a provider is through Cargo features. You only need to enable **one** of the following in your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml):

```toml
[dependencies]

# Option 1: Use ring (pure Rust, widely compatible)

iroh = { version = "0.XX", features = ["tls-ring"] }

# Option 2: Use aws‑lc‑rs (AWS libcrypto with potential performance benefits)

iroh = { version = "0.XX", features = ["tls-aws-lc-rs"] }

```

If you enable both features simultaneously, the **`Minimal` preset** in [`src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint/presets.rs) (lines 52‑73) prioritizes **ring** due to the `#[cfg(feature = "tls-ring")]` block appearing first. The `N0` preset (lines 81‑90) inherits this behavior while adding relay and discovery defaults. When using these presets via `Endpoint::builder()`, you do not need to manually set the provider.

## Manual Runtime Configuration

For scenarios requiring explicit provider selection, custom rustls configurations, or dynamic provider injection, use the `Builder::crypto_provider()` method. This approach bypasses the preset defaults and stores an `Arc<dyn CryptoProvider>` directly in the builder.

```rust
use std::sync::Arc;
use iroh::{Endpoint, endpoint::Builder};
use rustls::crypto::{ring, aws_lc_rs};

// Explicitly select aws‑lc‑rs
let provider = Arc::new(aws_lc_rs::default_provider());

let endpoint = Builder::empty()
    .crypto_provider(provider)
    .bind()
    .await?;

```

The `crypto_provider` method is part of the fluent builder API exposed in [`src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint.rs). After setting the provider, the `bind()` implementation passes this `Arc` to the TLS layer in [`src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/src/tls.rs) (lines 79‑84), which constructs the rustls client and server configurations using your chosen implementation.

## Validation and Error Handling

During endpoint initialization, `iroh` performs strict validation of the crypto configuration. The `Builder` struct requires that `crypto_provider` is populated before calling `bind()`. If you use `Builder::empty()` without chaining `crypto_provider()`, the `bind()` method returns `BindError::InvalidCryptoProvider` (as implemented in [`src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint.rs), lines 28‑31).

This validation ensures that rustls never attempts to initialize a QUIC connection without a valid cryptographic backend, preventing runtime panics deep in the TLS stack.

## Summary

- **ring** and **aws‑lc‑rs** are the two supported crypto providers, configurable via the `tls-ring` and `tls-aws-lc-rs` Cargo features.
- The `Minimal` and `N0` presets in [`src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint/presets.rs) automatically configure the provider based on enabled features, preferring ring when both are present.
- Manual configuration requires calling `Builder::crypto_provider()` with an `Arc<dyn CryptoProvider>` before invoking `bind()`.
- The builder validates the provider at bind time and returns `BindError::InvalidCryptoProvider` if none is set.
- The chosen provider is consumed by [`src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/src/tls.rs) to generate rustls configurations for all QUIC connections.

## Frequently Asked Questions

### What is the difference between ring and aws‑lc‑rs in iroh?

**ring** is a pure-Rust cryptographic implementation optimized for portability and safety, while **aws‑lc‑rs** is a Rust wrapper around AWS Libcrypto, which is derived from OpenSSL and may offer performance advantages on certain hardware or compliance with specific cryptographic certifications. Both implement the same `rustls::crypto::CryptoProvider` interface used by `iroh`.

### Can I use a custom crypto provider other than ring or aws‑lc‑rs?

Yes. While `iroh` only ships with feature flags for ring and aws‑lc‑rs, you can inject any type implementing `rustls::crypto::CryptoProvider` via `Builder::crypto_provider()`. This allows integration with FIPS-certified modules, hardware security modules (HSMs), or custom cryptographic implementations, provided they conform to the rustls provider trait.

### What happens if I enable both tls‑ring and tls‑aws‑lc‑rs features?

When both features are enabled, the `Minimal` preset in [`src/endpoint/presets.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint/presets.rs) (lines 66‑70) selects **ring** due to the conditional compilation order checking `tls-ring` first. Your binary will still compile both providers into the artifact, but `iroh` will default to ring unless you manually override the provider via the builder API.

### How do I troubleshoot InvalidCryptoProvider errors?

This error occurs when calling `bind()` on a `Builder` that lacks a crypto provider. If you are not using the `Minimal` or `N0` presets, ensure you explicitly call `.crypto_provider()` with a valid provider instance. If using presets, verify that at least one of `tls-ring` or `tls-aws-lc-rs` is enabled in your [`Cargo.toml`](https://github.com/n0-computer/iroh/blob/main/Cargo.toml) and that you have not disabled default features without providing an alternative.