How to set NWProtocolTLS handshake as server or client manually

When using Network framework, is it possible to set NWProtocolTLS behave like TLS Server or Client? In CFNetwork there is a kCFStreamSSLIsServer key which I could not find the same thing in Network.

I currently try to implement something like STARTTLS, both client and server side, after connection and some message, the client will behave like a TLS SERVER, and the connection in server(NWListener) will behave like a TLS CLIENT.

That's why i need to set something like kCFStreamSSLIsServer

In Swift-NIO, this can be easily implemented by adding a NIOSSLClientHandler or NIOSSLServerHandler

Below it's what I got currently based on another post in community

// main.swift
import Foundation
import Network
let params = NWParameters.tcp
let framer = STARTTLSFramer.options()

params.defaultProtocolStack.applicationProtocols = [framer]
let connection = NWConnection(
  host: .ipv4(IPv4Address("127.0.0.1")!), port: .init(integerLiteral: 8089), using: params)

connection.stateUpdateHandler = { newState in
  print("connection newState \(newState)")
}
connection.start(queue: .main)

RunLoop.main.run()
// STARTLSFramer.swift
import Foundation
import Network



final class STARTTLSFramer: NWProtocolFramerImplementation {
  static let label: String = "STARTTLSFramer"

  init(framer: NWProtocolFramer.Instance) {}

  func handleOutput(
    framer instance: NWProtocolFramer.Instance, message: NWProtocolFramer.Message,
    messageLength: Int, isComplete: Bool
  ) {
    fatalError()

  }

  func wakeup(framer instance: NWProtocolFramer.Instance) {
    fatalError()
  }

  func stop(framer instance: NWProtocolFramer.Instance) -> Bool { true }

  func cleanup(framer instance: NWProtocolFramer.Instance) {}

  func start(framer instance: NWProtocolFramer.Instance) -> NWProtocolFramer.StartResult {
    instance.writeOutput(data: Data("hello\n".utf8))
    return .willMarkReady
  }

  private var accumulated = Data()

  func doUpgrade(instance: NWProtocolFramer.Instance) {
    let tlsOptions = NWProtocolTLS.Options()

    sec_protocol_options_set_min_tls_protocol_version(tlsOptions.securityProtocolOptions, .TLSv12) 
    // load identity
    let secIdentity = createSecIdentity()!
    let identity = sec_identity_create(secIdentity)

    sec_protocol_options_set_local_identity(tlsOptions.securityProtocolOptions, identity!)
    try! instance.prependApplicationProtocol(options: tlsOptions)
    instance.passThroughOutput()
    instance.passThroughInput()
    instance.markReady()
  }

  func handleInput(framer instance: NWProtocolFramer.Instance) -> Int {
    repeat {
      let success = instance.parseInput(minimumIncompleteLength: 1, maximumLength: 2048) {
        buffer, _ in
        let count = buffer?.count ?? 0
        if let buffer {
          accumulated.append(contentsOf: buffer)
        }
        return count
      }
      if !success { break }
    } while true
    // some validation
    self.accumulated.removeAll()
    self.doUpgrade(instance: instance)

    return 0
  }

  static func options() -> NWProtocolFramer.Options {
    let startTLSDef = NWProtocolFramer.Definition(implementation: STARTTLSFramer.self)

    let result = NWProtocolFramer.Options(definition: startTLSDef)
    return result
  }

}

So you want to implement something STARTTLS-ish using Network framework on both the client and the server side?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@DTS Engineer Is there any progress about this ? Or simply can't be done by Network framework

Is there any progress about this?

I asked you a clarifying question:

So you want to implement something STARTTLS-ish using Network framework on both the client and the server side?

and you didn’t respond to that, so I’m a bit stuck.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@DTS Engineer

Yes, I want implement this with only Network framework if possible

The main problem for me is I can't control the TLS mode of connection in both side(server/client). In Node.js or CFNetwork there is a way to set the client connection as TLS Server for handshaking as described above.

I did post a comment in your reply. I guess something is wrong.

I did post a comment in your reply.

Oh, right. Sorry that I missed that. This is an ongoing DevForums… behaviour, meaning that it’s always best to reply as a reply. See Quinn’s Top Ten DevForums Tips for this and other tips.

Anyway, thanks for the clarification. I don’t see any way to make this work easily. While Network framework can be twisted into implementating STARTTLS, you’re correct in your assessment that there’s no TLS option to configure the client / server role.

Note I recommend that you file an enhancement request for that. It’s obscure, but I can see being kinda useful in other situations. Please make sure to include a detailed explanation of your requirements. Also, I’d appreciate you posting your bug number, just for the record.

As to a workaround, the only one I can think of is so clunky that I’m not sure it’s worth implementing; you’re probably be better off just using a different TLS implementation. Still, lemme explain…


I’m going to focus on the client side because that’s the trickier one. The basic idea is:

  1. Start a listener on the client with TLS configured.

  2. Make an outgoing connection.

  3. Use Network framework normally to do your setup on that connection.

  4. Once that’s done, and you want to start TLS, have your client make a connection to your local listener.

  5. And then forward traffic between the outgoing connection and the ‘incoming’ one.

You can play similar tricks with the server.

There are many drawbacks here:

  • It’s complex.

  • It’s definitely going to affect performance.

  • You have to be very careful to avoid security problems.

You’ll have to decide for yourself how those drawbacks compare to the drawbacks of carrying around your own TLS implementation.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

How to set NWProtocolTLS handshake as server or client manually
 
 
Q