diff --git a/bitchat/BitchatApp.swift b/bitchat/BitchatApp.swift index 0bf9012d..0fe1c4fd 100644 --- a/bitchat/BitchatApp.swift +++ b/bitchat/BitchatApp.swift @@ -246,7 +246,7 @@ final class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { // Get peer ID from userInfo if let peerID = userInfo["peerID"] as? String { // Don't show notification if the private chat is already open - if chatViewModel?.selectedPrivateChatPeer == peerID { + if chatViewModel?.selectedPrivateChatPeer == PeerID(str: peerID) { completionHandler([]) return } diff --git a/bitchat/Models/PeerID.swift b/bitchat/Models/PeerID.swift index 1248a520..d31e2275 100644 --- a/bitchat/Models/PeerID.swift +++ b/bitchat/Models/PeerID.swift @@ -76,6 +76,12 @@ extension PeerID { init(hexData: Data) { self.init(str: hexData.hexEncodedString()) } + + /// Convenience init to "hide" hex-encoding implementation detail + init?(hexData: Data?) { + guard let hexData else { return nil } + self.init(hexData: hexData) + } } // MARK: - Noise Public Key Helpers @@ -191,9 +197,7 @@ extension PeerID: Comparable { } } -// MARK: - String Interop Helpers - -// MARK: CustomStringConvertible +// MARK: - CustomStringConvertible extension PeerID: CustomStringConvertible { /// So it returns the actual `id` like before even inside another String @@ -201,17 +205,3 @@ extension PeerID: CustomStringConvertible { id } } - -// MARK: Custom Equatable w/ String & Optionality - -// PeerID <> String -extension Optional where Wrapped == PeerID { - static func ==(lhs: Optional, rhs: Optional) -> Bool { lhs?.id == rhs } - static func !=(lhs: Optional, rhs: Optional) -> Bool { lhs?.id != rhs } -} - -// String <> PeerID -extension Optional where Wrapped == String { - static func ==(lhs: Optional, rhs: Optional) -> Bool { lhs == rhs?.id } - static func !=(lhs: Optional, rhs: Optional) -> Bool { lhs != rhs?.id } -} diff --git a/bitchat/Services/BLEService.swift b/bitchat/Services/BLEService.swift index 76c24636..67cbb0c3 100644 --- a/bitchat/Services/BLEService.swift +++ b/bitchat/Services/BLEService.swift @@ -734,8 +734,7 @@ final class BLEService: NSObject { } private func sendEncrypted(_ packet: BitchatPacket, data: Data, pad: Bool) { - guard let recipientID = packet.recipientID else { return } - let recipientPeerID = PeerID(hexData: recipientID) + guard let recipientPeerID = PeerID(hexData: packet.recipientID) else { return } var sentEncrypted = false // Per-link limits for the specific peer @@ -1039,8 +1038,7 @@ final class BLEService: NSObject { // Skip directed packets that are not intended for us if let recipient = packet.recipientID { - let recipientHex = recipient.hexEncodedString() - if recipientHex != myPeerID && !recipient.allSatisfy({ $0 == 0xFF }) { + if PeerID(hexData: recipient) != myPeerID && !recipient.allSatisfy({ $0 == 0xFF }) { return } } @@ -1122,10 +1120,7 @@ final class BLEService: NSObject { marker = "[file] \(fileName)" } - let isPrivateMessage: Bool = { - guard let recipient = packet.recipientID else { return false } - return recipient.hexEncodedString() == myPeerID - }() + let isPrivateMessage = PeerID(hexData: packet.recipientID) == myPeerID if isPrivateMessage { updatePeerLastSeen(peerID) @@ -3078,9 +3073,9 @@ extension BLEService { // Verify that the sender's derived ID from the announced noise public key matches the packet senderID // This helps detect relayed or spoofed announces. Only warn in release; assert in debug. - let derivedFromKey = PeerID(publicKey: announcement.noisePublicKey).id + let derivedFromKey = PeerID(publicKey: announcement.noisePublicKey) if derivedFromKey != peerID { - SecureLogger.warning("⚠️ Announce sender mismatch: derived \(derivedFromKey.prefix(8))… vs packet \(peerID.id.prefix(8))…", category: .security) + SecureLogger.warning("⚠️ Announce sender mismatch: derived \(derivedFromKey.id.prefix(8))… vs packet \(peerID.id.prefix(8))…", category: .security) } @@ -3366,8 +3361,7 @@ extension BLEService { private func handleNoiseHandshake(_ packet: BitchatPacket, from peerID: PeerID) { // Use NoiseEncryptionService for handshake processing - if let recipientID = packet.recipientID, - recipientID.hexEncodedString() == myPeerID { + if PeerID(hexData: packet.recipientID) == myPeerID { // Handshake is for us do { if let response = try noiseService.processHandshakeMessage(from: peerID, message: packet.payload) { @@ -3400,14 +3394,13 @@ extension BLEService { private func handleNoiseEncrypted(_ packet: BitchatPacket, from peerID: PeerID) { SecureLogger.debug("🔐 handleNoiseEncrypted called for packet from \(peerID)") - guard let recipientID = packet.recipientID else { + guard let recipientID = PeerID(hexData: packet.recipientID) else { SecureLogger.warning("⚠️ Encrypted message has no recipient ID", category: .session) return } - let recipientHex = recipientID.hexEncodedString() - if recipientHex != myPeerID { - SecureLogger.debug("🔐 Encrypted message not for me (for \(recipientHex), I am \(myPeerID))", category: .session) + if recipientID != myPeerID { + SecureLogger.debug("🔐 Encrypted message not for me (for \(recipientID), I am \(myPeerID))", category: .session) return } diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index 6984baa9..91764a1f 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -261,7 +261,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate { nickname = trimmed } // Update mesh service nickname if it's initialized - if meshService.myPeerID != "" { + if !meshService.myPeerID.isEmpty { meshService.setNickname(nickname) } } @@ -4103,7 +4103,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate { if let spid = message.senderPeerID { if case .location(let ch) = activeChannel, spid.id.hasPrefix("nostr:") { if let myGeo = try? idBridge.deriveIdentity(forGeohash: ch.geohash) { - return spid == "nostr:\(myGeo.publicKeyHex.prefix(TransportConfig.nostrShortKeyDisplayLength))" + return spid == PeerID(nostr: myGeo.publicKeyHex) } } return spid == meshService.myPeerID @@ -5623,7 +5623,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate { } // Validate recipient - if let rid = packet.recipientID, rid.hexEncodedString() != meshService.myPeerID { + if PeerID(hexData: packet.recipientID) != meshService.myPeerID { return } diff --git a/bitchatTests/EndToEnd/PrivateChatE2ETests.swift b/bitchatTests/EndToEnd/PrivateChatE2ETests.swift index 2f0d45c3..ca4ff1f7 100644 --- a/bitchatTests/EndToEnd/PrivateChatE2ETests.swift +++ b/bitchatTests/EndToEnd/PrivateChatE2ETests.swift @@ -186,11 +186,11 @@ struct PrivateChatE2ETests { // Bob relays private messages for Charlie bob.packetDeliveryHandler = { packet in if let recipientID = packet.recipientID, - String(data: recipientID, encoding: .utf8) == charlie.peerID { + PeerID(data: recipientID) == charlie.peerID { // Relay to Charlie var relayPacket = packet relayPacket.ttl = packet.ttl - 1 - self.charlie.simulateIncomingPacket(relayPacket) + charlie.simulateIncomingPacket(relayPacket) } } diff --git a/bitchatTests/EndToEnd/PublicChatE2ETests.swift b/bitchatTests/EndToEnd/PublicChatE2ETests.swift index 8a7f678e..a8c65af1 100644 --- a/bitchatTests/EndToEnd/PublicChatE2ETests.swift +++ b/bitchatTests/EndToEnd/PublicChatE2ETests.swift @@ -388,7 +388,7 @@ struct PublicChatE2ETests { if let message = BitchatMessage(packet.payload) { // Don't relay own messages - guard message.senderPeerID?.id != node.peerID else { return } + guard message.senderPeerID != node.peerID else { return } // Create relay message let relayMessage = BitchatMessage( diff --git a/bitchatTests/Mocks/MockBLEService.swift b/bitchatTests/Mocks/MockBLEService.swift index c9002ff6..5aace0ab 100644 --- a/bitchatTests/Mocks/MockBLEService.swift +++ b/bitchatTests/Mocks/MockBLEService.swift @@ -203,10 +203,7 @@ final class MockBLEService: NSObject { let target = bus.service(for: recipientPeerID) { target.simulateIncomingPacket(packet) } else { - // Not directly connected: deliver to neighbors for relay; also deliver directly if target is known - if let target = bus.service(for: recipientPeerID) { - target.simulateIncomingPacket(packet) - } + // Not directly connected: deliver to neighbors for relay for neighbor in neighbors() where neighbor.peerID != recipientPeerID { neighbor.simulateIncomingPacket(packet) } diff --git a/bitchatTests/Utils/PeerIDTests.swift b/bitchatTests/Utils/PeerIDTests.swift index 9df7e83a..41726572 100644 --- a/bitchatTests/Utils/PeerIDTests.swift +++ b/bitchatTests/Utils/PeerIDTests.swift @@ -273,7 +273,7 @@ struct PeerIDTests { @Test func comparable_sorting_and_equality() { let p1 = PeerID(str: "aaa") let p2 = PeerID(str: "bbb") - let p3 = PeerID(str: "bbb") + let p3 = PeerID(str: "BBB") #expect(p1 < p2) #expect(p2 >= p1) @@ -284,44 +284,18 @@ struct PeerIDTests { } @Test func equality() { - let string = "aaa" - let peerID = PeerID(str: string) - let badString = "bbb" - - // PeerID == String - #expect(peerID == string) - #expect(peerID == Optional(string)) - #expect(Optional(peerID) == string) - #expect(Optional(peerID) == Optional(string)) - - // PeerID != String - #expect(peerID != badString) - #expect(peerID != Optional(badString)) - #expect(Optional(peerID) != badString) - #expect(Optional(peerID) != Optional(badString)) - - // String == PeerID - #expect(string == peerID) - #expect(Optional(string) == peerID) - #expect(string == Optional(peerID)) - #expect(Optional(string) == Optional(peerID)) - - // String != PeerID - #expect(badString != peerID) - #expect(Optional(badString) != peerID) - #expect(badString != Optional(peerID)) - #expect(Optional(badString) != Optional(peerID)) + let peerID = PeerID(str: "aaa") // Regular PeerID <> PeerID - #expect(peerID == PeerID(str: "aaa")) - #expect(peerID == Optional(PeerID(str: "aaa"))) - #expect(PeerID(str: "aaa") == peerID) - #expect(Optional(PeerID(str: "aaa")) == Optional(peerID)) + #expect(peerID == PeerID(str: "AAA")) + #expect(peerID == Optional(PeerID(str: "AAA"))) + #expect(PeerID(str: "AAA") == peerID) + #expect(Optional(PeerID(str: "AAA")) == Optional(peerID)) - #expect(peerID != PeerID(str: "bbb")) - #expect(peerID != Optional(PeerID(str: "bbb"))) - #expect(PeerID(str: "bbb") != peerID) - #expect(Optional(PeerID(str: "bbb")) != Optional(peerID)) + #expect(peerID != PeerID(str: "BBB")) + #expect(peerID != Optional(PeerID(str: "BBB"))) + #expect(PeerID(str: "BBB") != peerID) + #expect(Optional(PeerID(str: "BBB")) != Optional(peerID)) } // MARK: - Computed properties