How iroh Implements TLS Encryption Without Traditional Certificates
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 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, lines 44-45 extract this peer ID using super::name::decode, which reverses the transformation performed in 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 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 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 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 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:
// 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:
// 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 iniroh/src/tls/verifier.rsto extract expected public keys. - The
ServerCertVerifierandClientCertVerifierimplementations 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
ResolveRawPublicKeyCertresolver iniroh/src/tls/resolver.rsgenerates 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 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.
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 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 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →