# How Iroh Uses EndpointId as a TLS Identity for Mutual Authentication

> Discover how Iroh leverages EndpointId as a TLS identity for certificate-less mutual authentication over QUIC. Learn about SNI and RPK in this technical deep dive.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-06-19

---

**Iroh authenticates peers over its QUIC transport by treating each endpoint's 32-byte public key (EndpointId) as a TLS identity, encoding it as a DNS name for Server Name Indication (SNI) and embedding it directly as a Raw Public Key (RPK) during the TLS handshake to achieve certificate-less mutual authentication.**

The iroh networking library from n0-computer eliminates traditional certificate infrastructure by using **EndpointId** as a **TLS identity** for mutual authentication. Instead of relying on X.509 certificates, iroh encodes each endpoint's public key as a deterministic DNS name and leverages Raw Public Key (RPK) TLS extensions per RFC 7250. This approach enables cryptographic peer verification without certificate authorities or PKI overhead.

## Encoding EndpointId as a DNS Name for SNI

Iroh derives a deterministic server name from the `EndpointId` to facilitate connection routing and identity indication during the TLS handshake.

### Base32 DNS-SEC Encoding

The [`iroh/src/tls/name.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/name.rs) module encodes the 32-byte public key (`EndpointId`) using *base-32 DNS-SEC* encoding and appends the constant suffix `.iroh.invalid`. This creates a reserved, non-resolvable domain name that serves as the Server Name Indication (SNI) value in TLS client configurations.

For example, an `EndpointId` encodes to a string like:

```

7dl2ff6emqi2qol3l382krodedij45bn3nh479hqo14a32qpr8kg.iroh.invalid

```

### Implementation in name.rs

The encoding implementation constructs the DNS name by formatting the base32-encoded bytes with the iroh-specific TLD:

```rust
// iroh/src/tls/name.rs
format!("{}.iroh.invalid", BASE32_DNSSEC.encode(endpoint_id.as_bytes()))

```

This deterministic mapping ensures that the SNI value uniquely identifies the expected endpoint while remaining within valid DNS character constraints.

## Raw Public Key TLS Handshake

Rather than transmitting full X.509 certificate chains, iroh leverages **Raw Public Keys** (RPK) as defined in RFC 7250. This allows the endpoint's public key to function directly as its authentication credential.

### RFC 7250 Compliance

Both client and server generate `rustls::ClientConfig` and `rustls::ServerConfig` instances that include a `rustls::RootCertStore` populated with the peer's `EndpointId` as the sole trusted credential. During the handshake, the raw public key transmits in a TLS extension rather than a traditional certificate message.

### Verifier Implementation

The custom verifier in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) implements `verify_server_cert` and `verify_client_cert` to extract the presented raw public key from the TLS session. It compares this key against the expected `EndpointId` and aborts the connection immediately if they do not match cryptographically.

This verification occurs during the handshake phase, preventing unauthorized peers from establishing connections before application-level data transmits.

## Bidirectional Authentication Flow

Because both connection endpoints supply raw public keys, iroh achieves **mutual authentication** without additional handshake rounds. Each side independently verifies the other's identity.

### Extracting the Peer EndpointId

After the TLS handshake completes, the `peer_endpoint_id()` function in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) extracts the remote's identity from the connection's peer certificates. The implementation retrieves the first (and only) certificate—which contains the raw public key—and parses it into an `EndpointId`:

```rust
// iroh/src/endpoint/connection.rs (approx. line 350)
let certs = conn.peer_certificates()?;
let raw_key = certs[0].as_ref(); // RawPublicKey
EndpointId::from_bytes(raw_key).ok()

```

### Routing Table Integration

The endpoint records the verified remote `EndpointId` in its internal `RemoteMap` routing tables. This ensures that subsequent packets route to the correct peer based on cryptographic identity rather than IP address, enabling NAT traversal and connection migration while maintaining strong authentication guarantees.

## Implementation Examples

### Creating a TLS Client Configuration

Configure a rustls client that uses your `EndpointId` as its TLS identity and sets the appropriate SNI:

```rust
use iroh_base::EndpointId;
use iroh::tls::{self, name};
use std::sync::Arc;

fn client_config(my_id: EndpointId) -> rustls::ClientConfig {
    // Encode our own EndpointId as the SNI name
    let server_name = name::encode(my_id);
    let mut cfg = rustls::ClientConfig::builder()
        .with_safe_defaults()
        .with_custom_certificate_verifier(
            Arc::new(tls::verifier::Verifier::default())
        )
        .with_no_client_auth(); // RPK added separately

    // Set the SNI to indicate which EndpointId we expect
    cfg.set_sni(server_name);
    cfg
}

```

### Verifying Peer Identity Post-Handshake

Retrieve the authenticated peer's `EndpointId` from an established connection:

```rust
use rustls::Connection;
use iroh_base::EndpointId;

fn peer_endpoint_id(conn: &Connection) -> Option<EndpointId> {
    // The verifier already confirmed the raw key matches a known EndpointId
    let certs = conn.peer_certificates()?;
    let raw_key = certs[0].as_ref(); // First (and only) RawPublicKey
    EndpointId::from_bytes(raw_key).ok()
}

```

### Encoding and Decoding DNS Names

Convert between `EndpointId` and the DNS name format used for SNI:

```rust
use iroh_base::EndpointId;
use iroh::tls::name;

let id: EndpointId = /* ... */;
let dns_name = name::encode(id);
assert_eq!(Some(id), name::decode(&dns_name));

```

## Summary

- **EndpointId serves dual purpose**: It functions as both the network address (via DNS name encoding) and the cryptographic authentication credential (via Raw Public Key TLS).
- **Deterministic encoding**: The [`iroh/src/tls/name.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/name.rs) module encodes the 32-byte public key with base32 DNS-SEC and appends `.iroh.invalid` for SNI values.
- **Certificate-less verification**: The verifier in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) implements RFC 7250 to validate raw public keys directly against expected `EndpointId` values.
- **Post-handshake extraction**: The `peer_endpoint_id()` function in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) retrieves the authenticated peer's identity for routing table management.
- **Mutual authentication**: Both client and server present and verify raw public keys, eliminating the need for X.509 certificates or PKI infrastructure.

## Frequently Asked Questions

### What is an EndpointId in iroh?

An **EndpointId** is a 32-byte public key that uniquely identifies an iroh endpoint. It serves as the fundamental identity primitive for the entire networking stack, acting as both the addressing mechanism and the cryptographic root of trust for TLS authentication.

### Why does iroh use .iroh.invalid as a domain suffix?

The `.iroh.invalid` suffix is a reserved, non-resolvable top-level domain that ensures the encoded **EndpointId** conforms to DNS naming conventions required for TLS SNI fields. This guarantees compatibility with TLS libraries while preventing accidental resolution as a real domain name.

### How does iroh verify the peer's identity without certificates?

Iroh implements RFC 7250 **Raw Public Key** support, where the `EndpointId` (the public key itself) transmits directly in the TLS handshake. The custom verifier in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) compares this raw key against the expected `EndpointId` bytes, rejecting the connection if they do not match perfectly.

### Can iroh interoperate with standard TLS/X.509 infrastructure?

No, iroh's mutual authentication requires both endpoints to support Raw Public Key TLS extensions. Standard TLS implementations that expect X.509 certificates cannot complete handshakes with iroh endpoints, as the protocol intentionally avoids certificate chains to reduce complexity and attack surface.