|
16 | 16 |
|
17 | 17 | import Foundation |
18 | 18 | import Network |
| 19 | + import NIO |
19 | 20 | import NIOSSL |
20 | 21 | import NIOTransportServices |
21 | 22 |
|
|
58 | 59 |
|
59 | 60 | /// create NWProtocolTLS.Options for use with NIOTransportServices from the NIOSSL TLSConfiguration |
60 | 61 | /// |
61 | | - /// - Parameter queue: Dispatch queue to run `sec_protocol_options_set_verify_block` on. |
| 62 | + /// - Parameter eventLoop: EventLoop to wait for creation of options on |
| 63 | + /// - Returns: Future holding NWProtocolTLS Options |
| 64 | + func getNWProtocolTLSOptions(on eventLoop: EventLoop) -> EventLoopFuture<NWProtocolTLS.Options> { |
| 65 | + let promise = eventLoop.makePromise(of: NWProtocolTLS.Options.self) |
| 66 | + Self.tlsDispatchQueue.async { |
| 67 | + do { |
| 68 | + let options = try self.getNWProtocolTLSOptions() |
| 69 | + promise.succeed(options) |
| 70 | + } catch { |
| 71 | + promise.fail(error) |
| 72 | + } |
| 73 | + } |
| 74 | + return promise.futureResult |
| 75 | + } |
| 76 | + |
| 77 | + /// create NWProtocolTLS.Options for use with NIOTransportServices from the NIOSSL TLSConfiguration |
| 78 | + /// |
62 | 79 | /// - Returns: Equivalent NWProtocolTLS Options |
63 | | - func getNWProtocolTLSOptions() -> NWProtocolTLS.Options { |
| 80 | + func getNWProtocolTLSOptions() throws -> NWProtocolTLS.Options { |
64 | 81 | let options = NWProtocolTLS.Options() |
65 | 82 |
|
66 | 83 | let useMTELGExplainer = """ |
|
109 | 126 | preconditionFailure("TLSConfiguration.keyLogCallback is not supported. \(useMTELGExplainer)") |
110 | 127 | } |
111 | 128 |
|
| 129 | + // the certificate chain |
| 130 | + if self.certificateChain.count > 0 { |
| 131 | + preconditionFailure("TLSConfiguration.certificateChain is not supported. \(useMTELGExplainer)") |
| 132 | + } |
| 133 | + |
112 | 134 | // private key |
113 | 135 | if self.privateKey != nil { |
114 | 136 | preconditionFailure("TLSConfiguration.privateKey is not supported. \(useMTELGExplainer)") |
|
117 | 139 | // renegotiation support key is unsupported |
118 | 140 |
|
119 | 141 | // trust roots |
120 | | - if let trustRoots = self.trustRoots { |
121 | | - guard case .default = trustRoots else { |
122 | | - preconditionFailure("TLSConfiguration.trustRoots != .default is not supported. \(useMTELGExplainer)") |
| 142 | + var secTrustRoots: [SecCertificate]? |
| 143 | + switch trustRoots { |
| 144 | + case .some(.certificates(let certificates)): |
| 145 | + secTrustRoots = try certificates.compactMap { certificate in |
| 146 | + try SecCertificateCreateWithData(nil, Data(certificate.toDERBytes()) as CFData) |
| 147 | + } |
| 148 | + case .some(.file(let file)): |
| 149 | + let certificates = try NIOSSLCertificate.fromPEMFile(file) |
| 150 | + secTrustRoots = try certificates.compactMap { certificate in |
| 151 | + try SecCertificateCreateWithData(nil, Data(certificate.toDERBytes()) as CFData) |
123 | 152 | } |
| 153 | + |
| 154 | + case .some(.default), .none: |
| 155 | + break |
124 | 156 | } |
125 | 157 |
|
126 | | - switch self.certificateVerification { |
127 | | - case .none: |
| 158 | + precondition(self.certificateVerification != .noHostnameVerification, |
| 159 | + "TLSConfiguration.certificateVerification = .noHostnameVerification is not supported. \(useMTELGExplainer)") |
| 160 | + |
| 161 | + if certificateVerification != .fullVerification || trustRoots != nil { |
128 | 162 | // add verify block to control certificate verification |
129 | 163 | sec_protocol_options_set_verify_block( |
130 | 164 | options.securityProtocolOptions, |
131 | | - { _, _, sec_protocol_verify_complete in |
132 | | - sec_protocol_verify_complete(true) |
133 | | - }, TLSConfiguration.tlsDispatchQueue |
| 165 | + { _, sec_trust, sec_protocol_verify_complete in |
| 166 | + guard self.certificateVerification != .none else { |
| 167 | + sec_protocol_verify_complete(true) |
| 168 | + return |
| 169 | + } |
| 170 | + |
| 171 | + let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue() |
| 172 | + if let trustRootCertificates = secTrustRoots { |
| 173 | + SecTrustSetAnchorCertificates(trust, trustRootCertificates as CFArray) |
| 174 | + } |
| 175 | + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) { |
| 176 | + dispatchPrecondition(condition: .onQueue(Self.tlsDispatchQueue)) |
| 177 | + SecTrustEvaluateAsyncWithError(trust, Self.tlsDispatchQueue) { _, result, error in |
| 178 | + if let error = error { |
| 179 | + print("Trust failed: \(error.localizedDescription)") |
| 180 | + } |
| 181 | + sec_protocol_verify_complete(result) |
| 182 | + } |
| 183 | + } else { |
| 184 | + SecTrustEvaluateAsync(trust, Self.tlsDispatchQueue) { _, result in |
| 185 | + switch result { |
| 186 | + case .proceed, .unspecified: |
| 187 | + sec_protocol_verify_complete(true) |
| 188 | + default: |
| 189 | + sec_protocol_verify_complete(false) |
| 190 | + } |
| 191 | + } |
| 192 | + } |
| 193 | + }, Self.tlsDispatchQueue |
134 | 194 | ) |
135 | | - |
136 | | - case .noHostnameVerification: |
137 | | - precondition(self.certificateVerification != .noHostnameVerification, |
138 | | - "TLSConfiguration.certificateVerification = .noHostnameVerification is not supported. \(useMTELGExplainer)") |
139 | | - |
140 | | - case .fullVerification: |
141 | | - break |
142 | 195 | } |
143 | | - |
144 | 196 | return options |
145 | 197 | } |
146 | 198 | } |
|
0 commit comments