Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Threema/ThreemaFramework/Network/   (Social Medium ©)  Datei vom 26.9.2026 mit Größe 8 kB image not shown  

Quelle  SSLCAHelper.swift   Sprache: JAVA

 

import CocoaLumberjackSwift
import Foundation
import ThreemaEssentials
import TrustKit

enum SSLCAHelperError: Error {
    case authenticationChallengeNotHandeld
}

final class SSLCAHelper: NSObject, SSLCAHelperProtocol {

    /// Validation of a certificate chain against the systems trust store
    typealias SystemTrustEvaluation = (_ trust: SecTrust, _ domain: String) -> Bool

    private let serverInfoProvider: ServerInfoProvider
    private let systemTrustEvaluation: SystemTrustEvaluation

    private var cachedTrustKit: TrustKit?

    // MARK: - Lifecycle

    override convenience init() {
        self.init(serverInfoProvider: ServerInfoProviderFactory.makeServerInfoProvider())
    }

    /// - Parameters:
    ///   - serverInfoProvider: Provides the pinning configuration (e.g. from the OPPF for OnPrem)
    ///   - systemTrustEvaluation: Validation for domains without pins
    init(
        serverInfoProvider: ServerInfoProvider,
        systemTrustEvaluation: @escaping SystemTrustEvaluation = SSLCAHelper.evaluateWithSystemTrustStore
    ) {
        self.serverInfoProvider = serverInfoProvider
        self.systemTrustEvaluation = systemTrustEvaluation

        super.init()

        NotificationCenter.default.addObserver(
            self,
            selector: #selector(resetCache),
            name: .resetSSLCAHelperCache,
            object: nil
        )
    }

    // MARK: - Public methods

    /// Check is authentication mode like server trust.
    /// - Parameter protectionSpace: Received protection space
    /// - Returns: True if server trust
    static func canAuthenticate(_ protectionSpace: URLProtectionSpace) -> Bool {
        protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust
    }

    /// Handling authentication challenges received within a NSURLSessionDelegate.
    /// - Parameter challenge: Authentication challenge
    /// - Returns: Disposition and credential to respond to the challenge
    @objc func handle(challenge: URLAuthenticationChallenge) async throws
        -> (disposition: URLSession.AuthChallengeDisposition, credential: URLCredential?) {

        let trustKit = try await initTrustKit()

        return try await withCheckedThrowingContinuation { continuation in
            // Pass the authentication challenge to the validator; if the validation fails, the connection will be
            // blocked
            let isSuccessfullyHandled = trustKit.pinningValidator.handle(challenge) { disposition, credential in
                switch disposition {
                case .useCredential:
                    if let credential {
                        challenge.sender?.use(credential, for: challenge)
                    }
                    else {
                        challenge.sender?.continueWithoutCredential(for: challenge)
                    }
                case .performDefaultHandling:
                    challenge.sender?.performDefaultHandling?(for: challenge)
                case .cancelAuthenticationChallenge:
                    challenge.sender?.cancel(challenge)
                case .rejectProtectionSpace:
                    challenge.sender?.rejectProtectionSpaceAndContinue?(with: challenge)
                }

                continuation.resume(returning: (disposition, credential))
            }

            if !isSuccessfullyHandled {
                DDLogWarn("The challenge could not be handled because it was not for server certificate validation.")
                continuation.resume(throwing: SSLCAHelperError.authenticationChallengeNotHandeld)
            }
        }
    }

    /// Evaluate the supplied server trust against the SSL pinning policy.
    /// - Parameters:
    /// - trust: Server trust
    /// - domain: Hostname of the server
    /// - completionHandler: True means connection is allowed
    func evaluate(trust: SecTrust, domain: String, completionHandler: @escaping (Bool) -> Void) {
        Task {
            await completionHandler(evaluate(trust: trust, domain: domain))
        }
    }

    /// Evaluate the supplied server trust against the SSL pinning policy.
    /// - Parameters:
    /// - trust: Server trust
    /// - domain: Hostname of the server
    /// - Returns: True means connection is allowed
    ///
    /// This is the only trust evaluation for connections using a custom cert pinner (i.e. the mediator web socket).
    /// Such a pinner replaces the systems own chain validation, thus every case that is not pinned must still be
    /// validated here.
    func evaluate(trust: SecTrust, domain: String) async -> Bool {
        do {
            let trustKit = try await initTrustKit()

            let trustDecision = trustKit.pinningValidator.evaluateTrust(trust, forHostname: domain)

            switch trustDecision {
            case .shouldAllowConnection:
                return true

            case .domainNotPinned:
                // Pinning is optional (e.g. for OnPrem), so a domain without pins must not be rejected. It still needs
                // standard system validation, because no one else does it for us.
                return systemTrustEvaluation(trust, domain)

            case .shouldBlockConnection:
                DDLogError("Pinning validation of domain \(domain) failed")
                return false

            @unknown default:
                DDLogError("Unknown trust decision for domain \(domain)")
                return false
            }
        }
        catch {
            DDLogError("Evaluate of trust failed \(error)")
            return false
        }
    }

    /// Standard system validation of the certificate chain, including the hostname.
    /// - Returns: True if the chain is valid for this hostname
    static func evaluateWithSystemTrustStore(trust: SecTrust, domain: String) -> Bool {
        SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, domain as CFString))

        var error: CFError?
        let isValidChain = SecTrustEvaluateWithError(trust, &error)
        if !isValidChain {
            DDLogError("Standard trust evaluation of unpinned domain \(domain) failed: \(String(describing: error))")
        }

        return isValidChain
    }

    // MARK: - Private helper

    /// Reset cache to pick up most recent pins from OPPF if needed
    @objc private func resetCache() {
        cachedTrustKit = nil
    }

    /// Loads TrustKit with SSL cert pinning config for Public or OnPrem server
    private func initTrustKit() async throws -> TrustKit {
        if let cachedTrustKit {
            return cachedTrustKit
        }

        let provider = serverInfoProvider

        let trustKit: TrustKit = try await withCheckedThrowingContinuation { continuation in
            provider.domains { domains, error in
                if let error {
                    return continuation.resume(throwing: error)
                }

                guard let domains else {
                    return continuation.resume(throwing: OnPremConfigError.missingDomainsConfig)
                }

                // Add configured domains
                var trustKitConfig = [String: Any]()
                trustKitConfig[kTSKSwizzleNetworkDelegates] = false

                var pinnedDomains = [String: Any]()

                for rule in domains {
                    guard !rule.spkis.isEmpty else {
                        return continuation.resume(throwing: OnPremConfigError.noDomainSpkis)
                    }

                    guard rule.matchMode != .unsupported else {
                        return continuation.resume(throwing: OnPremConfigError.unsupportedDomainMatchMode)
                    }

                    guard rule.spkis.map(\.values.first!).filter({ $0 == .unsupported }).isEmpty else {
                        return continuation.resume(throwing: OnPremConfigError.unsupportedDomainSpkisAlgorithm)
                    }

                    var pinnedDomain = [String: Any]()

                    pinnedDomain[kTSKIncludeSubdomains] = rule.matchMode == .includeSubdomains

                    var publicKeyHashes: [String] = rule.spkis.map(\.keys.first!)

                    // TrustKit must have min. 2 pins! Adding fake pin if only one configured
                    if publicKeyHashes.count == 1 {
                        publicKeyHashes.append(BytesUtility.generateRandomBytes(length: 32)!.base64EncodedString())
                    }
                    pinnedDomain[kTSKPublicKeyHashes] = publicKeyHashes
                    pinnedDomain[kTSKEnforcePinning] = true
                    if let reportUris = rule.reportUris {
                        pinnedDomain[kTSKReportUris] = reportUris
                    }
                    else {
                        pinnedDomain[kTSKDisableDefaultReportUri] = true
                    }

                    pinnedDomains[rule.domain] = pinnedDomain
                }

                trustKitConfig[kTSKPinnedDomains] = pinnedDomains

                continuation.resume(returning: TrustKit(configuration: trustKitConfig))
            }
        }

        cachedTrustKit = trustKit

        return trustKit
    }
}

Messung
CHZG
849610093

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.