mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 02:25:21 +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 for key exchange notifications + gossip lookup
|
||||||
securityManager.delegate = object : SecurityManagerDelegate {
|
securityManager.delegate = object : SecurityManagerDelegate, SecurityManagerGossipLookup {
|
||||||
override fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) {
|
override fun onKeyExchangeCompleted(peerID: String, peerPublicKeyData: ByteArray) {
|
||||||
// Send announcement and cached messages after key exchange
|
// Send announcement and cached messages after key exchange
|
||||||
serviceScope.launch {
|
serviceScope.launch {
|
||||||
@@ -193,6 +193,11 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
override fun getPeerInfo(peerID: String): PeerInfo? {
|
override fun getPeerInfo(peerID: String): PeerInfo? {
|
||||||
return peerManager.getPeerInfo(peerID)
|
return peerManager.getPeerInfo(peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecurityManagerGossipLookup
|
||||||
|
override fun isInGossipSync(packet: BitchatPacket): Boolean {
|
||||||
|
return try { gossipSyncManager.hasSeenPacket(packet) } catch (_: Exception) { false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreForwardManager delegates
|
// StoreForwardManager delegates
|
||||||
@@ -366,6 +371,10 @@ class BluetoothMeshService(private val context: Context) {
|
|||||||
override fun onReadReceiptReceived(messageID: String, peerID: String) {
|
override fun onReadReceiptReceived(messageID: String, peerID: String) {
|
||||||
delegate?.didReceiveReadReceipt(messageID, peerID)
|
delegate?.didReceiveReadReceipt(messageID, peerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isInGossipSync(packet: BitchatPacket): Boolean {
|
||||||
|
return try { gossipSyncManager.hasSeenPacket(packet) } catch (_: Exception) { false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PacketProcessor delegates
|
// 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)")
|
Log.d(TAG, "Processing Noise encrypted message from $peerID (${packet.payload.size} bytes)")
|
||||||
|
|
||||||
// Skip our own messages
|
// Skip our own messages, except when coming from sync (TTL=SYNC_TTL_HOPS) and not tracked in gossip
|
||||||
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
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this message is for us
|
// Check if this message is for us
|
||||||
val recipientID = packet.recipientID?.toHexString()
|
val recipientID = packet.recipientID?.toHexString()
|
||||||
@@ -209,7 +213,12 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
|||||||
val packet = routed.packet
|
val packet = routed.packet
|
||||||
val peerID = routed.peerID ?: "unknown"
|
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
|
// Ignore stale announcements older than STALE_PEER_TIMEOUT
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
@@ -342,7 +351,11 @@ class MessageHandler(private val myPeerID: String, private val appContext: andro
|
|||||||
suspend fun handleMessage(routed: RoutedPacket) {
|
suspend fun handleMessage(routed: RoutedPacket) {
|
||||||
val packet = routed.packet
|
val packet = routed.packet
|
||||||
val peerID = routed.peerID ?: "unknown"
|
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)
|
val senderNickname = delegate?.getPeerNickname(peerID)
|
||||||
if (senderNickname != null) {
|
if (senderNickname != null) {
|
||||||
Log.d(TAG, "Received message from $senderNickname")
|
Log.d(TAG, "Received message from $senderNickname")
|
||||||
@@ -611,4 +624,7 @@ interface MessageHandlerDelegate {
|
|||||||
fun onChannelLeave(channel: String, fromPeer: String)
|
fun onChannelLeave(channel: String, fromPeer: String)
|
||||||
fun onDeliveryAckReceived(messageID: String, peerID: String)
|
fun onDeliveryAckReceived(messageID: String, peerID: String)
|
||||||
fun onReadReceiptReceived(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)
|
* Validate packet security (timestamp, replay attacks, duplicates, signatures)
|
||||||
*/
|
*/
|
||||||
fun validatePacket(packet: BitchatPacket, peerID: String): Boolean {
|
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) {
|
if (peerID == myPeerID) {
|
||||||
Log.d(TAG, "Skipping validation for our own packet")
|
val isSyncTtl = packet.ttl == com.bitchat.android.util.AppConstants.SYNC_TTL_HOPS
|
||||||
return false
|
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)
|
// Replay attack protection (same 5-minute window as iOS)
|
||||||
@@ -384,3 +388,8 @@ interface SecurityManagerDelegate {
|
|||||||
fun sendHandshakeResponse(peerID: String, response: ByteArray)
|
fun sendHandshakeResponse(peerID: String, response: ByteArray)
|
||||||
fun getPeerInfo(peerID: String): PeerInfo? // NEW: For signature verification
|
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() {
|
private fun sendRequestSync() {
|
||||||
val payload = buildGcsPayload()
|
val payload = buildGcsPayload()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user