# Security Considerations for iroh Endpoint Authentication and Peer Verification

> Learn about iroh endpoint authentication and peer verification. iroh uses raw Ed25519 public keys and TLS 1.3 for secure peer connections, avoiding X.509 certificates.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: security-considerations
- Published: 2026-07-05

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`:

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) (builder & bind), [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/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`:

```rust
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:

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) (hook registration), [`iroh/src/endpoint/hooks.rs`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.