mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 15:45:20 +00:00
Courier envelopes were sealed with one-way Noise X to the recipient's long-lived static key, so a later compromise of that key exposed every envelope captured in transit. This adds one-time prekey bundles: - PrekeyBundle (MessageType 0x24): 8 one-time Curve25519 public prekeys bound to the owner's Noise static key by an Ed25519 signature over "bitchat-prekey-bundle-v1" canonical bytes; gossiped mesh-wide on its own 60s sync round (SyncTypeFlags bit 9, 200-peer cap, 24h freshness) and verified against the announce-bound signing key before caching. - Sealed envelope v2: Noise X where the responder static is the one-time prekey, prologue "bitchat-prekey-v1" || prekeyID. Sender identity rides encrypted inside and is authenticated exactly like v1 (blocked-sender check included). CourierEnvelope gains an optional prekeyID TLV that v1 decoders skip as unknown. - Local prekeys live in the Keychain; consumed privates survive a 48h grace window for spray-and-wait redeliveries, then are deleted (the forward-secrecy clock starts at deletion). The batch tops back up and re-gossips when unconsumed count drops below 3, and everything is wiped in panic mode. - Routing: courier sealing picks a cached verified bundle when one exists (one prekey per message, reused across deposit retries), with the advertised .prekeys capability as a veto for on-mesh peers, and falls back to static sealing otherwise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
44 lines
1.4 KiB
Swift
44 lines
1.4 KiB
Swift
import BitFoundation
|
|
import Foundation
|
|
|
|
enum BLEOutboundPacketPolicy {
|
|
private static let fragmentFrameOverhead = 13 + 8 + 8 + 13
|
|
|
|
static func messageID(for packet: BitchatPacket) -> String {
|
|
BLEIngressLinkRegistry.messageID(for: packet)
|
|
}
|
|
|
|
static func padsBLEFrame(for packetType: UInt8) -> Bool {
|
|
switch MessageType(rawValue: packetType) {
|
|
case .noiseEncrypted, .noiseHandshake:
|
|
return true
|
|
case .none, .announce, .message, .leave, .requestSync, .fragment, .fileTransfer, .courierEnvelope, .prekeyBundle:
|
|
return false
|
|
}
|
|
}
|
|
|
|
static func priority(for packet: BitchatPacket, data: Data) -> BLEOutboundWritePriority {
|
|
guard let messageType = MessageType(rawValue: packet.type) else { return .low }
|
|
switch messageType {
|
|
case .fragment:
|
|
return .fragment(totalFragments: fragmentTotalCount(from: packet.payload))
|
|
case .fileTransfer:
|
|
return .fileTransfer
|
|
default:
|
|
return .high
|
|
}
|
|
}
|
|
|
|
static func fragmentChunkSize(forLinkLimit limit: Int) -> Int {
|
|
max(64, limit - fragmentFrameOverhead)
|
|
}
|
|
|
|
private static func fragmentTotalCount(from payload: Data) -> Int {
|
|
guard payload.count >= 12 else { return Int(UInt16.max) }
|
|
let totalHigh = Int(payload[10])
|
|
let totalLow = Int(payload[11])
|
|
let total = (totalHigh << 8) | totalLow
|
|
return max(total, 1)
|
|
}
|
|
}
|