From 1a6a08f92a31d08384a55deeddb4f3406b6ea7e2 Mon Sep 17 00:00:00 2001 From: jack Date: Thu, 11 Jun 2026 21:36:12 +0200 Subject: [PATCH] Make Noise-service replacement atomic with the identity swap During a panic reset, the new NoiseEncryptionService was assigned before the identity barrier ran, so a previously queued send block could observe the new crypto service alongside the old peer ID - signing with the new identity while carrying the old sender. The service teardown, replacement, callback configuration, and derived identity swap now run inside one messageQueue barrier (refreshPeerIdentity executes inline via its re-entrancy check), so queued sends see either the complete old identity or the complete new one, never a mix. Found by Codex review on #1336. Co-Authored-By: Claude Fable 5 --- bitchat/Services/BLE/BLEService.swift | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/bitchat/Services/BLE/BLEService.swift b/bitchat/Services/BLE/BLEService.swift index f761d88d..9584c5ae 100644 --- a/bitchat/Services/BLE/BLEService.swift +++ b/bitchat/Services/BLE/BLEService.swift @@ -317,13 +317,20 @@ final class BLEService: NSObject { } disconnectNotifyDebouncer.removeAll() - noiseService.clearEphemeralStateForPanic() - noiseService.clearPersistentIdentity() + // The crypto-service replacement and the derived identity swap must be + // one atomic unit with respect to messageQueue senders: a queued send + // must never observe the new Noise service alongside the old peer ID + // (it would sign with the new identity while carrying the old sender). + // refreshPeerIdentity() executes inline here via its re-entrancy check. + messageQueue.sync(flags: .barrier) { + noiseService.clearEphemeralStateForPanic() + noiseService.clearPersistentIdentity() - let newNoise = NoiseEncryptionService(keychain: keychain) - noiseService = newNoise - configureNoiseServiceCallbacks(for: newNoise) - refreshPeerIdentity() + let newNoise = NoiseEncryptionService(keychain: keychain) + noiseService = newNoise + configureNoiseServiceCallbacks(for: newNoise) + refreshPeerIdentity() + } restartGossipManager() setNickname(currentNickname)