import Foundation import CommonCrypto import Security /// ATS exception for the LAN host is declared in Info.plist. If the deployment /// uses a self-signed certificate, pin its SHA-384 here (extract with: /// `openssl s_client -connect 192.168.86.2:9178 /dev/null | openssl x509 -outform der | shasum -a 384`). /// While empty, standard system trust is used (works for the current deployment). enum TLSPinning { static let pinnedCertSHA384: [String] = [] static func validate(_ trust: SecTrust) -> Bool { guard !pinnedCertSHA384.isEmpty else { return true } var error: CFError? guard SecTrustEvaluateWithError(trust, &error) else { return false } let count = SecTrustGetCertificateCount(trust) guard count > 0 else { return false } guard let chain = SecTrustCopyCertificateChain(trust) as? [SecCertificate] else { return false } for cert in chain { guard let derData = SecCertificateCopyData(cert) as Data? else { continue } if pinnedCertSHA384.contains(sha384Hex(derData)) { return true } } return false } private static func sha384Hex(_ data: Data) -> String { var hash = [UInt8](repeating: 0, count: Int(CC_SHA384_DIGEST_LENGTH)) data.withUnsafeBytes { buffer in guard let base = buffer.baseAddress else { return } CC_SHA384(base, CC_LONG(data.count), &hash) } return hash.map { String(format: "%02x", $0) }.joined() } }