how to use NWParameters.tls

I been looking at Network api for apple and was able to setup web socket connection with tcp. I believe this creates ws:// instead of wss://. So I have been looking at tls connection but having a trouble figuring out how to set up NWParameters.tls. my goal is to create wss:// web socket server on MacOS.

my init for my server is

self.port = NWEndpoint.Port(rawValue: port)!
sec_protocol_options_append_tls_ciphersuite(NWProtocolTLS.Options().securityProtocolOptions,
tls_ciphersuite_t(rawValue:mUInt16((TLS_PSK_WITH_AES_128_GCM_SHA256)))!)
parameters = NWParameters.tls
parameters.allowLocalEndpointReuse = true
parameters.includePeerToPeer = true
let wsOptions = NWProtocolWebSocket.Options()
wsOptions.autoReplyPing = true    parameters.defaultProtocolStack.applicationProtocols.insert(wsOptions, at: 0)
listener = try! NWListener(using: parameters, on: self.port)

Using the tic-tac-toe example, I thought I was missing sec_protocol_options_append_tls_ciphersuite, but it doesn't allow me connecting to my server. Do I need authenticationKey convert it to HMAC.authenticationCode then get Dispatch Data and use sec_protocol_options_add_pre_shared_key? or am I missing different kind of certification? I am using https://www.piesocket.com/websocket-tester to test my connection currently.

Answered by ForumsContributor in
Accepted Answer

I created an example of the exact code you are looking for (WebSocket NWListener using TLS-PSK) in the sample project for Configuring a Wi-Fi Accessory to Join the User’s Network. Here is a small sample of this:

extension NWParameters {

    convenience init(psk: String, pskIdentity: String) {
        // Create parameters with custom TLS options (Default with TLS).
        self.init(tls: NWParameters.tlsOptions(psk: psk, pskIdentity: pskIdentity))

        // Use the WebSocket protocol.
        let webSocketOptions = NWProtocolWebSocket.Options()
        self.defaultProtocolStack.applicationProtocols.insert(webSocketOptions, at: 0)
    }

    /// Create TLS options with a preshared key and its identity to use on both NWListener and NWConnection.

    private static func tlsOptions(psk: String, pskIdentity: String) -> NWProtocolTLS.Options {

        let tlsOptions = NWProtocolTLS.Options()

        sec_protocol_options_set_min_tls_protocol_version(tlsOptions.securityProtocolOptions, .TLSv12)
        sec_protocol_options_append_tls_ciphersuite(
            tlsOptions.securityProtocolOptions,
            tls_ciphersuite_t(rawValue: UInt16(TLS_PSK_WITH_AES_128_GCM_SHA256))!
        )

        let pskData = Data(psk.utf8)
        let pskDispatchData = pskData.withUnsafeBytes { buf in
            DispatchData(bytes: buf)
        }

        let pskIdentityData = Data(pskIdentity.utf8)
        let pskIdentityDispatchData = pskIdentityData.withUnsafeBytes { buf in
            DispatchData(bytes: buf)
        }

        sec_protocol_options_add_pre_shared_key(
            tlsOptions.securityProtocolOptions,
            pskDispatchData as __DispatchData,
            pskIdentityDispatchData as __DispatchData
        )
        return tlsOptions
    }
}

I am currently trying to resolve Error: 4939879480:error:100000b8:SSL routines:OPENSSL_internal:NO_SHARED_CIPHER error. Am I missing an option here? any help would be appreciated.

   private static func tlsOptions(psk: String, pskIdentity: String,queue: DispatchQueue) -> NWProtocolTLS.Options {
        let tlsOptions = NWProtocolTLS.Options()

        let allowInsecure = true

        //   let pskData = Data(psk.utf8)

        let authenticationKey = SymmetricKey(data: psk.data(using: .utf8)!)

        var authenticationCode = HMAC<SHA256>.authenticationCode(for: "1234".data(using: .utf8)!, using: authenticationKey)

        let authenticationDispatchData = withUnsafeBytes(of: &authenticationCode) { (ptr: UnsafeRawBufferPointer) in

            DispatchData(bytes: ptr)

        }

        

        /*     let pskDispatchData = pskData.withUnsafeBytes { buf in

         DispatchData(bytes: buf)

         }*/

        

        let pskIdentityData = Data(pskIdentity.utf8)

        let pskIdentityDispatchData = pskIdentityData.withUnsafeBytes { buf in

            DispatchData(bytes: buf)

        }

        sec_protocol_options_set_min_tls_protocol_version(tlsOptions.securityProtocolOptions, .TLSv12)

        sec_protocol_options_append_tls_ciphersuite(

            tlsOptions.securityProtocolOptions,

            tls_ciphersuite_t(rawValue: UInt16(TLS_PSK_WITH_AES_128_CBC_SHA256))!

        )

        sec_protocol_options_append_tls_ciphersuite(

            tlsOptions.securityProtocolOptions,

            tls_ciphersuite_t(rawValue: UInt16(TLS_PSK_WITH_AES_128_GCM_SHA256))!

        )

        sec_protocol_options_append_tls_ciphersuite(

            tlsOptions.securityProtocolOptions,

            tls_ciphersuite_t(rawValue: UInt16(TLS_PSK_WITH_AES_256_CBC_SHA384))!

        )

        sec_protocol_options_append_tls_ciphersuite(

            tlsOptions.securityProtocolOptions,

            tls_ciphersuite_t(rawValue: UInt16(TLS_PSK_WITH_AES_256_GCM_SHA384))!

        )

        sec_protocol_options_set_verify_block(tlsOptions.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in

            

            let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()

            

            var error: CFError?

            if SecTrustEvaluateWithError(trust, &error) {

                sec_protocol_verify_complete(true)

            } else {

                if allowInsecure == true {

                    sec_protocol_verify_complete(true)

                } else {

                    sec_protocol_verify_complete(false)

                }

            }

            

        }, queue)

        sec_protocol_options_set_peer_authentication_required(tlsOptions.securityProtocolOptions, false)

        sec_protocol_options_add_pre_shared_key(

            tlsOptions.securityProtocolOptions,

            authenticationDispatchData as __DispatchData,

            pskIdentityDispatchData as __DispatchData

        )

        return tlsOptions

    }

I am currently trying to resolve Error: 4939879480:error:100000b8:SSL routines:OPENSSL_internal:NO_SHARED_CIPHER error.

One thing you could try here is taking a packet trace to see what the server is trying to negotiate. There is an entire block of cipher suites supported from RFC 5487 so maybe those have to be shuffled around a bit. As a side note, you'll also want to make sure that your server you are connecting to is using TLS 1.2 and not TLS 1.3. PSK support for TLS 1.3 uses a different set of ciphers and takes an entirely different code path through the TLS library than TLS 1.2 does, specifically regarding context callbacks versus early data. That is a whole other discussion though :-)

hello,

so I set max tls to 1.2 and trying to use terminal for packet trace. However, I never really did packet tracing in the past as I rarely dealt server side. I only studied fundamental network course in uni and I mostly worked front-end, so doing server related set up is very new to me. Can you give me an advise on how to search the packet that I need to be looking for? is there easier program that I can use other than using "sudo tcpdump -I en0 -n"? Maybe I am doing the packet trace wrong if I am running both app on the same MacOS?

I appreciate your help!

The error I get for server side app

2022-08-12 10:45:52.428775+0900 bhapticsPlayerSUI[2591:36563] [boringssl] boringssl_context_handle_fatal_alert(1967) [C6:2][0x108f0c9b0] write alert, level: fatal, description: handshake failure

2022-08-12 10:45:52.428873+0900 bhapticsPlayerSUI[2591:36563] [boringssl] boringssl_context_error_print(1957) [C6:2][0x108f0c9b0] Error: 4444958104:error:100000b8:SSL routines:OPENSSL_internal:NO_SHARED_CIPHER:/AppleInternal/Library/BuildRoots/20d6c351-ee94-11ec-bcaf-7247572f23b4/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/handshake_server.cc:841:

2022-08-12 10:45:52.429811+0900 bhapticsPlayerSUI[2591:36563] [boringssl] boringssl_session_handshake_incomplete(86) [C6:2][0x108f0c9b0] SSL library error

2022-08-12 10:45:52.429920+0900 bhapticsPlayerSUI[2591:36563] [boringssl] boringssl_session_handshake_error_print(41) [C6:2][0x108f0c9b0] Error: 4444958104:error:100000b8:SSL routines:OPENSSL_internal:NO_SHARED_CIPHER:/AppleInternal/Library/BuildRoots/20d6c351-ee94-11ec-bcaf-7247572f23b4/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/handshake_server.cc:841:

connection 5 did fail, error: -9858: Optional(handshake failed)

-9858: Optional(handshake failed)

error I get from client app

2022-08-12 10:45:52.428835+0900 SwiftWebSocketClient[3108:41786] [boringssl] boringssl_context_handle_fatal_alert(1967) [C1.1:2][0x13de06490] read alert, level: fatal, description: handshake failure

2022-08-12 10:45:52.429871+0900 SwiftWebSocketClient[3108:41786] [boringssl] boringssl_session_handshake_incomplete(86) [C1.1:2][0x13de06490] SSL library error

2022-08-12 10:45:52.429890+0900 SwiftWebSocketClient[3108:41786] [boringssl] boringssl_session_handshake_error_print(41) [C1.1:2][0x13de06490] Error: 5352022792:error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE:/AppleInternal/Library/BuildRoots/20d6c351-ee94-11ec-bcaf-7247572f23b4/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls_record.cc:594:SSL alert number 40

2022-08-12 10:45:52.429903+0900 SwiftWebSocketClient[3108:41786] [boringssl] boringssl_session_handshake_error_print(41) [C1.1:2][0x13de06490] Error: 5352022792:error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO:/AppleInternal/Library/BuildRoots/20d6c351-ee94-11ec-bcaf-7247572f23b4/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/handshake.cc:670:

2022-08-12 10:45:52.429913+0900 SwiftWebSocketClient[3108:41786] [boringssl] nw_protocol_boringssl_handshake_negotiate_proceed(757) [C1.1:2][0x13de06490] handshake failed at state 12288: not completed

2022-08-12 10:45:52.438117+0900 SwiftWebSocketClient[3108:41786] Connection 1: received failure notification

2022-08-12 10:45:52.438171+0900 SwiftWebSocketClient[3108:41786] Connection 1: failed to connect 3:-9824, reason -1

2022-08-12 10:45:52.438185+0900 SwiftWebSocketClient[3108:41786] Connection 1: encountered error(3:-9824)

2022-08-12 10:45:52.442545+0900 SwiftWebSocketClient[3108:41786] Task <870977F2-49BF-4B6A-91A4-2A5038C65020>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9824])

2022-08-12 10:45:52.443561+0900 SwiftWebSocketClient[3108:41792] Task <870977F2-49BF-4B6A-91A4-2A5038C65020>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={NSErrorFailingURLStringKey=wss://localhost:15882/, NSErrorFailingURLKey=wss://localhost:15882/, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _NSURLErrorRelatedURLSessionTaskErrorKey=(

    "LocalWebSocketTask <870977F2-49BF-4B6A-91A4-2A5038C65020>.<1>"

), _NSURLErrorFailingURLSessionTaskErrorKey=LocalWebSocketTask <870977F2-49BF-4B6A-91A4-2A5038C65020>.<1>, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made.}

is there easier program that I can use other than using "sudo tcpdump -I en0 -n"? 

Using tcpdump and then a packet analysis tool of your choice is the way to go here. I use Wireshark, for viewing the packets, but you'll have to use the tool that works for you. Also, checkout this article.

Regarding:

Maybe I am doing the packet trace wrong if I am running both app on the same MacOS?

So it sounds like the client and server are on the same machine, correct? If so, that helps a lot in terms of what TLS backend the server is using.

Regarding the logs, typically what happens is that your client initiates the TLS handshake and presents a set of ciphers (along with a lot of other things) that it can negotiate. The server takes a look at this and tries to decide upon which set of parameters it will negotiate for the handshake and it notifies the client with a server hello. In the case of PSK-TLS a few other things happen here and then encrypted data is exchanged. Now, when you see this on the client HANDSHAKE_FAILURE_ON_CLIENT_HELLO and then this on the server NO_SHARED_CIPHER this is a strong indication that the exchange of information for a cipher suite could not be agreed upon. This would be the first place to look. Try letting the TLS backend negotiate it's own cipher suite and do not pin a cipher to the context. Does that get you anywhere further?

Hello, Thanks for the replies

I been using Wireshark to check the packets.

I have a list of cipher suit that client is sending. Now the problem is, I am not certain how to do "TLS backend negotiate it's own cipher suite and do not pin a cipher to the context". I am not certain if my code is pinning a cipher to the context via sec_protocol_options_append_tls_ciphersuite. Would you elaborate this part further?

below is the client's cipher suit. this client is different from what I used on my macOS client. this was sent from Windows remotely from proper client that we have used.

Cipher Suites (31 suites)
    Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
    Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
    Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x009f)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
    Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
    Cipher Suite: TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xccaa)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x009e)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x006b)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 (0x0067)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x0039)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
    Cipher Suite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA (0x0033)
    Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
    Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
    Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)
    Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c)
    Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
    Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
    Cipher Suite: TLS_EMPTY_RENEGOTIATION_INFO_SCSV (0x00ff)

I am not certain how to do "TLS backend negotiate it's own cipher suite and do not pin a cipher to the context". I am not certain if my code is pinning a cipher to the context via sec_protocol_options_append_tls_ciphersuite. Would you elaborate this part further?

Sure, just don't set a cipher suite on either side of the connection and let BoringSSL do this work for you.

Regarding the list of ciphers you added; this looks like the problem in that there is no PSK-TLS cipher suites the client is sending to negotiate. Now, you had mentioned:

this was sent from Windows remotely from proper client that we have used.

I thought this was running both the client and server on the same machine? Is that not the case?

Here is the cipher of the client that is ran on my local machine.

    Cipher Suite: Reserved (GREASE) (0x0a0a)
    Cipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)
    Cipher Suite: TLS_AES_256_GCM_SHA384 (0x1302)
    Cipher Suite: TLS_CHACHA20_POLY1305_SHA256 (0x1303)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca9)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
    Cipher Suite: TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (0xc00a)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (0xc009)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
    Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
    Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
    Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
    Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
    Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
    Cipher Suite: TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA (0xc008)
    Cipher Suite: TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA (0xc012)
    Cipher Suite: TLS_RSA_WITH_3DES_EDE_CBC_SHA (0x000a)

I also see that ask is not included, so from my code, I commented out sec_protocol_options_add_pre_shared_key, but same no shared cipher error occurs.

how to use NWParameters.tls
 
 
Q