Switch FMP handshake from Noise IK to XX with version negotiation

Replace the 2-message IK handshake with a 3-message XX handshake for
FMP link establishment. XX requires no prior knowledge of the peer's
static key — both identities are revealed during the handshake
(responder in msg2, initiator in msg3). This is the foundation for
the forklift upgrade that enables rolling protocol upgrades.

Changes:
- Noise XX state machine alongside IK/XK (8 unit tests)
- Protocol negotiation payload codec: format byte, packed version
  min/max, 64-bit feature bitfield, TLV extensions (11 unit tests)
- FMP wire format version 0→1, msg3 header/builder, TCP stream framing
- FMP handshake switched to XX: PeerConnection 3-message flow,
  handle_msg1 simplified (no identity), handle_msg2 sends msg3 and
  promotes initiator, new handle_msg3 promotes responder with
  restart/rekey/cross-connection detection
- Rekey handshake switched to XX with negotiation payload hash chain
  fix (decrypt-and-discard in complete_rekey_msg2/msg3)
- Negotiation payload in msg2/msg3 (FMP version [1,1], features=0)
- Debug logging for handshake promotion paths
- Integration test convergence timeouts adjusted for extra round-trip

Squashed commits:
- Add Noise XX state machine alongside IK/XK
- Add protocol negotiation payload codec
- FMP wire format prep: version 1, msg3 header support
- Switch FMP handshake from Noise IK to XX
- Increase convergence timeouts for XX 3-message handshake
- Fix negotiation hash chain desync in rekey handshake
This commit is contained in:
Johnathan Corgan
2026-04-11 08:16:01 +00:00
parent 9ccaae5044
commit 179689d6f2
20 changed files with 2486 additions and 977 deletions
+11 -9
View File
@@ -24,25 +24,27 @@ mod discovery;
mod error;
mod filter;
mod link;
mod negotiation;
mod session;
mod tree;
// Re-export all public types at protocol:: level
pub use discovery::{LookupRequest, LookupResponse};
pub use error::ProtocolError;
pub use filter::FilterAnnounce;
pub use link::{
Disconnect, DisconnectReason, HandshakeMessageType, LinkMessageType,
SESSION_DATAGRAM_HEADER_SIZE, SessionDatagram,
Disconnect, DisconnectReason, HandshakeMessageType, LinkMessageType, SessionDatagram,
SESSION_DATAGRAM_HEADER_SIZE,
};
pub use tree::TreeAnnounce;
pub use filter::FilterAnnounce;
pub use discovery::{LookupRequest, LookupResponse};
pub use negotiation::{NegotiationPayload, TlvEntry, NEGOTIATION_HEADER_SIZE};
pub use session::{
COORDS_REQUIRED_SIZE, CoordsRequired, FspFlags, FspInnerFlags, MTU_EXCEEDED_SIZE, MtuExceeded,
PATH_MTU_NOTIFICATION_SIZE, PathBroken, PathMtuNotification, SESSION_RECEIVER_REPORT_SIZE,
SESSION_SENDER_REPORT_SIZE, SessionAck, SessionFlags, SessionMessageType, SessionMsg3,
SessionReceiverReport, SessionSenderReport, SessionSetup,
CoordsRequired, FspFlags, FspInnerFlags, MtuExceeded, PathBroken, PathMtuNotification,
SessionAck, SessionFlags, SessionMessageType, SessionMsg3, SessionReceiverReport,
SessionSenderReport, SessionSetup, COORDS_REQUIRED_SIZE, MTU_EXCEEDED_SIZE,
PATH_MTU_NOTIFICATION_SIZE, SESSION_RECEIVER_REPORT_SIZE, SESSION_SENDER_REPORT_SIZE,
};
pub(crate) use session::{coords_wire_size, decode_optional_coords, encode_coords};
pub use tree::TreeAnnounce;
/// Protocol version for message compatibility.
pub const PROTOCOL_VERSION: u8 = 1;