//! 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 } } }