GRPCNetworkFrameworkTests.swift 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. #if canImport(Network)
  18. import Dispatch
  19. import EchoImplementation
  20. import EchoModel
  21. import GRPC
  22. import Network
  23. import NIOCore
  24. import NIOPosix
  25. import NIOSSL
  26. import NIOTransportServices
  27. import Security
  28. import XCTest
  29. @available(macOS 10.14, iOS 12.0, watchOS 6.0, tvOS 12.0, *)
  30. final class GRPCNetworkFrameworkTests: GRPCTestCase {
  31. private var server: Server!
  32. private var client: ClientConnection!
  33. private var identity: SecIdentity!
  34. private var pkcs12Bundle: NIOSSLPKCS12Bundle!
  35. private var tsGroup: NIOTSEventLoopGroup!
  36. private var group: MultiThreadedEventLoopGroup!
  37. private let queue = DispatchQueue(label: "io.grpc.verify-handshake")
  38. private static let p12bundleURL = URL(fileURLWithPath: #filePath)
  39. .deletingLastPathComponent() // (this file)
  40. .deletingLastPathComponent() // GRPCTests
  41. .deletingLastPathComponent() // Tests
  42. .appendingPathComponent("Sources")
  43. .appendingPathComponent("GRPCSampleData")
  44. .appendingPathComponent("bundle")
  45. .appendingPathExtension("p12")
  46. // Not really 'async' but there is no 'func setUp() throws' to override.
  47. override func setUp() async throws {
  48. try await super.setUp()
  49. self.tsGroup = NIOTSEventLoopGroup(loopCount: 1)
  50. self.group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
  51. self.identity = try self.loadIdentity()
  52. XCTAssertNotNil(
  53. self.identity,
  54. "Unable to load identity from '\(GRPCNetworkFrameworkTests.p12bundleURL)'"
  55. )
  56. self.pkcs12Bundle = try NIOSSLPKCS12Bundle(
  57. file: GRPCNetworkFrameworkTests.p12bundleURL.path,
  58. passphrase: "password".utf8
  59. )
  60. XCTAssertNotNil(
  61. self.pkcs12Bundle,
  62. "Unable to load PCKS12 bundle from '\(GRPCNetworkFrameworkTests.p12bundleURL)'"
  63. )
  64. }
  65. override func tearDown() {
  66. XCTAssertNoThrow(try self.client?.close().wait())
  67. XCTAssertNoThrow(try self.server?.close().wait())
  68. XCTAssertNoThrow(try self.group?.syncShutdownGracefully())
  69. XCTAssertNoThrow(try self.tsGroup?.syncShutdownGracefully())
  70. super.tearDown()
  71. }
  72. private func loadIdentity() throws -> SecIdentity? {
  73. let data = try Data(contentsOf: GRPCNetworkFrameworkTests.p12bundleURL)
  74. let options = [kSecImportExportPassphrase as String: "password"]
  75. var rawItems: CFArray?
  76. let status = SecPKCS12Import(data as CFData, options as CFDictionary, &rawItems)
  77. switch status {
  78. case errSecSuccess:
  79. ()
  80. case errSecInteractionNotAllowed:
  81. throw XCTSkip("Unable to import PKCS12 bundle: no interaction allowed")
  82. default:
  83. XCTFail("SecPKCS12Import: failed with status \(status)")
  84. return nil
  85. }
  86. let items = rawItems! as! [[String: Any]]
  87. return items.first?[kSecImportItemIdentity as String] as! SecIdentity?
  88. }
  89. private func doEchoGet() throws {
  90. let echo = Echo_EchoNIOClient(channel: self.client)
  91. let get = echo.get(.with { $0.text = "hello" })
  92. XCTAssertNoThrow(try get.response.wait())
  93. }
  94. private func startServer(_ builder: Server.Builder) throws {
  95. self.server =
  96. try builder
  97. .withServiceProviders([EchoProvider()])
  98. .withLogger(self.serverLogger)
  99. .bind(host: "127.0.0.1", port: 0)
  100. .wait()
  101. }
  102. private func startClient(_ builder: ClientConnection.Builder) {
  103. self.client =
  104. builder
  105. .withBackgroundActivityLogger(self.clientLogger)
  106. .withConnectionReestablishment(enabled: false)
  107. .connect(host: "127.0.0.1", port: self.server.channel.localAddress!.port!)
  108. }
  109. func testNetworkFrameworkServerWithNIOSSLClient() throws {
  110. let serverBuilder = Server.usingTLSBackedByNetworkFramework(
  111. on: self.tsGroup,
  112. with: self.identity
  113. )
  114. XCTAssertNoThrow(try self.startServer(serverBuilder))
  115. let clientBuilder = ClientConnection.usingTLSBackedByNIOSSL(on: self.group)
  116. .withTLS(serverHostnameOverride: "localhost")
  117. .withTLS(trustRoots: .certificates(self.pkcs12Bundle.certificateChain))
  118. self.startClient(clientBuilder)
  119. XCTAssertNoThrow(try self.doEchoGet())
  120. }
  121. func testNIOSSLServerOnMTELGWithNetworkFrameworkClient() throws {
  122. try self.doTestNIOSSLServerWithNetworkFrameworkClient(serverGroup: self.group)
  123. }
  124. func testNIOSSLServerOnNIOTSGroupWithNetworkFrameworkClient() throws {
  125. try self.doTestNIOSSLServerWithNetworkFrameworkClient(serverGroup: self.tsGroup)
  126. }
  127. func doTestNIOSSLServerWithNetworkFrameworkClient(serverGroup: EventLoopGroup) throws {
  128. let serverBuilder = Server.usingTLSBackedByNIOSSL(
  129. on: serverGroup,
  130. certificateChain: self.pkcs12Bundle.certificateChain,
  131. privateKey: self.pkcs12Bundle.privateKey
  132. )
  133. XCTAssertNoThrow(try self.startServer(serverBuilder))
  134. var certificate: SecCertificate?
  135. guard SecIdentityCopyCertificate(self.identity, &certificate) == errSecSuccess else {
  136. XCTFail("Unable to extract certificate from identity")
  137. return
  138. }
  139. let clientBuilder = ClientConnection.usingTLSBackedByNetworkFramework(on: self.tsGroup)
  140. .withTLS(serverHostnameOverride: "localhost")
  141. .withTLSHandshakeVerificationCallback(on: self.queue) { _, trust, verify in
  142. let actualTrust = sec_trust_copy_ref(trust).takeRetainedValue()
  143. SecTrustSetAnchorCertificates(actualTrust, [certificate!] as CFArray)
  144. SecTrustEvaluateAsyncWithError(actualTrust, self.queue) { _, valid, error in
  145. if let error = error {
  146. XCTFail("Trust evaluation error: \(error)")
  147. }
  148. verify(valid)
  149. }
  150. }
  151. self.startClient(clientBuilder)
  152. XCTAssertNoThrow(try self.doEchoGet())
  153. }
  154. func testNetworkFrameworkTLServerAndClient() throws {
  155. let serverBuilder = Server.usingTLSBackedByNetworkFramework(
  156. on: self.tsGroup,
  157. with: self.identity
  158. )
  159. XCTAssertNoThrow(try self.startServer(serverBuilder))
  160. var certificate: SecCertificate?
  161. guard SecIdentityCopyCertificate(self.identity, &certificate) == errSecSuccess else {
  162. XCTFail("Unable to extract certificate from identity")
  163. return
  164. }
  165. let clientBuilder = ClientConnection.usingTLSBackedByNetworkFramework(on: self.tsGroup)
  166. .withTLS(serverHostnameOverride: "localhost")
  167. .withTLSHandshakeVerificationCallback(on: self.queue) { _, trust, verify in
  168. let actualTrust = sec_trust_copy_ref(trust).takeRetainedValue()
  169. SecTrustSetAnchorCertificates(actualTrust, [certificate!] as CFArray)
  170. SecTrustEvaluateAsyncWithError(actualTrust, self.queue) { _, valid, error in
  171. if let error = error {
  172. XCTFail("Trust evaluation error: \(error)")
  173. }
  174. verify(valid)
  175. }
  176. }
  177. self.startClient(clientBuilder)
  178. XCTAssertNoThrow(try self.doEchoGet())
  179. }
  180. }
  181. #endif // canImport(Network)
  182. #endif // canImport(NIOSSL)