mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 09:25:19 +00:00
Refactor fingerprint manager (#185)
* centralized fingerprint manager * centralized fingerprintmanager * update fingerprint only if no collision
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<String, String>() // peerID -> fingerprint
|
||||
private val fingerprintToPeerID = ConcurrentHashMap<String, String>() // 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<String, String> {
|
||||
return peerIDToFingerprint.toMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all current fingerprint to peer ID mappings
|
||||
*
|
||||
* @return Immutable copy of all reverse mappings
|
||||
*/
|
||||
fun getAllFingerprintMappings(): Map<String, String> {
|
||||
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<String>()
|
||||
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") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String>()
|
||||
private val announcedToPeers = CopyOnWriteArrayList<String>()
|
||||
|
||||
// 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<String, String> {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<String, String>() // peerID -> fingerprint
|
||||
private val fingerprintToPeerID = ConcurrentHashMap<String, String>() // 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class ChatState {
|
||||
private val _favoritePeers = MutableLiveData<Set<String>>(emptySet())
|
||||
val favoritePeers: LiveData<Set<String>> = _favoritePeers
|
||||
|
||||
val peerIDToPublicKeyFingerprint = mutableMapOf<String, String>()
|
||||
// peerIDToPublicKeyFingerprint REMOVED - fingerprints now handled centrally in PeerManager
|
||||
|
||||
// Navigation state
|
||||
private val _showAppInfo = MutableLiveData<Boolean>(false)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<String, String>()
|
||||
// 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<String, String> {
|
||||
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<String, String> {
|
||||
return peerIDToPublicKeyFingerprint.toMap()
|
||||
return fingerprintManager.getAllPeerFingerprints()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user