mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-11 09:07:44 +00:00
Bind the FSP session address to the authenticated peer key, on both the initial and rekey paths
The responder recorded a session under the source address carried in the datagram without ever checking that address against the static key the Noise handshake had just authenticated. A peer could therefore complete a genuine handshake while claiming another node's address, and the identity cache, the session map and the address the IPv6 shim reconstructs on delivery would all attribute its traffic to the node it named. Derive the address from the authenticated key at the point the key first becomes available in msg3, and reject the handshake when it does not match the claimed source. The entry has already been removed by that point, so returning drops the half-open session and neither the identity nor the session is recorded. The rekey responder path needed its own check rather than inheriting that one. It returns before the initial path's code is reached, and it never read the peer's static key at all, so a rekey could complete under an established session with a different key than the one that opened it. It now requires the key to be unchanged, which is the stronger comparison available there, and abandons the rekey while keeping the existing session intact on mismatch. Tearing the session down instead would have handed an attacker a way to kill established sessions. Both comparisons are on x-only keys. A stored peer key may carry a synthesized even parity because npubs encode no parity, while the handshake learns the true point, so comparing full keys would reject roughly half of legitimate peers on every rekey. Both rejections are counted separately in the session reject statistics. The tests drive real Noise handshakes through the datagram entry point and construct the mismatch rather than asserting the comparison exists.
This commit is contained in:
+14
-1
@@ -177,7 +177,8 @@ pub enum HandshakeReject {
|
||||
/// FSP session rejection reasons.
|
||||
///
|
||||
/// `UnknownSession` and `BadState` cover the session unknown-session
|
||||
/// and state-machine cluster.
|
||||
/// and state-machine cluster. `AddrMismatch` and `RekeyKeyMismatch`
|
||||
/// cover the peer-identity binding checks on the XK msg3 receive path.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum SessionReject {
|
||||
@@ -196,6 +197,18 @@ pub enum SessionReject {
|
||||
/// is not `AwaitingMsg3`. Tracked via
|
||||
/// [`SessionStats::bad_state`](crate::node::stats::SessionStats).
|
||||
BadState,
|
||||
/// Inbound XK msg3 completed the handshake, but the initiator's
|
||||
/// static key does not derive the source address the datagram
|
||||
/// claimed — the peer is opening a session under another node's
|
||||
/// address. Tracked via
|
||||
/// [`SessionStats::addr_mismatch`](crate::node::stats::SessionStats).
|
||||
AddrMismatch,
|
||||
/// Inbound XK msg3 completed a responder-side rekey, but the
|
||||
/// initiator's static key differs from the key the session was
|
||||
/// established with — the rekey is not from the established peer.
|
||||
/// Tracked via
|
||||
/// [`SessionStats::rekey_key_mismatch`](crate::node::stats::SessionStats).
|
||||
RekeyKeyMismatch,
|
||||
}
|
||||
|
||||
/// MMP rejection reasons.
|
||||
|
||||
Reference in New Issue
Block a user