mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 05:05:19 +00:00
PeerID 20/n: BLEService’s private functions (#771)
This commit is contained in:
@@ -549,7 +549,7 @@ final class BLEService: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func triggerHandshake(with peerID: PeerID) {
|
func triggerHandshake(with peerID: PeerID) {
|
||||||
initiateNoiseHandshake(with: peerID.id)
|
initiateNoiseHandshake(with: peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNoiseService() -> NoiseEncryptionService {
|
func getNoiseService() -> NoiseEncryptionService {
|
||||||
@@ -604,7 +604,7 @@ final class BLEService: NSObject {
|
|||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pendingNoisePayloadsAfterHandshake[peerID, default: []].append(payload)
|
self.pendingNoisePayloadsAfterHandshake[peerID, default: []].append(payload)
|
||||||
}
|
}
|
||||||
if !noiseService.hasSession(with: peerID) { initiateNoiseHandshake(with: peerID.id) }
|
if !noiseService.hasSession(with: peerID) { initiateNoiseHandshake(with: peerID) }
|
||||||
SecureLogger.debug("🕒 Queued READ receipt for \(peerID) until handshake completes", category: .session)
|
SecureLogger.debug("🕒 Queued READ receipt for \(peerID) until handshake completes", category: .session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -656,7 +656,7 @@ final class BLEService: NSObject {
|
|||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.pendingNoisePayloadsAfterHandshake[peerID, default: []].append(payload)
|
self.pendingNoisePayloadsAfterHandshake[peerID, default: []].append(payload)
|
||||||
}
|
}
|
||||||
if !noiseService.hasSession(with: peerID) { initiateNoiseHandshake(with: peerID.id) }
|
if !noiseService.hasSession(with: peerID) { initiateNoiseHandshake(with: peerID) }
|
||||||
SecureLogger.debug("🕒 Queued DELIVERED ack for \(peerID) until handshake completes", category: .session)
|
SecureLogger.debug("🕒 Queued DELIVERED ack for \(peerID) until handshake completes", category: .session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -665,12 +665,12 @@ final class BLEService: NSObject {
|
|||||||
|
|
||||||
func sendVerifyChallenge(to peerID: PeerID, noiseKeyHex: String, nonceA: Data) {
|
func sendVerifyChallenge(to peerID: PeerID, noiseKeyHex: String, nonceA: Data) {
|
||||||
let payload = VerificationService.shared.buildVerifyChallenge(noiseKeyHex: noiseKeyHex, nonceA: nonceA)
|
let payload = VerificationService.shared.buildVerifyChallenge(noiseKeyHex: noiseKeyHex, nonceA: nonceA)
|
||||||
sendNoisePayload(payload, to: peerID.id)
|
sendNoisePayload(payload, to: peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendVerifyResponse(to peerID: PeerID, noiseKeyHex: String, nonceA: Data) {
|
func sendVerifyResponse(to peerID: PeerID, noiseKeyHex: String, nonceA: Data) {
|
||||||
guard let payload = VerificationService.shared.buildVerifyResponse(noiseKeyHex: noiseKeyHex, nonceA: nonceA) else { return }
|
guard let payload = VerificationService.shared.buildVerifyResponse(noiseKeyHex: noiseKeyHex, nonceA: nonceA) else { return }
|
||||||
sendNoisePayload(payload, to: peerID.id)
|
sendNoisePayload(payload, to: peerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,9 +686,9 @@ extension BLEService: GossipSyncManager.Delegate {
|
|||||||
|
|
||||||
func sendPacket(to peerID: PeerID, packet: BitchatPacket) {
|
func sendPacket(to peerID: PeerID, packet: BitchatPacket) {
|
||||||
if DispatchQueue.getSpecific(key: messageQueueKey) != nil {
|
if DispatchQueue.getSpecific(key: messageQueueKey) != nil {
|
||||||
sendPacketDirected(packet, to: peerID.id)
|
sendPacketDirected(packet, to: peerID)
|
||||||
} else {
|
} else {
|
||||||
messageQueue.async { [weak self] in self?.sendPacketDirected(packet, to: peerID.id) }
|
messageQueue.async { [weak self] in self?.sendPacketDirected(packet, to: peerID) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -939,8 +939,8 @@ func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeriph
|
|||||||
// Get current peer list (after removal)
|
// Get current peer list (after removal)
|
||||||
let currentPeerIDs = self.collectionsQueue.sync { self.currentPeerIDs }
|
let currentPeerIDs = self.collectionsQueue.sync { self.currentPeerIDs }
|
||||||
|
|
||||||
if let peerID = peerID {
|
if let peerID {
|
||||||
self.notifyPeerDisconnectedDebounced(peerID.id)
|
self.notifyPeerDisconnectedDebounced(peerID)
|
||||||
}
|
}
|
||||||
self.requestPeerDataPublish()
|
self.requestPeerDataPublish()
|
||||||
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
||||||
@@ -1034,14 +1034,14 @@ extension BLEService {
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Test-only helper to inject packets into the receive pipeline
|
// Test-only helper to inject packets into the receive pipeline
|
||||||
extension BLEService {
|
extension BLEService {
|
||||||
func _test_handlePacket(_ packet: BitchatPacket, fromPeerID: String) {
|
func _test_handlePacket(_ packet: BitchatPacket, fromPeerID: PeerID) {
|
||||||
// Ensure the synthetic peer is known and marked verified for public-message tests
|
// Ensure the synthetic peer is known and marked verified for public-message tests
|
||||||
let normalizedID = PeerID(hexData: packet.senderID)
|
let normalizedID = PeerID(hexData: packet.senderID)
|
||||||
collectionsQueue.sync(flags: .barrier) {
|
collectionsQueue.sync(flags: .barrier) {
|
||||||
if peers[normalizedID] == nil {
|
if peers[normalizedID] == nil {
|
||||||
peers[normalizedID] = PeerInfo(
|
peers[normalizedID] = PeerInfo(
|
||||||
peerID: normalizedID,
|
peerID: normalizedID,
|
||||||
nickname: "TestPeer_\(fromPeerID.prefix(4))",
|
nickname: "TestPeer_\(fromPeerID.id.prefix(4))",
|
||||||
isConnected: true,
|
isConnected: true,
|
||||||
noisePublicKey: packet.senderID,
|
noisePublicKey: packet.senderID,
|
||||||
signingPublicKey: nil,
|
signingPublicKey: nil,
|
||||||
@@ -1218,13 +1218,13 @@ extension BLEService: CBPeripheralDelegate {
|
|||||||
collectionsQueue.async(flags: .barrier) { [weak self] in
|
collectionsQueue.async(flags: .barrier) { [weak self] in
|
||||||
self?.ingressByMessageID[msgID] = (.peripheral(peripheralUUID), Date())
|
self?.ingressByMessageID[msgID] = (.peripheral(peripheralUUID), Date())
|
||||||
}
|
}
|
||||||
handleReceivedPacket(packet, from: senderID.id)
|
handleReceivedPacket(packet, from: senderID)
|
||||||
} else {
|
} else {
|
||||||
let msgID = makeMessageID(for: packet)
|
let msgID = makeMessageID(for: packet)
|
||||||
collectionsQueue.async(flags: .barrier) { [weak self] in
|
collectionsQueue.async(flags: .barrier) { [weak self] in
|
||||||
self?.ingressByMessageID[msgID] = (.peripheral(peripheralUUID), Date())
|
self?.ingressByMessageID[msgID] = (.peripheral(peripheralUUID), Date())
|
||||||
}
|
}
|
||||||
handleReceivedPacket(packet, from: senderID.id)
|
handleReceivedPacket(packet, from: senderID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1360,7 +1360,7 @@ extension BLEService: CBPeripheralManagerDelegate {
|
|||||||
// Get current peer list (after removal)
|
// Get current peer list (after removal)
|
||||||
let currentPeerIDs = self.collectionsQueue.sync { self.currentPeerIDs }
|
let currentPeerIDs = self.collectionsQueue.sync { self.currentPeerIDs }
|
||||||
|
|
||||||
self.notifyPeerDisconnectedDebounced(peerID.id)
|
self.notifyPeerDisconnectedDebounced(peerID)
|
||||||
// Publish snapshots so UnifiedPeerService can refresh icons promptly
|
// Publish snapshots so UnifiedPeerService can refresh icons promptly
|
||||||
self.requestPeerDataPublish()
|
self.requestPeerDataPublish()
|
||||||
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
||||||
@@ -1460,7 +1460,7 @@ extension BLEService: CBPeripheralManagerDelegate {
|
|||||||
if let packet = BinaryProtocol.decode(combined) {
|
if let packet = BinaryProtocol.decode(combined) {
|
||||||
// Clear buffer on success
|
// Clear buffer on success
|
||||||
pendingWriteBuffers.removeValue(forKey: centralUUID)
|
pendingWriteBuffers.removeValue(forKey: centralUUID)
|
||||||
let senderID = packet.senderID.hexEncodedString()
|
let senderID = PeerID(hexData: packet.senderID)
|
||||||
if packet.type != MessageType.announce.rawValue {
|
if packet.type != MessageType.announce.rawValue {
|
||||||
SecureLogger.debug("📦 Decoded (combined) packet type: \(packet.type) from sender: \(senderID)", category: .session)
|
SecureLogger.debug("📦 Decoded (combined) packet type: \(packet.type) from sender: \(senderID)", category: .session)
|
||||||
}
|
}
|
||||||
@@ -1468,7 +1468,7 @@ extension BLEService: CBPeripheralManagerDelegate {
|
|||||||
subscribedCentrals.append(sorted[0].central)
|
subscribedCentrals.append(sorted[0].central)
|
||||||
}
|
}
|
||||||
if packet.type == MessageType.announce.rawValue {
|
if packet.type == MessageType.announce.rawValue {
|
||||||
if packet.ttl == messageTTL { centralToPeerID[centralUUID] = PeerID(str: senderID) }
|
if packet.ttl == messageTTL { centralToPeerID[centralUUID] = senderID }
|
||||||
// Record ingress link for last-hop suppression then process
|
// Record ingress link for last-hop suppression then process
|
||||||
let msgID = makeMessageID(for: packet)
|
let msgID = makeMessageID(for: packet)
|
||||||
collectionsQueue.async(flags: .barrier) { [weak self] in
|
collectionsQueue.async(flags: .barrier) { [weak self] in
|
||||||
@@ -1527,6 +1527,7 @@ extension BLEService {
|
|||||||
|
|
||||||
private func configureNoiseServiceCallbacks(for service: NoiseEncryptionService) {
|
private func configureNoiseServiceCallbacks(for service: NoiseEncryptionService) {
|
||||||
service.onPeerAuthenticated = { [weak self] peerID, fingerprint in
|
service.onPeerAuthenticated = { [weak self] peerID, fingerprint in
|
||||||
|
let peerID = PeerID(str: peerID)
|
||||||
SecureLogger.debug("🔐 Noise session authenticated with \(peerID), fingerprint: \(fingerprint.prefix(16))...")
|
SecureLogger.debug("🔐 Noise session authenticated with \(peerID), fingerprint: \(fingerprint.prefix(16))...")
|
||||||
self?.messageQueue.async { [weak self] in
|
self?.messageQueue.async { [weak self] in
|
||||||
self?.sendPendingMessagesAfterHandshake(for: peerID)
|
self?.sendPendingMessagesAfterHandshake(for: peerID)
|
||||||
@@ -1552,18 +1553,18 @@ extension BLEService {
|
|||||||
gossipSyncManager = sync
|
gossipSyncManager = sync
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sendNoisePayload(_ typedPayload: Data, to peerID: String) {
|
private func sendNoisePayload(_ typedPayload: Data, to peerID: PeerID) {
|
||||||
guard noiseService.hasSession(with: PeerID(str: peerID)) else {
|
guard noiseService.hasSession(with: peerID) else {
|
||||||
// Lazy-handshake path: queue? For now, initiate handshake and drop
|
// Lazy-handshake path: queue? For now, initiate handshake and drop
|
||||||
initiateNoiseHandshake(with: peerID)
|
initiateNoiseHandshake(with: peerID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let encrypted = try noiseService.encrypt(typedPayload, for: PeerID(str: peerID))
|
let encrypted = try noiseService.encrypt(typedPayload, for: peerID)
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
type: MessageType.noiseEncrypted.rawValue,
|
type: MessageType.noiseEncrypted.rawValue,
|
||||||
senderID: myPeerIDData,
|
senderID: myPeerIDData,
|
||||||
recipientID: Data(hexString: peerID),
|
recipientID: Data(hexString: peerID.id),
|
||||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||||
payload: encrypted,
|
payload: encrypted,
|
||||||
signature: nil,
|
signature: nil,
|
||||||
@@ -1801,7 +1802,7 @@ extension BLEService {
|
|||||||
pendingMessagesAfterHandshake[recipientID]?.append((content, messageID))
|
pendingMessagesAfterHandshake[recipientID]?.append((content, messageID))
|
||||||
}
|
}
|
||||||
|
|
||||||
initiateNoiseHandshake(with: recipientID.id)
|
initiateNoiseHandshake(with: recipientID)
|
||||||
|
|
||||||
// Notify delegate that message is pending
|
// Notify delegate that message is pending
|
||||||
notifyUI { [weak self] in
|
notifyUI { [weak self] in
|
||||||
@@ -1810,18 +1811,18 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initiateNoiseHandshake(with peerID: String) {
|
private func initiateNoiseHandshake(with peerID: PeerID) {
|
||||||
// Use NoiseEncryptionService for handshake
|
// Use NoiseEncryptionService for handshake
|
||||||
guard !noiseService.hasSession(with: PeerID(str: peerID)) else { return }
|
guard !noiseService.hasSession(with: peerID) else { return }
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let handshakeData = try noiseService.initiateHandshake(with: PeerID(str: peerID))
|
let handshakeData = try noiseService.initiateHandshake(with: peerID)
|
||||||
|
|
||||||
// Send handshake init
|
// Send handshake init
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
type: MessageType.noiseHandshake.rawValue,
|
type: MessageType.noiseHandshake.rawValue,
|
||||||
senderID: myPeerIDData,
|
senderID: myPeerIDData,
|
||||||
recipientID: Data(hexString: peerID),
|
recipientID: Data(hexString: peerID.id),
|
||||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||||
payload: handshakeData,
|
payload: handshakeData,
|
||||||
signature: nil,
|
signature: nil,
|
||||||
@@ -1840,8 +1841,7 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sendPendingMessagesAfterHandshake(for peerID: String) {
|
private func sendPendingMessagesAfterHandshake(for peerID: PeerID) {
|
||||||
let peerID = PeerID(str: peerID)
|
|
||||||
// Get and clear pending messages for this peer
|
// Get and clear pending messages for this peer
|
||||||
let pendingMessages = collectionsQueue.sync(flags: .barrier) { () -> [(content: String, messageID: String)]? in
|
let pendingMessages = collectionsQueue.sync(flags: .barrier) { () -> [(content: String, messageID: String)]? in
|
||||||
let messages = pendingMessagesAfterHandshake[peerID]
|
let messages = pendingMessagesAfterHandshake[peerID]
|
||||||
@@ -1946,13 +1946,13 @@ extension BLEService {
|
|||||||
if let pm = peripheralMaxLen, data.count > pm {
|
if let pm = peripheralMaxLen, data.count > pm {
|
||||||
let overhead = 13 + 8 + 8 + 13
|
let overhead = 13 + 8 + 8 + 13
|
||||||
let chunk = max(64, pm - overhead)
|
let chunk = max(64, pm - overhead)
|
||||||
sendFragmentedPacket(packet, pad: pad, maxChunk: chunk, directedOnlyPeer: recipientPeerID.id)
|
sendFragmentedPacket(packet, pad: pad, maxChunk: chunk, directedOnlyPeer: recipientPeerID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let cm = centralMaxLen, data.count > cm {
|
if let cm = centralMaxLen, data.count > cm {
|
||||||
let overhead = 13 + 8 + 8 + 13
|
let overhead = 13 + 8 + 8 + 13
|
||||||
let chunk = max(64, cm - overhead)
|
let chunk = max(64, cm - overhead)
|
||||||
sendFragmentedPacket(packet, pad: pad, maxChunk: chunk, directedOnlyPeer: recipientPeerID.id)
|
sendFragmentedPacket(packet, pad: pad, maxChunk: chunk, directedOnlyPeer: recipientPeerID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1983,7 +1983,7 @@ extension BLEService {
|
|||||||
|
|
||||||
if !sentEncrypted {
|
if !sentEncrypted {
|
||||||
// Flood as last resort with recipient set; link aware
|
// Flood as last resort with recipient set; link aware
|
||||||
sendOnAllLinks(packet: packet, data: data, pad: pad, directedOnlyPeer: recipientPeerID.id)
|
sendOnAllLinks(packet: packet, data: data, pad: pad, directedOnlyPeer: recipientPeerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1991,14 +1991,14 @@ extension BLEService {
|
|||||||
sendOnAllLinks(packet: packet, data: data, pad: pad, directedOnlyPeer: nil)
|
sendOnAllLinks(packet: packet, data: data, pad: pad, directedOnlyPeer: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sendOnAllLinks(packet: BitchatPacket, data: Data, pad: Bool, directedOnlyPeer: String?) {
|
private func sendOnAllLinks(packet: BitchatPacket, data: Data, pad: Bool, directedOnlyPeer: PeerID?) {
|
||||||
// Determine last-hop link for this message to avoid echoing back
|
// Determine last-hop link for this message to avoid echoing back
|
||||||
let messageID = makeMessageID(for: packet)
|
let messageID = makeMessageID(for: packet)
|
||||||
let ingressLink: LinkID? = collectionsQueue.sync { ingressByMessageID[messageID]?.link }
|
let ingressLink: LinkID? = collectionsQueue.sync { ingressByMessageID[messageID]?.link }
|
||||||
let directedPeerHint: String? = {
|
let directedPeerHint: PeerID? = {
|
||||||
if let explicit = directedOnlyPeer { return explicit }
|
if let explicit = directedOnlyPeer { return explicit }
|
||||||
if let recipient = packet.recipientID?.hexEncodedString(), !recipient.isEmpty {
|
if let recipient = packet.recipientID?.hexEncodedString(), !recipient.isEmpty {
|
||||||
return recipient
|
return PeerID(str: recipient)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}()
|
}()
|
||||||
@@ -2090,14 +2090,13 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Directed send helper (unicast to a specific peerID) without altering packet contents
|
// Directed send helper (unicast to a specific peerID) without altering packet contents
|
||||||
private func sendPacketDirected(_ packet: BitchatPacket, to peerID: String) {
|
private func sendPacketDirected(_ packet: BitchatPacket, to peerID: PeerID) {
|
||||||
guard let data = packet.toBinaryData(padding: false) else { return }
|
guard let data = packet.toBinaryData(padding: false) else { return }
|
||||||
sendOnAllLinks(packet: packet, data: data, pad: false, directedOnlyPeer: peerID)
|
sendOnAllLinks(packet: packet, data: data, pad: false, directedOnlyPeer: peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Directed store-and-forward
|
// MARK: Directed store-and-forward
|
||||||
private func spoolDirectedPacket(_ packet: BitchatPacket, recipientPeerID: String) {
|
private func spoolDirectedPacket(_ packet: BitchatPacket, recipientPeerID: PeerID) {
|
||||||
let recipientPeerID = PeerID(str: recipientPeerID)
|
|
||||||
let msgID = makeMessageID(for: packet)
|
let msgID = makeMessageID(for: packet)
|
||||||
collectionsQueue.async(flags: .barrier) { [weak self] in
|
collectionsQueue.async(flags: .barrier) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@@ -2162,7 +2161,7 @@ extension BLEService {
|
|||||||
|
|
||||||
// MARK: Fragmentation (Required for messages > BLE MTU)
|
// MARK: Fragmentation (Required for messages > BLE MTU)
|
||||||
|
|
||||||
private func sendFragmentedPacket(_ packet: BitchatPacket, pad: Bool, maxChunk: Int? = nil, directedOnlyPeer: String? = nil) {
|
private func sendFragmentedPacket(_ packet: BitchatPacket, pad: Bool, maxChunk: Int? = nil, directedOnlyPeer: PeerID? = nil) {
|
||||||
guard let fullData = packet.toBinaryData(padding: pad) else { return }
|
guard let fullData = packet.toBinaryData(padding: pad) else { return }
|
||||||
// Fragment the unpadded frame; each fragment will be encoded independently
|
// Fragment the unpadded frame; each fragment will be encoded independently
|
||||||
|
|
||||||
@@ -2197,7 +2196,7 @@ extension BLEService {
|
|||||||
|
|
||||||
// Choose recipient for the fragment: directed override if provided
|
// Choose recipient for the fragment: directed override if provided
|
||||||
let fragmentRecipient: Data? = {
|
let fragmentRecipient: Data? = {
|
||||||
if let only = directedOnlyPeer { return Data(hexString: only) }
|
if let only = directedOnlyPeer { return Data(hexString: only.id) }
|
||||||
return packet.recipientID
|
return packet.recipientID
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -2219,7 +2218,7 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleFragment(_ packet: BitchatPacket, from peerID: String) {
|
private func handleFragment(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
// Don't process our own fragments
|
// Don't process our own fragments
|
||||||
if peerID == myPeerID {
|
if peerID == myPeerID {
|
||||||
return
|
return
|
||||||
@@ -2288,9 +2287,9 @@ extension BLEService {
|
|||||||
|
|
||||||
// MARK: Packet Reception
|
// MARK: Packet Reception
|
||||||
|
|
||||||
private func handleReceivedPacket(_ packet: BitchatPacket, from peerID: String) {
|
private func handleReceivedPacket(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
// Deduplication (thread-safe)
|
// Deduplication (thread-safe)
|
||||||
let senderID = packet.senderID.hexEncodedString()
|
let senderID = PeerID(hexData: packet.senderID)
|
||||||
// Include packet type in message ID to prevent collisions between different packet types
|
// Include packet type in message ID to prevent collisions between different packet types
|
||||||
let messageID = "\(senderID)-\(packet.timestamp)-\(packet.type)"
|
let messageID = "\(senderID)-\(packet.timestamp)-\(packet.type)"
|
||||||
|
|
||||||
@@ -2403,8 +2402,7 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleAnnounce(_ packet: BitchatPacket, from peerID: String) {
|
private func handleAnnounce(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
let peerID = PeerID(str: peerID)
|
|
||||||
guard let announcement = AnnouncementPacket.decode(from: packet.payload) else {
|
guard let announcement = AnnouncementPacket.decode(from: packet.payload) else {
|
||||||
SecureLogger.error("❌ Failed to decode announce packet from \(peerID)", category: .session)
|
SecureLogger.error("❌ Failed to decode announce packet from \(peerID)", category: .session)
|
||||||
return
|
return
|
||||||
@@ -2595,17 +2593,17 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle REQUEST_SYNC: decode payload and respond with missing packets via sync manager
|
// Handle REQUEST_SYNC: decode payload and respond with missing packets via sync manager
|
||||||
private func handleRequestSync(_ packet: BitchatPacket, from peerID: String) {
|
private func handleRequestSync(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
guard let req = RequestSyncPacket.decode(from: packet.payload) else {
|
guard let req = RequestSyncPacket.decode(from: packet.payload) else {
|
||||||
SecureLogger.warning("⚠️ Malformed REQUEST_SYNC from \(peerID)", category: .session)
|
SecureLogger.warning("⚠️ Malformed REQUEST_SYNC from \(peerID)", category: .session)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gossipSyncManager?.handleRequestSync(from: PeerID(str: peerID), request: req)
|
gossipSyncManager?.handleRequestSync(from: peerID, request: req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mention parsing moved to ChatViewModel
|
// Mention parsing moved to ChatViewModel
|
||||||
|
|
||||||
private func handleMessage(_ packet: BitchatPacket, from peerID: String) {
|
private func handleMessage(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
// Ignore self-origin public messages except when returned via sync (TTL==0).
|
// Ignore self-origin public messages except when returned via sync (TTL==0).
|
||||||
// This allows our own messages to be surfaced when they come back via
|
// This allows our own messages to be surfaced when they come back via
|
||||||
// the sync path without re-processing regular relayed copies.
|
// the sync path without re-processing regular relayed copies.
|
||||||
@@ -2625,7 +2623,7 @@ extension BLEService {
|
|||||||
if nowMs >= ageThresholdMs {
|
if nowMs >= ageThresholdMs {
|
||||||
let cutoffMs = nowMs - ageThresholdMs
|
let cutoffMs = nowMs - ageThresholdMs
|
||||||
if packet.timestamp < cutoffMs {
|
if packet.timestamp < cutoffMs {
|
||||||
SecureLogger.debug("⏰ Ignoring stale broadcast message from \(peerID.prefix(8))… (age: \(Double(nowMs - packet.timestamp) / 1000.0)s)", category: .session)
|
SecureLogger.debug("⏰ Ignoring stale broadcast message from \(peerID.id.prefix(8))… (age: \(Double(nowMs - packet.timestamp) / 1000.0)s)", category: .session)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2639,20 +2637,20 @@ extension BLEService {
|
|||||||
accepted = true
|
accepted = true
|
||||||
senderNickname = myNickname
|
senderNickname = myNickname
|
||||||
}
|
}
|
||||||
else if let info = peers[PeerID(str: peerID)], info.isVerifiedNickname {
|
else if let info = peers[peerID], info.isVerifiedNickname {
|
||||||
// Known verified peer path
|
// Known verified peer path
|
||||||
accepted = true
|
accepted = true
|
||||||
senderNickname = info.nickname
|
senderNickname = info.nickname
|
||||||
// Handle nickname collisions
|
// Handle nickname collisions
|
||||||
let hasCollision = peers.values.contains { $0.isConnected && $0.nickname == info.nickname && $0.peerID != peerID } || (myNickname == info.nickname)
|
let hasCollision = peers.values.contains { $0.isConnected && $0.nickname == info.nickname && $0.peerID != peerID } || (myNickname == info.nickname)
|
||||||
if hasCollision {
|
if hasCollision {
|
||||||
senderNickname += "#" + String(peerID.prefix(4))
|
senderNickname += "#" + String(peerID.id.prefix(4))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback: verify signature using persisted signing key for this peerID's fingerprint prefix
|
// Fallback: verify signature using persisted signing key for this peerID's fingerprint prefix
|
||||||
if let signature = packet.signature, let packetData = packet.toBinaryDataForSigning() {
|
if let signature = packet.signature, let packetData = packet.toBinaryDataForSigning() {
|
||||||
// Find candidate identities by peerID prefix (16 hex)
|
// Find candidate identities by peerID prefix (16 hex)
|
||||||
let candidates = identityManager.getCryptoIdentitiesByPeerIDPrefix(PeerID(str: peerID))
|
let candidates = identityManager.getCryptoIdentitiesByPeerIDPrefix(peerID)
|
||||||
for candidate in candidates {
|
for candidate in candidates {
|
||||||
if let signingKey = candidate.signingPublicKey,
|
if let signingKey = candidate.signingPublicKey,
|
||||||
noiseService.verifySignature(signature, for: packetData, publicKey: signingKey) {
|
noiseService.verifySignature(signature, for: packetData, publicKey: signingKey) {
|
||||||
@@ -2661,7 +2659,7 @@ extension BLEService {
|
|||||||
if let social = identityManager.getSocialIdentity(for: candidate.fingerprint) {
|
if let social = identityManager.getSocialIdentity(for: candidate.fingerprint) {
|
||||||
senderNickname = social.localPetname ?? social.claimedNickname
|
senderNickname = social.localPetname ?? social.claimedNickname
|
||||||
} else {
|
} else {
|
||||||
senderNickname = "anon" + String(peerID.prefix(4))
|
senderNickname = "anon" + String(peerID.id.prefix(4))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -2672,7 +2670,7 @@ extension BLEService {
|
|||||||
// peers we haven't verified yet.
|
// peers we haven't verified yet.
|
||||||
if !accepted && packet.ttl == 0 {
|
if !accepted && packet.ttl == 0 {
|
||||||
accepted = true
|
accepted = true
|
||||||
senderNickname = "anon" + String(peerID.prefix(4))
|
senderNickname = "anon" + String(peerID.id.prefix(4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2686,7 +2684,7 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard accepted else {
|
guard accepted else {
|
||||||
SecureLogger.warning("🚫 Dropping public message from unverified or unknown peer \(peerID.prefix(8))…", category: .security)
|
SecureLogger.warning("🚫 Dropping public message from unverified or unknown peer \(peerID.id.prefix(8))…", category: .security)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2696,9 +2694,9 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
// Determine if we have a direct link to the sender
|
// Determine if we have a direct link to the sender
|
||||||
let hasDirectLink: Bool = collectionsQueue.sync {
|
let hasDirectLink: Bool = collectionsQueue.sync {
|
||||||
let perUUID = peerToPeripheralUUID[PeerID(str: peerID)]
|
let perUUID = peerToPeripheralUUID[peerID]
|
||||||
let perConnected = perUUID != nil && peripherals[perUUID!]?.isConnected == true
|
let perConnected = perUUID != nil && peripherals[perUUID!]?.isConnected == true
|
||||||
let hasCentral = centralToPeerID.values.contains(PeerID(str: peerID))
|
let hasCentral = centralToPeerID.values.contains(peerID)
|
||||||
return perConnected || hasCentral
|
return perConnected || hasCentral
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2707,22 +2705,22 @@ extension BLEService {
|
|||||||
|
|
||||||
let ts = Date(timeIntervalSince1970: Double(packet.timestamp) / 1000)
|
let ts = Date(timeIntervalSince1970: Double(packet.timestamp) / 1000)
|
||||||
notifyUI { [weak self] in
|
notifyUI { [weak self] in
|
||||||
self?.delegate?.didReceivePublicMessage(from: PeerID(str: peerID), nickname: senderNickname, content: content, timestamp: ts)
|
self?.delegate?.didReceivePublicMessage(from: peerID, nickname: senderNickname, content: content, timestamp: ts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleNoiseHandshake(_ packet: BitchatPacket, from peerID: String) {
|
private func handleNoiseHandshake(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
// Use NoiseEncryptionService for handshake processing
|
// Use NoiseEncryptionService for handshake processing
|
||||||
if let recipientID = packet.recipientID,
|
if let recipientID = packet.recipientID,
|
||||||
recipientID.hexEncodedString() == myPeerID {
|
recipientID.hexEncodedString() == myPeerID {
|
||||||
// Handshake is for us
|
// Handshake is for us
|
||||||
do {
|
do {
|
||||||
if let response = try noiseService.processHandshakeMessage(from: PeerID(str: peerID), message: packet.payload) {
|
if let response = try noiseService.processHandshakeMessage(from: peerID, message: packet.payload) {
|
||||||
// Send response
|
// Send response
|
||||||
let responsePacket = BitchatPacket(
|
let responsePacket = BitchatPacket(
|
||||||
type: MessageType.noiseHandshake.rawValue,
|
type: MessageType.noiseHandshake.rawValue,
|
||||||
senderID: myPeerIDData,
|
senderID: myPeerIDData,
|
||||||
recipientID: Data(hexString: peerID),
|
recipientID: Data(hexString: peerID.id),
|
||||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||||
payload: response,
|
payload: response,
|
||||||
signature: nil,
|
signature: nil,
|
||||||
@@ -2737,15 +2735,14 @@ extension BLEService {
|
|||||||
} catch {
|
} catch {
|
||||||
SecureLogger.error("Failed to process handshake: \(error)")
|
SecureLogger.error("Failed to process handshake: \(error)")
|
||||||
// Try initiating a new handshake
|
// Try initiating a new handshake
|
||||||
if !noiseService.hasSession(with: PeerID(str: peerID)) {
|
if !noiseService.hasSession(with: peerID) {
|
||||||
initiateNoiseHandshake(with: peerID)
|
initiateNoiseHandshake(with: peerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleNoiseEncrypted(_ packet: BitchatPacket, from peerID: String) {
|
private func handleNoiseEncrypted(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
let peerID = PeerID(str: peerID)
|
|
||||||
SecureLogger.debug("🔐 handleNoiseEncrypted called for packet from \(peerID)")
|
SecureLogger.debug("🔐 handleNoiseEncrypted called for packet from \(peerID)")
|
||||||
|
|
||||||
guard let recipientID = packet.recipientID else {
|
guard let recipientID = packet.recipientID else {
|
||||||
@@ -2760,7 +2757,7 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update lastSeen for the peer we received from (important for private messages)
|
// Update lastSeen for the peer we received from (important for private messages)
|
||||||
updatePeerLastSeen(peerID.id)
|
updatePeerLastSeen(peerID)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let decrypted = try noiseService.decrypt(packet.payload, from: peerID)
|
let decrypted = try noiseService.decrypt(packet.payload, from: peerID)
|
||||||
@@ -2804,20 +2801,20 @@ extension BLEService {
|
|||||||
// Trigger a handshake so future messages can be decrypted.
|
// Trigger a handshake so future messages can be decrypted.
|
||||||
SecureLogger.debug("🔑 Encrypted message from \(peerID) without session; initiating handshake")
|
SecureLogger.debug("🔑 Encrypted message from \(peerID) without session; initiating handshake")
|
||||||
if !noiseService.hasSession(with: peerID) {
|
if !noiseService.hasSession(with: peerID) {
|
||||||
initiateNoiseHandshake(with: peerID.id)
|
initiateNoiseHandshake(with: peerID)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
SecureLogger.error("❌ Failed to decrypt message from \(peerID): \(error)")
|
SecureLogger.error("❌ Failed to decrypt message from \(peerID): \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func handleLeave(_ packet: BitchatPacket, from peerID: String) {
|
private func handleLeave(_ packet: BitchatPacket, from peerID: PeerID) {
|
||||||
_ = collectionsQueue.sync(flags: .barrier) {
|
_ = collectionsQueue.sync(flags: .barrier) {
|
||||||
// Remove the peer when they leave
|
// Remove the peer when they leave
|
||||||
peers.removeValue(forKey: PeerID(str: peerID))
|
peers.removeValue(forKey: peerID)
|
||||||
}
|
}
|
||||||
// Remove any stored announcement for sync purposes
|
// Remove any stored announcement for sync purposes
|
||||||
gossipSyncManager?.removeAnnouncementForPeer(PeerID(str: peerID))
|
gossipSyncManager?.removeAnnouncementForPeer(peerID)
|
||||||
// Send on main thread
|
// Send on main thread
|
||||||
notifyUI { [weak self] in
|
notifyUI { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@@ -2825,7 +2822,7 @@ extension BLEService {
|
|||||||
// Get current peer list (after removal)
|
// Get current peer list (after removal)
|
||||||
let currentPeerIDs = self.collectionsQueue.sync { self.currentPeerIDs }
|
let currentPeerIDs = self.collectionsQueue.sync { self.currentPeerIDs }
|
||||||
|
|
||||||
self.delegate?.didDisconnectFromPeer(PeerID(str: peerID))
|
self.delegate?.didDisconnectFromPeer(peerID)
|
||||||
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
self.delegate?.didUpdatePeerList(currentPeerIDs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2903,8 +2900,7 @@ extension BLEService {
|
|||||||
gossipSyncManager?.onPublicPacketSeen(signedPacket)
|
gossipSyncManager?.onPublicPacketSeen(signedPacket)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func sendPendingNoisePayloadsAfterHandshake(for peerID: String) {
|
private func sendPendingNoisePayloadsAfterHandshake(for peerID: PeerID) {
|
||||||
let peerID = PeerID(str: peerID)
|
|
||||||
let payloads = collectionsQueue.sync(flags: .barrier) { () -> [Data] in
|
let payloads = collectionsQueue.sync(flags: .barrier) { () -> [Data] in
|
||||||
let list = pendingNoisePayloadsAfterHandshake[peerID] ?? []
|
let list = pendingNoisePayloadsAfterHandshake[peerID] ?? []
|
||||||
pendingNoisePayloadsAfterHandshake.removeValue(forKey: peerID)
|
pendingNoisePayloadsAfterHandshake.removeValue(forKey: peerID)
|
||||||
@@ -2931,8 +2927,7 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePeerLastSeen(_ peerID: String) {
|
private func updatePeerLastSeen(_ peerID: PeerID) {
|
||||||
let peerID = PeerID(str: peerID)
|
|
||||||
// Use async to avoid deadlock - we don't need immediate consistency for last seen updates
|
// Use async to avoid deadlock - we don't need immediate consistency for last seen updates
|
||||||
collectionsQueue.async(flags: .barrier) {
|
collectionsQueue.async(flags: .barrier) {
|
||||||
if var peer = self.peers[peerID] {
|
if var peer = self.peers[peerID] {
|
||||||
@@ -2943,9 +2938,8 @@ extension BLEService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Debounced disconnect notifier to avoid duplicate disconnect callbacks within a short window
|
// Debounced disconnect notifier to avoid duplicate disconnect callbacks within a short window
|
||||||
private func notifyPeerDisconnectedDebounced(_ peerID: String) {
|
private func notifyPeerDisconnectedDebounced(_ peerID: PeerID) {
|
||||||
let now = Date()
|
let now = Date()
|
||||||
let peerID = PeerID(str: peerID)
|
|
||||||
let last = recentDisconnectNotifies[peerID]
|
let last = recentDisconnectNotifies[peerID]
|
||||||
if last == nil || now.timeIntervalSince(last!) >= TransportConfig.bleDisconnectNotifyDebounceSeconds {
|
if last == nil || now.timeIntervalSince(last!) >= TransportConfig.bleDisconnectNotifyDebounceSeconds {
|
||||||
delegate?.didDisconnectFromPeer(peerID)
|
delegate?.didDisconnectFromPeer(peerID)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ struct FragmentationTests {
|
|||||||
ble.delegate = capture
|
ble.delegate = capture
|
||||||
|
|
||||||
// Construct a big packet (3KB) from a remote sender (not our own ID)
|
// Construct a big packet (3KB) from a remote sender (not our own ID)
|
||||||
let remoteShortID = "1122334455667788"
|
let remoteShortID: PeerID = "1122334455667788"
|
||||||
let original = makeLargePublicPacket(senderShortHex: remoteShortID, size: 3_000)
|
let original = makeLargePublicPacket(senderShortHex: remoteShortID, size: 3_000)
|
||||||
|
|
||||||
// Use a small fragment size to ensure multiple pieces
|
// Use a small fragment size to ensure multiple pieces
|
||||||
@@ -59,7 +59,7 @@ struct FragmentationTests {
|
|||||||
let capture = CaptureDelegate()
|
let capture = CaptureDelegate()
|
||||||
ble.delegate = capture
|
ble.delegate = capture
|
||||||
|
|
||||||
let remoteShortID = "A1B2C3D4E5F60708"
|
let remoteShortID: PeerID = "A1B2C3D4E5F60708"
|
||||||
let original = makeLargePublicPacket(senderShortHex: remoteShortID, size: 2048)
|
let original = makeLargePublicPacket(senderShortHex: remoteShortID, size: 2048)
|
||||||
var frags = fragmentPacket(original, fragmentSize: 300)
|
var frags = fragmentPacket(original, fragmentSize: 300)
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ struct FragmentationTests {
|
|||||||
let capture = CaptureDelegate()
|
let capture = CaptureDelegate()
|
||||||
ble.delegate = capture
|
ble.delegate = capture
|
||||||
|
|
||||||
let remoteShortID = "0011223344556677"
|
let remoteShortID: PeerID = "0011223344556677"
|
||||||
let original = makeLargePublicPacket(senderShortHex: remoteShortID, size: 1000)
|
let original = makeLargePublicPacket(senderShortHex: remoteShortID, size: 1000)
|
||||||
let fragments = fragmentPacket(original, fragmentSize: 250)
|
let fragments = fragmentPacket(original, fragmentSize: 250)
|
||||||
|
|
||||||
@@ -143,12 +143,12 @@ extension FragmentationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper: build a large message packet (unencrypted public message)
|
// Helper: build a large message packet (unencrypted public message)
|
||||||
private func makeLargePublicPacket(senderShortHex: String, size: Int) -> BitchatPacket {
|
private func makeLargePublicPacket(senderShortHex: PeerID, size: Int) -> BitchatPacket {
|
||||||
let content = String(repeating: "A", count: size)
|
let content = String(repeating: "A", count: size)
|
||||||
let payload = Data(content.utf8)
|
let payload = Data(content.utf8)
|
||||||
let pkt = BitchatPacket(
|
let pkt = BitchatPacket(
|
||||||
type: MessageType.message.rawValue,
|
type: MessageType.message.rawValue,
|
||||||
senderID: Data(hexString: senderShortHex) ?? Data(),
|
senderID: Data(hexString: senderShortHex.id) ?? Data(),
|
||||||
recipientID: nil,
|
recipientID: nil,
|
||||||
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
|
||||||
payload: payload,
|
payload: payload,
|
||||||
|
|||||||
Reference in New Issue
Block a user