How iroh Validates Peer Identity and Authenticates: A Deep Dive into TLS 1.3 Raw Public Key Verification
iroh authenticates peers using TLS 1.3 with raw public keys (Ed25519), encoding the peer identity in DNS names and verifying the certificate's SubjectPublicKeyInfo (SPKI) matches the expected public key without relying on certificate authorities.
The n0-computer/iroh repository implements a peer-to-peer networking stack that eliminates traditional certificate authorities by using raw public key authentication. Understanding how iroh validates peer identity requires examining its custom TLS verifiers that perform cryptographic checks against Ed25519 keys during the handshake process.
The Foundation: TLS 1.3 with Raw Public Keys
Unlike traditional TLS implementations that rely on X.509 certificate chains and certificate authorities, iroh uses raw public keys as defined in RFC 7250. This approach removes the complexity of certificate management while maintaining cryptographic security. The system implements two custom rustls verifiers in iroh/src/tls/verifier.rs:
ServerCertificateVerifier– Validates that the server certificate encodes the exact public key of the expected peerClientCertificateVerifier– Ensures the client presents a raw public key that matches the expected peer identity
Both verifiers explicitly require raw public keys by returning true from requires_raw_public_keys() and disable TLS 1.2 by making verify_tls12_signature return fatal errors.
Encoding Peer Identity in DNS Names
The iroh-<hex> Format
Before the TLS handshake begins, iroh encodes the peer's 32-byte Ed25519 public key into a DNS-compatible string format. The encoding follows the pattern:
iroh-<hex-encoded-public-key>
This format allows the public key to be transmitted as part of the TLS ServerName extension while maintaining human readability.
Decoding in name.rs
In iroh/src/tls/name.rs, the decode function parses the DNS name back into a PublicKey type defined in iroh-base/src/key.rs. This decoded key becomes the expected identity for the TLS handshake. The module also provides an encode function that converts a PublicKey into the DNS name format, ensuring consistency across the codebase.
Server-Side Identity Verification
Extracting the Expected Peer ID
The ServerCertificateVerifier implementation in iroh/src/tls/verifier.rs performs a three-step validation process:
- DNS Name Decoding – The verifier extracts the
ServerNamefrom the TLS handshake and decodes it using the logic inname.rsto obtain the expectedPublicKey - Certificate Chain Validation – The verifier rejects any certificate chains with intermediate certificates. iroh only accepts a single self-signed certificate containing the raw public key
- SPKI Comparison – The certificate's SubjectPublicKeyInfo (SPKI) is extracted and compared byte-for-byte against the SPKI derived from the expected public key (
remote_public_spki)
If the SPKI values match exactly, the server returns ServerCertVerified::assertion(), indicating successful authentication.
Implementation Example
The following code demonstrates how to configure a server with the custom client certificate verifier:
use iroh::tls::{ClientCertificateVerifier};
use rustls::server::ServerConfig;
let server_config = ServerConfig::builder()
.with_safe_defaults()
.with_custom_certificate_verifier(
Arc::new(ClientCertificateVerifier::default())
)
.with_single_cert(cert_chain, private_key)
.expect("failed to create server config");
Client-Side Authentication
Raw Key Verification
The ClientCertificateVerifier (also in iroh/src/tls/verifier.rs) mirrors the server logic but for incoming client connections. It enforces the same restrictions:
- No intermediate certificates – Only accepts end-entity certificates with raw public keys
- TLS 1.3 only – Explicitly rejects TLS 1.2 signature verification attempts
- Raw key requirement – Returns
truefromrequires_raw_public_keys()
Signature Validation
Unlike traditional client certificate verification, iroh does not re-validate the client certificate itself after the handshake. Instead, it relies on the TLS 1.3 handshake process to verify signatures using the raw public key via verify_tls13_signature_with_raw_key. Upon successful verification, it returns ClientCertVerified::assertion().
To configure a client with server verification:
use iroh::tls::{ServerCertificateVerifier};
use rustls::client::ClientConfig;
let client_config = ClientConfig::builder()
.with_safe_defaults()
.with_custom_certificate_verifier(
Arc::new(ServerCertificateVerifier::default())
)
.with_no_client_auth();
The Cryptographic Verification Layer
Ed25519 Signature Verification
The actual cryptographic verification is handled by the Ed25519Dalek struct in iroh/src/tls/verifier.rs, which implements the SignatureVerificationAlgorithm trait. This wrapper converts the raw key and signature into iroh's native PublicKey and Signature types (defined in iroh-base/src/key.rs) and calls public_key.verify(message, &signature).
The verification uses webpki_types::verify_tls13_signature_with_raw_key as the underlying mechanism, ensuring compatibility with the TLS 1.3 specification while using Ed25519 for the cryptographic operations.
Integration with the iroh Networking Stack
The TLS configuration is constructed in iroh/src/tls.rs, which wires the custom verifiers into rustls::ClientConfig and rustls::ServerConfig. When connections are established via iroh/src/socket.rs (through the connect and listen methods), the TLS session automatically runs these verifiers.
Once the handshake completes successfully, the verified public key is cached in the session and used for subsequent cryptographic operations, such as signing messages in the QUIC transport layer. This ensures that only peers possessing the correct Ed25519 private key can complete the handshake and participate in the network.
Summary
- iroh uses TLS 1.3 with raw public keys (RFC 7250) instead of traditional X.509 certificate chains
- Peer identities are encoded as DNS names in the format
iroh-<hex-encoded-public-key>and decoded iniroh/src/tls/name.rs - The
ServerCertificateVerifieriniroh/src/tls/verifier.rsvalidates servers by comparing the certificate's SPKI against the expected public key extracted from the DNS name - The
ClientCertificateVerifierperforms equivalent validation for client connections - Ed25519 signatures are verified using
webpki_types::verify_tls13_signature_with_raw_keyvia theEd25519Dalekwrapper - No certificate authorities are involved; authentication relies entirely on direct public key comparison during the TLS handshake
Frequently Asked Questions
Does iroh use traditional X.509 certificates for authentication?
No. iroh uses raw public keys (RFC 7250) rather than X.509 certificate chains. The system encodes the Ed25519 public key directly into a self-signed certificate and validates it by comparing the SPKI against the expected peer identity, eliminating the need for certificate authorities entirely.
What cryptographic algorithm does iroh use for peer identity verification?
iroh uses Ed25519 for all peer identity operations. The 32-byte public keys are encoded in DNS names, and signatures are verified using the Ed25519Dalek implementation in iroh/src/tls/verifier.rs, which wraps the Ed25519 verification algorithm for use with rustls.
How does iroh prevent man-in-the-middle attacks without certificate authorities?
iroh prevents MITM attacks by requiring exact byte-for-byte SPKI matches between the certificate presented during the TLS handshake and the expected public key encoded in the DNS name. Since the expected peer ID is known beforehand (typically distributed through the application layer or pre-shared), an attacker cannot substitute their own certificate without detection.
Can iroh interoperate with standard TLS implementations?
No, iroh cannot interoperate with standard TLS implementations that expect X.509 certificate chains. Both the client and server must support raw public keys and TLS 1.3. The custom verifiers in iroh/src/tls/verifier.rs explicitly require raw public key support and disable TLS 1.2, making interoperability with traditional TLS stacks impossible.
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 →