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

Enable the tls-ring or tls-aws-lc-rs Cargo feature in your 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 (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:

[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 (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.

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. After setting the provider, the bind() implementation passes this Arc to the TLS layer in 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, 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 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 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 (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 and that you have not disabled default features without providing an alternative.

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 →