Skip to content

Instantly share code, notes, and snippets.

@rklaehn
Last active April 28, 2026 07:41
Show Gist options
  • Select an option

  • Save rklaehn/da71c07c312c0f809636dbbdcb3c5787 to your computer and use it in GitHub Desktop.

Select an option

Save rklaehn/da71c07c312c0f809636dbbdcb3c5787 to your computer and use it in GitHub Desktop.
Use iroh with a post quantum key exchange algorithm (X25519MLKEM768)
//! Force iroh to negotiate ONLY a post-quantum key exchange (X25519MLKEM768).
//!
//! Requires `tls-aws-lc-rs` (the only rustls backend with ML-KEM today).
//! Stripping `kx_groups` to the PQ group makes PQ *required* rather than
//! *preferred*: peers without it fail the TLS handshake.
//!
//! Note: iroh's `crypto_provider` is shared with relay/discovery TLS, and
//! n0's infra speaks classical kx — so a PQ-only endpoint can't relay or
//! publish to pkarr today. This demo therefore disables relay and exchanges
//! the address out-of-band.
use std::sync::Arc;
use anyhow::Result;
use iroh::{
RelayMode,
endpoint::{Endpoint, presets::Empty},
};
use rustls::crypto::aws_lc_rs;
const ALPN: &[u8] = b"iroh-pq-demo/1";
#[tokio::main]
async fn main() -> Result<()> {
let pq = pq_only_provider();
let server = Endpoint::builder(Empty)
.crypto_provider(pq.clone())
.alpns(vec![ALPN.to_vec()])
.relay_mode(RelayMode::Disabled)
.bind()
.await?;
let server_addr = server.addr();
let server_task = tokio::spawn({
let server = server.clone();
async move {
let conn = server.accept().await.expect("incoming").accept()?.await?;
let mut recv = conn.accept_uni().await?;
let msg = recv.read_to_end(1024).await?;
println!("server received: {:?}", String::from_utf8_lossy(&msg));
let mut send = conn.open_uni().await?;
send.write_all(b"pong over PQ").await?;
send.finish()?;
conn.closed().await;
anyhow::Ok(())
}
});
let client = Endpoint::builder(Empty)
.crypto_provider(pq)
.relay_mode(RelayMode::Disabled)
.bind()
.await?;
let conn = client.connect(server_addr, ALPN).await?;
println!("client handshake done (X25519MLKEM768 was the only kx offered)");
let mut send = conn.open_uni().await?;
send.write_all(b"ping over PQ").await?;
send.finish()?;
let mut recv = conn.accept_uni().await?;
let msg = recv.read_to_end(1024).await?;
println!("client received: {:?}", String::from_utf8_lossy(&msg));
conn.close(0u32.into(), b"done");
server_task.await??;
client.close().await;
server.close().await;
Ok(())
}
fn pq_only_provider() -> Arc<rustls::crypto::CryptoProvider> {
let mut p = aws_lc_rs::default_provider();
p.kx_groups = vec![aws_lc_rs::kx_group::X25519MLKEM768];
Arc::new(p)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment