mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-27 05:45:19 +00:00
Bind Noise sessions to claimed peer identities
This commit is contained in:
@@ -49,7 +49,11 @@ final class BLENoisePacketHandler {
|
||||
self.environment = environment
|
||||
}
|
||||
|
||||
func handleHandshake(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||
/// Returns true when the handshake message was processed successfully.
|
||||
/// Callers use this to distinguish an authenticated replacement completion
|
||||
/// from a rejected candidate while an older session remains established.
|
||||
@discardableResult
|
||||
func handleHandshake(_ packet: BitchatPacket, from peerID: PeerID) -> Bool {
|
||||
let env = environment
|
||||
// Use NoiseEncryptionService for handshake processing
|
||||
if PeerID(hexData: packet.recipientID) == env.localPeerID() {
|
||||
@@ -72,14 +76,26 @@ final class BLENoisePacketHandler {
|
||||
|
||||
// Session establishment will trigger onPeerAuthenticated callback
|
||||
// which will send any pending messages at the right time
|
||||
return true
|
||||
} catch NoiseSessionError.peerIdentityMismatch {
|
||||
// The candidate was already discarded by the session manager.
|
||||
// Do not let a spoofed claimed ID trigger a fresh outbound
|
||||
// handshake or recreate state for the attacker-selected ID.
|
||||
SecureLogger.warning(
|
||||
"Rejected Noise handshake whose static key does not match \(peerID.id.prefix(8))…",
|
||||
category: .security
|
||||
)
|
||||
return false
|
||||
} catch {
|
||||
SecureLogger.error("Failed to process handshake: \(error)")
|
||||
// Try initiating a new handshake
|
||||
if !env.hasNoiseSession(peerID) {
|
||||
env.initiateHandshake(peerID)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func handleEncrypted(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||
|
||||
@@ -1792,7 +1792,44 @@ final class BLEService: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
private func handleLeave(_: BitchatPacket, from peerID: PeerID) {
|
||||
/// Accept a leave only when the claimed sender proves possession of the
|
||||
/// signing key bound by a verified announce. The persisted identity cache
|
||||
/// keeps delayed/relayed leaves verifiable after the live registry entry
|
||||
/// has aged out.
|
||||
private func handleLeave(_ packet: BitchatPacket, from peerID: PeerID) -> Bool {
|
||||
let registrySigningKey = collectionsQueue.sync {
|
||||
peerRegistry.info(for: peerID)?.signingPublicKey
|
||||
}
|
||||
let verifiedViaRegistry = registrySigningKey.map {
|
||||
noiseService.verifyPacketSignature(packet, publicKey: $0)
|
||||
} ?? false
|
||||
let verifiedViaPersistedIdentity = !verifiedViaRegistry
|
||||
&& identityManager.getCryptoIdentitiesByPeerIDPrefix(peerID).contains { identity in
|
||||
PeerID(publicKey: identity.publicKey) == peerID
|
||||
&& identity.signingPublicKey.map {
|
||||
noiseService.verifyPacketSignature(packet, publicKey: $0)
|
||||
} == true
|
||||
}
|
||||
|
||||
guard verifiedViaRegistry || verifiedViaPersistedIdentity else {
|
||||
SecureLogger.warning(
|
||||
"🚫 Dropping leave with missing/invalid signature for claimed sender \(peerID.id.prefix(8))…",
|
||||
category: .security
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
// A valid departure retires transport state too; otherwise
|
||||
// canDeliverSecurely could remain true for a peer we just removed.
|
||||
noiseService.clearSession(for: peerID)
|
||||
readLinkState { _ in
|
||||
let departedLinks = noiseAuthenticatedLinkOwners.compactMap { link, owner in
|
||||
owner == peerID ? link : nil
|
||||
}
|
||||
for link in departedLinks {
|
||||
noiseAuthenticatedLinkOwners.removeValue(forKey: link)
|
||||
}
|
||||
}
|
||||
_ = collectionsQueue.sync(flags: .barrier) {
|
||||
// Remove the peer when they leave
|
||||
peerRegistry.remove(peerID)
|
||||
@@ -1809,6 +1846,7 @@ final class BLEService: NSObject {
|
||||
self.deliverTransportEvent(.peerDisconnected(peerID))
|
||||
self.deliverTransportEvent(.peerListUpdated(currentPeerIDs))
|
||||
}
|
||||
return true
|
||||
}
|
||||
private func sendAnnounce(forceSend: Bool = false) {
|
||||
guard !isPanicSuspended else { return }
|
||||
@@ -2553,6 +2591,12 @@ extension BLEService {
|
||||
}
|
||||
}
|
||||
|
||||
func _test_isNoiseAuthenticatedCentral(_ centralUUID: String, for peerID: PeerID) -> Bool {
|
||||
bleQueue.sync {
|
||||
noiseAuthenticatedLinkOwners[.central(centralUUID)] == peerID
|
||||
}
|
||||
}
|
||||
|
||||
func _test_seedConnectedPeer(_ peerID: PeerID, nickname: String) {
|
||||
collectionsQueue.sync(flags: .barrier) {
|
||||
peerRegistry.upsert(BLEPeerInfo(
|
||||
@@ -5084,7 +5128,9 @@ extension BLEService {
|
||||
handleMeshPong(packet, from: senderID)
|
||||
|
||||
case .leave:
|
||||
handleLeave(packet, from: senderID)
|
||||
// A forged leave must neither evict the claimed peer nor spread
|
||||
// to downstream nodes.
|
||||
guard handleLeave(packet, from: senderID) else { return }
|
||||
|
||||
case .none:
|
||||
SecureLogger.warning("⚠️ Unknown message type: \(packet.type)", category: .session)
|
||||
@@ -5725,8 +5771,14 @@ extension BLEService {
|
||||
|
||||
private func handleNoiseHandshake(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||
let wasEstablished = noiseService.hasEstablishedSession(with: peerID)
|
||||
noisePacketHandler.handleHandshake(packet, from: peerID)
|
||||
if !wasEstablished, noiseService.hasEstablishedSession(with: peerID) {
|
||||
let processed = noisePacketHandler.handleHandshake(packet, from: peerID)
|
||||
let isEstablished = noiseService.hasEstablishedSession(with: peerID)
|
||||
// XX message 1 is exactly the unauthenticated 32-byte ephemeral key.
|
||||
// While replacing an existing session, do not authenticate its ingress
|
||||
// link until a later message completes and validates the candidate.
|
||||
let completedAuthenticatedHandshake = !wasEstablished
|
||||
|| packet.payload.count != NoiseSecurityConstants.xxInitialMessageSize
|
||||
if processed, isEstablished, completedAuthenticatedHandshake {
|
||||
markNoiseAuthenticatedIngressLink(for: packet, peerID: peerID)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user