Fix NoiseSessionManager to always accept handshake initiations

Previously, NoiseSessionManager would reject handshake initiations if it had an existing established session. This caused deadlocks when one peer cleared their session (e.g., after decryption failure) but the other peer rejected the new handshake.

Changes:
- NoiseSessionManager now always accepts handshake initiations, clearing any existing session
- Added comprehensive tests for handshake recovery scenarios
- Tests verify proper re-establishment after decryption failures and nonce desynchronization
This commit is contained in:
jack
2025-07-23 19:03:41 +02:00
parent a84d6f22ef
commit 2759202616
3 changed files with 252 additions and 5 deletions
+7 -5
View File
@@ -338,12 +338,14 @@ class NoiseSessionManager {
var existingSession: NoiseSession? = nil
if let existing = sessions[peerID] {
// If we have an established session, reject new handshake attempts
// If we have an established session, the peer must have cleared their session
// for a good reason (e.g., decryption failure, restart, etc.)
// We should accept the new handshake to re-establish encryption
if existing.isEstablished() {
// Don't destroy our working session just because the other side is confused
// They should detect the established session through successful message exchange
SecureLogger.log("Rejecting handshake attempt - session already established with \(peerID)", category: SecureLogger.session, level: .debug)
throw NoiseSessionError.alreadyEstablished
SecureLogger.log("Accepting handshake from \(peerID) despite existing session - peer likely cleared their session",
category: SecureLogger.session, level: .info)
_ = sessions.removeValue(forKey: peerID)
shouldCreateNew = true
} 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)