GRPCTLSConfiguration.swift 24 KB

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