Files
tether/core/examples/iroh_e2e.rs
Patrick Ecord 3f023402e3 core: iroh_e2e example — prove the clipboard is end-to-end encrypted
Three peers join the SAME room → SAME gossip topic, so all three receive the same
broadcast bytes off the wire: A sends, B (same account → same key) must decrypt,
C (wrong account → wrong key) must NOT. If C — sitting in the identical topic,
handed the same ciphertext — can't read what B can, the payload is sealed
end-to-end, not merely relayed. Documents/locks in the E2E property of the iroh
transport (ChaCha20-Poly1305 seal before gossip; rendezvous + topic membership
grant no content access).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:26:59 -05:00

84 lines
3.4 KiB
Rust

//! Proves the clipboard is genuinely END-TO-END encrypted over iroh.
//! TETHER_RENDEZVOUS=http://localhost:8765 cargo run --features iroh-transport --example iroh_e2e
//!
//! Three peers join the SAME room → SAME gossip topic, so all three receive the
//! same broadcast bytes off the wire:
//! A (sender) — account "shared" → key K1
//! B (right key) — account "shared" → key K1 → MUST decrypt
//! C (WRONG key) — account "wrong" → key K2 → MUST NOT decrypt
//! The room is derived from nothing here (passed explicitly), so B and C are in
//! the identical topic and C truly sees the ciphertext — it just can't open it.
//! If C can't read what A sent but B can, the payload is E2E-sealed, not merely
//! relayed in the clear.
use std::sync::mpsc;
use std::time::Duration;
use tethercore::{Engine, Message, MessageHandler};
struct Tap(&'static str, mpsc::Sender<(&'static str, String)>);
impl MessageHandler for Tap {
fn on_message(&self, m: Message) {
let _ = self.1.send((self.0, m.text));
}
fn on_status(&self, _c: bool) {}
fn on_file(&self, _n: String, _m: String, _d: Vec<u8>) {}
}
fn main() {
let server = std::env::var("TETHER_RENDEZVOUS").unwrap_or_else(|_| "http://localhost:8765".into());
let room = "e2e-verify";
let secret = "TOP-SECRET-CLIPBOARD-🔒";
let (tx, rx) = mpsc::channel();
// A = sender (opens the topic).
let a = Engine::new(server.clone(), room.into(), "peer-a".into(), "macos".into(), "A".into(), "shared-key".into());
a.start(Box::new(Tap("A", tx.clone())));
std::thread::sleep(Duration::from_secs(4)); // let A register as the bootstrap
// B = same account → same key (should decrypt). C = wrong account → wrong key.
let b = Engine::new(server.clone(), room.into(), "peer-b".into(), "ios".into(), "B".into(), "shared-key".into());
let c = Engine::new(server, room.into(), "peer-c".into(), "android".into(), "C".into(), "wrong-key".into());
b.start(Box::new(Tap("B", tx.clone())));
c.start(Box::new(Tap("C", tx)));
eprintln!("waiting for the gossip mesh to form…");
std::thread::sleep(Duration::from_secs(8));
eprintln!("A broadcasting (sealed): {secret:?}");
a.send(secret.to_string());
// Collect for a few seconds.
let mut b_got: Option<String> = None;
let mut c_got: Option<String> = None;
let deadline = std::time::Instant::now() + Duration::from_secs(8);
while std::time::Instant::now() < deadline {
if let Ok((who, text)) = rx.recv_timeout(Duration::from_millis(500)) {
match who {
"B" => b_got = Some(text),
"C" => c_got = Some(text),
_ => {}
}
}
if b_got.is_some() && c_got.is_some() {
break;
}
}
println!("\n── results ──");
println!("B (right key): {:?}", b_got);
println!("C (WRONG key): {:?}", c_got);
let b_ok = b_got.as_deref() == Some(secret);
let c_blind = c_got.is_none();
if b_ok && c_blind {
println!("\n✅ E2E PROVEN: B (shared key) decrypted the clipboard; C — in the SAME gossip");
println!(" topic, receiving the same bytes — could NOT read it (wrong key). The payload");
println!(" is sealed end-to-end, not just relayed.");
} else {
println!("\n❌ E2E check failed: b_ok={b_ok} c_blind={c_blind}");
std::process::exit(1);
}
a.stop();
b.stop();
c.stop();
}