Files
fips/src
Johnathan Corgan 044d467387 Keep a rekey responder's handshake through a forged msg3 or setup
A session rekey responder that has sent its SessionAck holds the only
handshake the initiator's msg3 can complete: by then the initiator has read
the SessionAck and holds the new keys. Two unauthenticated messages could
discard that handshake inside the window. The responder arm of the
SessionMsg3 handler abandoned it when the msg3 read failed, though nothing
authenticates a msg3 before that read beyond a source address the sender
chooses. And a setup message naming the peer while the handshake was armed
ran the dual-initiation tie-break, which at the larger address abandoned the
handshake to answer the setup. Either way the genuine msg3 and its resends
found no handshake, the initiator cut over to keys this node never derived,
and frames from it stopped decoding until a later rekey completed. The
initial-handshake arm had the same flaw: it removed the half-open entry to
read msg3 and did not put it back when the read failed, so a garbage msg3
naming the initiator, arriving ahead of the genuine one, left the genuine
msg3 to an unknown session.

The Noise handshake gains a rolling-back msg3 read, shaped like the msg2
one, and both msg3 arms use it and put the handshake back on a failed read.
The rollback matters because the read advances the cipher nonce before it
opens the first AEAD, so a handshake put back after a plain read could never
read the genuine msg3. The restore leaves the rekey deadline, which runs from
the peer's setup, and the half-open entry's activity stamp unchanged, so a
spray cannot hold a handshake open. The abandons and drops after a
successful read stay: each follows a read that authenticated the sender.

A setup message is now dropped, at either address, while a handshake the
peer armed awaits its msg3, and counted as rekey_held; the tie-break runs
only when this node initiated the armed handshake. The cost is that a
genuine retry from a peer that abandoned the rekey this node answered
completes one handshake timeout later, once the held handshake expires, as
it already did at the smaller address.

New tests drive a genuine rekey to the point where the initiator has read the
SessionAck, deliver a garbage msg3 or, at the larger-address end, a forged
setup, and follow the genuine msg3, the cutover, the msg3 resend budget and
the next rekey cycle, asserting the forged setup was held off by the armed
handshake. Unit tests check that a forged msg3 leaves a stranger-armed
handshake able to read the msg3 that finishes it and a peer-armed one with
its deadline unchanged, that a second setup leaves a peer-armed handshake
and the completed epoch intact, and that a forged initial msg3 leaves the
half-open entry and its activity stamp untouched. Noise tests cover the rolling-back
read against a msg3 failing at either AEAD, with a control showing the plain
read cannot recover. The retry tests for a responder holding a stale
handshake assert rekey_held rather than the tie-break counters. No wire
format change.
2026-10-01 14:21:36 +00:00
..