Fix session destruction race condition causing handshake delays

Root cause: When receiving a handshake initiation (32 bytes) from a peer
with whom we already had an established session, the code would destroy
the existing session to "help" the other side. This created a cascade:
- Peer A completes handshake with Peer B
- Peer A sends message, realizes no session, initiates handshake
- Peer B destroys its working session to "help"
- Peer B now has no session, initiates handshake
- Both peers keep destroying each other's sessions

Fix:
- Never destroy an established session when receiving new handshake attempts
- Add early check in handshake initiation to skip if session exists
- Clear handshake rate limit timers when session already established

This eliminates the delays and repeated handshakes seen in the logs.
This commit is contained in:
jack
2025-07-22 11:06:57 +02:00
parent 0be35a5378
commit 8fccbe69b9
2 changed files with 12 additions and 11 deletions
+3 -10
View File
@@ -319,18 +319,11 @@ class NoiseSessionManager {
var existingSession: NoiseSession? = nil
if let existing = sessions[peerID] {
// If we have an established session, we might need to help the other side complete theirs
// If we have an established session, reject new handshake attempts
if existing.isEstablished() {
// If this is a handshake initiation (32 bytes), the other side doesn't have a session
// We should complete the handshake to help them establish their session
if message.count == 32 {
// Remove existing session and create new one
sessions.removeValue(forKey: peerID)
shouldCreateNew = true
} else {
// For other handshake messages, ignore if already established
// Don't destroy our working session just because the other side is confused
// They should detect the established session through successful message exchange
throw NoiseSessionError.alreadyEstablished
}
} else {
// If we're in the middle of a handshake and receive a new initiation,
// reset and start fresh (the other side may have restarted)
@@ -3368,6 +3368,14 @@ extension BluetoothMeshService: CBPeripheralManagerDelegate {
SecurityLogger.log("Initiating Noise handshake with \(peerID)", category: SecurityLogger.noise, level: .info)
// Check if we already have an established session
if noiseService.hasEstablishedSession(with: peerID) {
SecurityLogger.log("Already have established session with \(peerID)", category: SecurityLogger.noise, level: .debug)
// Clear any lingering handshake attempt time
handshakeAttemptTimes.removeValue(forKey: peerID)
return
}
// Check if we've recently tried to handshake with this peer
if let lastAttempt = handshakeAttemptTimes[peerID],
Date().timeIntervalSince(lastAttempt) < handshakeTimeout {