GRPCNetworkFrameworkTests.swift 6.9 KB

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