mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 02:25:20 +00:00
PeerID 9/n: NoiseEncryptionService (#747)
This commit is contained in:
@@ -675,10 +675,10 @@ final class BLEService: NSObject {
|
|||||||
var payload = Data([NoisePayloadType.readReceipt.rawValue])
|
var payload = Data([NoisePayloadType.readReceipt.rawValue])
|
||||||
payload.append(contentsOf: receipt.originalMessageID.utf8)
|
payload.append(contentsOf: receipt.originalMessageID.utf8)
|
||||||
|
|
||||||
if noiseService.hasEstablishedSession(with: peerID) {
|
if noiseService.hasEstablishedSession(with: PeerID(str: peerID)) {
|
||||||
SecureLogger.debug("📤 Sending READ receipt for message \(receipt.originalMessageID) to \(peerID)", category: .session)
|
SecureLogger.debug("📤 Sending READ receipt for message \(receipt.originalMessageID) to \(peerID)", category: .session)
|
||||||
do {
|
do {
|
||||||
let encrypted = try noiseService.encrypt(payload, for: peerID)
|
let encrypted = try noiseService.encrypt(payload, for: PeerID(str: peerID))
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
type: MessageType.noiseEncrypted.rawValue,
|
type: MessageType.noiseEncrypted.rawValue,
|
||||||
senderID: myPeerIDData,
|
senderID: myPeerIDData,
|
||||||
@@ -702,7 +702,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) }
|
if !noiseService.hasSession(with: PeerID(str: 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -723,13 +723,13 @@ final class BLEService: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func sendNoisePayload(_ typedPayload: Data, to peerID: String) {
|
private func sendNoisePayload(_ typedPayload: Data, to peerID: String) {
|
||||||
guard noiseService.hasSession(with: peerID) else {
|
guard noiseService.hasSession(with: PeerID(str: 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)
|
let encrypted = try noiseService.encrypt(typedPayload, for: PeerID(str: peerID))
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
type: MessageType.noiseEncrypted.rawValue,
|
type: MessageType.noiseEncrypted.rawValue,
|
||||||
senderID: myPeerIDData,
|
senderID: myPeerIDData,
|
||||||
@@ -759,9 +759,9 @@ final class BLEService: NSObject {
|
|||||||
|
|
||||||
|
|
||||||
func getNoiseSessionState(for peerID: String) -> LazyHandshakeState {
|
func getNoiseSessionState(for peerID: String) -> LazyHandshakeState {
|
||||||
if noiseService.hasEstablishedSession(with: peerID) {
|
if noiseService.hasEstablishedSession(with: PeerID(str: peerID)) {
|
||||||
return .established
|
return .established
|
||||||
} else if noiseService.hasSession(with: peerID) {
|
} else if noiseService.hasSession(with: PeerID(str: peerID)) {
|
||||||
return .handshaking
|
return .handshaking
|
||||||
} else {
|
} else {
|
||||||
return .none
|
return .none
|
||||||
@@ -892,7 +892,7 @@ final class BLEService: NSObject {
|
|||||||
SecureLogger.debug("📨 Sending PM to \(recipientID): \(content.prefix(30))...", category: .session)
|
SecureLogger.debug("📨 Sending PM to \(recipientID): \(content.prefix(30))...", category: .session)
|
||||||
|
|
||||||
// Check if we have an established Noise session
|
// Check if we have an established Noise session
|
||||||
if noiseService.hasEstablishedSession(with: recipientID) {
|
if noiseService.hasEstablishedSession(with: PeerID(str: recipientID)) {
|
||||||
// Encrypt and send
|
// Encrypt and send
|
||||||
do {
|
do {
|
||||||
// Create TLV-encoded private message
|
// Create TLV-encoded private message
|
||||||
@@ -906,7 +906,7 @@ final class BLEService: NSObject {
|
|||||||
var messagePayload = Data([NoisePayloadType.privateMessage.rawValue])
|
var messagePayload = Data([NoisePayloadType.privateMessage.rawValue])
|
||||||
messagePayload.append(tlvData)
|
messagePayload.append(tlvData)
|
||||||
|
|
||||||
let encrypted = try noiseService.encrypt(messagePayload, for: recipientID)
|
let encrypted = try noiseService.encrypt(messagePayload, for: PeerID(str: recipientID))
|
||||||
|
|
||||||
// Convert recipientID to Data (assuming it's a hex string)
|
// Convert recipientID to Data (assuming it's a hex string)
|
||||||
var recipientData = Data()
|
var recipientData = Data()
|
||||||
@@ -972,10 +972,10 @@ final class BLEService: NSObject {
|
|||||||
|
|
||||||
private func initiateNoiseHandshake(with peerID: String) {
|
private func initiateNoiseHandshake(with peerID: String) {
|
||||||
// Use NoiseEncryptionService for handshake
|
// Use NoiseEncryptionService for handshake
|
||||||
guard !noiseService.hasSession(with: peerID) else { return }
|
guard !noiseService.hasSession(with: PeerID(str: peerID)) else { return }
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let handshakeData = try noiseService.initiateHandshake(with: peerID)
|
let handshakeData = try noiseService.initiateHandshake(with: PeerID(str: peerID))
|
||||||
|
|
||||||
// Send handshake init
|
// Send handshake init
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
@@ -1025,7 +1025,7 @@ final class BLEService: NSObject {
|
|||||||
var messagePayload = Data([NoisePayloadType.privateMessage.rawValue])
|
var messagePayload = Data([NoisePayloadType.privateMessage.rawValue])
|
||||||
messagePayload.append(tlvData)
|
messagePayload.append(tlvData)
|
||||||
|
|
||||||
let encrypted = try noiseService.encrypt(messagePayload, for: peerID)
|
let encrypted = try noiseService.encrypt(messagePayload, for: PeerID(str: peerID))
|
||||||
|
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
type: MessageType.noiseEncrypted.rawValue,
|
type: MessageType.noiseEncrypted.rawValue,
|
||||||
@@ -1841,7 +1841,7 @@ final class BLEService: NSObject {
|
|||||||
recipientID.hexEncodedString() == myPeerID {
|
recipientID.hexEncodedString() == myPeerID {
|
||||||
// Handshake is for us
|
// Handshake is for us
|
||||||
do {
|
do {
|
||||||
if let response = try noiseService.processHandshakeMessage(from: peerID, message: packet.payload) {
|
if let response = try noiseService.processHandshakeMessage(from: PeerID(str: peerID), message: packet.payload) {
|
||||||
// Send response
|
// Send response
|
||||||
let responsePacket = BitchatPacket(
|
let responsePacket = BitchatPacket(
|
||||||
type: MessageType.noiseHandshake.rawValue,
|
type: MessageType.noiseHandshake.rawValue,
|
||||||
@@ -1861,7 +1861,7 @@ final class BLEService: NSObject {
|
|||||||
} 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) {
|
if !noiseService.hasSession(with: PeerID(str: peerID)) {
|
||||||
initiateNoiseHandshake(with: peerID)
|
initiateNoiseHandshake(with: peerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1886,7 +1886,7 @@ final class BLEService: NSObject {
|
|||||||
updatePeerLastSeen(peerID)
|
updatePeerLastSeen(peerID)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let decrypted = try noiseService.decrypt(packet.payload, from: peerID)
|
let decrypted = try noiseService.decrypt(packet.payload, from: PeerID(str: peerID))
|
||||||
guard decrypted.count > 0 else { return }
|
guard decrypted.count > 0 else { return }
|
||||||
|
|
||||||
// First byte indicates the payload type
|
// First byte indicates the payload type
|
||||||
@@ -1926,7 +1926,7 @@ final class BLEService: NSObject {
|
|||||||
// We received an encrypted message before establishing a session with this peer.
|
// We received an encrypted message before establishing a session with this peer.
|
||||||
// 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(str: peerID)) {
|
||||||
initiateNoiseHandshake(with: peerID)
|
initiateNoiseHandshake(with: peerID)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -2031,9 +2031,9 @@ final class BLEService: NSObject {
|
|||||||
var payload = Data([NoisePayloadType.delivered.rawValue])
|
var payload = Data([NoisePayloadType.delivered.rawValue])
|
||||||
payload.append(contentsOf: messageID.utf8)
|
payload.append(contentsOf: messageID.utf8)
|
||||||
|
|
||||||
if noiseService.hasEstablishedSession(with: peerID) {
|
if noiseService.hasEstablishedSession(with: PeerID(str: peerID)) {
|
||||||
do {
|
do {
|
||||||
let encrypted = try noiseService.encrypt(payload, for: peerID)
|
let encrypted = try noiseService.encrypt(payload, for: PeerID(str: peerID))
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
type: MessageType.noiseEncrypted.rawValue,
|
type: MessageType.noiseEncrypted.rawValue,
|
||||||
senderID: myPeerIDData,
|
senderID: myPeerIDData,
|
||||||
@@ -2053,7 +2053,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) }
|
if !noiseService.hasSession(with: PeerID(str: 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2068,7 +2068,7 @@ final class BLEService: NSObject {
|
|||||||
SecureLogger.debug("📤 Sending \(payloads.count) pending noise payloads to \(peerID) after handshake", category: .session)
|
SecureLogger.debug("📤 Sending \(payloads.count) pending noise payloads to \(peerID) after handshake", category: .session)
|
||||||
for payload in payloads {
|
for payload in payloads {
|
||||||
do {
|
do {
|
||||||
let encrypted = try noiseService.encrypt(payload, for: peerID)
|
let encrypted = try noiseService.encrypt(payload, for: PeerID(str: peerID))
|
||||||
let packet = BitchatPacket(
|
let packet = BitchatPacket(
|
||||||
type: MessageType.noiseEncrypted.rawValue,
|
type: MessageType.noiseEncrypted.rawValue,
|
||||||
senderID: myPeerIDData,
|
senderID: myPeerIDData,
|
||||||
|
|||||||
@@ -162,8 +162,8 @@ final class NoiseEncryptionService {
|
|||||||
private let sessionManager: NoiseSessionManager
|
private let sessionManager: NoiseSessionManager
|
||||||
|
|
||||||
// Peer fingerprints (SHA256 hash of static public key)
|
// Peer fingerprints (SHA256 hash of static public key)
|
||||||
private var peerFingerprints: [String: String] = [:] // peerID -> fingerprint
|
private var peerFingerprints: [PeerID: String] = [:]
|
||||||
private var fingerprintToPeerID: [String: String] = [:] // fingerprint -> peerID
|
private var fingerprintToPeerID: [String: PeerID] = [:]
|
||||||
|
|
||||||
// Thread safety
|
// Thread safety
|
||||||
private let serviceQueue = DispatchQueue(label: "chat.bitchat.noise.service", attributes: .concurrent)
|
private let serviceQueue = DispatchQueue(label: "chat.bitchat.noise.service", attributes: .concurrent)
|
||||||
@@ -251,7 +251,7 @@ final class NoiseEncryptionService {
|
|||||||
|
|
||||||
// Set up session callbacks
|
// Set up session callbacks
|
||||||
sessionManager.onSessionEstablished = { [weak self] peerID, remoteStaticKey in
|
sessionManager.onSessionEstablished = { [weak self] peerID, remoteStaticKey in
|
||||||
self?.handleSessionEstablished(peerID: peerID, remoteStaticKey: remoteStaticKey)
|
self?.handleSessionEstablished(peerID: PeerID(str: peerID), remoteStaticKey: remoteStaticKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start session maintenance timer
|
// Start session maintenance timer
|
||||||
@@ -276,8 +276,8 @@ final class NoiseEncryptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get peer's public key data
|
/// Get peer's public key data
|
||||||
func getPeerPublicKeyData(_ peerID: String) -> Data? {
|
func getPeerPublicKeyData(_ peerID: PeerID) -> Data? {
|
||||||
return sessionManager.getRemoteStaticKey(for: peerID)?.rawRepresentation
|
return sessionManager.getRemoteStaticKey(for: peerID.id)?.rawRepresentation
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear persistent identity (for panic mode)
|
/// Clear persistent identity (for panic mode)
|
||||||
@@ -404,52 +404,52 @@ final class NoiseEncryptionService {
|
|||||||
// MARK: - Handshake Management
|
// MARK: - Handshake Management
|
||||||
|
|
||||||
/// Initiate a Noise handshake with a peer
|
/// Initiate a Noise handshake with a peer
|
||||||
func initiateHandshake(with peerID: String) throws -> Data {
|
func initiateHandshake(with peerID: PeerID) throws -> Data {
|
||||||
|
|
||||||
// Validate peer ID
|
// Validate peer ID
|
||||||
guard PeerID(str: peerID).isValid else {
|
guard peerID.isValid else {
|
||||||
SecureLogger.warning(.authenticationFailed(peerID: peerID))
|
SecureLogger.warning(.authenticationFailed(peerID: peerID.id))
|
||||||
throw NoiseSecurityError.invalidPeerID
|
throw NoiseSecurityError.invalidPeerID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rate limit
|
// Check rate limit
|
||||||
guard rateLimiter.allowHandshake(from: PeerID(str: peerID)) else {
|
guard rateLimiter.allowHandshake(from: peerID) else {
|
||||||
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peerID)"))
|
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peerID)"))
|
||||||
throw NoiseSecurityError.rateLimitExceeded
|
throw NoiseSecurityError.rateLimitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
SecureLogger.info(.handshakeStarted(peerID: peerID))
|
SecureLogger.info(.handshakeStarted(peerID: peerID.id))
|
||||||
|
|
||||||
// Return raw handshake data without wrapper
|
// Return raw handshake data without wrapper
|
||||||
// The Noise protocol handles its own message format
|
// The Noise protocol handles its own message format
|
||||||
let handshakeData = try sessionManager.initiateHandshake(with: peerID)
|
let handshakeData = try sessionManager.initiateHandshake(with: peerID.id)
|
||||||
return handshakeData
|
return handshakeData
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Process an incoming handshake message
|
/// Process an incoming handshake message
|
||||||
func processHandshakeMessage(from peerID: String, message: Data) throws -> Data? {
|
func processHandshakeMessage(from peerID: PeerID, message: Data) throws -> Data? {
|
||||||
|
|
||||||
// Validate peer ID
|
// Validate peer ID
|
||||||
guard PeerID(str: peerID).isValid else {
|
guard peerID.isValid else {
|
||||||
SecureLogger.warning(.authenticationFailed(peerID: peerID))
|
SecureLogger.warning(.authenticationFailed(peerID: peerID.id))
|
||||||
throw NoiseSecurityError.invalidPeerID
|
throw NoiseSecurityError.invalidPeerID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate message size
|
// Validate message size
|
||||||
guard NoiseSecurityValidator.validateHandshakeMessageSize(message) else {
|
guard NoiseSecurityValidator.validateHandshakeMessageSize(message) else {
|
||||||
SecureLogger.warning(.handshakeFailed(peerID: peerID, error: "Message too large"))
|
SecureLogger.warning(.handshakeFailed(peerID: peerID.id, error: "Message too large"))
|
||||||
throw NoiseSecurityError.messageTooLarge
|
throw NoiseSecurityError.messageTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rate limit
|
// Check rate limit
|
||||||
guard rateLimiter.allowHandshake(from: PeerID(str: peerID)) else {
|
guard rateLimiter.allowHandshake(from: peerID) else {
|
||||||
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peerID)"))
|
SecureLogger.warning(.authenticationFailed(peerID: "Rate limited: \(peerID)"))
|
||||||
throw NoiseSecurityError.rateLimitExceeded
|
throw NoiseSecurityError.rateLimitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
// For handshakes, we process the raw data directly without NoiseMessage wrapper
|
// For handshakes, we process the raw data directly without NoiseMessage wrapper
|
||||||
// The Noise protocol handles its own message format
|
// The Noise protocol handles its own message format
|
||||||
let responsePayload = try sessionManager.handleIncomingHandshake(from: peerID, message: message)
|
let responsePayload = try sessionManager.handleIncomingHandshake(from: peerID.id, message: message)
|
||||||
|
|
||||||
|
|
||||||
// Return raw response without wrapper
|
// Return raw response without wrapper
|
||||||
@@ -457,48 +457,48 @@ final class NoiseEncryptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if we have an established session with a peer
|
/// Check if we have an established session with a peer
|
||||||
func hasEstablishedSession(with peerID: String) -> Bool {
|
func hasEstablishedSession(with peerID: PeerID) -> Bool {
|
||||||
return sessionManager.getSession(for: peerID)?.isEstablished() ?? false
|
return sessionManager.getSession(for: peerID.id)?.isEstablished() ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if we have a session (established or handshaking) with a peer
|
/// Check if we have a session (established or handshaking) with a peer
|
||||||
func hasSession(with peerID: String) -> Bool {
|
func hasSession(with peerID: PeerID) -> Bool {
|
||||||
return sessionManager.getSession(for: peerID) != nil
|
return sessionManager.getSession(for: peerID.id) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Encryption/Decryption
|
// MARK: - Encryption/Decryption
|
||||||
|
|
||||||
/// Encrypt data for a specific peer
|
/// Encrypt data for a specific peer
|
||||||
func encrypt(_ data: Data, for peerID: String) throws -> Data {
|
func encrypt(_ data: Data, for peerID: PeerID) throws -> Data {
|
||||||
// Validate message size
|
// Validate message size
|
||||||
guard NoiseSecurityValidator.validateMessageSize(data) else {
|
guard NoiseSecurityValidator.validateMessageSize(data) else {
|
||||||
throw NoiseSecurityError.messageTooLarge
|
throw NoiseSecurityError.messageTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rate limit
|
// Check rate limit
|
||||||
guard rateLimiter.allowMessage(from: PeerID(str: peerID)) else {
|
guard rateLimiter.allowMessage(from: peerID) else {
|
||||||
throw NoiseSecurityError.rateLimitExceeded
|
throw NoiseSecurityError.rateLimitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have an established session
|
// Check if we have an established session
|
||||||
guard hasEstablishedSession(with: peerID) else {
|
guard hasEstablishedSession(with: peerID) else {
|
||||||
// Signal that handshake is needed
|
// Signal that handshake is needed
|
||||||
onHandshakeRequired?(peerID)
|
onHandshakeRequired?(peerID.id)
|
||||||
throw NoiseEncryptionError.handshakeRequired
|
throw NoiseEncryptionError.handshakeRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
return try sessionManager.encrypt(data, for: peerID)
|
return try sessionManager.encrypt(data, for: peerID.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt data from a specific peer
|
/// Decrypt data from a specific peer
|
||||||
func decrypt(_ data: Data, from peerID: String) throws -> Data {
|
func decrypt(_ data: Data, from peerID: PeerID) throws -> Data {
|
||||||
// Validate message size
|
// Validate message size
|
||||||
guard NoiseSecurityValidator.validateMessageSize(data) else {
|
guard NoiseSecurityValidator.validateMessageSize(data) else {
|
||||||
throw NoiseSecurityError.messageTooLarge
|
throw NoiseSecurityError.messageTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check rate limit
|
// Check rate limit
|
||||||
guard rateLimiter.allowMessage(from: PeerID(str: peerID)) else {
|
guard rateLimiter.allowMessage(from: peerID) else {
|
||||||
throw NoiseSecurityError.rateLimitExceeded
|
throw NoiseSecurityError.rateLimitExceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,39 +507,18 @@ final class NoiseEncryptionService {
|
|||||||
throw NoiseEncryptionError.sessionNotEstablished
|
throw NoiseEncryptionError.sessionNotEstablished
|
||||||
}
|
}
|
||||||
|
|
||||||
return try sessionManager.decrypt(data, from: peerID)
|
return try sessionManager.decrypt(data, from: peerID.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Peer Management
|
// MARK: - Peer Management
|
||||||
|
|
||||||
/// Get fingerprint for a peer
|
/// Get fingerprint for a peer
|
||||||
func getPeerFingerprint(_ peerID: String) -> String? {
|
func getPeerFingerprint(_ peerID: PeerID) -> String? {
|
||||||
return serviceQueue.sync {
|
return serviceQueue.sync {
|
||||||
return peerFingerprints[peerID]
|
return peerFingerprints[peerID]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get peer ID for a fingerprint
|
|
||||||
func getPeerID(for fingerprint: String) -> String? {
|
|
||||||
return serviceQueue.sync {
|
|
||||||
return fingerprintToPeerID[fingerprint]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a peer session
|
|
||||||
func removePeer(_ peerID: String) {
|
|
||||||
sessionManager.removeSession(for: peerID)
|
|
||||||
|
|
||||||
serviceQueue.sync(flags: .barrier) {
|
|
||||||
if let fingerprint = peerFingerprints[peerID] {
|
|
||||||
fingerprintToPeerID.removeValue(forKey: fingerprint)
|
|
||||||
}
|
|
||||||
peerFingerprints.removeValue(forKey: peerID)
|
|
||||||
}
|
|
||||||
|
|
||||||
SecureLogger.info(.sessionExpired(peerID: peerID))
|
|
||||||
}
|
|
||||||
|
|
||||||
func clearEphemeralStateForPanic() {
|
func clearEphemeralStateForPanic() {
|
||||||
sessionManager.removeAllSessions()
|
sessionManager.removeAllSessions()
|
||||||
serviceQueue.sync(flags: .barrier) {
|
serviceQueue.sync(flags: .barrier) {
|
||||||
@@ -551,7 +530,7 @@ final class NoiseEncryptionService {
|
|||||||
|
|
||||||
// MARK: - Private Helpers
|
// MARK: - Private Helpers
|
||||||
|
|
||||||
private func handleSessionEstablished(peerID: String, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) {
|
private func handleSessionEstablished(peerID: PeerID, remoteStaticKey: Curve25519.KeyAgreement.PublicKey) {
|
||||||
// Calculate fingerprint
|
// Calculate fingerprint
|
||||||
let fingerprint = remoteStaticKey.rawRepresentation.sha256Fingerprint()
|
let fingerprint = remoteStaticKey.rawRepresentation.sha256Fingerprint()
|
||||||
|
|
||||||
@@ -562,12 +541,12 @@ final class NoiseEncryptionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Log security event
|
// Log security event
|
||||||
SecureLogger.info(.handshakeCompleted(peerID: peerID))
|
SecureLogger.info(.handshakeCompleted(peerID: peerID.id))
|
||||||
|
|
||||||
// Notify all handlers about authentication
|
// Notify all handlers about authentication
|
||||||
serviceQueue.async { [weak self] in
|
serviceQueue.async { [weak self] in
|
||||||
self?.onPeerAuthenticatedHandlers.forEach { handler in
|
self?.onPeerAuthenticatedHandlers.forEach { handler in
|
||||||
handler(peerID, fingerprint)
|
handler(peerID.id, fingerprint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -310,7 +310,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
if let mapped = shortIDToNoiseKey[shortPeerID] { return mapped }
|
if let mapped = shortIDToNoiseKey[shortPeerID] { return mapped }
|
||||||
// Fallback: derive from active Noise session if available
|
// Fallback: derive from active Noise session if available
|
||||||
if shortPeerID.count == 16,
|
if shortPeerID.count == 16,
|
||||||
let key = meshService.getNoiseService().getPeerPublicKeyData(shortPeerID) {
|
let key = meshService.getNoiseService().getPeerPublicKeyData(PeerID(str: shortPeerID)) {
|
||||||
let stable = key.hexEncodedString()
|
let stable = key.hexEncodedString()
|
||||||
shortIDToNoiseKey[shortPeerID] = stable
|
shortIDToNoiseKey[shortPeerID] = stable
|
||||||
return stable
|
return stable
|
||||||
@@ -3737,7 +3737,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
func updateEncryptionStatusForPeer(_ peerID: String) {
|
func updateEncryptionStatusForPeer(_ peerID: String) {
|
||||||
let noiseService = meshService.getNoiseService()
|
let noiseService = meshService.getNoiseService()
|
||||||
|
|
||||||
if noiseService.hasEstablishedSession(with: peerID) {
|
if noiseService.hasEstablishedSession(with: PeerID(str: peerID)) {
|
||||||
// Check if fingerprint is verified using our persisted data
|
// Check if fingerprint is verified using our persisted data
|
||||||
if let fingerprint = getFingerprint(for: peerID),
|
if let fingerprint = getFingerprint(for: peerID),
|
||||||
verifiedFingerprints.contains(fingerprint) {
|
verifiedFingerprints.contains(fingerprint) {
|
||||||
@@ -3745,7 +3745,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
} else {
|
} else {
|
||||||
peerEncryptionStatus[peerID] = .noiseSecured
|
peerEncryptionStatus[peerID] = .noiseSecured
|
||||||
}
|
}
|
||||||
} else if noiseService.hasSession(with: peerID) {
|
} else if noiseService.hasSession(with: PeerID(str: peerID)) {
|
||||||
// Session exists but not established - handshaking
|
// Session exists but not established - handshaking
|
||||||
peerEncryptionStatus[peerID] = .noiseHandshaking
|
peerEncryptionStatus[peerID] = .noiseHandshaking
|
||||||
} else {
|
} else {
|
||||||
@@ -4195,7 +4195,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
private func updateEncryptionStatus(for peerID: String) {
|
private func updateEncryptionStatus(for peerID: String) {
|
||||||
let noiseService = meshService.getNoiseService()
|
let noiseService = meshService.getNoiseService()
|
||||||
|
|
||||||
if noiseService.hasEstablishedSession(with: peerID) {
|
if noiseService.hasEstablishedSession(with: PeerID(str: peerID)) {
|
||||||
if let fingerprint = getFingerprint(for: peerID) {
|
if let fingerprint = getFingerprint(for: peerID) {
|
||||||
if verifiedFingerprints.contains(fingerprint) {
|
if verifiedFingerprints.contains(fingerprint) {
|
||||||
peerEncryptionStatus[peerID] = .noiseVerified
|
peerEncryptionStatus[peerID] = .noiseVerified
|
||||||
@@ -4206,7 +4206,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
// Session established but no fingerprint yet
|
// Session established but no fingerprint yet
|
||||||
peerEncryptionStatus[peerID] = .noiseSecured
|
peerEncryptionStatus[peerID] = .noiseSecured
|
||||||
}
|
}
|
||||||
} else if noiseService.hasSession(with: peerID) {
|
} else if noiseService.hasSession(with: PeerID(str: peerID)) {
|
||||||
peerEncryptionStatus[peerID] = .noiseHandshaking
|
peerEncryptionStatus[peerID] = .noiseHandshaking
|
||||||
} else {
|
} else {
|
||||||
peerEncryptionStatus[peerID] = Optional.none
|
peerEncryptionStatus[peerID] = Optional.none
|
||||||
@@ -4357,7 +4357,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
|
|
||||||
// Cache shortID -> full Noise key mapping as soon as session authenticates
|
// Cache shortID -> full Noise key mapping as soon as session authenticates
|
||||||
if self.shortIDToNoiseKey[peerID] == nil,
|
if self.shortIDToNoiseKey[peerID] == nil,
|
||||||
let keyData = self.meshService.getNoiseService().getPeerPublicKeyData(peerID) {
|
let keyData = self.meshService.getNoiseService().getPeerPublicKeyData(PeerID(str: peerID)) {
|
||||||
let stable = keyData.hexEncodedString()
|
let stable = keyData.hexEncodedString()
|
||||||
self.shortIDToNoiseKey[peerID] = stable
|
self.shortIDToNoiseKey[peerID] = stable
|
||||||
SecureLogger.debug("🗺️ Mapped short peerID to Noise key for header continuity: \(peerID) -> \(stable.prefix(8))…", category: .session)
|
SecureLogger.debug("🗺️ Mapped short peerID to Noise key for header continuity: \(peerID) -> \(stable.prefix(8))…", category: .session)
|
||||||
@@ -4586,7 +4586,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate {
|
|||||||
pendingQRVerifications[peerID] = pending
|
pendingQRVerifications[peerID] = pending
|
||||||
// If Noise session is established, send immediately; otherwise trigger handshake and send on auth
|
// If Noise session is established, send immediately; otherwise trigger handshake and send on auth
|
||||||
let noise = meshService.getNoiseService()
|
let noise = meshService.getNoiseService()
|
||||||
if noise.hasEstablishedSession(with: peerID) {
|
if noise.hasEstablishedSession(with: PeerID(str: peerID)) {
|
||||||
meshService.sendVerifyChallenge(to: peerID, noiseKeyHex: qr.noiseKeyHex, nonceA: nonce)
|
meshService.sendVerifyChallenge(to: peerID, noiseKeyHex: qr.noiseKeyHex, nonceA: nonce)
|
||||||
pending.sent = true
|
pending.sent = true
|
||||||
pendingQRVerifications[peerID] = pending
|
pendingQRVerifications[peerID] = pending
|
||||||
@@ -4637,7 +4637,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)
|
// 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]
|
var derivedStableKeyHex: String? = shortIDToNoiseKey[peerID]
|
||||||
if derivedStableKeyHex == nil,
|
if derivedStableKeyHex == nil,
|
||||||
let key = meshService.getNoiseService().getPeerPublicKeyData(peerID) {
|
let key = meshService.getNoiseService().getPeerPublicKeyData(PeerID(str: peerID)) {
|
||||||
derivedStableKeyHex = key.hexEncodedString()
|
derivedStableKeyHex = key.hexEncodedString()
|
||||||
shortIDToNoiseKey[peerID] = derivedStableKeyHex
|
shortIDToNoiseKey[peerID] = derivedStableKeyHex
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user