diff options
| author | tv <tv@krebsco.de> | 2026-10-26 04:32:36 +0100 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-10-26 04:32:36 +0100 |
| commit | da8340c7c1da0115189afe159f013557461b6a91 (patch) | |
| tree | 86032907a6e2c00e07b576fa710d92b7151ca636 /src/swarm/peer/admit.rs | |
| parent | 2f3d191a12b6e03ef1845c202d15c7b90736558c (diff) | |
split peer module
Diffstat (limited to 'src/swarm/peer/admit.rs')
| -rw-r--r-- | src/swarm/peer/admit.rs | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/swarm/peer/admit.rs b/src/swarm/peer/admit.rs new file mode 100644 index 0000000..1800775 --- /dev/null +++ b/src/swarm/peer/admit.rs @@ -0,0 +1,79 @@ +//! Admission with a swarm key: only peers that proved it get past `admit`. + +use tracing::{debug, info, trace, warn}; + +use super::handshake::remember_peer; +use crate::state::{AppState, HelloState, Standing}; +use crate::transport::ParsedMessage; + +/// With a swarm key, only peers that proved it get past this point. A HELLO or +/// HELLO_REPLY must carry a valid tag, and it admits its sender; anything else is only +/// accepted from a peer admitted that way. Everything else is ignored without a reply, +/// so strangers learn nothing, not even whether we know the key. +pub(super) async fn admit(state: &AppState, peer: &str, message: &ParsedMessage) -> bool { + let Some(key) = &state.swarm_key else { + return true; + }; + let local = state.local_id(); + + let proof = match message { + ParsedMessage::Hello(hello) => Some(key.verify_hello( + &hello.auth, + hello.swarm_id.as_deref().unwrap_or(""), + peer, + local, + )), + ParsedMessage::HelloReply(reply) => { + Some(key.verify_hello_reply(&reply.auth, &reply.swarm_id, peer, local)) + } + _ => None, + }; + + match proof { + Some(true) => { + remember_peer(state, peer).await; + if let Some(peer_state) = state.get_peer_state(peer).await { + let mut peer_state = peer_state.lock().await; + if peer_state.standing == Standing::Stranger { + peer_state.standing = Standing::Admitted; + debug!(peer = %peer, "Peer proved the swarm key"); + } + } + true + } + Some(false) => { + // A known peer with a wrong key (e.g. one that was given another key) loses + // its admission until it proves the right one, and with it its membership: + // we stop syncing our document to it, not just listening to it. + if let Some(peer_state) = state.get_peer_state(peer).await { + let mut peer_state = peer_state.lock().await; + if peer_state.standing.is_admitted() { + warn!(peer = %peer, "Peer no longer proves the swarm key; dropping its membership"); + } + peer_state.standing = Standing::Stranger; + peer_state.hello_state = HelloState::NotSent; + peer_state.reset_sync(); + } + // The one place an operator can see why a node can't join: rate limited, + // since anyone can send us handshakes. + if let Some(suppressed) = key.note_rejection() { + info!( + peer = %peer, + suppressed, + "Rejected handshake without a valid swarm key proof" + ); + } + false + } + None => { + let admitted = match state.get_peer_state(peer).await { + Some(peer_state) => peer_state.lock().await.standing.is_admitted(), + None => false, + }; + if !admitted { + trace!(peer = %peer, "Ignoring message from a peer that hasn't proven the swarm key"); + } + admitted + } + } +} |
