mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-09-19 04:59:59 +00:00
Merge 38bc03fb2dd1c7c298cc08b07fddcc145716782d into c127eb83ab94c069c32d37530d2faecd381cd2a8
This commit is contained in:
commit
08ce879a28
@ -3,7 +3,6 @@ package com.bitchat.android.mesh
|
||||
import android.util.Log
|
||||
import com.bitchat.android.protocol.BitchatPacket
|
||||
import com.bitchat.android.protocol.MessageType
|
||||
import com.bitchat.android.protocol.MessagePadding
|
||||
import com.bitchat.android.model.FragmentPayload
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
@ -66,20 +65,19 @@ class FragmentManager {
|
||||
Log.w(TAG, "Rejecting invalid outbound fragment limit: $maxFragments")
|
||||
return emptyList()
|
||||
}
|
||||
val encoded = packet.toBinaryData()
|
||||
if (encoded == null) {
|
||||
// Fragment the unpadded frame; each fragment will be encoded (and padded) independently - iOS fix.
|
||||
// Ask the encoder to skip padding rather than un-padding afterwards:
|
||||
// pad() declines a request needing more than 255 bytes and
|
||||
// optimalBlockSize() returns the input size above 2048, so frames of
|
||||
// 513-768, 1009-1792 and >=2033 bytes are never padded. Those end
|
||||
// with the last byte of the signature, and unpad() reads a value of
|
||||
// 0x01 as a one-byte pad and strips a real signature byte.
|
||||
val fullData = packet.toBinaryData(padding = false)
|
||||
if (fullData == null) {
|
||||
Log.e(TAG, "Failed to encode packet to binary data")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// Fragment the unpadded frame; each fragment will be encoded (and padded) independently - iOS fix
|
||||
val fullData = try {
|
||||
MessagePadding.unpad(encoded)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to unpad data: ${e.message}", e)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// iOS logic: if data.count > 512 && packet.type != MessageType.fragment.rawValue
|
||||
if (fullData.size <= FRAGMENT_SIZE_THRESHOLD) {
|
||||
return listOf(packet) // No fragmentation needed
|
||||
|
||||
@ -3,6 +3,7 @@ package com.bitchat.android.mesh
|
||||
import com.bitchat.android.protocol.BitchatPacket
|
||||
import com.bitchat.android.protocol.MessageType
|
||||
import com.bitchat.android.model.FragmentPayload
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
@ -244,6 +245,68 @@ class FragmentManagerTest {
|
||||
assertEquals(257, plan(low).size)
|
||||
}
|
||||
|
||||
/**
|
||||
* pad() declines any request needing more than 255 bytes and
|
||||
* optimalBlockSize() returns the input size unchanged above 2048, so frames
|
||||
* of 513-768, 1009-1792 and >=2033 bytes are emitted unpadded. The frame
|
||||
* then ends with the last byte of the signature, which is uniformly random,
|
||||
* and a value of 0x01 reads as a one-byte PKCS#7 pad.
|
||||
*/
|
||||
@Test
|
||||
fun `an unpadded frame keeps every byte through fragmentation`() {
|
||||
for (payloadSize in listOf(600, 1500, 2400)) {
|
||||
val packet = BitchatPacket(
|
||||
version = 1u,
|
||||
type = MessageType.MESSAGE.value,
|
||||
senderID = hexStringToByteArray(senderID),
|
||||
recipientID = hexStringToByteArray(recipientID),
|
||||
timestamp = 1u,
|
||||
payload = ByteArray(payloadSize).also { Random(payloadSize.toLong()).nextBytes(it) },
|
||||
signature = ByteArray(64) { index -> if (index == 63) 0x01 else 0x7F },
|
||||
ttl = 7u
|
||||
)
|
||||
|
||||
val frame = packet.toBinaryData(padding = false)!!
|
||||
val padded = packet.toBinaryData()!!
|
||||
assertEquals("frame of $payloadSize should not be padded", frame.size, padded.size)
|
||||
|
||||
val fragments = fragmentManager.createFragments(packet)
|
||||
assertTrue("frame of $payloadSize should fragment", fragments.size > 1)
|
||||
|
||||
val reassembled = fragments
|
||||
.map { FragmentPayload.decode(it.payload)!!.data }
|
||||
.reduce { acc, next -> acc + next }
|
||||
assertArrayEquals(
|
||||
"fragments of a $payloadSize-byte payload must carry the whole frame",
|
||||
frame,
|
||||
reassembled
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A frame that really was padded must still fragment unpadded. */
|
||||
@Test
|
||||
fun `a padded frame is fragmented without its padding`() {
|
||||
val packet = BitchatPacket(
|
||||
version = 1u,
|
||||
type = MessageType.MESSAGE.value,
|
||||
senderID = hexStringToByteArray(senderID),
|
||||
recipientID = hexStringToByteArray(recipientID),
|
||||
timestamp = 1u,
|
||||
payload = ByteArray(900).also { Random(900).nextBytes(it) },
|
||||
signature = ByteArray(64) { 0x7F },
|
||||
ttl = 7u
|
||||
)
|
||||
|
||||
val frame = packet.toBinaryData(padding = false)!!
|
||||
assertTrue("frame should be padded", packet.toBinaryData()!!.size > frame.size)
|
||||
|
||||
val reassembled = fragmentManager.createFragments(packet)
|
||||
.map { FragmentPayload.decode(it.payload)!!.data }
|
||||
.reduce { acc, next -> acc + next }
|
||||
assertArrayEquals(frame, reassembled)
|
||||
}
|
||||
|
||||
private fun hexStringToByteArray(hexString: String): ByteArray {
|
||||
val result = ByteArray(8)
|
||||
for (i in 0 until 8) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user