Fix gossip announce address binding
This commit is contained in:
+23
-6
@@ -79,6 +79,8 @@ enum GossipReject {
|
|||||||
BadSignature,
|
BadSignature,
|
||||||
/// Timestamp outside the freshness window — stale (replay) or implausibly future.
|
/// Timestamp outside the freshness window — stale (replay) or implausibly future.
|
||||||
OutOfWindow,
|
OutOfWindow,
|
||||||
|
/// A signed Announce advertised an address for a different node id.
|
||||||
|
AnnounceAddressMismatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authenticate a received payload against the room topic and local clock. The
|
/// Authenticate a received payload against the room topic and local clock. The
|
||||||
@@ -99,6 +101,10 @@ fn verify_gossip(
|
|||||||
if now_ms.abs_diff(payload.ts) > window_ms {
|
if now_ms.abs_diff(payload.ts) > window_ms {
|
||||||
return Err(GossipReject::OutOfWindow);
|
return Err(GossipReject::OutOfWindow);
|
||||||
}
|
}
|
||||||
|
if let GossipMessage::Announce(state) = &payload.msg
|
||||||
|
&& state.addr.id != payload.author {
|
||||||
|
return Err(GossipReject::AnnounceAddressMismatch);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,10 +496,8 @@ mod tests {
|
|||||||
use crate::network::PeerState;
|
use crate::network::PeerState;
|
||||||
use iroh::SecretKey;
|
use iroh::SecretKey;
|
||||||
|
|
||||||
fn sample_peer_state() -> PeerState {
|
fn sample_peer_state_for(id: EndpointId) -> PeerState {
|
||||||
let secret = SecretKey::generate();
|
let addr = iroh::EndpointAddr::from(id);
|
||||||
let public = secret.public();
|
|
||||||
let addr = iroh::EndpointAddr::from(public);
|
|
||||||
PeerState {
|
PeerState {
|
||||||
name: "TestPeerGossip".to_string(),
|
name: "TestPeerGossip".to_string(),
|
||||||
is_muted: true,
|
is_muted: true,
|
||||||
@@ -563,7 +567,7 @@ mod tests {
|
|||||||
fn test_gossip_payload_announce_round_trip() {
|
fn test_gossip_payload_announce_round_trip() {
|
||||||
let secret = SecretKey::generate();
|
let secret = SecretKey::generate();
|
||||||
let topic = [9u8; 32];
|
let topic = [9u8; 32];
|
||||||
let peer_state = sample_peer_state();
|
let peer_state = sample_peer_state_for(secret.public());
|
||||||
let payload = sign_gossip(&secret, &topic, 1000, GossipMessage::Announce(peer_state.clone()));
|
let payload = sign_gossip(&secret, &topic, 1000, GossipMessage::Announce(peer_state.clone()));
|
||||||
|
|
||||||
let serialized = serde_json::to_string(&payload).unwrap();
|
let serialized = serde_json::to_string(&payload).unwrap();
|
||||||
@@ -732,5 +736,18 @@ mod tests {
|
|||||||
// Within the window (clock skew tolerance) → accepted.
|
// Within the window (clock skew tolerance) → accepted.
|
||||||
assert!(verify_gossip(&p, &topic, 1_000_000 + GOSSIP_FRESHNESS_MS - 1, GOSSIP_FRESHNESS_MS).is_ok());
|
assert!(verify_gossip(&p, &topic, 1_000_000 + GOSSIP_FRESHNESS_MS - 1, GOSSIP_FRESHNESS_MS).is_ok());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn verify_rejects_announce_with_address_for_another_identity() {
|
||||||
|
let signer = SecretKey::generate();
|
||||||
|
let advertised = SecretKey::generate();
|
||||||
|
let topic = [6u8; 32];
|
||||||
|
let state = sample_peer_state_for(advertised.public());
|
||||||
|
let p = sign_gossip(&signer, &topic, 5_000, GossipMessage::Announce(state));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
verify_gossip(&p, &topic, 5_000, GOSSIP_FRESHNESS_MS),
|
||||||
|
Err(GossipReject::AnnounceAddressMismatch)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user