Last active
April 28, 2026 07:41
-
-
Save rklaehn/da71c07c312c0f809636dbbdcb3c5787 to your computer and use it in GitHub Desktop.
Use iroh with a post quantum key exchange algorithm (X25519MLKEM768)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //! 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