mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-26 23:25:20 +00:00
Closes the last cleartext private-content path over BLE: private DM images/voice were sent as plaintext signed fileTransfer packets, TTL-relayed across the mesh, so every relay saw the full bytes. Now the complete BitchatFilePacket is encrypted as a single Noise AEAD message (inner type 0x20, matching Android) and the opaque ciphertext is fragmented. Adds an authenticated in-session capability proof (0x21 TLV: capabilities + Ed25519 key), TOFU-style downgrade pinning, a per-send consent dialog for the signed-cleartext fallback to legacy peers, and a cancellation/admission registry so cancel/delete cannot race a deferred cleartext send. Android wire constants (0x20 / 0x21 / capability bit 8) confirmed shipping. The 256-fragment preflight cap applies only to the directed fileTransfer migration fallback; encrypted media to capable peers uses the full receiver ceiling. Rebased over #1428/#1349: identity reads go through BLELocalIdentityStateStore; the session-bound authenticated signing-key check and the announce-path TOFU pin are kept as complementary checks. Full local suite green (1744+197 tests).
88 lines
3.3 KiB
Swift
88 lines
3.3 KiB
Swift
import BitFoundation
|
|
import Foundation
|
|
import Testing
|
|
@testable import bitchat
|
|
|
|
struct BLENoiseSessionQueuesTests {
|
|
@Test
|
|
func privateMessagesDrainInPeerOrderAndClearOnlyThatPeer() {
|
|
let firstPeer = PeerID(str: "aaaaaaaaaaaaaaaa")
|
|
let secondPeer = PeerID(str: "bbbbbbbbbbbbbbbb")
|
|
var queues = BLENoiseSessionQueues()
|
|
|
|
queues.appendPrivateMessage(content: "first", messageID: "m1", for: firstPeer)
|
|
queues.appendPrivateMessage(content: "second", messageID: "m2", for: firstPeer)
|
|
queues.appendPrivateMessage(content: "other", messageID: "m3", for: secondPeer)
|
|
|
|
let drained = queues.takePrivateMessages(for: firstPeer)
|
|
|
|
#expect(drained == [
|
|
BLEPendingPrivateMessage(content: "first", messageID: "m1"),
|
|
BLEPendingPrivateMessage(content: "second", messageID: "m2")
|
|
])
|
|
#expect(queues.takePrivateMessages(for: firstPeer).isEmpty)
|
|
#expect(queues.takePrivateMessages(for: secondPeer) == [
|
|
BLEPendingPrivateMessage(content: "other", messageID: "m3")
|
|
])
|
|
}
|
|
|
|
@Test
|
|
func prependPrivateMessagesRestoresFailedMessagesAheadOfNewerOnes() {
|
|
let peerID = PeerID(str: "aaaaaaaaaaaaaaaa")
|
|
var queues = BLENoiseSessionQueues()
|
|
|
|
queues.appendPrivateMessage(content: "new", messageID: "m2", for: peerID)
|
|
queues.prependPrivateMessages([
|
|
BLEPendingPrivateMessage(content: "retry", messageID: "m1")
|
|
], for: peerID)
|
|
|
|
#expect(queues.takePrivateMessages(for: peerID).map(\.messageID) == ["m1", "m2"])
|
|
}
|
|
|
|
@Test
|
|
func typedPayloadsDrainIndependentlyFromPrivateMessages() {
|
|
let peerID = PeerID(str: "aaaaaaaaaaaaaaaa")
|
|
var queues = BLENoiseSessionQueues()
|
|
|
|
queues.appendPrivateMessage(content: "queued", messageID: "m1", for: peerID)
|
|
queues.appendTypedPayload(Data([0x01]), for: peerID)
|
|
queues.appendTypedPayload(Data([0x02]), for: peerID)
|
|
|
|
#expect(queues.takeTypedPayloads(for: peerID) == [
|
|
BLEPendingTypedPayload(payload: Data([0x01]), transferId: nil),
|
|
BLEPendingTypedPayload(payload: Data([0x02]), transferId: nil)
|
|
])
|
|
#expect(queues.takeTypedPayloads(for: peerID).isEmpty)
|
|
#expect(queues.takePrivateMessages(for: peerID).map(\.messageID) == ["m1"])
|
|
}
|
|
|
|
@Test
|
|
func removeAllClearsBothQueueTypes() {
|
|
let peerID = PeerID(str: "aaaaaaaaaaaaaaaa")
|
|
var queues = BLENoiseSessionQueues()
|
|
|
|
queues.appendPrivateMessage(content: "queued", messageID: "m1", for: peerID)
|
|
queues.appendTypedPayload(Data([0x01]), for: peerID)
|
|
queues.removeAll()
|
|
|
|
#expect(queues.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
func transferIDSurvivesHandshakeQueueAndCanBeCancelledBeforeDrain() {
|
|
let peerID = PeerID(str: "aaaaaaaaaaaaaaaa")
|
|
var queues = BLENoiseSessionQueues()
|
|
|
|
queues.appendTypedPayload(Data([0x20, 0xAA]), transferId: "media-1", for: peerID)
|
|
queues.appendTypedPayload(Data([0x01, 0xBB]), for: peerID)
|
|
|
|
let removed = queues.removeTypedPayload(transferId: "media-1")
|
|
let removedAgain = queues.removeTypedPayload(transferId: "media-1")
|
|
#expect(removed)
|
|
#expect(!removedAgain)
|
|
#expect(queues.takeTypedPayloads(for: peerID) == [
|
|
BLEPendingTypedPayload(payload: Data([0x01, 0xBB]), transferId: nil)
|
|
])
|
|
}
|
|
}
|