mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 01:05:20 +00:00
drop duplicate announces based on TTL (#558)
This commit is contained in:
@@ -56,18 +56,24 @@ class SecurityManager(private val encryptionService: EncryptionService, private
|
|||||||
|
|
||||||
// Duplicate detection
|
// Duplicate detection
|
||||||
val messageID = generateMessageID(packet, peerID)
|
val messageID = generateMessageID(packet, peerID)
|
||||||
if (messageType != MessageType.ANNOUNCE) {
|
|
||||||
if (processedMessages.contains(messageID)) {
|
if (processedMessages.contains(messageID)) {
|
||||||
|
// Check for ANNOUNCE exception: allow if it looks like a direct neighbor (max TTL)
|
||||||
|
// This ensures we catch the "first announce" on a new connection for binding,
|
||||||
|
// while still dropping looped/relayed duplicates.
|
||||||
|
val isFreshAnnounce = messageType == MessageType.ANNOUNCE &&
|
||||||
|
packet.ttl >= com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS
|
||||||
|
|
||||||
|
if (!isFreshAnnounce) {
|
||||||
Log.d(TAG, "Dropping duplicate packet: $messageID")
|
Log.d(TAG, "Dropping duplicate packet: $messageID")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Add to processed messages
|
Log.d(TAG, "Allowing duplicate ANNOUNCE from direct neighbor: $messageID")
|
||||||
processedMessages.add(messageID)
|
|
||||||
messageTimestamps[messageID] = currentTime
|
|
||||||
} else {
|
|
||||||
// Do not deduplicate ANNOUNCE at the security layer.
|
|
||||||
// They are signed/idempotent and we need to ensure first-announce per-connection can bind.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add to processed messages
|
||||||
|
processedMessages.add(messageID)
|
||||||
|
messageTimestamps[messageID] = currentTime
|
||||||
|
|
||||||
// Enforce mandatory signature verification
|
// Enforce mandatory signature verification
|
||||||
if (!verifyPacketSignature(packet, peerID)) {
|
if (!verifyPacketSignature(packet, peerID)) {
|
||||||
|
|||||||
@@ -239,6 +239,37 @@ class SecurityManagerTest {
|
|||||||
assertFalse("Duplicate packet should be rejected", result2)
|
assertFalse("Duplicate packet should be rejected", result2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `validatePacket - handles ANNOUNCE duplicates correctly`() {
|
||||||
|
val announcement = IdentityAnnouncement(
|
||||||
|
nickname = "New User",
|
||||||
|
noisePublicKey = otherNoiseKey,
|
||||||
|
signingPublicKey = otherSigningKey
|
||||||
|
)
|
||||||
|
val payload = announcement.encode()!!
|
||||||
|
|
||||||
|
// 1. Initial Announce (Fresh)
|
||||||
|
val packet1 = BitchatPacket(
|
||||||
|
type = MessageType.ANNOUNCE.value,
|
||||||
|
ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS, // 7u
|
||||||
|
senderID = unknownPeerID,
|
||||||
|
payload = payload
|
||||||
|
)
|
||||||
|
packet1.signature = validSignature
|
||||||
|
|
||||||
|
whenever(mockDelegate.getPeerInfo(unknownPeerID)).thenReturn(null)
|
||||||
|
|
||||||
|
assertTrue("First ANNOUNCE should be accepted", securityManager.validatePacket(packet1, unknownPeerID))
|
||||||
|
|
||||||
|
// 2. Relayed Duplicate (Lower TTL)
|
||||||
|
val packet2 = packet1.copy(ttl = (com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS - 1u).toUByte())
|
||||||
|
assertFalse("Relayed duplicate ANNOUNCE should be rejected", securityManager.validatePacket(packet2, unknownPeerID))
|
||||||
|
|
||||||
|
// 3. Direct Duplicate (Max TTL)
|
||||||
|
val packet3 = packet1.copy(ttl = com.bitchat.android.util.AppConstants.MESSAGE_TTL_HOPS)
|
||||||
|
assertTrue("Fresh duplicate ANNOUNCE should be accepted", securityManager.validatePacket(packet3, unknownPeerID))
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupKnownPeer(peerID: String, signingKey: ByteArray) {
|
private fun setupKnownPeer(peerID: String, signingKey: ByteArray) {
|
||||||
val info = PeerInfo(
|
val info = PeerInfo(
|
||||||
id = peerID,
|
id = peerID,
|
||||||
|
|||||||
Reference in New Issue
Block a user