Config+TLS.swift 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Copyright 2024, 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(Network)
  17. public import GRPCNIOTransportCore
  18. public import Network
  19. private import struct Foundation.Data
  20. private import struct Foundation.URL
  21. extension HTTP2ServerTransport.TransportServices.Config {
  22. /// The security configuration for this connection.
  23. public struct TransportSecurity: Sendable {
  24. package enum Wrapped: Sendable {
  25. case plaintext
  26. case tls(TLS)
  27. }
  28. package let wrapped: Wrapped
  29. /// This connection is plaintext: no encryption will take place.
  30. public static let plaintext = Self(wrapped: .plaintext)
  31. /// This connection will use TLS.
  32. public static func tls(_ tls: TLS) -> Self {
  33. Self(wrapped: .tls(tls))
  34. }
  35. }
  36. public struct TLS: Sendable {
  37. /// How to verify the client certificate, if one is presented.
  38. public var clientCertificateVerification: TLSConfig.CertificateVerification
  39. /// The trust roots to be used when verifying client certificates.
  40. public var trustRoots: TLSConfig.TrustRootsSource
  41. /// Whether ALPN is required.
  42. ///
  43. /// If this is set to `true` but the client does not support ALPN, then the connection will be rejected.
  44. public var requireALPN: Bool
  45. /// A provider for the `SecIdentity` to be used when setting up TLS.
  46. public var identityProvider: @Sendable () throws -> SecIdentity
  47. /// Create a new HTTP2 NIO Transport Services transport TLS config.
  48. /// - Parameters:
  49. /// - clientCertificateVerification: How to verify the client certificate, if one is presented.
  50. /// - trustRoots: The trust roots to be used when verifying client certificates.
  51. /// - requireALPN: Whether ALPN is required.
  52. /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS.
  53. public init(
  54. clientCertificateVerification: TLSConfig.CertificateVerification,
  55. trustRoots: TLSConfig.TrustRootsSource,
  56. requireALPN: Bool,
  57. identityProvider: @Sendable @escaping () throws -> SecIdentity
  58. ) {
  59. self.clientCertificateVerification = clientCertificateVerification
  60. self.trustRoots = trustRoots
  61. self.requireALPN = requireALPN
  62. self.identityProvider = identityProvider
  63. }
  64. /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted:
  65. /// - `clientCertificateVerificationMode` equals `doNotVerify`
  66. /// - `trustRoots` equals `systemDefault`
  67. /// - `requireALPN` equals `false`
  68. ///
  69. /// - Parameters:
  70. /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS.
  71. /// - Returns: A new HTTP2 NIO Transport Services transport TLS config.
  72. public static func defaults(
  73. identityProvider: @Sendable @escaping () throws -> SecIdentity,
  74. configure: (_ config: inout Self) -> Void = { _ in }
  75. ) -> Self {
  76. var config = Self(
  77. clientCertificateVerification: .noVerification,
  78. trustRoots: .systemDefault,
  79. requireALPN: false,
  80. identityProvider: identityProvider
  81. )
  82. configure(&config)
  83. return config
  84. }
  85. /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted to match
  86. /// the requirements of mTLS:
  87. /// - `clientCertificateVerificationMode` equals `noHostnameVerification`
  88. /// - `trustRoots` equals `systemDefault`
  89. /// - `requireALPN` equals `false`
  90. ///
  91. /// - Parameters:
  92. /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS.
  93. /// - configure: A closure which allows you to modify the defaults before returning them.
  94. /// - Returns: A new HTTP2 NIO Transport Services transport TLS config.
  95. public static func mTLS(
  96. identityProvider: @Sendable @escaping () throws -> SecIdentity,
  97. configure: (_ config: inout Self) -> Void = { _ in }
  98. ) -> Self {
  99. var config = Self(
  100. clientCertificateVerification: .noHostnameVerification,
  101. trustRoots: .systemDefault,
  102. requireALPN: false,
  103. identityProvider: identityProvider
  104. )
  105. configure(&config)
  106. return config
  107. }
  108. }
  109. }
  110. extension HTTP2ClientTransport.TransportServices.Config {
  111. /// The security configuration for this connection.
  112. public struct TransportSecurity: Sendable {
  113. package enum Wrapped: Sendable {
  114. case plaintext
  115. case tls(TLS)
  116. }
  117. package let wrapped: Wrapped
  118. /// This connection is plaintext: no encryption will take place.
  119. public static let plaintext = Self(wrapped: .plaintext)
  120. /// This connection will use TLS.
  121. public static func tls(_ tls: TLS) -> Self {
  122. Self(wrapped: .tls(tls))
  123. }
  124. }
  125. public struct TLS: Sendable {
  126. /// How to verify the server certificate, if one is presented.
  127. public var serverCertificateVerification: TLSConfig.CertificateVerification
  128. /// The trust roots to be used when verifying server certificates.
  129. /// - Important: If specifying custom certificates, they must be DER-encoded X509 certificates.
  130. public var trustRoots: TLSConfig.TrustRootsSource
  131. /// An optional provider for the `SecIdentity` to be used when setting up TLS.
  132. public var identityProvider: (@Sendable () throws -> SecIdentity)?
  133. /// Create a new HTTP2 NIO Transport Services transport TLS config.
  134. /// - Parameters:
  135. /// - serverCertificateVerification: How to verify the server certificate, if one is presented.
  136. /// - trustRoots: The trust roots to be used when verifying server certificates.
  137. /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS.
  138. public init(
  139. serverCertificateVerification: TLSConfig.CertificateVerification,
  140. trustRoots: TLSConfig.TrustRootsSource,
  141. identityProvider: (@Sendable () throws -> SecIdentity)?
  142. ) {
  143. self.serverCertificateVerification = serverCertificateVerification
  144. self.trustRoots = trustRoots
  145. self.identityProvider = identityProvider
  146. }
  147. /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted:
  148. /// - `serverCertificateVerification` equals `fullVerification`
  149. /// - `trustRoots` equals `systemDefault`
  150. /// - `identityProvider` equals `nil`
  151. ///
  152. /// - Parameters:
  153. /// - configure: A closure which allows you to modify the defaults before returning them.
  154. /// - Returns: A new HTTP2 NIO Posix transport TLS config.
  155. public static func defaults(
  156. configure: (_ config: inout Self) -> Void = { _ in }
  157. ) -> Self {
  158. var config = Self(
  159. serverCertificateVerification: .fullVerification,
  160. trustRoots: .systemDefault,
  161. identityProvider: nil
  162. )
  163. configure(&config)
  164. return config
  165. }
  166. /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted:
  167. /// - `serverCertificateVerification` equals `fullVerification`
  168. /// - `trustRoots` equals `systemDefault`
  169. /// - `identityProvider` equals `nil`
  170. public static var defaults: Self { .defaults() }
  171. /// Create a new HTTP2 NIO Transport Services transport TLS config, with some values defaulted to match
  172. /// the requirements of mTLS:
  173. /// - `serverCertificateVerification` equals `fullVerification`
  174. /// - `trustRoots` equals `systemDefault`
  175. ///
  176. /// - Parameters:
  177. /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS.
  178. /// - configure: A closure which allows you to modify the defaults before returning them.
  179. /// - Returns: A new HTTP2 NIO Posix transport TLS config.
  180. public static func mTLS(
  181. identityProvider: @Sendable @escaping () throws -> SecIdentity,
  182. configure: (_ config: inout Self) -> Void = { _ in }
  183. ) -> Self {
  184. var config = Self(
  185. serverCertificateVerification: .fullVerification,
  186. trustRoots: .systemDefault,
  187. identityProvider: identityProvider
  188. )
  189. configure(&config)
  190. return config
  191. }
  192. }
  193. }
  194. extension NWProtocolTLS.Options {
  195. func setUpVerifyBlock(trustRootsSource: TLSConfig.TrustRootsSource) {
  196. if let (verifyQueue, verifyBlock) = trustRootsSource.makeTrustRootsConfig() {
  197. sec_protocol_options_set_verify_block(
  198. self.securityProtocolOptions,
  199. verifyBlock,
  200. verifyQueue
  201. )
  202. }
  203. }
  204. }
  205. extension TLSConfig.TrustRootsSource {
  206. internal func makeTrustRootsConfig() -> (DispatchQueue, sec_protocol_verify_t)? {
  207. switch self.wrapped {
  208. case .certificates(let certificates):
  209. let verifyQueue = DispatchQueue(label: "io.grpc.CertificateVerification")
  210. let verifyBlock: sec_protocol_verify_t = { (metadata, trust, verifyCompleteCallback) in
  211. let actualTrust = sec_trust_copy_ref(trust).takeRetainedValue()
  212. let customAnchors: [SecCertificate]
  213. do {
  214. customAnchors = try certificates.map { certificateSource in
  215. let certificateBytes: Data
  216. switch certificateSource.wrapped {
  217. case .file(let path, .der):
  218. certificateBytes = try Data(contentsOf: URL(filePath: path))
  219. case .bytes(let bytes, .der):
  220. certificateBytes = Data(bytes)
  221. case .file(_, let format), .bytes(_, let format):
  222. fatalError("Certificate format must be DER, but was \(format).")
  223. }
  224. guard let certificate = SecCertificateCreateWithData(nil, certificateBytes as CFData)
  225. else {
  226. fatalError("Certificate was not a valid DER-encoded X509 certificate.")
  227. }
  228. return certificate
  229. }
  230. } catch {
  231. verifyCompleteCallback(false)
  232. return
  233. }
  234. SecTrustSetAnchorCertificates(actualTrust, customAnchors as CFArray)
  235. SecTrustEvaluateAsyncWithError(actualTrust, verifyQueue) { _, trusted, _ in
  236. verifyCompleteCallback(trusted)
  237. }
  238. }
  239. return (verifyQueue, verifyBlock)
  240. case .systemDefault:
  241. return nil
  242. }
  243. }
  244. }
  245. #endif