# How iroh Implements TLS Encryption Without Traditional Certificates

> Discover how iroh achieves TLS encryption without X.509 certificates. Learn about its Ed25519 public-key authentication and custom rustls verifiers for secure peer connections.

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

---

**iroh replaces X.509 certificate infrastructure with raw Ed25519 public-key authentication, using custom rustls verifiers that validate self-signed certificates containing only peer public keys while embedding identities directly in TLS ServerNames.**

The [n0-computer/iroh](https://github.com/n0-computer/iroh) library achieves certificateless security by reimagining how TLS 1.3 establishes trust between peers. Instead of relying on certificate authorities and complex revocation chains, iroh's TLS encryption works without traditional certificates through direct public-key validation, allowing end-to-end encrypted connections using only Ed25519 key pairs derived from endpoint IDs.

## Raw Public Key Authentication Architecture

### Encoding Peer Identity in DNS Names

iroh embeds the remote peer's base64-url-encoded Ed25519 public key directly into the TLS ServerName (e.g., `peer_id.iroh`). This encoding allows the transport to carry identity metadata without certificates. In [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs), lines 44-45 extract this peer ID using `super::name::decode`, which reverses the transformation performed in [`iroh/src/name.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/name.rs) to recover the raw public key bytes from the DNS-formatted string.

### Self-Signed Certificates Without Chains

The system expects the server to present a self-signed certificate that contains only the peer's Ed25519 public key in its SubjectPublicKeyInfo (SPKI) field. Unlike traditional TLS, iroh explicitly rejects any intermediate certificates—line 50 in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) verifies that `intermediates.is_empty()` returns true, ensuring no certificate chain validation occurs.

## TLS 1.3 Verification Implementation

### Server Certificate Verification

The `ServerCertVerifier` implementation in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) performs the critical identity check on lines 62-71. It converts the presented end-entity certificate to a `SubjectPublicKeyInfoDer` and compares it against the expected public key SPKI (`remote_public_spki`). If the SPKIs differ, verification fails immediately, ensuring the peer possesses the private key corresponding to the expected identity.

### Ed25519 Signature Validation

For TLS 1.3 signature verification, iroh delegates to `rustls::crypto::verify_tls13_signature_with_raw_key` with a restricted algorithm set. Lines 21-24 and 94-99 in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) define `SUPPORTED_SIG_ALGS` to include only Ed25519, eliminating the attack surface of other signature algorithms. The implementation explicitly rejects TLS 1.2 connections via `PeerIncompatible::Tls12NotOffered`, ensuring only modern cryptography is used.

### Client Certificate Verification

A symmetric `ClientCertVerifier` implementation exists in the same file, performing identical raw-key checks for client authentication. This enables mutual TLS authentication where both parties verify each other's Ed25519 public keys without exchanging traditional certificates.

## Certificate Resolution for Handshakes

To participate in the TLS handshake, iroh must present its own credentials. The [`iroh/src/tls/resolver.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/resolver.rs) module creates a `CertifiedKey` from the raw Ed25519 public key and its associated private key using `ResolveRawPublicKeyCert`. This resolver constructs the necessary rustls structures on-the-fly, wrapping the raw public key in a self-signed certificate format that the custom verifiers expect.

## Implementation Examples

Create a client that trusts raw public keys:

```rust
// Create a TLS client that trusts raw public keys
let cfg = rustls::ClientConfig::builder()
    .with_safe_defaults()
    .with_custom_certificate_verifier(Arc::new(
        iroh::tls::verifier::ClientCertificateVerifier::default(),
    ))
    .with_no_client_auth(); // server will present its raw‑key cert

let connector = TlsConnector::from(Arc::new(cfg));
let stream = connector.connect("peerid.iroh", tcp_stream).await?;

```

Configure a server to present and verify raw-key certificates:

```rust
// Server side – use iroh’s ResolveRawPublicKeyCert to present the raw‑key cert
let server_cfg = rustls::ServerConfig::builder()
    .with_safe_defaults()
    .with_client_cert_verifier(Arc::new(
        iroh::tls::verifier::ServerCertificateVerifier::default(),
    ))
    .with_cert_resolver(Arc::new(
        iroh::tls::resolver::ResolveRawPublicKeyCert::new(
            my_public_key, my_private_key,
        ),
    ));
let acceptor = TlsAcceptor::from(Arc::new(server_cfg));
let tls_stream = acceptor.accept(tcp_stream).await?;

```

## Summary

- iroh uses **raw public-key authentication** instead of X.509 certificate chains, eliminating certificate authority dependencies.
- Peer identities are **encoded in DNS names** (e.g., `peer_id.iroh`) and decoded in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) to extract expected public keys.
- The `ServerCertVerifier` and `ClientCertVerifier` implementations validate **self-signed certificates** containing only Ed25519 public keys in their SPKI fields.
- **TLS 1.3 signatures** are verified exclusively against Ed25519 using `rustls::crypto::verify_tls13_signature_with_raw_key`, with TLS 1.2 explicitly disabled.
- The `ResolveRawPublicKeyCert` resolver in [`iroh/src/tls/resolver.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/resolver.rs) generates ephemeral credentials from raw key pairs to complete the handshake.
- This architecture provides **end-to-end encryption** using only long-term Ed25519 keys, simplifying key management while maintaining TLS 1.3 security guarantees.

## Frequently Asked Questions

### How does iroh verify server identity without a certificate authority?

iroh verifies server identity by comparing the SubjectPublicKeyInfo (SPKI) extracted from the self-signed certificate against the expected Ed25519 public key derived from the peer ID. The `ServerCertVerifier` in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) performs this check on lines 62-71, ensuring the certificate contains exactly the public key encoded in the DNS name, without validating any chain of trust.

### Why does iroh reject TLS 1.2 connections?

iroh explicitly rejects TLS 1.2 via `PeerIncompatible::Tls12NotOffered` to enforce modern cryptography standards. The raw public-key verification mechanism relies on TLS 1.3's improved handshake and signature algorithms, specifically using `verify_tls13_signature_with_raw_key` with Ed25519 support defined in lines 21-24 of [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs).

### What prevents man-in-the-middle attacks without traditional certificates?

Man-in-the-middle attacks are prevented through **direct public-key pinning**. Since the peer ID is encoded in the DNS name (e.g., `peer_id.iroh`) and the certificate must contain exactly that Ed25519 public key in its SPKI field, an attacker cannot substitute a different key without detection. The verification logic in [`iroh/src/tls/verifier.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/verifier.rs) rejects any certificate where the embedded public key does not match the expected value extracted via `super::name::decode`.

### How are certificates generated if there's no certificate authority?

Certificates are generated locally as self-signed structures containing only the public key. The `ResolveRawPublicKeyCert` in [`iroh/src/tls/resolver.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/resolver.rs) creates a `CertifiedKey` by wrapping the raw Ed25519 public key and private key in a format rustls understands. These ephemeral certificates are presented during the handshake but contain no CA signatures, relying instead on the raw-key verification logic to establish trust.