mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-10 08:37:02 +00:00
Notify initiator when inbound ACL rejects a Noise XX handshake
Under Noise XX the responder only sees the initiator's static key in msg3, so the inbound-handshake ACL check cannot fire until msg3 has already been processed. By then the initiator has received msg2, passed its own outbound ACL check, and promoted its side of the peering. Silently tearing down the responder's state left the initiator as a "connected" peer with no traffic until the link-dead timeout fired tens of seconds later; the acl-allowlist integration test on the next branch timed out at the 5s convergence check on blocked nodes. The responder now sends an encrypted Disconnect on the freshly-completed Noise session before tearing down. The initiator decodes it via the existing handle_disconnect path and cleans up the zombie peer within one RTT. DisconnectReason::Other is used instead of SecurityViolation so the wire payload does not name the ACL mechanism; the behavioural signature (explicit reject right after handshake) is the only observable leak. Supporting changes: - Add send_encrypted_link_message_raw in node/mod.rs that operates on a raw NoiseSession + indices without going through self.peers, since the peer has not yet been promoted at the reject site. - Declare the acl test module in node/tests/mod.rs. It had been orphaned since PR #50 so none of its tests ran; two of them no longer compiled against the current next branch. Fixed the two and replaced test_inbound_msg1_denied_by_acl (asserts IK-era behaviour that cannot happen under XX) with test_inbound_msg3_denied_triggers_disconnect, a full end-to-end UDP test covering both the responder cleanup and the initiator's Disconnect-driven cleanup.
This commit is contained in:
@@ -1901,6 +1901,63 @@ impl Node {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Encrypt and send a link-layer message using a raw Noise session.
|
||||
///
|
||||
/// Unlike `send_encrypted_link_message`, this does not look the peer up
|
||||
/// in `self.peers`. It takes the session and wire parameters directly,
|
||||
/// so it can be used during handshake teardown (before promotion) where
|
||||
/// the peer state lives in `self.connections` rather than `self.peers`.
|
||||
///
|
||||
/// The inner-header timestamp is set to 0 — the session has just been
|
||||
/// established and no session-elapsed reference is available yet; this
|
||||
/// is acceptable because the frame is a one-shot sent before teardown.
|
||||
/// No MMP stats or K-bit flag are recorded.
|
||||
pub(super) async fn send_encrypted_link_message_raw(
|
||||
&self,
|
||||
node_addr: NodeAddr,
|
||||
transport_id: crate::transport::TransportId,
|
||||
remote_addr: &crate::transport::TransportAddr,
|
||||
session: &mut crate::noise::NoiseSession,
|
||||
their_index: crate::utils::index::SessionIndex,
|
||||
plaintext: &[u8],
|
||||
) -> Result<(), NodeError> {
|
||||
let inner_plaintext = prepend_inner_header(0, plaintext);
|
||||
|
||||
let counter = session.current_send_counter();
|
||||
let payload_len = inner_plaintext.len() as u16;
|
||||
let header = build_established_header(their_index, counter, 0u8, payload_len);
|
||||
|
||||
let ciphertext = session
|
||||
.encrypt_with_aad(&inner_plaintext, &header)
|
||||
.map_err(|e| NodeError::SendFailed {
|
||||
node_addr,
|
||||
reason: format!("encryption failed: {}", e),
|
||||
})?;
|
||||
|
||||
let wire_packet = build_encrypted(&header, &ciphertext);
|
||||
|
||||
let transport = self
|
||||
.transports
|
||||
.get(&transport_id)
|
||||
.ok_or(NodeError::TransportNotFound(transport_id))?;
|
||||
|
||||
transport
|
||||
.send(remote_addr, &wire_packet)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| match e {
|
||||
TransportError::MtuExceeded { packet_size, mtu } => NodeError::MtuExceeded {
|
||||
node_addr,
|
||||
packet_size,
|
||||
mtu,
|
||||
},
|
||||
other => NodeError::SendFailed {
|
||||
node_addr,
|
||||
reason: format!("transport send: {}", other),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Node {
|
||||
|
||||
Reference in New Issue
Block a user