mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 10:25:18 +00:00
196 lines
7.8 KiB
Swift
196 lines
7.8 KiB
Swift
import BitFoundation
|
|
import Foundation
|
|
import Testing
|
|
|
|
@testable import bitchat
|
|
|
|
struct PacketsTests {
|
|
@Test
|
|
func announcementPacketRoundTripsNeighborsAndSkipsUnknownTLVs() throws {
|
|
let neighbors = (0..<12).map { index in
|
|
Data(repeating: UInt8(index), count: 8)
|
|
}
|
|
let packet = AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: Data(repeating: 0x11, count: 32),
|
|
signingPublicKey: Data(repeating: 0x22, count: 32),
|
|
directNeighbors: neighbors
|
|
)
|
|
|
|
var encoded = try #require(packet.encode())
|
|
encoded.append(makeTLV(type: 0xFF, value: Data([0xAB])))
|
|
|
|
let decoded = try #require(AnnouncementPacket.decode(from: encoded))
|
|
#expect(decoded.nickname == "alice")
|
|
#expect(decoded.noisePublicKey == Data(repeating: 0x11, count: 32))
|
|
#expect(decoded.signingPublicKey == Data(repeating: 0x22, count: 32))
|
|
#expect(decoded.directNeighbors?.count == 10)
|
|
#expect(decoded.directNeighbors?.first == neighbors.first)
|
|
#expect(decoded.directNeighbors?.last == neighbors[9])
|
|
}
|
|
|
|
@Test
|
|
func announcementPacketEncodeRejectsOversizedFieldsAndInvalidNeighborGroups() {
|
|
let oversizedNickname = String(repeating: "a", count: 256)
|
|
let validKey = Data(repeating: 0x44, count: 32)
|
|
|
|
#expect(
|
|
AnnouncementPacket(
|
|
nickname: oversizedNickname,
|
|
noisePublicKey: validKey,
|
|
signingPublicKey: validKey,
|
|
directNeighbors: nil
|
|
).encode() == nil
|
|
)
|
|
|
|
#expect(
|
|
AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: Data(repeating: 0x55, count: 256),
|
|
signingPublicKey: validKey,
|
|
directNeighbors: nil
|
|
).encode() == nil
|
|
)
|
|
|
|
#expect(
|
|
AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: validKey,
|
|
signingPublicKey: Data(repeating: 0x66, count: 256),
|
|
directNeighbors: nil
|
|
).encode() == nil
|
|
)
|
|
|
|
let invalidNeighborPacket = AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: validKey,
|
|
signingPublicKey: validKey,
|
|
directNeighbors: [Data([0x01, 0x02, 0x03])]
|
|
)
|
|
let encodedWithoutNeighbors = AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: validKey,
|
|
signingPublicKey: validKey,
|
|
directNeighbors: nil
|
|
).encode()
|
|
#expect(invalidNeighborPacket.encode() == encodedWithoutNeighbors)
|
|
}
|
|
|
|
@Test
|
|
func announcementPacketDecodeRejectsMissingFieldsAndTruncation() throws {
|
|
let missingSigningKey = makeTLV(type: 0x01, value: Data("alice".utf8))
|
|
+ makeTLV(type: 0x02, value: Data(repeating: 0x11, count: 32))
|
|
#expect(AnnouncementPacket.decode(from: missingSigningKey) == nil)
|
|
|
|
let validPacket = try #require(
|
|
AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: Data(repeating: 0x11, count: 32),
|
|
signingPublicKey: Data(repeating: 0x22, count: 32),
|
|
directNeighbors: nil
|
|
).encode()
|
|
)
|
|
#expect(AnnouncementPacket.decode(from: validPacket.dropLast()) == nil)
|
|
}
|
|
|
|
@Test
|
|
func announcementPacketDecodeIgnoresInvalidNeighborLengths() throws {
|
|
var encoded = try #require(
|
|
AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: Data(repeating: 0x11, count: 32),
|
|
signingPublicKey: Data(repeating: 0x22, count: 32),
|
|
directNeighbors: nil
|
|
).encode()
|
|
)
|
|
encoded.append(makeTLV(type: 0x04, value: Data(repeating: 0x99, count: 7)))
|
|
|
|
let decoded = try #require(AnnouncementPacket.decode(from: encoded))
|
|
#expect(decoded.directNeighbors == nil)
|
|
}
|
|
|
|
@Test
|
|
func announcementPacketRoundTripsCapabilities() throws {
|
|
let capabilities: PeerCapabilities = [.prekeys, .board, .meshDiagnostics]
|
|
let packet = AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: Data(repeating: 0x11, count: 32),
|
|
signingPublicKey: Data(repeating: 0x22, count: 32),
|
|
directNeighbors: nil,
|
|
capabilities: capabilities
|
|
)
|
|
|
|
let encoded = try #require(packet.encode())
|
|
let decoded = try #require(AnnouncementPacket.decode(from: encoded))
|
|
#expect(decoded.capabilities == capabilities)
|
|
}
|
|
|
|
@Test
|
|
func announcementPacketWithoutCapabilitiesDecodesNilAndUnknownBitsSurvive() throws {
|
|
let legacy = try #require(
|
|
AnnouncementPacket(
|
|
nickname: "alice",
|
|
noisePublicKey: Data(repeating: 0x11, count: 32),
|
|
signingPublicKey: Data(repeating: 0x22, count: 32),
|
|
directNeighbors: nil
|
|
).encode()
|
|
)
|
|
// The TLV is emitted only when capabilities are set, so legacy peers
|
|
// (and this packet) decode as nil rather than empty.
|
|
#expect(try #require(AnnouncementPacket.decode(from: legacy)).capabilities == nil)
|
|
|
|
var withFutureBits = legacy
|
|
withFutureBits.append(makeTLV(type: 0x05, value: Data([0x80, 0x01])))
|
|
let decoded = try #require(AnnouncementPacket.decode(from: withFutureBits))
|
|
#expect(decoded.capabilities?.rawValue == 0x0180)
|
|
}
|
|
|
|
@Test
|
|
func authenticatedPeerStateUsesVersionedCanonicalTLVs() throws {
|
|
let signingKey = Data(repeating: 0xA5, count: 32)
|
|
let packet = AuthenticatedPeerStatePacket(
|
|
capabilities: [.privateMedia, .vouch],
|
|
signingPublicKey: signingKey
|
|
)
|
|
|
|
var encoded = try #require(packet.encode())
|
|
#expect(encoded.prefix(5) == Data([0x01, 0x01, 0x02, 0x20, 0x01]))
|
|
// Unknown TLVs are forward-compatible and do not alter v1 state.
|
|
encoded.append(makeTLV(type: 0x7F, value: Data([0xCA, 0xFE])))
|
|
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: encoded) == packet)
|
|
}
|
|
|
|
@Test
|
|
func authenticatedPeerStateRejectsMalformedAmbiguousOrUnknownVersion() {
|
|
let key = Data(repeating: 0x44, count: 32)
|
|
let capabilities = makeTLV(type: 0x01, value: Data([0x00, 0x01]))
|
|
let signing = makeTLV(type: 0x02, value: key)
|
|
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x02]) + capabilities + signing) == nil)
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + signing) == nil)
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + capabilities + capabilities + signing) == nil)
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01, 0x01, 0x00]) + signing) == nil)
|
|
// 0x0001 is non-minimal little endian; the canonical form is [0x01].
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + makeTLV(type: 0x01, value: Data([0x01, 0x00])) + signing) == nil)
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + capabilities + makeTLV(type: 0x02, value: Data(key.dropLast()))) == nil)
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + capabilities + Data(signing.dropLast())) == nil)
|
|
#expect(AuthenticatedPeerStatePacket.decode(from: Data([0x01]) + makeTLV(type: 0x01, value: Data(repeating: 0x01, count: 9)) + signing) == nil)
|
|
}
|
|
|
|
@Test
|
|
func privateMessagePacketRejectsUnknownTypeAndTruncation() {
|
|
let unknownTLV = Data([0x7F, 0x01, 0x41])
|
|
#expect(PrivateMessagePacket.decode(from: unknownTLV) == nil)
|
|
|
|
let truncated = Data([0x00, 0x05, 0x61])
|
|
#expect(PrivateMessagePacket.decode(from: truncated) == nil)
|
|
}
|
|
|
|
private func makeTLV(type: UInt8, value: Data) -> Data {
|
|
var data = Data([type, UInt8(value.count)])
|
|
data.append(value)
|
|
return data
|
|
}
|
|
}
|