diff --git a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt index 34ef891e..72ac05af 100644 --- a/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt +++ b/app/src/main/java/com/bitchat/android/mesh/BluetoothMeshService.kt @@ -66,7 +66,7 @@ class BluetoothMeshService(private val context: Context) { // Wire up PacketProcessor reference for recursive handling in MessageHandler messageHandler.packetProcessor = packetProcessor sendPeriodicBroadcastAnnounce() - //startPeriodicDebugLogging() + startPeriodicDebugLogging() } /** @@ -126,9 +126,6 @@ class BluetoothMeshService(private val context: Context) { // SecurityManager delegate for key exchange notifications securityManager.delegate = object : SecurityManagerDelegate { override fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray, receivedAddress: String?) { - // Notify delegate about key exchange completion so it can register peer fingerprint - delegate?.registerPeerPublicKey(peerID, peerPublicKeyData) - receivedAddress?.let { address -> connectionManager.addressPeerMap[address] = peerID } @@ -259,19 +256,21 @@ class BluetoothMeshService(private val context: Context) { } } - override fun updatePeerIDBinding(newPeerID: String, fingerprint: String, nickname: String, + override fun updatePeerIDBinding(newPeerID: String, nickname: String, publicKey: ByteArray, previousPeerID: String?) { + + Log.d(TAG, "Updating peer ID binding: $newPeerID (was: $previousPeerID) with nickname: $nickname and public key: ${publicKey.toHexString().take(16)}...") // Update peer mapping in the PeerManager for peer ID rotation support peerManager.addOrUpdatePeer(newPeerID, nickname) + // Store fingerprint for the peer via centralized fingerprint manager + val fingerprint = peerManager.storeFingerprintForPeer(newPeerID, publicKey) + // If there was a previous peer ID, remove it to avoid duplicates previousPeerID?.let { oldPeerID -> peerManager.removePeer(oldPeerID) } - // Register the public key with the delegate (ChatViewModel) - delegate?.registerPeerPublicKey(newPeerID, publicKey) - Log.d(TAG, "Updated peer ID binding: $newPeerID (was: $previousPeerID), fingerprint: ${fingerprint.take(16)}...") } @@ -794,7 +793,7 @@ class BluetoothMeshService(private val context: Context) { * Get peer fingerprint for identity management */ fun getPeerFingerprint(peerID: String): String? { - return encryptionService.getPeerFingerprint(peerID) + return peerManager.getFingerprintForPeer(peerID) } /** @@ -853,6 +852,8 @@ class BluetoothMeshService(private val context: Context) { appendLine() appendLine(peerManager.getDebugInfo(connectionManager.addressPeerMap)) appendLine() + appendLine(peerManager.getFingerprintDebugInfo()) + appendLine() appendLine(fragmentManager.getDebugInfo()) appendLine() appendLine(securityManager.getDebugInfo()) @@ -910,5 +911,5 @@ interface BluetoothMeshDelegate { fun decryptChannelMessage(encryptedContent: ByteArray, channel: String): String? fun getNickname(): String? fun isFavorite(peerID: String): Boolean - fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) + // registerPeerPublicKey REMOVED - fingerprints now handled centrally in PeerManager } diff --git a/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt b/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt index de4a885b..53896269 100644 --- a/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt +++ b/app/src/main/java/com/bitchat/android/mesh/MessageHandler.kt @@ -138,7 +138,6 @@ class MessageHandler(private val myPeerID: String) { // Update peer binding in the delegate (ChatViewModel/BluetoothMeshService) delegate?.updatePeerIDBinding( newPeerID = announcement.peerID, - fingerprint = announcement.fingerprint ?: "", nickname = announcement.nickname, publicKey = announcement.publicKey, previousPeerID = announcement.previousPeerID @@ -409,7 +408,7 @@ interface MessageHandlerDelegate { // Noise protocol operations fun hasNoiseSession(peerID: String): Boolean fun initiateNoiseHandshake(peerID: String) - fun updatePeerIDBinding(newPeerID: String, fingerprint: String, nickname: String, + fun updatePeerIDBinding(newPeerID: String, nickname: String, publicKey: ByteArray, previousPeerID: String?) // Message operations diff --git a/app/src/main/java/com/bitchat/android/mesh/PeerFingerprintManager.kt b/app/src/main/java/com/bitchat/android/mesh/PeerFingerprintManager.kt new file mode 100644 index 00000000..56de17da --- /dev/null +++ b/app/src/main/java/com/bitchat/android/mesh/PeerFingerprintManager.kt @@ -0,0 +1,247 @@ +package com.bitchat.android.mesh + +import android.util.Log +import java.security.MessageDigest +import java.util.concurrent.ConcurrentHashMap + +/** + * Centralized peer fingerprint management singleton + * + * This class manages all peer fingerprint storage and retrieval operations, + * providing a single source of truth for peer identity across the entire application. + * + * Fingerprints are SHA-256 hashes of peer static public keys and are only stored + * after successful Noise handshake session establishment. + * + * Key Design Principles: + * - Thread-safe operations using ConcurrentHashMap + * - Bidirectional mapping (peerID ↔ fingerprint) + * - Support for peer ID rotation while maintaining persistent identity + * - Centralized logging for debugging identity management + */ +class PeerFingerprintManager private constructor() { + + companion object { + private const val TAG = "PeerFingerprintManager" + + @Volatile + private var INSTANCE: PeerFingerprintManager? = null + + /** + * Get the singleton instance + */ + fun getInstance(): PeerFingerprintManager { + return INSTANCE ?: synchronized(this) { + INSTANCE ?: PeerFingerprintManager().also { INSTANCE = it } + } + } + } + + // Bidirectional mapping for efficient lookups + private val peerIDToFingerprint = ConcurrentHashMap() // peerID -> fingerprint + private val fingerprintToPeerID = ConcurrentHashMap() // fingerprint -> current peerID + + // MARK: - Fingerprint Storage (Only called after successful Noise handshake) + + /** + * Store fingerprint mapping after successful Noise handshake session establishment + * This is the ONLY place where fingerprints should be stored + * + * @param peerID The peer's current ID + * @param publicKey The peer's static public key from Noise handshake + */ + fun storeFingerprintForPeer(peerID: String, publicKey: ByteArray): String { + // get existing fingerprint for this peer and compare + val existingFingerprint = getFingerprintForPeer(peerID) + val fingerprint = calculateFingerprint(publicKey) + + if (existingFingerprint != null && existingFingerprint != fingerprint) { + Log.w(TAG, "Fingerprint mismatch for peer $peerID: $existingFingerprint != $fingerprint") + throw IllegalStateException("Fingerprint mismatch for peer $peerID: $existingFingerprint != $fingerprint") + } + + // Store bidirectional mapping + peerIDToFingerprint[peerID] = fingerprint + fingerprintToPeerID[fingerprint] = peerID + + Log.d(TAG, "Stored fingerprint for peer $peerID: ${fingerprint.take(16)}...") + return fingerprint + } + + /** + * Update peer ID mapping while preserving fingerprint identity + * Used for peer ID rotation - when a peer changes their ID but maintains the same static key + * + * @param oldPeerID The previous peer ID (nullable if this is a fresh mapping) + * @param newPeerID The new peer ID + * @param fingerprint The persistent fingerprint (should match existing one) + */ + fun updatePeerIDMapping(oldPeerID: String?, newPeerID: String, fingerprint: String) { + if (newPeerID.isBlank()) { + Log.w(TAG, "Attempted to update mapping with blank newPeerID") + return + } + + if (fingerprint.isBlank()) { + Log.w(TAG, "Attempted to update mapping with blank fingerprint") + return + } + + // Remove old mapping if exists + oldPeerID?.takeIf { it.isNotBlank() }?.let { oldID -> + val removedFingerprint = peerIDToFingerprint.remove(oldID) + if (removedFingerprint != null && removedFingerprint == fingerprint) { + Log.d(TAG, "Removed old mapping: $oldID -> ${removedFingerprint.take(16)}...") + } + } + + // Add new mapping + peerIDToFingerprint[newPeerID] = fingerprint + fingerprintToPeerID[fingerprint] = newPeerID + + Log.d(TAG, "Updated peer ID mapping: $newPeerID (was: $oldPeerID), fingerprint: ${fingerprint.take(16)}...") + } + + // MARK: - Fingerprint Retrieval + + /** + * Get fingerprint for a specific peer ID + * + * @param peerID The peer ID to look up + * @return The fingerprint if found, null otherwise + */ + fun getFingerprintForPeer(peerID: String): String? { + if (peerID.isBlank()) return null + return peerIDToFingerprint[peerID] + } + + /** + * Get current peer ID for a specific fingerprint + * + * @param fingerprint The fingerprint to look up + * @return The current peer ID if found, null otherwise + */ + fun getPeerIDForFingerprint(fingerprint: String): String? { + if (fingerprint.isBlank()) return null + return fingerprintToPeerID[fingerprint] + } + + /** + * Check if we have a fingerprint for a specific peer + * + * @param peerID The peer ID to check + * @return True if we have a fingerprint for this peer, false otherwise + */ + fun hasFingerprintForPeer(peerID: String): Boolean { + return getFingerprintForPeer(peerID) != null + } + + /** + * Get all current peer ID to fingerprint mappings + * + * @return Immutable copy of all mappings + */ + fun getAllPeerFingerprints(): Map { + return peerIDToFingerprint.toMap() + } + + /** + * Get all current fingerprint to peer ID mappings + * + * @return Immutable copy of all reverse mappings + */ + fun getAllFingerprintMappings(): Map { + return fingerprintToPeerID.toMap() + } + + // MARK: - Peer Management + + /** + * Remove all mappings for a specific peer (called when peer disconnects) + * + * @param peerID The peer ID to remove + */ + fun removePeer(peerID: String) { + if (peerID.isBlank()) return + + val fingerprint = peerIDToFingerprint.remove(peerID) + if (fingerprint != null) { + fingerprintToPeerID.remove(fingerprint) + Log.d(TAG, "Removed peer mappings for $peerID: ${fingerprint.take(16)}...") + } + } + + /** + * Remove all mappings for a specific fingerprint + * + * @param fingerprint The fingerprint to remove + */ + fun removeFingerprint(fingerprint: String) { + if (fingerprint.isBlank()) return + + val peerID = fingerprintToPeerID.remove(fingerprint) + if (peerID != null) { + peerIDToFingerprint.remove(peerID) + Log.d(TAG, "Removed fingerprint mappings for ${fingerprint.take(16)}...: $peerID") + } + } + + /** + * Clear all fingerprint mappings (used for emergency clear/panic mode) + */ + fun clearAllFingerprints() { + val count = peerIDToFingerprint.size + peerIDToFingerprint.clear() + fingerprintToPeerID.clear() + Log.d(TAG, "Cleared all fingerprint mappings ($count entries)") + } + + // MARK: - Utility Functions + + /** + * Calculate SHA-256 fingerprint from public key + * + * @param publicKey The peer's static public key + * @return The hex-encoded SHA-256 hash + */ + private fun calculateFingerprint(publicKey: ByteArray): String { + val digest = MessageDigest.getInstance("SHA-256") + val hash = digest.digest(publicKey) + return hash.joinToString("") { "%02x".format(it) } + } + + /** + * Get debug information about current fingerprint mappings + * + * @return Debug string with mapping counts and summary + */ + fun getDebugInfo(): String { + return buildString { + appendLine("=== PeerFingerprintManager Debug Info ===") + appendLine("Total mappings: ${peerIDToFingerprint.size}") + + if (peerIDToFingerprint.isNotEmpty()) { + appendLine("Peer ID -> Fingerprint mappings:") + peerIDToFingerprint.forEach { (peerID, fingerprint) -> + appendLine(" $peerID -> ${fingerprint.take(16)}...") + } + } else { + appendLine("No fingerprint mappings stored") + } + + // Verify bidirectional mapping consistency + val inconsistentMappings = mutableListOf() + peerIDToFingerprint.forEach { (peerID, fingerprint) -> + val reversePeerID = fingerprintToPeerID[fingerprint] + if (reversePeerID != peerID) { + inconsistentMappings.add("$peerID -> $fingerprint -> $reversePeerID") + } + } + + if (inconsistentMappings.isNotEmpty()) { + appendLine("⚠️ INCONSISTENT MAPPINGS DETECTED:") + inconsistentMappings.forEach { appendLine(" $it") } + } + } + } +} diff --git a/app/src/main/java/com/bitchat/android/mesh/PeerManager.kt b/app/src/main/java/com/bitchat/android/mesh/PeerManager.kt index a1234509..9933d8ac 100644 --- a/app/src/main/java/com/bitchat/android/mesh/PeerManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/PeerManager.kt @@ -7,8 +7,10 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList /** - * Manages active peers, nicknames, and RSSI tracking + * Manages active peers, nicknames, RSSI tracking, and peer fingerprints * Extracted from BluetoothMeshService for better separation of concerns + * + * Now includes centralized peer fingerprint management via PeerFingerprintManager singleton */ class PeerManager { @@ -25,6 +27,9 @@ class PeerManager { private val announcedPeers = CopyOnWriteArrayList() private val announcedToPeers = CopyOnWriteArrayList() + // Centralized fingerprint management + private val fingerprintManager = PeerFingerprintManager.getInstance() + // Delegate for callbacks var delegate: PeerManagerDelegate? = null @@ -81,7 +86,7 @@ class PeerManager { notifyPeerListUpdate() return true } - + Log.d(TAG, "Updated peer: $peerID ($nickname)") return false } @@ -95,6 +100,9 @@ class PeerManager { announcedPeers.remove(peerID) announcedToPeers.remove(peerID) + // Also remove fingerprint mappings + fingerprintManager.removePeer(peerID) + if (notifyDelegate && nickname != null) { delegate?.onPeerDisconnected(nickname) notifyPeerListUpdate() @@ -177,6 +185,10 @@ class PeerManager { peerRSSI.clear() announcedPeers.clear() announcedToPeers.clear() + + // Also clear fingerprint mappings + fingerprintManager.clearAllFingerprints() + notifyPeerListUpdate() } @@ -264,6 +276,83 @@ class PeerManager { } } + // MARK: - Fingerprint Management (Centralized) + + /** + * Store fingerprint for a peer after successful Noise handshake + * This should only be called when a Noise session is established + * + * @param peerID The peer's ID + * @param publicKey The peer's static public key from Noise handshake + */ + fun storeFingerprintForPeer(peerID: String, publicKey: ByteArray): String { + return fingerprintManager.storeFingerprintForPeer(peerID, publicKey) + } + + /** + * Update peer ID mapping for peer ID rotation + * + * @param oldPeerID The previous peer ID (nullable) + * @param newPeerID The new peer ID + * @param fingerprint The persistent fingerprint + */ + fun updatePeerIDMapping(oldPeerID: String?, newPeerID: String, fingerprint: String) { + fingerprintManager.updatePeerIDMapping(oldPeerID, newPeerID, fingerprint) + } + + /** + * Get fingerprint for a specific peer + * + * @param peerID The peer ID to look up + * @return The fingerprint if found, null otherwise + */ + fun getFingerprintForPeer(peerID: String): String? { + return fingerprintManager.getFingerprintForPeer(peerID) + } + + /** + * Get current peer ID for a specific fingerprint + * + * @param fingerprint The fingerprint to look up + * @return The current peer ID if found, null otherwise + */ + fun getPeerIDForFingerprint(fingerprint: String): String? { + return fingerprintManager.getPeerIDForFingerprint(fingerprint) + } + + /** + * Check if we have a fingerprint for a specific peer + * + * @param peerID The peer ID to check + * @return True if we have a fingerprint for this peer, false otherwise + */ + fun hasFingerprintForPeer(peerID: String): Boolean { + return fingerprintManager.hasFingerprintForPeer(peerID) + } + + /** + * Get all current peer ID to fingerprint mappings + * + * @return Immutable copy of all mappings + */ + fun getAllPeerFingerprints(): Map { + return fingerprintManager.getAllPeerFingerprints() + } + + /** + * Clear all fingerprint mappings (used for emergency clear) + */ + fun clearAllFingerprints() { + fingerprintManager.clearAllFingerprints() + } + + /** + * Get fingerprint manager debug info + */ + fun getFingerprintDebugInfo(): String { + return fingerprintManager.getDebugInfo() + } + /** * Shutdown the manager */ diff --git a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt index 7c292de5..f66c38f6 100644 --- a/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt +++ b/app/src/main/java/com/bitchat/android/mesh/SecurityManager.kt @@ -146,46 +146,46 @@ class SecurityManager(private val encryptionService: EncryptionService, private } } - /** - * Handle key exchange packet - */ - suspend fun handleKeyExchange(routed: RoutedPacket): Boolean { - val packet = routed.packet - val peerID = routed.peerID ?: "unknown" - - if (peerID == myPeerID) return false - - if (packet.payload.isEmpty()) { - Log.w(TAG, "Key exchange packet has empty payload") - return false - } - - // Prevent duplicate key exchange processing - val exchangeKey = "$peerID-${packet.payload.sliceArray(0 until minOf(16, packet.payload.size)).contentHashCode()}" - - if (processedKeyExchanges.contains(exchangeKey)) { - Log.d(TAG, "Already processed key exchange: $exchangeKey") - return false - } - - processedKeyExchanges.add(exchangeKey) - - try { - // Process the key exchange - encryptionService.addPeerPublicKey(peerID, packet.payload) - - Log.d(TAG, "Successfully processed key exchange from $peerID") - - // Notify delegate - delegate?.onKeyExchangeCompleted(peerID, packet.payload, routed.relayAddress) - - return true - - } catch (e: Exception) { - Log.e(TAG, "Failed to process key exchange from $peerID: ${e.message}") - return false - } - } +// /** +// * Handle key exchange packet +// */ +// suspend fun handleKeyExchange(routed: RoutedPacket): Boolean { +// val packet = routed.packet +// val peerID = routed.peerID ?: "unknown" +// +// if (peerID == myPeerID) return false +// +// if (packet.payload.isEmpty()) { +// Log.w(TAG, "Key exchange packet has empty payload") +// return false +// } +// +// // Prevent duplicate key exchange processing +// val exchangeKey = "$peerID-${packet.payload.sliceArray(0 until minOf(16, packet.payload.size)).contentHashCode()}" +// +// if (processedKeyExchanges.contains(exchangeKey)) { +// Log.d(TAG, "Already processed key exchange: $exchangeKey") +// return false +// } +// +// processedKeyExchanges.add(exchangeKey) +// +// try { +// // Process the key exchange +// encryptionService.addPeerPublicKey(peerID, packet.payload) +// +// Log.d(TAG, "Successfully processed key exchange from $peerID") +// +// // Notify delegate +// delegate?.onKeyExchangeCompleted(peerID, packet.payload, routed.relayAddress) +// +// return true +// +// } catch (e: Exception) { +// Log.e(TAG, "Failed to process key exchange from $peerID: ${e.message}") +// return false +// } +// } /** * Verify packet signature diff --git a/app/src/main/java/com/bitchat/android/noise/NoiseEncryptionService.kt b/app/src/main/java/com/bitchat/android/noise/NoiseEncryptionService.kt index 96a624fe..0e1fbc60 100644 --- a/app/src/main/java/com/bitchat/android/noise/NoiseEncryptionService.kt +++ b/app/src/main/java/com/bitchat/android/noise/NoiseEncryptionService.kt @@ -3,6 +3,7 @@ package com.bitchat.android.noise import android.content.Context import android.util.Log import com.bitchat.android.identity.SecureIdentityStateManager +import com.bitchat.android.mesh.PeerFingerprintManager import com.bitchat.android.noise.southernstorm.protocol.Noise import java.security.MessageDigest import java.security.SecureRandom @@ -40,9 +41,8 @@ class NoiseEncryptionService(private val context: Context) { // Identity management for peer ID rotation support private val identityStateManager: SecureIdentityStateManager - // Peer fingerprints mapping (peerID -> fingerprint and fingerprint -> peerID) - private val peerFingerprints = ConcurrentHashMap() // peerID -> fingerprint - private val fingerprintToPeerID = ConcurrentHashMap() // fingerprint -> current peerID + // Centralized fingerprint management - NO LOCAL STORAGE + private val fingerprintManager = PeerFingerprintManager.getInstance() // Callbacks var onPeerAuthenticated: ((String, String) -> Unit)? = null // (peerID, fingerprint) @@ -188,14 +188,14 @@ class NoiseEncryptionService(private val context: Context) { * Get fingerprint for a peer (returns null if peer unknown) */ fun getPeerFingerprint(peerID: String): String? { - return peerFingerprints[peerID] + return fingerprintManager.getFingerprintForPeer(peerID) } /** * Get current peer ID for a fingerprint (returns null if not currently online) */ fun getPeerID(fingerprint: String): String? { - return fingerprintToPeerID[fingerprint] + return fingerprintManager.getPeerIDForFingerprint(fingerprint) } /** @@ -204,11 +204,8 @@ class NoiseEncryptionService(private val context: Context) { fun removePeer(peerID: String) { sessionManager.removeSession(peerID) - // Clean up fingerprint mappings - val fingerprint = peerFingerprints.remove(peerID) - if (fingerprint != null) { - fingerprintToPeerID.remove(fingerprint) - } + // Clean up fingerprint mappings via centralized manager + fingerprintManager.removePeer(peerID) } /** @@ -216,14 +213,8 @@ class NoiseEncryptionService(private val context: Context) { * This allows favorites/blocking to persist across peer ID changes */ fun updatePeerIDMapping(oldPeerID: String?, newPeerID: String, fingerprint: String) { - // Remove old mapping if exists - oldPeerID?.let { oldID -> - peerFingerprints.remove(oldID) - } - - // Add new mapping - peerFingerprints[newPeerID] = fingerprint - fingerprintToPeerID[fingerprint] = newPeerID + // Use centralized fingerprint manager for peer ID rotation + fingerprintManager.updatePeerIDMapping(oldPeerID, newPeerID, fingerprint) } // MARK: - Channel Encryption @@ -318,12 +309,12 @@ class NoiseEncryptionService(private val context: Context) { * Handle session establishment (called when Noise handshake completes) */ private fun handleSessionEstablished(peerID: String, remoteStaticKey: ByteArray) { - // Calculate fingerprint from remote static key - val fingerprint = calculateFingerprint(remoteStaticKey) + // Store fingerprint mapping via centralized manager + // This is the ONLY place where fingerprints are stored - after successful Noise handshake + fingerprintManager.storeFingerprintForPeer(peerID, remoteStaticKey) - // Store fingerprint mapping - peerFingerprints[peerID] = fingerprint - fingerprintToPeerID[fingerprint] = peerID + // Calculate fingerprint for logging and callback + val fingerprint = calculateFingerprint(remoteStaticKey) Log.d(TAG, "Session established with $peerID, fingerprint: ${fingerprint.take(16)}...") @@ -346,8 +337,7 @@ class NoiseEncryptionService(private val context: Context) { fun shutdown() { sessionManager.shutdown() channelEncryption.clear() - peerFingerprints.clear() - fingerprintToPeerID.clear() + // No need to clear fingerprints here - they are managed centrally } } diff --git a/app/src/main/java/com/bitchat/android/ui/ChatState.kt b/app/src/main/java/com/bitchat/android/ui/ChatState.kt index 4a2e9bda..9b32ea9b 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatState.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatState.kt @@ -83,7 +83,7 @@ class ChatState { private val _favoritePeers = MutableLiveData>(emptySet()) val favoritePeers: LiveData> = _favoritePeers - val peerIDToPublicKeyFingerprint = mutableMapOf() + // peerIDToPublicKeyFingerprint REMOVED - fingerprints now handled centrally in PeerManager // Navigation state private val _showAppInfo = MutableLiveData(false) diff --git a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt index d8fc1634..6f49d7a6 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatViewModel.kt @@ -344,9 +344,7 @@ class ChatViewModel( return meshDelegateHandler.isFavorite(peerID) } - override fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) { - privateChatManager.registerPeerPublicKey(peerID, publicKeyData) - } + // registerPeerPublicKey REMOVED - fingerprints now handled centrally in PeerManager // MARK: - Emergency Clear diff --git a/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt b/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt index ce37c477..daf30451 100644 --- a/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt +++ b/app/src/main/java/com/bitchat/android/ui/MeshDelegateHandler.kt @@ -153,7 +153,5 @@ class MeshDelegateHandler( return privateChatManager.isFavorite(peerID) } - override fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) { - privateChatManager.registerPeerPublicKey(peerID, publicKeyData) - } + // registerPeerPublicKey REMOVED - fingerprints now handled centrally in PeerManager } diff --git a/app/src/main/java/com/bitchat/android/ui/PrivateChatManager.kt b/app/src/main/java/com/bitchat/android/ui/PrivateChatManager.kt index 6c70241e..d50f71d9 100644 --- a/app/src/main/java/com/bitchat/android/ui/PrivateChatManager.kt +++ b/app/src/main/java/com/bitchat/android/ui/PrivateChatManager.kt @@ -2,11 +2,13 @@ package com.bitchat.android.ui import com.bitchat.android.model.BitchatMessage import com.bitchat.android.model.DeliveryStatus +import com.bitchat.android.mesh.PeerFingerprintManager import java.util.* import android.util.Log /** * Handles private chat functionality including peer management and blocking + * Now uses centralized PeerFingerprintManager for all fingerprint operations */ class PrivateChatManager( private val state: ChatState, @@ -18,8 +20,8 @@ class PrivateChatManager( private const val TAG = "PrivateChatManager" } - // Peer identification mapping - private val peerIDToPublicKeyFingerprint = mutableMapOf() + // Use centralized fingerprint management - NO LOCAL STORAGE + private val fingerprintManager = PeerFingerprintManager.getInstance() // MARK: - Private Chat Lifecycle @@ -89,20 +91,13 @@ class PrivateChatManager( // MARK: - Peer Management - fun registerPeerPublicKey(peerID: String, publicKeyData: ByteArray) { - val md = java.security.MessageDigest.getInstance("SHA-256") - val hash = md.digest(publicKeyData) - val fingerprint = hash.take(8).joinToString("") { "%02x".format(it) } - peerIDToPublicKeyFingerprint[peerID] = fingerprint - } - fun isPeerBlocked(peerID: String): Boolean { - val fingerprint = peerIDToPublicKeyFingerprint[peerID] + val fingerprint = fingerprintManager.getFingerprintForPeer(peerID) return fingerprint != null && dataManager.isUserBlocked(fingerprint) } fun toggleFavorite(peerID: String) { - val fingerprint = peerIDToPublicKeyFingerprint[peerID] ?: return + val fingerprint = fingerprintManager.getFingerprintForPeer(peerID) ?: return Log.d(TAG, "toggleFavorite called for peerID: $peerID, fingerprint: $fingerprint") @@ -125,28 +120,28 @@ class PrivateChatManager( state.setFavoritePeers(newFavorites) Log.d(TAG, "Force updated favorite peers state. New favorites: $newFavorites") - Log.d(TAG, "All peer fingerprints: $peerIDToPublicKeyFingerprint") + Log.d(TAG, "All peer fingerprints: ${fingerprintManager.getAllPeerFingerprints()}") } fun isFavorite(peerID: String): Boolean { - val fingerprint = peerIDToPublicKeyFingerprint[peerID] ?: return false + val fingerprint = fingerprintManager.getFingerprintForPeer(peerID) ?: return false val isFav = dataManager.isFavorite(fingerprint) Log.d(TAG, "isFavorite check: peerID=$peerID, fingerprint=$fingerprint, result=$isFav") return isFav } fun getPeerFingerprint(peerID: String): String? { - return peerIDToPublicKeyFingerprint[peerID] + return fingerprintManager.getFingerprintForPeer(peerID) } fun getPeerFingerprints(): Map { - return peerIDToPublicKeyFingerprint.toMap() + return fingerprintManager.getAllPeerFingerprints() } // MARK: - Block/Unblock Operations fun blockPeer(peerID: String, meshService: Any): Boolean { - val fingerprint = peerIDToPublicKeyFingerprint[peerID] + val fingerprint = fingerprintManager.getFingerprintForPeer(peerID) if (fingerprint != null) { dataManager.addBlockedUser(fingerprint) @@ -170,7 +165,7 @@ class PrivateChatManager( } fun unblockPeer(peerID: String, meshService: Any): Boolean { - val fingerprint = peerIDToPublicKeyFingerprint[peerID] + val fingerprint = fingerprintManager.getFingerprintForPeer(peerID) if (fingerprint != null && dataManager.isUserBlocked(fingerprint)) { dataManager.removeBlockedUser(fingerprint) @@ -208,7 +203,7 @@ class PrivateChatManager( val peerID = getPeerIDForNickname(targetName, meshService) if (peerID != null) { - val fingerprint = peerIDToPublicKeyFingerprint[peerID] + val fingerprint = fingerprintManager.getFingerprintForPeer(peerID) if (fingerprint != null && dataManager.isUserBlocked(fingerprint)) { return unblockPeer(peerID, meshService) } else { @@ -288,12 +283,14 @@ class PrivateChatManager( fun clearAllPrivateChats() { state.setSelectedPrivateChatPeer(null) state.setUnreadPrivateMessages(emptySet()) - peerIDToPublicKeyFingerprint.clear() + + // Clear fingerprints via centralized manager (only if needed for emergency clear) + // Note: This will be handled by the parent PeerManager.clearAllPeers() } // MARK: - Public Getters fun getAllPeerFingerprints(): Map { - return peerIDToPublicKeyFingerprint.toMap() + return fingerprintManager.getAllPeerFingerprints() } }