PeerID 31/n: Remove String interop to be explicit (#840)

* PeerID 28/n: `ChatViewModel.getShortIDForNoiseKey`

* PeerID 29/n: `BLEService` + remove dupe funcs from #823

* `handleFileTransfer` to use PeerID

* `sendMessage` and `sendPrivateMessage`

* PeerID 30/n: Update some leftovers

* `lowercased()` inside PeerID for normalization

* PeerID 31/n: Remove String interop to be explicit

* MockBLEService: Remove direct target delivery

This causes a delivery even if the sender and receiver are not connected

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
Islam
2025-10-19 20:50:44 +02:00
committed by GitHub
co-authored by jack
parent 0776c9813c
commit b839ce5f6c
8 changed files with 34 additions and 80 deletions
+1 -1
View File
@@ -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
}
+7 -17
View File
@@ -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<Wrapped>, rhs: Optional<String>) -> Bool { lhs?.id == rhs }
static func !=(lhs: Optional<Wrapped>, rhs: Optional<String>) -> Bool { lhs?.id != rhs }
}
// String <> PeerID
extension Optional where Wrapped == String {
static func ==(lhs: Optional<Wrapped>, rhs: Optional<PeerID>) -> Bool { lhs == rhs?.id }
static func !=(lhs: Optional<Wrapped>, rhs: Optional<PeerID>) -> Bool { lhs != rhs?.id }
}
+9 -16
View File
@@ -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
}
+3 -3
View File
@@ -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
}
@@ -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)
}
}
@@ -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(
+1 -4
View File
@@ -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)
}
+10 -36
View File
@@ -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