Files
Luma/Sources/Shared/Models/SASLMechanismPreference.swift
T
wt 1650145d4d
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
serverinfoview
2026-09-03 12:42:20 +07:00

50 lines
2.1 KiB
Swift

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")
}
}
/// Mechanisms that send credentials in a way a TLS MITM can replay or
/// read directly; shown with a warning on the server-information screen.
static let weakMechanisms: Set<String> = ["PLAIN", "OAUTHBEARER"]
/// 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)
}
}