summaryrefslogtreecommitdiffstats
path: root/src/swarm/peer/admit.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-10-26 04:32:36 +0100
committertv <tv@krebsco.de>2026-10-26 04:32:36 +0100
commitda8340c7c1da0115189afe159f013557461b6a91 (patch)
tree86032907a6e2c00e07b576fa710d92b7151ca636 /src/swarm/peer/admit.rs
parent2f3d191a12b6e03ef1845c202d15c7b90736558c (diff)
split peer module
Diffstat (limited to 'src/swarm/peer/admit.rs')
-rw-r--r--src/swarm/peer/admit.rs79
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
+ }
+ }
+}