/src/grpc-swift/Sources/GRPC/GRPCTLSConfiguration.swift
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2021, gRPC Authors All rights reserved. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | #if canImport(NIOSSL) |
17 | | import NIOCore |
18 | | import NIOSSL |
19 | | #endif |
20 | | |
21 | | #if canImport(Network) |
22 | | import Network |
23 | | import NIOTransportServices |
24 | | import Security |
25 | | #endif |
26 | | |
27 | | /// TLS configuration. |
28 | | /// |
29 | | /// This structure allow configuring TLS for a wide range of TLS implementations. Some |
30 | | /// options are removed from the user's control to ensure the configuration complies with |
31 | | /// the gRPC specification. |
32 | | public struct GRPCTLSConfiguration: Sendable { |
33 | | fileprivate enum Backend: Sendable { |
34 | | #if canImport(NIOSSL) |
35 | | /// Configuration for NIOSSSL. |
36 | | case nio(NIOConfiguration) |
37 | | #endif |
38 | | #if canImport(Network) |
39 | | /// Configuration for Network.framework. |
40 | | case network(NetworkConfiguration) |
41 | | #endif |
42 | | } |
43 | | |
44 | | /// The TLS backend. |
45 | | private var backend: Backend |
46 | | |
47 | | #if canImport(NIOSSL) |
48 | 0 | fileprivate init(nio: NIOConfiguration) { |
49 | 0 | self.backend = .nio(nio) |
50 | 0 | } |
51 | | #endif |
52 | | |
53 | | #if canImport(Network) |
54 | | fileprivate init(network: NetworkConfiguration) { |
55 | | self.backend = .network(network) |
56 | | } |
57 | | #endif |
58 | | |
59 | | /// Return the configuration for NIOSSL or `nil` if Network.framework is being used as the |
60 | | /// TLS backend. |
61 | | #if canImport(NIOSSL) |
62 | 0 | internal var nioConfiguration: NIOConfiguration? { |
63 | 0 | switch self.backend { |
64 | 0 | case let .nio(configuration): |
65 | 0 | return configuration |
66 | | #if canImport(Network) |
67 | | case .network: |
68 | | return nil |
69 | | #endif |
70 | 0 | } |
71 | 0 | } |
72 | | #endif // canImport(NIOSSL) |
73 | | |
74 | 0 | internal var isNetworkFrameworkTLSBackend: Bool { |
75 | 0 | switch self.backend { |
76 | | #if canImport(NIOSSL) |
77 | 0 | case .nio: |
78 | 0 | return false |
79 | | #endif |
80 | | #if canImport(Network) |
81 | | case .network: |
82 | | return true |
83 | | #endif |
84 | 0 | } |
85 | 0 | } |
86 | | |
87 | | /// The server hostname override as used by the TLS SNI extension. |
88 | | /// |
89 | | /// This value is ignored when the configuration is used for a server. |
90 | | /// |
91 | | /// - Note: when using the Network.framework backend, this value may not be set to `nil`. |
92 | | internal var hostnameOverride: String? { |
93 | 0 | get { |
94 | 0 | switch self.backend { |
95 | | #if canImport(NIOSSL) |
96 | 0 | case let .nio(config): |
97 | 0 | return config.hostnameOverride |
98 | | #endif |
99 | 0 |
|
100 | | #if canImport(Network) |
101 | | case let .network(config): |
102 | | return config.hostnameOverride |
103 | | #endif |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | 0 | set { |
108 | 0 | switch self.backend { |
109 | | #if canImport(NIOSSL) |
110 | 0 | case var .nio(config): |
111 | 0 | config.hostnameOverride = newValue |
112 | 0 | self.backend = .nio(config) |
113 | | #endif |
114 | 0 |
|
115 | | #if canImport(Network) |
116 | | case var .network(config): |
117 | | if #available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) { |
118 | | if let hostnameOverride = newValue { |
119 | | config.updateHostnameOverride(to: hostnameOverride) |
120 | | } else { |
121 | | // We can't unset the value so error instead. |
122 | | fatalError("Can't unset hostname override when using Network.framework TLS backend.") |
123 | | // FIXME: lazily set the value on the backend when applying the options. |
124 | | } |
125 | | } else { |
126 | | // We can only make the `.network` backend if we meet the above availability checks so |
127 | | // this should be unreachable. |
128 | | preconditionFailure() |
129 | | } |
130 | | self.backend = .network(config) |
131 | | #endif |
132 | 0 | } |
133 | 0 | } |
134 | | } |
135 | | |
136 | | /// Whether the configuration requires ALPN to be used. |
137 | | /// |
138 | | /// The Network.framework backend does not support this option and always requires ALPN. |
139 | | internal var requireALPN: Bool { |
140 | 0 | get { |
141 | 0 | switch self.backend { |
142 | | #if canImport(NIOSSL) |
143 | 0 | case let .nio(config): |
144 | 0 | return config.requireALPN |
145 | | #endif |
146 | 0 |
|
147 | | #if canImport(Network) |
148 | | case .network: |
149 | | return true |
150 | | #endif |
151 | 0 | } |
152 | 0 | } |
153 | 0 | set { |
154 | 0 | switch self.backend { |
155 | | #if canImport(NIOSSL) |
156 | 0 | case var .nio(config): |
157 | 0 | config.requireALPN = newValue |
158 | 0 | self.backend = .nio(config) |
159 | | #endif |
160 | 0 |
|
161 | | #if canImport(Network) |
162 | | case .network: |
163 | | () |
164 | | #endif |
165 | 0 | } |
166 | 0 | } |
167 | | } |
168 | | |
169 | | #if canImport(NIOSSL) |
170 | | // Marked to silence the deprecation warning |
171 | | @available(*, deprecated) |
172 | 0 | internal init(transforming deprecated: ClientConnection.Configuration.TLS) { |
173 | 0 | self.backend = .nio( |
174 | 0 | .init( |
175 | 0 | configuration: deprecated.configuration, |
176 | 0 | customVerificationCallback: deprecated.customVerificationCallback, |
177 | 0 | hostnameOverride: deprecated.hostnameOverride, |
178 | 0 | requireALPN: false // Not currently supported. |
179 | 0 | ) |
180 | 0 | ) |
181 | 0 | } |
182 | | |
183 | | // Marked to silence the deprecation warning |
184 | | @available(*, deprecated) |
185 | 0 | internal init(transforming deprecated: Server.Configuration.TLS) { |
186 | 0 | self.backend = .nio( |
187 | 0 | .init(configuration: deprecated.configuration, requireALPN: deprecated.requireALPN) |
188 | 0 | ) |
189 | 0 | } |
190 | | |
191 | | @available(*, deprecated) |
192 | 0 | internal var asDeprecatedClientConfiguration: ClientConnection.Configuration.TLS? { |
193 | 0 | if case let .nio(config) = self.backend { |
194 | 0 | var tls = ClientConnection.Configuration.TLS( |
195 | 0 | configuration: config.configuration, |
196 | 0 | hostnameOverride: config.hostnameOverride |
197 | 0 | ) |
198 | 0 | tls.customVerificationCallback = config.customVerificationCallback |
199 | 0 | return tls |
200 | 0 | } |
201 | 0 |
|
202 | 0 | return nil |
203 | 0 | } |
204 | | |
205 | | @available(*, deprecated) |
206 | 0 | internal var asDeprecatedServerConfiguration: Server.Configuration.TLS? { |
207 | 0 | if case let .nio(config) = self.backend { |
208 | 0 | return Server.Configuration.TLS(configuration: config.configuration) |
209 | 0 | } |
210 | 0 | return nil |
211 | 0 | } |
212 | | #endif // canImport(NIOSSL) |
213 | | } |
214 | | |
215 | | // MARK: - NIO Backend |
216 | | |
217 | | // canImport(NIOSSL) |
218 | | #if canImport(NIOSSL) |
219 | | extension GRPCTLSConfiguration { |
220 | | internal struct NIOConfiguration { |
221 | | var configuration: TLSConfiguration |
222 | | var customVerificationCallback: NIOSSLCustomVerificationCallback? |
223 | | var hostnameOverride: String? |
224 | | // The client doesn't support this yet (https://github.com/grpc/grpc-swift/issues/1042). |
225 | | var requireALPN: Bool |
226 | | } |
227 | | |
228 | | /// TLS Configuration with suitable defaults for clients, using `NIOSSL`. |
229 | | /// |
230 | | /// This is a wrapper around `NIOSSL.TLSConfiguration` to restrict input to values which comply |
231 | | /// with the gRPC protocol. |
232 | | /// |
233 | | /// - Parameter certificateChain: The certificate to offer during negotiation, defaults to an |
234 | | /// empty array. |
235 | | /// - Parameter privateKey: The private key associated with the leaf certificate. This defaults |
236 | | /// to `nil`. |
237 | | /// - Parameter trustRoots: The trust roots to validate certificates, this defaults to using a |
238 | | /// root provided by the platform. |
239 | | /// - Parameter certificateVerification: Whether to verify the remote certificate. Defaults to |
240 | | /// `.fullVerification`. |
241 | | /// - Parameter hostnameOverride: Value to use for TLS SNI extension; this must not be an IP |
242 | | /// address, defaults to `nil`. |
243 | | /// - Parameter customVerificationCallback: A callback to provide to override the certificate verification logic, |
244 | | /// defaults to `nil`. |
245 | | public static func makeClientConfigurationBackedByNIOSSL( |
246 | | certificateChain: [NIOSSLCertificateSource] = [], |
247 | | privateKey: NIOSSLPrivateKeySource? = nil, |
248 | | trustRoots: NIOSSLTrustRoots = .default, |
249 | | certificateVerification: CertificateVerification = .fullVerification, |
250 | | hostnameOverride: String? = nil, |
251 | | customVerificationCallback: NIOSSLCustomVerificationCallback? = nil |
252 | 0 | ) -> GRPCTLSConfiguration { |
253 | 0 | var configuration = TLSConfiguration.makeClientConfiguration() |
254 | 0 | configuration.minimumTLSVersion = .tlsv12 |
255 | 0 | configuration.certificateVerification = certificateVerification |
256 | 0 | configuration.trustRoots = trustRoots |
257 | 0 | configuration.certificateChain = certificateChain |
258 | 0 | configuration.privateKey = privateKey |
259 | 0 | configuration.applicationProtocols = GRPCApplicationProtocolIdentifier.client |
260 | 0 |
|
261 | 0 | return GRPCTLSConfiguration.makeClientConfigurationBackedByNIOSSL( |
262 | 0 | configuration: configuration, |
263 | 0 | hostnameOverride: hostnameOverride, |
264 | 0 | customVerificationCallback: customVerificationCallback |
265 | 0 | ) |
266 | 0 | } |
267 | | |
268 | | /// Creates a gRPC TLS Configuration using the given `NIOSSL.TLSConfiguration`. |
269 | | /// |
270 | | /// - Note: If no ALPN tokens are set in `configuration.applicationProtocols` then "grpc-exp" |
271 | | /// and "h2" will be used. |
272 | | /// - Parameters: |
273 | | /// - configuration: The `NIOSSL.TLSConfiguration` to base this configuration on. |
274 | | /// - hostnameOverride: The hostname override to use for the TLS SNI extension. |
275 | | public static func makeClientConfigurationBackedByNIOSSL( |
276 | | configuration: TLSConfiguration, |
277 | | hostnameOverride: String? = nil, |
278 | | customVerificationCallback: NIOSSLCustomVerificationCallback? = nil |
279 | 0 | ) -> GRPCTLSConfiguration { |
280 | 0 | var configuration = configuration |
281 | 0 |
|
282 | 0 | // Set the ALPN tokens if none were set. |
283 | 0 | if configuration.applicationProtocols.isEmpty { |
284 | 0 | configuration.applicationProtocols = GRPCApplicationProtocolIdentifier.client |
285 | 0 | } |
286 | 0 |
|
287 | 0 | let nioConfiguration = NIOConfiguration( |
288 | 0 | configuration: configuration, |
289 | 0 | customVerificationCallback: customVerificationCallback, |
290 | 0 | hostnameOverride: hostnameOverride, |
291 | 0 | requireALPN: false // We don't currently support this. |
292 | 0 | ) |
293 | 0 |
|
294 | 0 | return GRPCTLSConfiguration(nio: nioConfiguration) |
295 | 0 | } |
296 | | |
297 | | /// TLS Configuration with suitable defaults for servers. |
298 | | /// |
299 | | /// This is a wrapper around `NIOSSL.TLSConfiguration` to restrict input to values which comply |
300 | | /// with the gRPC protocol. |
301 | | /// |
302 | | /// - Parameter certificateChain: The certificate to offer during negotiation. |
303 | | /// - Parameter privateKey: The private key associated with the leaf certificate. |
304 | | /// - Parameter trustRoots: The trust roots to validate certificates, this defaults to using a |
305 | | /// root provided by the platform. |
306 | | /// - Parameter certificateVerification: Whether to verify the remote certificate. Defaults to |
307 | | /// `.none`. |
308 | | /// - Parameter requireALPN: Whether ALPN is required or not. |
309 | | public static func makeServerConfigurationBackedByNIOSSL( |
310 | | certificateChain: [NIOSSLCertificateSource], |
311 | | privateKey: NIOSSLPrivateKeySource, |
312 | | trustRoots: NIOSSLTrustRoots = .default, |
313 | | certificateVerification: CertificateVerification = .none, |
314 | | requireALPN: Bool = true |
315 | 0 | ) -> GRPCTLSConfiguration { |
316 | 0 | return Self.makeServerConfigurationBackedByNIOSSL( |
317 | 0 | certificateChain: certificateChain, |
318 | 0 | privateKey: privateKey, |
319 | 0 | trustRoots: trustRoots, |
320 | 0 | certificateVerification: certificateVerification, |
321 | 0 | requireALPN: requireALPN, |
322 | 0 | customVerificationCallback: nil |
323 | 0 | ) |
324 | 0 | } |
325 | | |
326 | | /// TLS Configuration with suitable defaults for servers. |
327 | | /// |
328 | | /// This is a wrapper around `NIOSSL.TLSConfiguration` to restrict input to values which comply |
329 | | /// with the gRPC protocol. |
330 | | /// |
331 | | /// - Parameter certificateChain: The certificate to offer during negotiation. |
332 | | /// - Parameter privateKey: The private key associated with the leaf certificate. |
333 | | /// - Parameter trustRoots: The trust roots to validate certificates, this defaults to using a |
334 | | /// root provided by the platform. |
335 | | /// - Parameter certificateVerification: Whether to verify the remote certificate. Defaults to |
336 | | /// `.none`. |
337 | | /// - Parameter requireALPN: Whether ALPN is required or not. |
338 | | /// - Parameter customVerificationCallback: A callback to provide to override the certificate verification logic, |
339 | | /// defaults to `nil`. |
340 | | public static func makeServerConfigurationBackedByNIOSSL( |
341 | | certificateChain: [NIOSSLCertificateSource], |
342 | | privateKey: NIOSSLPrivateKeySource, |
343 | | trustRoots: NIOSSLTrustRoots = .default, |
344 | | certificateVerification: CertificateVerification = .none, |
345 | | requireALPN: Bool = true, |
346 | | customVerificationCallback: NIOSSLCustomVerificationCallback? = nil |
347 | 0 | ) -> GRPCTLSConfiguration { |
348 | 0 | var configuration = TLSConfiguration.makeServerConfiguration( |
349 | 0 | certificateChain: certificateChain, |
350 | 0 | privateKey: privateKey |
351 | 0 | ) |
352 | 0 |
|
353 | 0 | configuration.minimumTLSVersion = .tlsv12 |
354 | 0 | configuration.certificateVerification = certificateVerification |
355 | 0 | configuration.trustRoots = trustRoots |
356 | 0 | configuration.applicationProtocols = GRPCApplicationProtocolIdentifier.server |
357 | 0 |
|
358 | 0 | return GRPCTLSConfiguration.makeServerConfigurationBackedByNIOSSL( |
359 | 0 | configuration: configuration, |
360 | 0 | requireALPN: requireALPN, |
361 | 0 | customVerificationCallback: customVerificationCallback |
362 | 0 | ) |
363 | 0 | } |
364 | | |
365 | | /// Creates a gRPC TLS Configuration suitable for servers using the given |
366 | | /// `NIOSSL.TLSConfiguration`. |
367 | | /// |
368 | | /// - Note: If no ALPN tokens are set in `configuration.applicationProtocols` then "grpc-exp", |
369 | | /// "h2", and "http/1.1" will be used. |
370 | | /// - Parameters: |
371 | | /// - configuration: The `NIOSSL.TLSConfiguration` to base this configuration on. |
372 | | /// - requiresALPN: Whether the server enforces ALPN. Defaults to `true`. |
373 | | public static func makeServerConfigurationBackedByNIOSSL( |
374 | | configuration: TLSConfiguration, |
375 | | requireALPN: Bool = true |
376 | 0 | ) -> GRPCTLSConfiguration { |
377 | 0 | return Self.makeServerConfigurationBackedByNIOSSL( |
378 | 0 | configuration: configuration, |
379 | 0 | requireALPN: requireALPN, |
380 | 0 | customVerificationCallback: nil |
381 | 0 | ) |
382 | 0 | } |
383 | | |
384 | | /// Creates a gRPC TLS Configuration suitable for servers using the given |
385 | | /// `NIOSSL.TLSConfiguration`. |
386 | | /// |
387 | | /// - Note: If no ALPN tokens are set in `configuration.applicationProtocols` then "grpc-exp", |
388 | | /// "h2", and "http/1.1" will be used. |
389 | | /// - Parameters: |
390 | | /// - configuration: The `NIOSSL.TLSConfiguration` to base this configuration on. |
391 | | /// - requiresALPN: Whether the server enforces ALPN. Defaults to `true`. |
392 | | /// - Parameter customVerificationCallback: A callback to provide to override the certificate verification logic, |
393 | | /// defaults to `nil`. |
394 | | public static func makeServerConfigurationBackedByNIOSSL( |
395 | | configuration: TLSConfiguration, |
396 | | requireALPN: Bool = true, |
397 | | customVerificationCallback: NIOSSLCustomVerificationCallback? = nil |
398 | 0 | ) -> GRPCTLSConfiguration { |
399 | 0 | var configuration = configuration |
400 | 0 |
|
401 | 0 | // Set the ALPN tokens if none were set. |
402 | 0 | if configuration.applicationProtocols.isEmpty { |
403 | 0 | configuration.applicationProtocols = GRPCApplicationProtocolIdentifier.server |
404 | 0 | } |
405 | 0 |
|
406 | 0 | let nioConfiguration = NIOConfiguration( |
407 | 0 | configuration: configuration, |
408 | 0 | customVerificationCallback: customVerificationCallback, |
409 | 0 | hostnameOverride: nil, |
410 | 0 | requireALPN: requireALPN |
411 | 0 | ) |
412 | 0 |
|
413 | 0 | return GRPCTLSConfiguration(nio: nioConfiguration) |
414 | 0 | } |
415 | | |
416 | | @usableFromInline |
417 | 0 | internal func makeNIOSSLContext() throws -> NIOSSLContext? { |
418 | 0 | switch self.backend { |
419 | 0 | case let .nio(configuration): |
420 | 0 | return try NIOSSLContext(configuration: configuration.configuration) |
421 | | #if canImport(Network) |
422 | | case .network: |
423 | | return nil |
424 | | #endif |
425 | 0 | } |
426 | 0 | } |
427 | | |
428 | 0 | internal var nioSSLCustomVerificationCallback: NIOSSLCustomVerificationCallback? { |
429 | 0 | switch self.backend { |
430 | 0 | case let .nio(configuration): |
431 | 0 | return configuration.customVerificationCallback |
432 | | #if canImport(Network) |
433 | | case .network: |
434 | | return nil |
435 | | #endif |
436 | 0 | } |
437 | 0 | } |
438 | | |
439 | 0 | internal mutating func updateNIOCertificateChain(to certificateChain: [NIOSSLCertificate]) { |
440 | 0 | self.modifyingNIOConfiguration { |
441 | 0 | $0.configuration.certificateChain = certificateChain.map { .certificate($0) } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | 0 | internal mutating func updateNIOPrivateKey(to privateKey: NIOSSLPrivateKey) { |
446 | 0 | self.modifyingNIOConfiguration { |
447 | 0 | $0.configuration.privateKey = .privateKey(privateKey) |
448 | 0 | } |
449 | 0 | } |
450 | | |
451 | 0 | internal mutating func updateNIOTrustRoots(to trustRoots: NIOSSLTrustRoots) { |
452 | 0 | self.modifyingNIOConfiguration { |
453 | 0 | $0.configuration.trustRoots = trustRoots |
454 | 0 | } |
455 | 0 | } |
456 | | |
457 | | internal mutating func updateNIOCertificateVerification( |
458 | | to verification: CertificateVerification |
459 | 0 | ) { |
460 | 0 | self.modifyingNIOConfiguration { |
461 | 0 | $0.configuration.certificateVerification = verification |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | | internal mutating func updateNIOCustomVerificationCallback( |
466 | | to callback: @escaping NIOSSLCustomVerificationCallback |
467 | 0 | ) { |
468 | 0 | self.modifyingNIOConfiguration { |
469 | 0 | $0.customVerificationCallback = callback |
470 | 0 | } |
471 | 0 | } |
472 | | |
473 | 0 | private mutating func modifyingNIOConfiguration(_ modify: (inout NIOConfiguration) -> Void) { |
474 | 0 | switch self.backend { |
475 | 0 | case var .nio(configuration): |
476 | 0 | modify(&configuration) |
477 | 0 | self.backend = .nio(configuration) |
478 | | #if canImport(Network) |
479 | | case .network: |
480 | | preconditionFailure() |
481 | | #endif |
482 | 0 | } |
483 | 0 | } |
484 | | } |
485 | | #endif |
486 | | |
487 | | // MARK: - Network Backend |
488 | | |
489 | | #if canImport(Network) |
490 | | extension GRPCTLSConfiguration { |
491 | | internal struct NetworkConfiguration { |
492 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
493 | | internal var options: NWProtocolTLS.Options { |
494 | | get { |
495 | | return self._options as! NWProtocolTLS.Options |
496 | | } |
497 | | set { |
498 | | self._options = newValue |
499 | | } |
500 | | } |
501 | | |
502 | | /// Always a NWProtocolTLS.Options. |
503 | | /// |
504 | | /// This somewhat insane type-erasure is necessary because we need to availability-guard the NWProtocolTLS.Options |
505 | | /// (it isn't available in older SDKs), but we cannot have stored properties guarded by availability in this way, only |
506 | | /// computed ones. To that end, we have to erase the type and then un-erase it. This is fairly silly. |
507 | | private var _options: Any |
508 | | |
509 | | // This is set privately via `updateHostnameOverride(to:)` because we require availability |
510 | | // guards to update the value in the underlying `sec_protocol_options`. |
511 | | internal private(set) var hostnameOverride: String? |
512 | | |
513 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
514 | | init(options: NWProtocolTLS.Options, hostnameOverride: String?) { |
515 | | self._options = options |
516 | | self.hostnameOverride = hostnameOverride |
517 | | } |
518 | | |
519 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
520 | | internal mutating func updateHostnameOverride(to hostnameOverride: String) { |
521 | | self.hostnameOverride = hostnameOverride |
522 | | sec_protocol_options_set_tls_server_name( |
523 | | self.options.securityProtocolOptions, |
524 | | hostnameOverride |
525 | | ) |
526 | | } |
527 | | } |
528 | | |
529 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
530 | | public static func makeClientConfigurationBackedByNetworkFramework( |
531 | | identity: SecIdentity? = nil, |
532 | | hostnameOverride: String? = nil, |
533 | | verifyCallbackWithQueue: (sec_protocol_verify_t, DispatchQueue)? = nil |
534 | | ) -> GRPCTLSConfiguration { |
535 | | let options = NWProtocolTLS.Options() |
536 | | |
537 | | if let identity = identity { |
538 | | sec_protocol_options_set_local_identity( |
539 | | options.securityProtocolOptions, |
540 | | sec_identity_create(identity)! |
541 | | ) |
542 | | } |
543 | | |
544 | | if let hostnameOverride = hostnameOverride { |
545 | | sec_protocol_options_set_tls_server_name( |
546 | | options.securityProtocolOptions, |
547 | | hostnameOverride |
548 | | ) |
549 | | } |
550 | | |
551 | | if let verifyCallbackWithQueue = verifyCallbackWithQueue { |
552 | | sec_protocol_options_set_verify_block( |
553 | | options.securityProtocolOptions, |
554 | | verifyCallbackWithQueue.0, |
555 | | verifyCallbackWithQueue.1 |
556 | | ) |
557 | | } |
558 | | |
559 | | if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { |
560 | | sec_protocol_options_set_min_tls_protocol_version(options.securityProtocolOptions, .TLSv12) |
561 | | } else { |
562 | | sec_protocol_options_set_tls_min_version(options.securityProtocolOptions, .tlsProtocol12) |
563 | | } |
564 | | |
565 | | for `protocol` in GRPCApplicationProtocolIdentifier.client { |
566 | | sec_protocol_options_add_tls_application_protocol( |
567 | | options.securityProtocolOptions, |
568 | | `protocol` |
569 | | ) |
570 | | } |
571 | | |
572 | | return .makeClientConfigurationBackedByNetworkFramework( |
573 | | options: options, |
574 | | hostnameOverride: hostnameOverride |
575 | | ) |
576 | | } |
577 | | |
578 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
579 | | public static func makeClientConfigurationBackedByNetworkFramework( |
580 | | options: NWProtocolTLS.Options, |
581 | | hostnameOverride: String? = nil |
582 | | ) -> GRPCTLSConfiguration { |
583 | | let network = NetworkConfiguration(options: options, hostnameOverride: hostnameOverride) |
584 | | return GRPCTLSConfiguration(network: network) |
585 | | } |
586 | | |
587 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
588 | | public static func makeServerConfigurationBackedByNetworkFramework( |
589 | | identity: SecIdentity |
590 | | ) -> GRPCTLSConfiguration { |
591 | | let options = NWProtocolTLS.Options() |
592 | | |
593 | | sec_protocol_options_set_local_identity( |
594 | | options.securityProtocolOptions, |
595 | | sec_identity_create(identity)! |
596 | | ) |
597 | | |
598 | | if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) { |
599 | | sec_protocol_options_set_min_tls_protocol_version(options.securityProtocolOptions, .TLSv12) |
600 | | } else { |
601 | | sec_protocol_options_set_tls_min_version(options.securityProtocolOptions, .tlsProtocol12) |
602 | | } |
603 | | |
604 | | for `protocol` in GRPCApplicationProtocolIdentifier.server { |
605 | | sec_protocol_options_add_tls_application_protocol( |
606 | | options.securityProtocolOptions, |
607 | | `protocol` |
608 | | ) |
609 | | } |
610 | | |
611 | | return GRPCTLSConfiguration.makeServerConfigurationBackedByNetworkFramework(options: options) |
612 | | } |
613 | | |
614 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
615 | | public static func makeServerConfigurationBackedByNetworkFramework( |
616 | | options: NWProtocolTLS.Options |
617 | | ) -> GRPCTLSConfiguration { |
618 | | let network = NetworkConfiguration(options: options, hostnameOverride: nil) |
619 | | return GRPCTLSConfiguration(network: network) |
620 | | } |
621 | | |
622 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
623 | | internal mutating func updateNetworkLocalIdentity(to identity: SecIdentity) { |
624 | | self.modifyingNetworkConfiguration { |
625 | | sec_protocol_options_set_local_identity( |
626 | | $0.options.securityProtocolOptions, |
627 | | sec_identity_create(identity)! |
628 | | ) |
629 | | } |
630 | | } |
631 | | |
632 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
633 | | internal mutating func updateNetworkVerifyCallbackWithQueue( |
634 | | callback: @escaping sec_protocol_verify_t, |
635 | | queue: DispatchQueue |
636 | | ) { |
637 | | self.modifyingNetworkConfiguration { |
638 | | sec_protocol_options_set_verify_block( |
639 | | $0.options.securityProtocolOptions, |
640 | | callback, |
641 | | queue |
642 | | ) |
643 | | } |
644 | | } |
645 | | |
646 | | private mutating func modifyingNetworkConfiguration( |
647 | | _ modify: (inout NetworkConfiguration) -> Void |
648 | | ) { |
649 | | switch self.backend { |
650 | | case var .network(_configuration): |
651 | | modify(&_configuration) |
652 | | self.backend = .network(_configuration) |
653 | | #if canImport(NIOSSL) |
654 | | case .nio: |
655 | | preconditionFailure() |
656 | | #endif // canImport(NIOSSL) |
657 | | } |
658 | | } |
659 | | } |
660 | | #endif |
661 | | |
662 | | #if canImport(Network) |
663 | | extension GRPCTLSConfiguration { |
664 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
665 | | internal func applyNetworkTLSOptions( |
666 | | to bootstrap: NIOTSConnectionBootstrap |
667 | | ) -> NIOTSConnectionBootstrap { |
668 | | switch self.backend { |
669 | | case let .network(_configuration): |
670 | | return bootstrap.tlsOptions(_configuration.options) |
671 | | |
672 | | #if canImport(NIOSSL) |
673 | | case .nio: |
674 | | // We're using NIOSSL with Network.framework; that's okay and permitted for backwards |
675 | | // compatibility. |
676 | | return bootstrap |
677 | | #endif // canImport(NIOSSL) |
678 | | } |
679 | | } |
680 | | |
681 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
682 | | internal func applyNetworkTLSOptions( |
683 | | to bootstrap: NIOTSListenerBootstrap |
684 | | ) -> NIOTSListenerBootstrap { |
685 | | switch self.backend { |
686 | | case let .network(_configuration): |
687 | | return bootstrap.tlsOptions(_configuration.options) |
688 | | |
689 | | #if canImport(NIOSSL) |
690 | | case .nio: |
691 | | // We're using NIOSSL with Network.framework; that's okay and permitted for backwards |
692 | | // compatibility. |
693 | | return bootstrap |
694 | | #endif // canImport(NIOSSL) |
695 | | } |
696 | | } |
697 | | } |
698 | | |
699 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
700 | | extension NIOTSConnectionBootstrap { |
701 | | internal func tlsOptions( |
702 | | from _configuration: GRPCTLSConfiguration |
703 | | ) -> NIOTSConnectionBootstrap { |
704 | | return _configuration.applyNetworkTLSOptions(to: self) |
705 | | } |
706 | | } |
707 | | |
708 | | @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *) |
709 | | extension NIOTSListenerBootstrap { |
710 | | internal func tlsOptions( |
711 | | from _configuration: GRPCTLSConfiguration |
712 | | ) -> NIOTSListenerBootstrap { |
713 | | return _configuration.applyNetworkTLSOptions(to: self) |
714 | | } |
715 | | } |
716 | | #endif |