tls sasl
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s

This commit is contained in:
wt committed 2026-09-02 11:46:11 +07:00
1 parent e3fcee3351
commit d4b4c92759
29 files changed
+2177 -61

No files matched your search

+60 -1
View File
@@ -72,7 +72,7 @@ final class LumaOMEMO2Tests: XCTestCase {
)
let envelope = try XCTUnwrap(Element.from(string: xml))
XCTAssertEqual(envelope.name, "envelope")
XCTAssertEqual(envelope.xmlns, "urn:xmpp:sce:1")
XCTAssertEqual(envelope.xmlns, "urn:xmpp:sce:0")
let content = try XCTUnwrap(envelope.findChild(name: "content"))
let body = try XCTUnwrap(content.findChild(name: "body"))
@@ -102,6 +102,16 @@ final class LumaOMEMO2Tests: XCTestCase {
)
}
func testSCEEnvelopeUsesSCE0Namespace() throws {
// XEP-0420 Stanza Content Encryption uses urn:xmpp:sce:0; OMEMO 2
// relies on that exact namespace for the encrypted payload envelope.
XCTAssertEqual(LumaOMEMO2Module.SCE_XMLNS, "urn:xmpp:sce:0")
let xml = LumaOMEMO2Module.envelopeXML(body: "hi", from: "a@b.c", to: nil)
let envelope = try XCTUnwrap(Element.from(string: xml))
XCTAssertEqual(envelope.name, "envelope")
XCTAssertEqual(envelope.xmlns, "urn:xmpp:sce:0")
}
// MARK: - Bundle parsing
func testBundleParsing() throws {
@@ -126,6 +136,55 @@ final class LumaOMEMO2Tests: XCTestCase {
XCTAssertNil(OMEMO2Bundle(from: bundle))
}
// MARK: - OMEMODevice protocol label & store wiring
func testOMEMODeviceProtocolLabel() {
let legacy = OMEMODevice(
jid: "bob@example.org",
deviceID: 5,
fingerprint: "aa",
trust: .trusted,
isActive: true,
isOwn: false,
isOMEMO2: false
)
let omemo2 = OMEMODevice(
jid: "bob@example.org",
deviceID: 5,
fingerprint: "bb",
trust: .trusted,
isActive: true,
isOwn: false,
isOMEMO2: true
)
XCTAssertEqual(legacy.protocolName, "OMEMO")
XCTAssertEqual(omemo2.protocolName, "OMEMO 2")
// Same JID/deviceID across protocols must not collide as list rows.
XCTAssertEqual(legacy.id, "bob@example.org|5|legacy")
XCTAssertEqual(omemo2.id, "bob@example.org|5|omemo2")
XCTAssertNotEqual(legacy.id, omemo2.id)
}
func testStoreMarksOMEMO2DeviceIDs() throws {
let store = LumaOMEMOStore(accountJID: "alice@example.org")
let context = try XCTUnwrap(SignalContext(withStorage: store))
_ = context
let ownID = store.localRegistrationID
XCTAssertNotEqual(ownID, 0)
store.setOMEMO2DeviceIDs([Int32(bitPattern: ownID)], for: "alice@example.org")
let devices = store.devices(for: "alice@example.org")
let own = try XCTUnwrap(devices.first { $0.isOwn })
XCTAssertTrue(own.isOMEMO2)
// Clearing the published set drops the badge.
store.setOMEMO2DeviceIDs([], for: "alice@example.org")
let cleared = store.devices(for: "alice@example.org")
let clearedOwn = try XCTUnwrap(cleared.first { $0.isOwn })
XCTAssertFalse(clearedOwn.isOMEMO2)
}
// MARK: - Double Ratchet round trip (shared with the legacy module)
func testRatchetSessionRoundTrip() throws {
+104
View File
@@ -0,0 +1,104 @@
import XCTest
@testable import Luma
final class SASLMechanismPreferenceTests: XCTestCase {
func testPrefersScramSha512WhenAdvertised() {
XCTAssertEqual(
SASLMechanismPreference.selected(among: [
"PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-512", "SCRAM-SHA-256",
]),
"SCRAM-SHA-512"
)
}
func testFallsBackToSha256WithoutSha512() {
XCTAssertEqual(
SASLMechanismPreference.selected(among: ["SCRAM-SHA-256", "PLAIN"]),
"SCRAM-SHA-256"
)
}
func testFallsBackToSha1AndPlain() {
XCTAssertEqual(
SASLMechanismPreference.selected(among: ["PLAIN", "SCRAM-SHA-1"]),
"SCRAM-SHA-1"
)
XCTAssertEqual(
SASLMechanismPreference.selected(among: ["PLAIN"]),
"PLAIN"
)
}
func testReturnsNilWhenNothingKnownIsOffered() {
XCTAssertNil(
SASLMechanismPreference.selected(among: ["X-OAUTH2", "ANONYMOUS"])
)
XCTAssertNil(SASLMechanismPreference.selected(among: []))
}
func testStrongestAvailableRanking() {
XCTAssertTrue(
SASLMechanismPreference.isStrongestAvailable(
"SCRAM-SHA-512",
among: ["SCRAM-SHA-512", "SCRAM-SHA-256", "PLAIN"]
)
)
XCTAssertFalse(
SASLMechanismPreference.isStrongestAvailable(
"SCRAM-SHA-1",
among: ["SCRAM-SHA-512", "SCRAM-SHA-1"]
)
)
XCTAssertFalse(
SASLMechanismPreference.isStrongestAvailable(
"X-OAUTH2",
among: ["X-OAUTH2"]
)
)
}
func testPLAINIsNeverChosenWhileSCRAMIsOffered() {
let offered = ["SCRAM-SHA-1", "PLAIN", "SCRAM-SHA-512"]
XCTAssertEqual(SASLMechanismPreference.selected(among: offered), "SCRAM-SHA-512")
XCTAssertNotEqual(SASLMechanismPreference.selected(among: offered), "PLAIN")
}
func testPlusVariantsArePreferredOverPlainSCRAM() {
let offered = ["PLAIN", "SCRAM-SHA-1", "SCRAM-SHA-256-PLUS", "SCRAM-SHA-512"]
XCTAssertEqual(
SASLMechanismPreference.selected(among: offered),
"SCRAM-SHA-256-PLUS"
)
XCTAssertEqual(
SASLMechanismPreference.selected(among: [
"SCRAM-SHA-512", "SCRAM-SHA-1-PLUS", "SCRAM-SHA-256-PLUS",
]),
"SCRAM-SHA-256-PLUS"
)
XCTAssertEqual(
SASLMechanismPreference.selected(among: [
"SCRAM-SHA-512-PLUS", "SCRAM-SHA-256-PLUS", "SCRAM-SHA-512",
]),
"SCRAM-SHA-512-PLUS"
)
}
func testPlusVariantsSkippedWithoutChannelBinding() {
let offered = ["SCRAM-SHA-512-PLUS", "SCRAM-SHA-512", "PLAIN"]
XCTAssertEqual(
SASLMechanismPreference.selected(
among: offered,
allowsChannelBinding: false
),
"SCRAM-SHA-512"
)
// Without any plain SCRAM alternative, PLAIN is the only choice.
XCTAssertEqual(
SASLMechanismPreference.selected(
among: ["SCRAM-SHA-1-PLUS", "PLAIN"],
allowsChannelBinding: false
),
"PLAIN"
)
}
}
+106
View File
@@ -0,0 +1,106 @@
import CryptoKit
import XCTest
@testable import Luma
final class SCRAMPlusTests: XCTestCase {
/// Reference vectors computed with an independent Python
/// implementation of RFC 5802 SCRAM-SHA-1 with channel binding
/// (tls-server-end-point, deterministic 32-byte binding data).
private func referenceExchange() -> SCRAMPlusExchange {
SCRAMPlusExchange(
hash: .sha1,
username: "user",
password: "pencil",
channelBindingType: "tls-server-end-point",
channelBindingData: Data(0..<32),
clientNonce: "fyko+d2lbbFgONRv9qkxdawL"
)
}
private static let referenceServerFirst =
"r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=W22ZaJ0SNY7soEsUEjb6gQ==,i=4096"
func testClientFirstMessageMatchesReference() {
let exchange = referenceExchange()
let first = exchange.clientFirstMessage.data(using: .utf8)!.base64EncodedString()
XCTAssertEqual(
first,
"cD10bHMtc2VydmVyLWVuZC1wb2ludCwsbj11c2VyLHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdM"
)
}
func testClientFinalMessageMatchesReference() throws {
let exchange = referenceExchange()
let final = try exchange.clientFinalMessage(serverFirst: Self.referenceServerFirst)
let encoded = final.data(using: .utf8)!.base64EncodedString()
XCTAssertEqual(
encoded,
"Yz1jRDEwYkhNdGMyVnlkbVZ5TFdWdVpDMXdiMmx1ZEN3c0FBRUNBd1FGQmdjSUNRb0xEQTBPRHhBUkVoTVVGUllYR0JrYUd4d2RIaDg9LHI9ZnlrbytkMmxiYkZnT05Sdjlxa3hkYXdMM3JmY05IWUpZMVpWdldWczdqLHA9RkVLbDNvQU13UFErc0JsYnR6SU1oaGdKSFJjPQ=="
)
}
func testExpectedServerSignatureMatchesReference() throws {
let exchange = referenceExchange()
let signature = try exchange.expectedServerSignature(serverFirst: Self.referenceServerFirst)
XCTAssertEqual(signature.base64EncodedString(), "CCSN72nmVmqopGNXz27xIKr6CxA=")
}
func testVerifierValueExtractsOnlyTheBase64Payload() {
XCTAssertEqual(
SCRAMPlusExchange.verifierValue(in: "v=DQNSo72nmVmqopGNXz27xIKr6CxA="),
"DQNSo72nmVmqopGNXz27xIKr6CxA="
)
XCTAssertEqual(
SCRAMPlusExchange.verifierValue(in: "v=abc123"),
"abc123"
)
XCTAssertEqual(
SCRAMPlusExchange.verifierValue(in: "v=abc123,ext=value"),
"abc123"
)
XCTAssertNil(SCRAMPlusExchange.verifierValue(in: "e=invalid-proof"))
XCTAssertNil(SCRAMPlusExchange.verifierValue(in: ""))
}
func testChannelBindingStoreDerivesEndPointDigest() {
let store = LumaChannelBindingStore()
let leaf = Data("certificate".utf8)
store.setTLSState(
LumaTLSState(
version: "TLSv1.3",
cipher: "TLS_AES_256_GCM_SHA384",
leafCertificateDER: leaf,
exporter: Data(0..<32)
)
)
store.setAdvertisedChannelBindingTypes(["tls-exporter", "tls-server-end-point"])
XCTAssertEqual(store.preferredChannelBindingType, "tls-exporter")
XCTAssertEqual(
store.channelBindingData(for: "tls-server-end-point"),
Data(SHA256.hash(data: leaf))
)
XCTAssertEqual(
store.channelBindingData(for: "tls-exporter"),
Data(0..<32),
)
}
func testChannelBindingStoreFallsBackToEndPoint() {
let store = LumaChannelBindingStore()
store.setTLSState(
LumaTLSState(
version: "TLSv1.3",
cipher: "TLS_AES_256_GCM_SHA384",
leafCertificateDER: Data("leaf".utf8),
exporter: nil,
)
)
// Only the certificate digest is available: pick tls-server-end-point.
store.setAdvertisedChannelBindingTypes(["tls-server-end-point"])
XCTAssertEqual(store.preferredChannelBindingType, "tls-server-end-point")
// Nothing usable when the server does not advertise any known type.
store.setAdvertisedChannelBindingTypes([])
XCTAssertNil(store.preferredChannelBindingType)
XCTAssertFalse(store.canUseChannelBinding)
}
}