mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-24 22:25:19 +00:00
wip
This commit is contained in:
@@ -160,8 +160,8 @@ class BluetoothMeshService(private val context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// SecurityManager delegate for key exchange notifications
|
||||
securityManager.delegate = object : SecurityManagerDelegate {
|
||||
// SecurityManager delegate for key exchange notifications + gossip lookup
|
||||
securityManager.delegate = object : SecurityManagerDelegate, SecurityManagerGossipLookup {
|
||||
override fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) {
|
||||
// Send announcement and cached messages after key exchange
|
||||
serviceScope.launch {
|
||||
@@ -193,6 +193,11 @@ class BluetoothMeshService(private val context: Context) {
|
||||
override fun getPeerInfo(peerID: String): PeerInfo? {
|
||||
return peerManager.getPeerInfo(peerID)
|
||||
}
|
||||
|
||||
// SecurityManagerGossipLookup
|
||||
override fun isInGossipSync(packet: BitchatPacket): Boolean {
|
||||
return try { gossipSyncManager.hasSeenPacket(packet) } catch (_: Exception) { false }
|
||||
}
|
||||
}
|
||||
|
||||
// StoreForwardManager delegates
|
||||
@@ -366,6 +371,10 @@ class BluetoothMeshService(private val context: Context) {
|
||||
override fun onReadReceiptReceived(messageID: String, peerID: String) {
|
||||
delegate?.didReceiveReadReceipt(messageID, peerID)
|
||||
}
|
||||
|
||||
override fun isInGossipSync(packet: BitchatPacket): Boolean {
|
||||
return try { gossipSyncManager.hasSeenPacket(packet) } catch (_: Exception) { false }
|
||||
}
|
||||
}
|
||||
|
||||
// PacketProcessor delegates
|
||||
|
||||
@@ -41,8 +41,12 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
||||
|
||||
Log.d(TAG, "Processing Noise encrypted message from $peerID (${packet.payload.size} bytes)")
|
||||
|
||||
// Skip our own messages
|
||||
if (peerID == myPeerID) return
|
||||
// Skip our own messages, except when coming from sync (TTL=SYNC_TTL_HOPS) and not tracked in gossip
|
||||
if (peerID == myPeerID) {
|
||||
val isSyncTtl = packet.ttl == com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS
|
||||
val seenInGossip = try { delegate?.isInGossipSync(packet) } catch (_: Exception) { null }
|
||||
if (!(isSyncTtl && (seenInGossip == false))) return
|
||||
}
|
||||
|
||||
// Check if this message is for us
|
||||
val recipientID = packet.recipientID?.toHexString()
|
||||
@@ -209,7 +213,12 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
||||
val packet = routed.packet
|
||||
val peerID = routed.peerID ?: "unknown"
|
||||
|
||||
if (peerID == myPeerID) return false
|
||||
// Skip our own announce, except when coming from sync (TTL=SYNC_TTL_HOPS) and not tracked in gossip
|
||||
if (peerID == myPeerID) {
|
||||
val isSyncTtl = packet.ttl == com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS
|
||||
val seenInGossip = try { delegate?.isInGossipSync(packet) } catch (_: Exception) { null }
|
||||
if (!(isSyncTtl && (seenInGossip == false))) return false
|
||||
}
|
||||
|
||||
// Ignore stale announcements older than STALE_PEER_TIMEOUT
|
||||
val now = System.currentTimeMillis()
|
||||
@@ -342,7 +351,11 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
||||
suspend fun handleMessage(routed: RoutedPacket) {
|
||||
val packet = routed.packet
|
||||
val peerID = routed.peerID ?: "unknown"
|
||||
if (peerID == myPeerID) return
|
||||
if (peerID == myPeerID) {
|
||||
val isSyncTtl = packet.ttl == com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS
|
||||
val seenInGossip = try { delegate?.isInGossipSync(packet) } catch (_: Exception) { null }
|
||||
if (!(isSyncTtl && (seenInGossip == false))) return
|
||||
}
|
||||
val senderNickname = delegate?.getPeerNickname(peerID)
|
||||
if (senderNickname != null) {
|
||||
Log.d(TAG, "Received message from $senderNickname")
|
||||
@@ -611,4 +624,7 @@ interface MessageHandlerDelegate {
|
||||
fun onChannelLeave(channel: String, fromPeer: String)
|
||||
fun onDeliveryAckReceived(messageID: String, peerID: String)
|
||||
fun onReadReceiptReceived(messageID: String, peerID: String)
|
||||
|
||||
// Gossip sync visibility
|
||||
fun isInGossipSync(packet: BitchatPacket): Boolean
|
||||
}
|
||||
|
||||
@@ -44,10 +44,14 @@ class SecurityManager(private val encryptionService: EncryptionService, private
|
||||
* Validate packet security (timestamp, replay attacks, duplicates, signatures)
|
||||
*/
|
||||
fun validatePacket(packet: BitchatPacket, peerID: String): Boolean {
|
||||
// Skip validation for our own packets
|
||||
// Skip our own packets, except allow sync copies of our own packets
|
||||
if (peerID == myPeerID) {
|
||||
Log.d(TAG, "Skipping validation for our own packet")
|
||||
return false
|
||||
val isSyncTtl = packet.ttl == com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS
|
||||
val seenInGossip = try { delegate?.let { (it as? SecurityManagerGossipLookup)?.isInGossipSync(packet) } } catch (_: Exception) { null }
|
||||
if (!(isSyncTtl && (seenInGossip == false))) {
|
||||
Log.d(TAG, "Skipping validation for our own packet (not sync-eligible)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Replay attack protection (same 5-minute window as iOS)
|
||||
@@ -384,3 +388,8 @@ interface SecurityManagerDelegate {
|
||||
fun sendHandshakeResponse(peerID: String, response: ByteArray)
|
||||
fun getPeerInfo(peerID: String): PeerInfo? // NEW: For signature verification
|
||||
}
|
||||
|
||||
// Optional extension for gossip sync visibility (implemented by host service)
|
||||
interface SecurityManagerGossipLookup {
|
||||
fun isInGossipSync(packet: BitchatPacket): Boolean
|
||||
}
|
||||
|
||||
@@ -133,6 +133,20 @@ class GossipSyncManager(
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we've already tracked this packet in our gossip sync state
|
||||
fun hasSeenPacket(packet: BitchatPacket): Boolean {
|
||||
return try {
|
||||
val idBytes = PacketIdUtil.computeIdBytes(packet)
|
||||
val id = idBytes.joinToString("") { b -> "%02x".format(b) }
|
||||
// Check broadcast messages cache
|
||||
synchronized(messages) {
|
||||
if (messages.containsKey(id)) return true
|
||||
}
|
||||
// Check announcements cache (latest by peer keeps id hex)
|
||||
latestAnnouncementByPeer.values.any { pair -> pair.first.equals(id, ignoreCase = true) }
|
||||
} catch (_: Exception) { false }
|
||||
}
|
||||
|
||||
private fun sendRequestSync() {
|
||||
val payload = buildGcsPayload()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user