Update NoiseEncryptionService to use Peer

This commit is contained in:
islam
2025-09-20 01:26:19 +01:00
parent 7267fb6404
commit 2d8d45a4d7
3 changed files with 77 additions and 76 deletions
+21 -20
View File
@@ -616,10 +616,11 @@ final class BLEService: NSObject {
var payload = Data([NoisePayloadType.readReceipt.rawValue])
payload.append(contentsOf: receipt.originalMessageID.utf8)
if noiseService.hasEstablishedSession(with: peerID) {
let peer = Peer(str: peerID)
if noiseService.hasEstablishedSession(with: peer) {
SecureLogger.debug("📤 Sending READ receipt for message \(receipt.originalMessageID) to \(peerID)", category: .session)
do {
let encrypted = try noiseService.encrypt(payload, for: peerID)
let encrypted = try noiseService.encrypt(payload, for: peer)
let packet = BitchatPacket(
type: MessageType.noiseEncrypted.rawValue,
senderID: myPeerIDData,
@@ -643,7 +644,7 @@ final class BLEService: NSObject {
guard let self = self else { return }
self.pendingNoisePayloadsAfterHandshake[peerID, default: []].append(payload)
}
if !noiseService.hasSession(with: peerID) { initiateNoiseHandshake(with: peerID) }
if !noiseService.hasSession(with: peer) { initiateNoiseHandshake(with: peerID) }
SecureLogger.debug("🕒 Queued READ receipt for \(peerID) until handshake completes", category: .session)
}
}
@@ -664,13 +665,13 @@ final class BLEService: NSObject {
}
private func sendNoisePayload(_ typedPayload: Data, to peerID: String) {
guard noiseService.hasSession(with: peerID) else {
guard noiseService.hasSession(with: Peer(str: peerID)) else {
// Lazy-handshake path: queue? For now, initiate handshake and drop
initiateNoiseHandshake(with: peerID)
return
}
do {
let encrypted = try noiseService.encrypt(typedPayload, for: peerID)
let encrypted = try noiseService.encrypt(typedPayload, for: Peer(str: peerID))
let packet = BitchatPacket(
type: MessageType.noiseEncrypted.rawValue,
senderID: myPeerIDData,
@@ -705,9 +706,9 @@ final class BLEService: NSObject {
func getNoiseSessionState(for peerID: String) -> LazyHandshakeState {
if noiseService.hasEstablishedSession(with: peerID) {
if noiseService.hasEstablishedSession(with: Peer(str: peerID)) {
return .established
} else if noiseService.hasSession(with: peerID) {
} else if noiseService.hasSession(with: Peer(str: peerID)) {
return .handshaking
} else {
return .none
@@ -838,7 +839,7 @@ final class BLEService: NSObject {
SecureLogger.debug("📨 Sending PM to \(recipientID): \(content.prefix(30))...", category: .session)
// Check if we have an established Noise session
if noiseService.hasEstablishedSession(with: recipientID) {
if noiseService.hasEstablishedSession(with: Peer(str: recipientID)) {
// Encrypt and send
do {
// Create TLV-encoded private message
@@ -852,7 +853,7 @@ final class BLEService: NSObject {
var messagePayload = Data([NoisePayloadType.privateMessage.rawValue])
messagePayload.append(tlvData)
let encrypted = try noiseService.encrypt(messagePayload, for: recipientID)
let encrypted = try noiseService.encrypt(messagePayload, for: Peer(str: recipientID))
// Convert recipientID to Data (assuming it's a hex string)
var recipientData = Data()
@@ -918,10 +919,10 @@ final class BLEService: NSObject {
private func initiateNoiseHandshake(with peerID: String) {
// Use NoiseEncryptionService for handshake
guard !noiseService.hasSession(with: peerID) else { return }
guard !noiseService.hasSession(with: Peer(str: peerID)) else { return }
do {
let handshakeData = try noiseService.initiateHandshake(with: peerID)
let handshakeData = try noiseService.initiateHandshake(with: Peer(str: peerID))
// Send handshake init
let packet = BitchatPacket(
@@ -971,7 +972,7 @@ final class BLEService: NSObject {
var messagePayload = Data([NoisePayloadType.privateMessage.rawValue])
messagePayload.append(tlvData)
let encrypted = try noiseService.encrypt(messagePayload, for: peerID)
let encrypted = try noiseService.encrypt(messagePayload, for: Peer(str: peerID))
let packet = BitchatPacket(
type: MessageType.noiseEncrypted.rawValue,
@@ -1785,7 +1786,7 @@ final class BLEService: NSObject {
recipientID.hexEncodedString() == myPeerID {
// Handshake is for us
do {
if let response = try noiseService.processHandshakeMessage(from: peerID, message: packet.payload) {
if let response = try noiseService.processHandshakeMessage(from: Peer(str: peerID), message: packet.payload) {
// Send response
let responsePacket = BitchatPacket(
type: MessageType.noiseHandshake.rawValue,
@@ -1805,7 +1806,7 @@ final class BLEService: NSObject {
} catch {
SecureLogger.error("Failed to process handshake: \(error)")
// Try initiating a new handshake
if !noiseService.hasSession(with: peerID) {
if !noiseService.hasSession(with: Peer(str: peerID)) {
initiateNoiseHandshake(with: peerID)
}
}
@@ -1830,7 +1831,7 @@ final class BLEService: NSObject {
updatePeerLastSeen(peerID)
do {
let decrypted = try noiseService.decrypt(packet.payload, from: peerID)
let decrypted = try noiseService.decrypt(packet.payload, from: Peer(str: peerID))
guard decrypted.count > 0 else { return }
// First byte indicates the payload type
@@ -1870,7 +1871,7 @@ final class BLEService: NSObject {
// We received an encrypted message before establishing a session with this peer.
// Trigger a handshake so future messages can be decrypted.
SecureLogger.debug("🔑 Encrypted message from \(peerID) without session; initiating handshake")
if !noiseService.hasSession(with: peerID) {
if !noiseService.hasSession(with: Peer(str: peerID)) {
initiateNoiseHandshake(with: peerID)
}
} catch {
@@ -1975,9 +1976,9 @@ final class BLEService: NSObject {
var payload = Data([NoisePayloadType.delivered.rawValue])
payload.append(contentsOf: messageID.utf8)
if noiseService.hasEstablishedSession(with: peerID) {
if noiseService.hasEstablishedSession(with: Peer(str: peerID)) {
do {
let encrypted = try noiseService.encrypt(payload, for: peerID)
let encrypted = try noiseService.encrypt(payload, for: Peer(str: peerID))
let packet = BitchatPacket(
type: MessageType.noiseEncrypted.rawValue,
senderID: myPeerIDData,
@@ -1997,7 +1998,7 @@ final class BLEService: NSObject {
guard let self = self else { return }
self.pendingNoisePayloadsAfterHandshake[peerID, default: []].append(payload)
}
if !noiseService.hasSession(with: peerID) { initiateNoiseHandshake(with: peerID) }
if !noiseService.hasSession(with: Peer(str: peerID)) { initiateNoiseHandshake(with: peerID) }
SecureLogger.debug("🕒 Queued DELIVERED ack for \(peerID) until handshake completes", category: .session)
}
}
@@ -2012,7 +2013,7 @@ final class BLEService: NSObject {
SecureLogger.debug("📤 Sending \(payloads.count) pending noise payloads to \(peerID) after handshake", category: .session)
for payload in payloads {
do {
let encrypted = try noiseService.encrypt(payload, for: peerID)
let encrypted = try noiseService.encrypt(payload, for: Peer(str: peerID))
let packet = BitchatPacket(
type: MessageType.noiseEncrypted.rawValue,
senderID: myPeerIDData,
+48 -48
View File
@@ -148,8 +148,8 @@ final class NoiseEncryptionService {
private let sessionManager: NoiseSessionManager
// Peer fingerprints (SHA256 hash of static public key)
private var peerFingerprints: [String: String] = [:] // peerID -> fingerprint
private var fingerprintToPeerID: [String: String] = [:] // fingerprint -> peerID
private var peerFingerprints: [Peer: String] = [:] // Peer -> fingerprint
private var fingerprintToPeer: [String: Peer] = [:] // fingerprint -> Peer
// Thread safety
private let serviceQueue = DispatchQueue(label: "chat.bitchat.noise.service", attributes: .concurrent)
@@ -237,7 +237,7 @@ final class NoiseEncryptionService {
// Set up session callbacks
sessionManager.onSessionEstablished = { [weak self] peerID, remoteStaticKey in
self?.handleSessionEstablished(peerID: peerID, remoteStaticKey: remoteStaticKey)
self?.handleSessionEstablished(peer: Peer(str: peerID), remoteStaticKey: remoteStaticKey)
}
// Start session maintenance timer
@@ -263,8 +263,8 @@ final class NoiseEncryptionService {
}
/// Get peer's public key data
func getPeerPublicKeyData(_ peerID: String) -> Data? {
return sessionManager.getRemoteStaticKey(for: peerID)?.rawRepresentation
func getPeerPublicKeyData(_ peer: Peer) -> Data? {
return sessionManager.getRemoteStaticKey(for: peer.id)?.rawRepresentation
}
/// Clear persistent identity (for panic mode)
@@ -391,52 +391,52 @@ final class NoiseEncryptionService {
// MARK: - Handshake Management
/// Initiate a Noise handshake with a peer
func initiateHandshake(with peerID: String) throws -> Data {
func initiateHandshake(with peer: Peer) throws -> Data {
// Validate peer ID
guard Peer(str: peerID).isValid else {
SecureLogger.warning(.authenticationFailed(peerID: peerID))
guard peer.isValid else {
SecureLogger.warning(.authenticationFailed(peerID: peer.id))
throw NoiseSecurityError.invalidPeerID
}
// Check rate limit
guard rateLimiter.allowHandshake(from: Peer(str: peerID)) else {
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peerID)"))
guard rateLimiter.allowHandshake(from: peer) else {
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peer.id)"))
throw NoiseSecurityError.rateLimitExceeded
}
SecureLogger.info(.handshakeStarted(peerID: peerID))
SecureLogger.info(.handshakeStarted(peerID: peer.id))
// Return raw handshake data without wrapper
// The Noise protocol handles its own message format
let handshakeData = try sessionManager.initiateHandshake(with: peerID)
let handshakeData = try sessionManager.initiateHandshake(with: peer.id)
return handshakeData
}
/// Process an incoming handshake message
func processHandshakeMessage(from peerID: String, message: Data) throws -> Data? {
func processHandshakeMessage(from peer: Peer, message: Data) throws -> Data? {
// Validate peer ID
guard Peer(str: peerID).isValid else {
SecureLogger.warning(.authenticationFailed(peerID: peerID))
guard peer.isValid else {
SecureLogger.warning(.authenticationFailed(peerID: peer.id))
throw NoiseSecurityError.invalidPeerID
}
// Validate message size
guard NoiseSecurityValidator.validateHandshakeMessageSize(message) else {
SecureLogger.warning(.handshakeFailed(peerID: peerID, error: "Message too large"))
SecureLogger.warning(.handshakeFailed(peerID: peer.id, error: "Message too large"))
throw NoiseSecurityError.messageTooLarge
}
// Check rate limit
guard rateLimiter.allowHandshake(from: Peer(str: peerID)) else {
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peerID)"))
guard rateLimiter.allowHandshake(from: peer) else {
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peer.id)"))
throw NoiseSecurityError.rateLimitExceeded
}
// For handshakes, we process the raw data directly without NoiseMessage wrapper
// The Noise protocol handles its own message format
let responsePayload = try sessionManager.handleIncomingHandshake(from: peerID, message: message)
let responsePayload = try sessionManager.handleIncomingHandshake(from: peer.id, message: message)
// Return raw response without wrapper
@@ -444,117 +444,117 @@ final class NoiseEncryptionService {
}
/// Check if we have an established session with a peer
func hasEstablishedSession(with peerID: String) -> Bool {
return sessionManager.getSession(for: peerID)?.isEstablished() ?? false
func hasEstablishedSession(with peer: Peer) -> Bool {
return sessionManager.getSession(for: peer.id)?.isEstablished() ?? false
}
/// Check if we have a session (established or handshaking) with a peer
func hasSession(with peerID: String) -> Bool {
return sessionManager.getSession(for: peerID) != nil
func hasSession(with peer: Peer) -> Bool {
return sessionManager.getSession(for: peer.id) != nil
}
// MARK: - Encryption/Decryption
/// Encrypt data for a specific peer
func encrypt(_ data: Data, for peerID: String) throws -> Data {
func encrypt(_ data: Data, for peer: Peer) throws -> Data {
// Validate message size
guard NoiseSecurityValidator.validateMessageSize(data) else {
throw NoiseSecurityError.messageTooLarge
}
// Check rate limit
guard rateLimiter.allowMessage(from: Peer(str: peerID)) else {
guard rateLimiter.allowMessage(from: peer) else {
throw NoiseSecurityError.rateLimitExceeded
}
// Check if we have an established session
guard hasEstablishedSession(with: peerID) else {
guard hasEstablishedSession(with: peer) else {
// Signal that handshake is needed
onHandshakeRequired?(peerID)
onHandshakeRequired?(peer.id)
throw NoiseEncryptionError.handshakeRequired
}
return try sessionManager.encrypt(data, for: peerID)
return try sessionManager.encrypt(data, for: peer.id)
}
/// Decrypt data from a specific peer
func decrypt(_ data: Data, from peerID: String) throws -> Data {
func decrypt(_ data: Data, from peer: Peer) throws -> Data {
// Validate message size
guard NoiseSecurityValidator.validateMessageSize(data) else {
throw NoiseSecurityError.messageTooLarge
}
// Check rate limit
guard rateLimiter.allowMessage(from: Peer(str: peerID)) else {
guard rateLimiter.allowMessage(from: peer) else {
throw NoiseSecurityError.rateLimitExceeded
}
// Check if we have an established session
guard hasEstablishedSession(with: peerID) else {
guard hasEstablishedSession(with: peer) else {
throw NoiseEncryptionError.sessionNotEstablished
}
return try sessionManager.decrypt(data, from: peerID)
return try sessionManager.decrypt(data, from: peer.id)
}
// MARK: - Peer Management
/// Get fingerprint for a peer
func getPeerFingerprint(_ peerID: String) -> String? {
func getPeerFingerprint(_ peer: Peer) -> String? {
return serviceQueue.sync {
return peerFingerprints[peerID]
return peerFingerprints[peer]
}
}
/// Get peer ID for a fingerprint
func getPeerID(for fingerprint: String) -> String? {
func getPeer(for fingerprint: String) -> Peer? {
return serviceQueue.sync {
return fingerprintToPeerID[fingerprint]
return fingerprintToPeer[fingerprint]
}
}
/// Remove a peer session
func removePeer(_ peerID: String) {
sessionManager.removeSession(for: peerID)
func removePeer(_ peer: Peer) {
sessionManager.removeSession(for: peer.id)
serviceQueue.sync(flags: .barrier) {
if let fingerprint = peerFingerprints[peerID] {
fingerprintToPeerID.removeValue(forKey: fingerprint)
if let fingerprint = peerFingerprints[peer] {
fingerprintToPeer.removeValue(forKey: fingerprint)
}
peerFingerprints.removeValue(forKey: peerID)
peerFingerprints.removeValue(forKey: peer)
}
SecureLogger.info(.sessionExpired(peerID: peerID))
SecureLogger.info(.sessionExpired(peerID: peer.id))
}
func clearEphemeralStateForPanic() {
sessionManager.removeAllSessions()
serviceQueue.sync(flags: .barrier) {
peerFingerprints.removeAll()
fingerprintToPeerID.removeAll()
fingerprintToPeer.removeAll()
}
rateLimiter.resetAll()
}
// MARK: - Private Helpers
private func handleSessionEstablished(peerID: String, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) {
private func handleSessionEstablished(peer: Peer, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) {
// Calculate fingerprint
let fingerprint = calculateFingerprint(for: remoteStaticKey)
// Store fingerprint mapping
serviceQueue.sync(flags: .barrier) {
peerFingerprints[peerID] = fingerprint
fingerprintToPeerID[fingerprint] = peerID
peerFingerprints[peer] = fingerprint
fingerprintToPeer[fingerprint] = peer
}
// Log security event
SecureLogger.info(.handshakeCompleted(peerID: peerID))
SecureLogger.info(.handshakeCompleted(peerID: peer.id))
// Notify all handlers about authentication
serviceQueue.async { [weak self] in
self?.onPeerAuthenticatedHandlers.forEach { handler in
handler(peerID, fingerprint)
handler(peer.id, fingerprint)
}
}
}
+8 -8
View File
@@ -310,7 +310,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
if let mapped = shortIDToNoiseKey[shortPeerID] { return mapped }
// Fallback: derive from active Noise session if available
if shortPeerID.count == 16,
let key = meshService.getNoiseService().getPeerPublicKeyData(shortPeerID) {
let key = meshService.getNoiseService().getPeerPublicKeyData(Peer(str: shortPeerID)) {
let stable = key.hexEncodedString()
shortIDToNoiseKey[shortPeerID] = stable
return stable
@@ -3730,7 +3730,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
func updateEncryptionStatusForPeer(_ peerID: String) {
let noiseService = meshService.getNoiseService()
if noiseService.hasEstablishedSession(with: peerID) {
if noiseService.hasEstablishedSession(with: Peer(str: peerID)) {
// Check if fingerprint is verified using our persisted data
if let fingerprint = getFingerprint(for: peerID),
verifiedFingerprints.contains(fingerprint) {
@@ -3738,7 +3738,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
} else {
peerEncryptionStatus[peerID] = .noiseSecured
}
} else if noiseService.hasSession(with: peerID) {
} else if noiseService.hasSession(with: Peer(str: peerID)) {
// Session exists but not established - handshaking
peerEncryptionStatus[peerID] = .noiseHandshaking
} else {
@@ -4196,7 +4196,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
private func updateEncryptionStatus(for peerID: String) {
let noiseService = meshService.getNoiseService()
if noiseService.hasEstablishedSession(with: peerID) {
if noiseService.hasEstablishedSession(with: Peer(str: peerID)) {
if let fingerprint = getFingerprint(for: peerID) {
if verifiedFingerprints.contains(fingerprint) {
peerEncryptionStatus[peerID] = .noiseVerified
@@ -4207,7 +4207,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
// Session established but no fingerprint yet
peerEncryptionStatus[peerID] = .noiseSecured
}
} else if noiseService.hasSession(with: peerID) {
} else if noiseService.hasSession(with: Peer(str: peerID)) {
peerEncryptionStatus[peerID] = .noiseHandshaking
} else {
peerEncryptionStatus[peerID] = Optional.none
@@ -4358,7 +4358,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
// Cache shortID -> full Noise key mapping as soon as session authenticates
if self.shortIDToNoiseKey[peerID] == nil,
let keyData = self.meshService.getNoiseService().getPeerPublicKeyData(peerID) {
let keyData = self.meshService.getNoiseService().getPeerPublicKeyData(Peer(str: peerID)) {
let stable = keyData.hexEncodedString()
self.shortIDToNoiseKey[peerID] = stable
SecureLogger.debug("🗺️ Mapped short peerID to Noise key for header continuity: \(peerID) -> \(stable.prefix(8))", category: .session)
@@ -4587,7 +4587,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
pendingQRVerifications[peerID] = pending
// If Noise session is established, send immediately; otherwise trigger handshake and send on auth
let noise = meshService.getNoiseService()
if noise.hasEstablishedSession(with: peerID) {
if noise.hasEstablishedSession(with: Peer(str: peerID)) {
meshService.sendVerifyChallenge(to: peerID, noiseKeyHex: qr.noiseKeyHex, nonceA: nonce)
pending.sent = true
pendingQRVerifications[peerID] = pending
@@ -4638,7 +4638,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
// If the open PM is tied to this short peer ID, switch UI context to the full Noise key (offline favorite)
var derivedStableKeyHex: String? = shortIDToNoiseKey[peerID]
if derivedStableKeyHex == nil,
let key = meshService.getNoiseService().getPeerPublicKeyData(peerID) {
let key = meshService.getNoiseService().getPeerPublicKeyData(Peer(str: peerID)) {
derivedStableKeyHex = key.hexEncodedString()
shortIDToNoiseKey[peerID] = derivedStableKeyHex
}