mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 23:45:18 +00:00
Mechanical style fixes across the enabled rule set, mostly via swiftlint --fix (trailing_comma, comma, colon, trailing_newline, comment_spacing, unused_closure_parameter, unneeded_break_in_switch, opening_brace) plus hand fixes: - non_optional_string_data_conversion (45): .data(using: .utf8)! and ?? Data() fallbacks replaced with the non-optional Data(_.utf8), including two production sites (NIP-44 HKDF info constant and the announce canonicalization context/nickname bytes — byte-identical output, only the impossible-nil handling is gone). - switch_case_alignment: LocationChannel had a misindented closing brace; also repaired an --fix artifact in BLEService's .none case. - redundant_string_enum_value: TrustLevel raw values equal to the case names (encoded form unchanged). - unused_optional_binding: let _ = binds replaced with != nil / is Bool. - static_over_final_class: PreviewView.layerClass. - Resolved the BinaryProtocolTests TODO by documenting that 8-byte recipient ID truncation is the fixed wire-field size, not a bug. The 4 remaining violations are all todo markers for a shared test-helpers module (tracked in #1088) and one Reuse note. Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
223 lines
7.0 KiB
Swift
223 lines
7.0 KiB
Swift
//
|
|
// XChaCha20Poly1305CompatTests.swift
|
|
// bitchatTests
|
|
//
|
|
// Tests for XChaCha20-Poly1305 encryption with proper error handling.
|
|
// This is free and unencumbered software released into the public domain.
|
|
//
|
|
|
|
import Testing
|
|
import struct Foundation.Data
|
|
@testable import bitchat
|
|
|
|
struct XChaCha20Poly1305CompatTests {
|
|
|
|
@Test func sealAndOpenRoundtrip() throws {
|
|
let plaintext = Data("Hello, XChaCha20-Poly1305!".utf8)
|
|
let key = Data(repeating: 0x42, count: 32)
|
|
let nonce = Data(repeating: 0x24, count: 24)
|
|
|
|
let sealed = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: nonce)
|
|
let decrypted = try XChaCha20Poly1305Compat.open(
|
|
ciphertext: sealed.ciphertext,
|
|
tag: sealed.tag,
|
|
key: key,
|
|
nonce24: nonce
|
|
)
|
|
|
|
#expect(decrypted == plaintext)
|
|
}
|
|
|
|
@Test func sealAndOpenWithAAD() throws {
|
|
let plaintext = Data("Secret message".utf8)
|
|
let key = Data(repeating: 0xAB, count: 32)
|
|
let nonce = Data(repeating: 0xCD, count: 24)
|
|
let aad = Data("additional authenticated data".utf8)
|
|
|
|
let sealed = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: nonce, aad: aad)
|
|
let decrypted = try XChaCha20Poly1305Compat.open(
|
|
ciphertext: sealed.ciphertext,
|
|
tag: sealed.tag,
|
|
key: key,
|
|
nonce24: nonce,
|
|
aad: aad
|
|
)
|
|
|
|
#expect(decrypted == plaintext)
|
|
}
|
|
|
|
@Test func sealProducesDifferentCiphertextWithDifferentNonces() throws {
|
|
let plaintext = Data("Same plaintext".utf8)
|
|
let key = Data(repeating: 0x42, count: 32)
|
|
let nonce1 = Data(repeating: 0x01, count: 24)
|
|
let nonce2 = Data(repeating: 0x02, count: 24)
|
|
|
|
let sealed1 = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: nonce1)
|
|
let sealed2 = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: nonce2)
|
|
|
|
#expect(sealed1.ciphertext != sealed2.ciphertext)
|
|
}
|
|
|
|
@Test func sealThrowsOnShortKey() {
|
|
let plaintext = Data("Test".utf8)
|
|
let shortKey = Data(repeating: 0x42, count: 16)
|
|
let nonce = Data(repeating: 0x24, count: 24)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: shortKey, nonce24: nonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func sealThrowsOnLongKey() {
|
|
let plaintext = Data("Test".utf8)
|
|
let longKey = Data(repeating: 0x42, count: 64)
|
|
let nonce = Data(repeating: 0x24, count: 24)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: longKey, nonce24: nonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func sealThrowsOnEmptyKey() {
|
|
let plaintext = Data("Test".utf8)
|
|
let emptyKey = Data()
|
|
let nonce = Data(repeating: 0x24, count: 24)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: emptyKey, nonce24: nonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func openThrowsOnInvalidKeyLength() {
|
|
let ciphertext = Data(repeating: 0x00, count: 16)
|
|
let tag = Data(repeating: 0x00, count: 16)
|
|
let shortKey = Data(repeating: 0x42, count: 31)
|
|
let nonce = Data(repeating: 0x24, count: 24)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.open(ciphertext: ciphertext, tag: tag, key: shortKey, nonce24: nonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func sealThrowsOnShortNonce() {
|
|
let plaintext = Data("Test".utf8)
|
|
let key = Data(repeating: 0x42, count: 32)
|
|
let shortNonce = Data(repeating: 0x24, count: 12)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: shortNonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func sealThrowsOnLongNonce() {
|
|
let plaintext = Data("Test".utf8)
|
|
let key = Data(repeating: 0x42, count: 32)
|
|
let longNonce = Data(repeating: 0x24, count: 32)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: longNonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func sealThrowsOnEmptyNonce() {
|
|
let plaintext = Data("Test".utf8)
|
|
let key = Data(repeating: 0x42, count: 32)
|
|
let emptyNonce = Data()
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: emptyNonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func openThrowsOnInvalidNonceLength() {
|
|
let ciphertext = Data(repeating: 0x00, count: 16)
|
|
let tag = Data(repeating: 0x00, count: 16)
|
|
let key = Data(repeating: 0x42, count: 32)
|
|
let shortNonce = Data(repeating: 0x24, count: 23)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.open(ciphertext: ciphertext, tag: tag, key: key, nonce24: shortNonce)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func openFailsWithWrongKey() throws {
|
|
let plaintext = Data("Secret".utf8)
|
|
let correctKey = Data(repeating: 0x42, count: 32)
|
|
let wrongKey = Data(repeating: 0x43, count: 32)
|
|
let nonce = Data(repeating: 0x24, count: 24)
|
|
|
|
let sealed = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: correctKey, nonce24: nonce)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.open(
|
|
ciphertext: sealed.ciphertext,
|
|
tag: sealed.tag,
|
|
key: wrongKey,
|
|
nonce24: nonce
|
|
)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
|
|
@Test func openFailsWithTamperedCiphertext() throws {
|
|
let plaintext = Data("Secret".utf8)
|
|
let key = Data(repeating: 0x42, count: 32)
|
|
let nonce = Data(repeating: 0x24, count: 24)
|
|
|
|
let sealed = try XChaCha20Poly1305Compat.seal(plaintext: plaintext, key: key, nonce24: nonce)
|
|
|
|
// Create tampered ciphertext by changing first byte
|
|
var tamperedBytes = [UInt8](sealed.ciphertext)
|
|
tamperedBytes[0] = tamperedBytes[0] ^ 0xFF
|
|
let tampered = Data(tamperedBytes)
|
|
|
|
var didThrow = false
|
|
do {
|
|
_ = try XChaCha20Poly1305Compat.open(
|
|
ciphertext: tampered,
|
|
tag: sealed.tag,
|
|
key: key,
|
|
nonce24: nonce
|
|
)
|
|
} catch {
|
|
didThrow = true
|
|
}
|
|
#expect(didThrow)
|
|
}
|
|
}
|