secret_rotation/
secret_rotation.rs

1use anyhow::Result;
2use ed25519_dalek::SigningKey;
3use iroh::{Endpoint, SecretKey};
4use iroh_gossip::{api::Event, net::Gossip};
5use sha2::Digest;
6
7// Imports from distrubuted-topic-tracker
8use distributed_topic_tracker::{
9    AutoDiscoveryGossip, RecordPublisher, RotationHandle, SecretRotation, TopicId,
10};
11
12struct MySecretRotation;
13impl SecretRotation for MySecretRotation {
14    fn derive(
15        &self,
16        topic_hash: [u8; 32],
17        unix_minute: u64,
18        initial_secret_hash: [u8; 32],
19    ) -> [u8; 32] {
20        let mut hash = sha2::Sha512::new();
21        hash.update(topic_hash);
22        hash.update(unix_minute.to_be_bytes());
23        hash.update(initial_secret_hash);
24        hash.update(b"as long as you return 32 bytes this is a valid secret rotation function");
25        hash.finalize()[..32].try_into().expect("hashing failed")
26    }
27}
28
29#[tokio::main]
30async fn main() -> Result<()> {
31    // Generate a new random secret key
32    let secret_key = SecretKey::generate(&mut rand::rng());
33    let signing_key = SigningKey::from_bytes(&secret_key.to_bytes());
34
35    // Set up endpoint with discovery enabled
36    let endpoint = Endpoint::builder()
37        .secret_key(secret_key.clone())
38        .bind()
39        .await?;
40
41    // Initialize gossip with auto-discovery
42    let gossip = Gossip::builder().spawn(endpoint.clone());
43
44    // Set up protocol router
45    let _router = iroh::protocol::Router::builder(endpoint.clone())
46        .accept(iroh_gossip::ALPN, gossip.clone())
47        .spawn();
48
49    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
50    let initial_secret = b"my-initial-secret".to_vec();
51
52    // Split into sink (sending) and stream (receiving)
53
54    let record_publisher = RecordPublisher::new(
55        topic_id.clone(),
56        signing_key.verifying_key(),
57        signing_key.clone(),
58        Some(RotationHandle::new(MySecretRotation)),
59        initial_secret,
60    );
61    let (gossip_sender, gossip_receiver) = gossip
62        .subscribe_and_join_with_auto_discovery(record_publisher)
63        .await?
64        .split()
65        .await?;
66
67    println!("Joined topic");
68
69    // Spawn listener for incoming messages
70    tokio::spawn(async move {
71        while let Some(Ok(event)) = gossip_receiver.next().await {
72            if let Event::Received(msg) = event {
73                println!(
74                    "\nMessage from {}: {}",
75                    &msg.delivered_from.to_string()[0..8],
76                    String::from_utf8(msg.content.to_vec()).unwrap()
77                );
78            } else if let Event::NeighborUp(peer) = event {
79                println!("\nJoined by {}", &peer.to_string()[0..8]);
80            }
81        }
82    });
83
84    // Main input loop for sending messages
85    let mut buffer = String::new();
86    let stdin = std::io::stdin();
87    loop {
88        print!("\n> ");
89        stdin.read_line(&mut buffer).unwrap();
90        gossip_sender
91            .broadcast(buffer.clone().replace("\n", "").into())
92            .await
93            .unwrap();
94        println!(" - (sent)");
95        buffer.clear();
96    }
97}