# Security Model for iroh Connections Using SecretKey/PublicKey Authentication: Ed25519, Raw Public Keys, and TLS 1.3

> Discover Iroh's security model authentication using Ed25519 SecretKey/PublicKey and TLS 1.3 Raw Public Key extension. Learn how Iroh eliminates X.509 certificates for secure connections.

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

---

**Iroh authenticates every connection using a permanent Ed25519 key pair where the `PublicKey` serves as the node’s endpoint ID and the `SecretKey` proves ownership through TLS 1.3 with the Raw Public Key extension, eliminating X.509 certificates and external certificate authorities.**

The `n0-computer/iroh` project implements a PKI-free trust model for peer-to-peer and relay networking. In the security model for iroh connections using SecretKey/PublicKey authentication, each node generates a permanent Ed25519 identity that acts as both its cryptographic credential and its globally unique address. Every outbound and inbound QUIC connection verifies this identity through a lightweight TLS 1.3 handshake that relies on raw public keys rather than traditional X.509 certificates.

## The Three-Layer Architecture of iroh’s Authentication Model

As implemented in `n0-computer/iroh`, the authentication stack is divided into three tightly coupled layers: identity, TLS handshake, and mutual verification. Each layer is responsible for a distinct phase of the connection lifecycle, from key generation to cryptographic proof.

### Identity Layer: SecretKey and PublicKey as the Node ID

In [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs), iroh defines **SecretKey** and **PublicKey** as wrappers around an Ed25519 key pair. `SecretKey::generate()` creates a fresh private key, and `secret_key.public()` derives the matching public key. This **PublicKey** becomes the node's immutable endpoint identifier and is used by higher-level protocols for discovery and routing.

### TLS Handshake Layer: QUIC over TLS 1.3 with Raw Public Keys

All network traffic in iroh runs over QUIC wrapped in TLS 1.3. In [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs), the codebase constructs a `TlsConfig` that enables the **Raw Public Key** TLS extension (RFC 7250). This means the handshake carries the public key directly instead of an X.509 certificate, keeping the payload small and removing any dependency on certificate authorities.

### Mutual Verification Layer: Custom Resolvers and Verifiers

[`iroh/src/tls/resolver.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/resolver.rs) supplies the public key to rustls via `ResolveRawPublicKeyCert`, while [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) implements both `ServerCertificateVerifier` and `ClientCertificateVerifier`. During the handshake, the server presents its public key through the resolver, the client validates it using `ServerCertificateVerifier`, the client presents its public key, and the server validates it using `ClientCertificateVerifier`. Both sides compare the presented raw key against the expected `PublicKey`, ensuring each peer proves possession of its `SecretKey` without ever transmitting it.

## How the iroh Handshake Establishes an Authenticated Connection

When an `Endpoint` is constructed in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), the builder accepts a secret key through `Endpoint::builder().secret_key(...)`. The builder passes this key to `TlsConfig::new`, which initializes the raw-public-key infrastructure. The secret key remains in-process and is only used to sign TLS handshake data.

### Connection Establishment and Key Verification

The QUIC stack, powered by rustls `QuicClientConfig` and `QuicServerConfig`, executes the handshake. Because the Raw Public Key extension is active, the proof of identity is simply a signature made with the node's `SecretKey`. After completion, both peers possess a cryptographically verified `PublicKey` that serves as the principal for all subsequent protocol operations.

### Session Resumption and Zero-RTT Data

Iroh optionally caches TLS tickets (`DEFAULT_MAX_TLS_TICKETS`) to support 0-RTT data on subsequent connections. Each resumed session reuses the same public-key verification path, so the authentication guarantee remains intact even when the connection establishes in a single round trip.

## Security Guarantees Provided by iroh’s SecretKey/PublicKey Model

- **Confidentiality and integrity**: All payloads are encrypted with QUIC-TLS 1.3 using keys derived from the handshake.
- **Authentication without a PKI**: No external certificate authorities are required; the `PublicKey` itself is the identity.
- **Forward secrecy**: Ephemeral Diffie-Hellman keys are generated per QUIC connection, so compromise of a secret key does not retroactively expose past traffic.
- **Minimal secret-key exposure**: The `SecretKey` never leaves the process and only signs raw-public-key TLS structures.

## Rust Code Example: Authenticating an iroh Endpoint

The following example demonstrates how to generate a secret key, bind an endpoint, and connect to a verified peer using the APIs exposed in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) and [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs).

```rust
use iroh::endpoint::Endpoint;
use iroh_base::SecretKey;

// Generate a fresh Ed25519 secret key
let my_secret = SecretKey::generate();

// Build an endpoint that authenticates with that key
let ep = Endpoint::builder()
    .secret_key(my_secret.clone())
    .bind()
    .await?;

// Connect to a remote endpoint using its known PublicKey (peer_id)
let conn = ep
    .connect(peer_id, "example.com:443".parse()?)
    .await?;

// The connection is authenticated; retrieve the peer's verified identity
println!("Connected to {}", conn.remote_id().fmt_short());

// Open a stream and send encrypted data
let mut stream = conn.open_stream().await?;
stream.write_all(b"hello iroh").await?;

```

If you have already persisted a secret key through your application’s storage layer, pass it to the same builder before binding:

```rust
let secret = SecretKey::from_file("my-key.bin")?; // application-specific I/O
let ep = Endpoint::builder().secret_key(secret).bind().await?;

```

## Summary

- Each iroh node owns a permanent Ed25519 `SecretKey` and `PublicKey`, defined in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs), where the public key acts as the global endpoint ID.
- All connections use QUIC wrapped in TLS 1.3 with the Raw Public Key extension (RFC 7250), configured centrally in [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs).
- Mutual authentication is enforced by `ServerCertificateVerifier` and `ClientCertificateVerifier` in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs), ensuring both peers verify each other’s raw public key.
- The `SecretKey` never leaves the process and is only used to sign the raw-public-key TLS structure, delivering strong authentication without a PKI.
- Optional 0-RTT session resumption via TLS tickets preserves these authentication guarantees across reconnections.

## Frequently Asked Questions

### What cryptographic algorithm does iroh use for SecretKey/PublicKey authentication?

Iroh uses Ed25519 for all node identity keys. The `SecretKey` and `PublicKey` types in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) wrap Ed25519 key material, and the TLS handshake produces signatures with this key pair to prove ownership without transmitting the secret key.

### Does iroh require X.509 certificates or a certificate authority?

No. As implemented in [`iroh/src/tls.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls.rs), the connection security model uses the TLS 1.3 Raw Public Key extension (RFC 7250) to send public keys directly, relying on the `PublicKey` itself as the trusted identity rather than X.509 certificates.

### How does iroh verify the peer’s identity during a connection?

During the QUIC handshake, [`iroh/src/tls/resolver.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/resolver.rs) presents the raw public key, and [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) validates it. Both the client and server run custom verifiers—`ServerCertificateVerifier` and `ClientCertificateVerifier`—that compare the presented raw public key against the expected endpoint ID, enforcing mutual authentication.

### Is forward secrecy supported in iroh’s SecretKey/PublicKey security model?

Yes. Iroh achieves forward secrecy by generating ephemeral Diffie-Hellman keys for each QUIC connection during the TLS 1.3 handshake. Even if a long-term `SecretKey` is compromised later, past traffic remains secure because the session keys were derived from the ephemeral exchange, not from the static Ed25519 key pair.