2
0

GRPCNetworkFrameworkTests.swift 7.0 KB

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