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

Enable either the tls-ring or tls-aws-lc-rs feature in your 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.

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 uses conditional compilation to set the provider:

#[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:


# 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:

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:

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:

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:

[dependencies]
iroh = { version = "0.12", features = ["tls-ring"] }
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:

[dependencies]
iroh = { version = "0.12", default-features = false }
rustls = { version = "0.23", features = ["dangerous_configuration"] }
rustls-crypto-aws-lc-rs = "0.23"
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 demonstrating explicit provider configuration:

#[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:

Summary

  • Feature flags provide automatic configuration: enable tls-ring or tls-aws-lc-rs in Cargo.toml and use presets::N0 or presets::Minimal.
  • Builder API offers explicit control via Builder::crypto_provider() in 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 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, 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 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 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.

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 →