Security Considerations for iroh Endpoint Authentication and Peer Verification

iroh authenticates peers exclusively using raw Ed25519 public keys bound to EndpointIds, eliminating X.509 certificates and Certificate Authorities in favor of TLS 1.3 with raw public key verification.

The n0-computer/iroh networking library implements a zero-trust security model where cryptographic identity is decoupled from traditional PKI infrastructure. Understanding the security considerations for iroh endpoint authentication and peer verification is essential for building secure distributed applications. This article examines the raw public key infrastructure, TLS 1.3 enforcement, and verification mechanisms implemented in the iroh source code.

Raw Public Key Cryptography and Identity

Ed25519 EndpointId Binding

Every iroh Endpoint owns a SecretKey, and its public counterpart serves as the EndpointId. This identifier is never transmitted in plain text; instead, it is embedded within the TLS ServerName using the encoding logic in iroh/src/tls/name.rs. The name::encode function ensures that peer identities are cryptographically bound to the TLS handshake from the outset.

Elimination of Certificate Authorities

Unlike traditional TLS implementations, iroh does not rely on X.509 certificate chains or Certificate Authority hierarchies. According to the source code in iroh/src/tls.rs, the ca_tls_config field is reserved exclusively for external TLS connections such as relays and DNS-over-HTTPS. Peer-to-peer authentication operates entirely outside the traditional PKI ecosystem, mitigating supply-chain attacks targeting root certificate stores.

TLS 1.3 and Raw Key Verification

Protocol Version Enforcement

The PROTOCOL_VERSIONS constant in iroh/src/tls/verifier.rs is hard-coded to TLS 1.3 only. Any attempt to negotiate TLS 1.2 results in PeerIncompatible::Tls12NotOffered. This enforcement prevents downgrade attacks and guarantees the use of modern, authenticated encryption cipher suites.

RFC 7250 Raw Public Key Certificates

In iroh/src/tls/verifier.rs, the requires_raw_public_keys function ensures that certificate chains containing intermediates are rejected. iroh uses Raw Public Key (RFC 7250) extensions, presenting only the raw Ed25519 public key. This eliminates the attack surface associated with certificate parsing and validation logic while limiting trust to a single, verifiable public key.

Peer Verification Mechanisms

ServerName Verification Logic

The ServerCertificateVerifier implementation in iroh/src/tls/verifier.rs performs the critical security check during connection establishment. When verify_server_cert is invoked, it decodes the expected EndpointId from the TLS ServerName and compares it against the Subject Public Key Info (SPKI) extracted from the raw public key certificate. If the keys do not match exactly, the handshake aborts with a rustls::Error::InvalidCertificate.

Signature Algorithm Constraints

iroh accepts only Ed25519 signatures. The verification is performed by the Ed25519Dalek implementation, which directly validates the signature against the public key derived from the peer's EndpointId. This constraint is enforced in iroh/src/tls/verifier.rs, ensuring that no other signature algorithms can be negotiated during the handshake.

Session Security and Attack Prevention

Session Ticket Limiting

To mitigate ticket-reuse attacks and control 0-RTT state exposure, DEFAULT_MAX_TLS_TICKETS in iroh/src/tls.rs caps cached tickets at 8 per peer (approximately 150KB total). This per-peer isolation prevents amplification attacks and limits the impact of compromised session state.

Self-Connection Prevention

The connect_with_opts implementation in iroh/src/endpoint.rs explicitly checks endpoint_id != self.id() before initiating connections. This prevents loopback attacks that could otherwise bypass authentication mechanisms by connecting an endpoint to itself.

Pre-Connection Hooks

iroh provides EndpointHooks for application-level policy enforcement before the TLS handshake occurs. As defined in iroh/src/endpoint/hooks.rs, the before_connect hook allows inspection of the target EndpointAddr. If the hook returns BeforeConnectOutcome::Reject, the connection aborts immediately, providing a decisive control point for rate limiting or access control lists.

Implementation Examples

Building an Endpoint with Explicit Authentication

The following example demonstrates creating an endpoint with a deterministic secret key, which forces a specific public key and EndpointId:

use iroh::{Endpoint, endpoint::presets};
use iroh_base::SecretKey;

// Generate a deterministic key for the example
let secret = SecretKey::from_bytes(&[0u8; 32]);    // ← replace with your own key
let endpoint = Endpoint::builder(presets::N0)
    .secret_key(secret)               // <-- forces the public key / EndpointId
    .alpns(vec![b"my-alpn".to_vec()]) // required for inbound connections
    .bind()
    .await?;                          // <- creates the TLS config with raw‑key verifier

Key files: iroh/src/endpoint.rs (builder & bind), iroh/src/tls.rs (TLS config creation).

Connecting to a Remote Peer

When connecting, the TLS handshake automatically validates the peer's identity against the expected EndpointId:

use iroh::{Endpoint, endpoint::presets, EndpointAddr};

let local = Endpoint::builder(presets::N0).bind().await?;
let remote_id = /* obtain the remote EndpointId via address lookup */;
let remote_addr = EndpointAddr {
    id: remote_id,
    relay_urls: vec![],          // optional – can be omitted if direct addresses are known
    ip_addrs: vec![],            // optional – direct IPs
    ..Default::default()
};

let conn = local.connect(remote_addr, b"my-alpn").await?; // TLS handshake validates peer

If the remote’s raw public key does not correspond to the EndpointId encoded in its server name, the call fails with an authentication error.

Implementing Custom Pre-Connect Validation

You can implement EndpointHooks to reject connections before the TLS handshake begins:

use iroh::{Endpoint, endpoint::hooks::EndpointHooks};

struct RejectUnknown; // implements EndpointHooks

impl EndpointHooks for RejectUnknown {
    async fn before_connect(
        &self,
        addr: &iroh::address_lookup::EndpointAddr,
        _alpn: &[u8],
    ) -> iroh::endpoint::hooks::BeforeConnectOutcome {
        // Example: only allow peers whose ID is in an allow‑list
        if ALLOWED_IDS.contains(&addr.id) {
            iroh::endpoint::hooks::BeforeConnectOutcome::Accept
        } else {
            iroh::endpoint::hooks::BeforeConnectOutcome::Reject
        }
    }
}

// Register the hook in the builder
let ep = Endpoint::builder(presets::N0)
    .hooks(RejectUnknown)
    .bind()
    .await?;

The hook runs before any TLS handshake, giving the application a chance to abort connections early.

Key files: iroh/src/endpoint.rs (hook registration), iroh/src/endpoint/hooks.rs (hook trait).

Summary

  • iroh uses raw Ed25519 public keys as EndpointIds, eliminating X.509 certificate chains and Certificate Authorities from peer authentication.
  • TLS 1.3 is mandatory, with raw public key verification (RFC 7250) enforced by custom verifiers in iroh/src/tls/verifier.rs.
  • Peer verification compares the expected EndpointId (encoded in ServerName) against the actual public key presented in the TLS handshake.
  • Security mitigations include session ticket limits, self-connection rejection, and pre-connection hooks for application-level policy enforcement.

Frequently Asked Questions

Does iroh use traditional X.509 certificates for peer authentication?

No. iroh uses Raw Public Key (RFC 7250) extensions where the certificate contains only the raw Ed25519 public key. The requires_raw_public_keys verifier in iroh/src/tls/verifier.rs explicitly rejects certificate chains containing intermediates or traditional X.509 structures, limiting the attack surface to a single cryptographic key.

How does iroh prevent downgrade attacks to TLS 1.2?

The PROTOCOL_VERSIONS constant in iroh/src/tls/verifier.rs is hard-coded to allow only TLS 1.3. Any peer attempting to negotiate TLS 1.2 receives a PeerIncompatible::Tls12NotOffered error, forcing the use of modern cipher suites and preventing protocol downgrade attacks that could weaken security guarantees.

What happens if a peer presents a different public key than expected?

The ServerCertificateVerifier::verify_server_cert implementation extracts the expected public key from the decoded ServerName and compares it to the SPKI in the received certificate. If the keys do not match exactly, the handshake fails immediately with rustls::Error::InvalidCertificate, preventing identity spoofing and man-in-the-middle attacks.

Can I implement custom authentication policies in iroh?

Yes. The EndpointHooks trait in iroh/src/endpoint/hooks.rs allows you to implement before_connect methods that inspect the target EndpointAddr before the TLS handshake. Returning BeforeConnectOutcome::Reject aborts the connection early, enabling custom allow-lists, rate limiting, or other policy enforcement mechanisms without modifying the underlying TLS 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 →