Coverage Report

Created: 2026-04-29 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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