tls sasl
This commit is contained in:
1 parent
e3fcee3351
commit
d4b4c92759
29 files changed
+2177
-61
No files matched your search
@@ -1913,6 +1913,10 @@ final class AppModel: ObservableObject {
|
||||
errorMessage = nil
|
||||
}
|
||||
|
||||
func clearInformationalMessage() {
|
||||
informationalMessage = nil
|
||||
}
|
||||
|
||||
private func beginMediaSendActivity() -> UUID {
|
||||
let token = mediaSendActivity.begin()
|
||||
isSendingAttachment = mediaSendActivity.isActive
|
||||
@@ -3100,14 +3104,32 @@ final class AppModel: ObservableObject {
|
||||
cursor: lastSuccessfulMAMCursor
|
||||
)
|
||||
}
|
||||
// One-time cleanup of phantom «Не удалось расшифровать сообщение»
|
||||
// placeholders: outgoing echoes the client could not decrypt back
|
||||
// (broken encrypt-to-self sessions) used to be rendered as
|
||||
// undecryptable bubbles. XMPPService now drops those echoes, so purge
|
||||
// the leftovers from the local store instead of keeping the noise.
|
||||
let phantomPlaceholderIDs = Set(
|
||||
snapshot.messages
|
||||
.filter { $0.direction == .outgoing && $0.security == .decryptionFailed }
|
||||
.map(\.clientID)
|
||||
)
|
||||
for message in snapshot.messages
|
||||
where phantomPlaceholderIDs.contains(message.clientID) {
|
||||
modelContext.delete(message)
|
||||
}
|
||||
messages = snapshot.messages.filter {
|
||||
!locallyDeletedMessageIDs.contains(
|
||||
Self.localDeletionKey(
|
||||
messageID: $0.clientID,
|
||||
conversationID: $0.conversationID
|
||||
))
|
||||
!phantomPlaceholderIDs.contains($0.clientID)
|
||||
&& !locallyDeletedMessageIDs.contains(
|
||||
Self.localDeletionKey(
|
||||
messageID: $0.clientID,
|
||||
conversationID: $0.conversationID
|
||||
))
|
||||
}
|
||||
rebuildMessageIndex()
|
||||
if !phantomPlaceholderIDs.isEmpty {
|
||||
schedulePersist()
|
||||
}
|
||||
let avatarJIDs = Set(snapshot.conversations.filter { !$0.isGroup }.map(\.jid) + [jid])
|
||||
for avatarJID in avatarJIDs {
|
||||
if let data = await avatarCache.data(for: avatarJID) {
|
||||
|
||||
@@ -14,8 +14,14 @@ struct OMEMODevice: Identifiable, Hashable, Sendable {
|
||||
let trust: Trust
|
||||
let isActive: Bool
|
||||
let isOwn: Bool
|
||||
let isOMEMO2: Bool
|
||||
|
||||
var id: String { "\(jid)|\(deviceID)" }
|
||||
var id: String { "\(jid)|\(deviceID)|\(isOMEMO2 ? "omemo2" : "legacy")" }
|
||||
|
||||
/// Short protocol label for the encryption screen badge.
|
||||
var protocolName: String {
|
||||
isOMEMO2 ? "OMEMO 2" : "OMEMO"
|
||||
}
|
||||
|
||||
var formattedFingerprint: String {
|
||||
fingerprint.chunked(every: 8).joined(separator: " ")
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import Foundation
|
||||
|
||||
/// Order in which Luma prefers SASL mechanisms during authentication.
|
||||
/// Channel-binding SCRAM first (tls-exporter / tls-server-end-point prove
|
||||
/// the exchange runs over the client's own TLS channel, defeating MITM
|
||||
/// relays), then the strongest plain SCRAM variant, and PLAIN as the
|
||||
/// last-resort fallback. The server must advertise a mechanism for it to be
|
||||
/// eligible — Martin's `SaslModule.guessSaslMechanism` walks exactly this
|
||||
/// order, and the PLUS mechanisms additionally gate on available binding
|
||||
/// data in `isAllowedToUse`.
|
||||
enum SASLMechanismPreference {
|
||||
static let order: [String] = [
|
||||
"SCRAM-SHA-512-PLUS",
|
||||
"SCRAM-SHA-256-PLUS",
|
||||
"SCRAM-SHA-1-PLUS",
|
||||
"SCRAM-SHA-512",
|
||||
"SCRAM-SHA-256",
|
||||
"SCRAM-SHA-1",
|
||||
"PLAIN",
|
||||
]
|
||||
|
||||
/// The most secure mechanism from `order` that the server advertises,
|
||||
/// or nil when no password-based mechanism is offered. When
|
||||
/// `allowsChannelBinding` is false the -PLUS variants are skipped, since
|
||||
/// without binding data the exchange would fail.
|
||||
static func selected(
|
||||
among offered: [String],
|
||||
allowsChannelBinding: Bool = true
|
||||
) -> String? {
|
||||
order.first { candidate in
|
||||
guard offered.contains(candidate) else { return false }
|
||||
return allowsChannelBinding || !candidate.hasSuffix("-PLUS")
|
||||
}
|
||||
}
|
||||
|
||||
/// True when `mechanism` is the strongest one available among
|
||||
/// `offered`: the server offers nothing better than the chosen one.
|
||||
static func isStrongestAvailable(_ mechanism: String, among offered: [String]) -> Bool {
|
||||
guard let chosenRank = order.firstIndex(of: mechanism) else { return false }
|
||||
for stronger in order[..<chosenRank] where offered.contains(stronger) {
|
||||
return false
|
||||
}
|
||||
return offered.contains(mechanism)
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,14 @@ struct ServerInformation: Equatable, Sendable {
|
||||
let externalServices: [ExternalService]
|
||||
let connectionStats: ConnectionStats
|
||||
let saslMethods: [String]
|
||||
/// TLS version negotiated with the server during this connection
|
||||
/// (probed from a parallel handshake when the library does not expose it).
|
||||
let tlsVersion: String?
|
||||
/// Cipher suite negotiated alongside the TLS version.
|
||||
let tlsCipher: String?
|
||||
/// SASL mechanism actually selected for authentication (the strongest
|
||||
/// one the server advertises).
|
||||
let saslMechanism: String?
|
||||
let supportsStreamManagement: Bool
|
||||
let supportsCarbons: Bool
|
||||
let supportsClientState: Bool
|
||||
|
||||
Reference in new issue
Block a user