mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 19:25:23 +00:00
Cap only migration-fallback media at Android's 256 fragments
The private-media preflight applied Android's 256-fragment reassembly cap to every directed media send, including the encrypted 0x20 path. That regressed iOS→iOS private photos in the ~120-512 KiB range, which work today: iOS reassembles up to 10,000 fragments and main compresses images to <=512 KiB. Encrypted private media is only ever sent to peers that advertised the .privateMedia capability (modern clients with the full receiver ceiling); current Android receives private media exclusively over the directed raw-file migration fallback. Restrict the 256-fragment cap to that fileTransfer fallback path so capable peers use the normal ceiling. Leaves a TODO(#1434) to negotiate an explicit per-peer fragment limit for a future Android client that adopts 0x20 but keeps a small reassembler. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -741,7 +741,7 @@ struct PrivateMediaEndToEndTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func encryptedAndConsentedLegacySendsRejectAboveAndroidFragmentCap() async throws {
|
||||
func consentedLegacySendRejectsAboveAndroidFragmentCapButEncryptedDoesNot() async throws {
|
||||
let root = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent("private-media-fragment-cap-\(UUID().uuidString)", isDirectory: true)
|
||||
defer { try? FileManager.default.removeItem(at: root) }
|
||||
@@ -790,14 +790,24 @@ struct PrivateMediaEndToEndTests {
|
||||
allowLegacyFallback: true
|
||||
)
|
||||
|
||||
let bothRejected = await TestHelpers.waitUntil(
|
||||
{ rejections.contains(encryptedID) && rejections.contains(legacyID) },
|
||||
// The directed raw-file migration fallback (Android-style peer without
|
||||
// the .privateMedia capability) still honors the 256-fragment ceiling.
|
||||
let legacyRejected = await TestHelpers.waitUntil(
|
||||
{ rejections.contains(legacyID) },
|
||||
timeout: TestConstants.longTimeout
|
||||
)
|
||||
#expect(bothRejected)
|
||||
#expect(rejections.reason(for: encryptedID)?.contains("256") == true)
|
||||
#expect(legacyRejected)
|
||||
#expect(rejections.reason(for: legacyID)?.contains("256") == true)
|
||||
#expect(tap.snapshot().isEmpty, "No outer packet or fragment may be exposed before size rejection")
|
||||
|
||||
// Encrypted private media to a .privateMedia-capable peer is NOT forced
|
||||
// down to Android's 256 cap: it uses the full receiver ceiling and
|
||||
// proceeds to fragment/emit (a 130 KiB file exceeds 256 fragments).
|
||||
let encryptedEmitted = await TestHelpers.waitUntil(
|
||||
{ !tap.snapshot().isEmpty },
|
||||
timeout: TestConstants.longTimeout
|
||||
)
|
||||
#expect(encryptedEmitted, "Encrypted send to a capable peer must not be blocked by the Android cap")
|
||||
#expect(!rejections.contains(encryptedID))
|
||||
_ = cancellable
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user