GRPCNetworkFrameworkTests.swift 6.9 KB

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