Hello,
I was able to use the TicTackToe code base and modify it such that I have a toggle at the top of the screen that allows me to start / stop the NWBrowser and NWListener. I have it setup so when the browser finds another device it attempts to connect to it. I support N devices / connections. I am able to use the NWParameters extension that is in the TickTackToe game that uses a passcode and TLS. I am able to send messages between devices just fine. Here is what I used
extension NWParameters {
// Create parameters for use in PeerConnection and PeerListener.
convenience init(passcode: String) {
// Customize TCP options to enable keepalives.
let tcpOptions = NWProtocolTCP.Options()
tcpOptions.enableKeepalive = true
tcpOptions.keepaliveIdle = 2
// Create parameters with custom TLS and TCP options.
self.init(tls: NWParameters.tlsOptions(passcode: passcode), tcp: tcpOptions)
// Enable using a peer-to-peer link.
self.includePeerToPeer = true
}
// Create TLS options using a passcode to derive a preshared key.
private static func tlsOptions(passcode: String) -> NWProtocolTLS.Options {
let tlsOptions = NWProtocolTLS.Options()
let authenticationKey = SymmetricKey(data: passcode.data(using: .utf8)!)
let authenticationCode = HMAC<SHA256>.authenticationCode(for: "HI".data(using: .utf8)!, using: authenticationKey)
let authenticationDispatchData = authenticationCode.withUnsafeBytes {
DispatchData(bytes: $0)
}
sec_protocol_options_add_pre_shared_key(tlsOptions.securityProtocolOptions,
authenticationDispatchData as __DispatchData,
stringToDispatchData("HI")! as __DispatchData)
sec_protocol_options_append_tls_ciphersuite(tlsOptions.securityProtocolOptions,
tls_ciphersuite_t(rawValue: TLS_PSK_WITH_AES_128_GCM_SHA256)!)
return tlsOptions
}
// Create a utility function to encode strings as preshared key data.
private static func stringToDispatchData(_ string: String) -> DispatchData? {
guard let stringData = string.data(using: .utf8) else {
return nil
}
let dispatchData = stringData.withUnsafeBytes {
DispatchData(bytes: $0)
}
return dispatchData
}
}
When I try to modify it to use QUIC and TLS 1.3 like so
extension NWParameters {
// Create parameters for use in PeerConnection and PeerListener.
convenience init(psk: String) {
self.init(quic: NWParameters.quicOptions(psk: psk))
self.includePeerToPeer = true
}
private static func quicOptions(psk: String) -> NWProtocolQUIC.Options {
let quicOptions = NWProtocolQUIC.Options(alpn: ["h3"])
let authenticationKey = SymmetricKey(data: psk.data(using: .utf8)!)
let authenticationCode = HMAC<SHA256>.authenticationCode(for: "hello".data(using: .utf8)!, using: authenticationKey)
let authenticationDispatchData = authenticationCode.withUnsafeBytes {
DispatchData(bytes: $0)
}
sec_protocol_options_set_min_tls_protocol_version(quicOptions.securityProtocolOptions, .TLSv13)
sec_protocol_options_set_max_tls_protocol_version(quicOptions.securityProtocolOptions, .TLSv13)
sec_protocol_options_add_pre_shared_key(quicOptions.securityProtocolOptions,
authenticationDispatchData as __DispatchData,
stringToDispatchData("hello")! as __DispatchData)
sec_protocol_options_append_tls_ciphersuite(quicOptions.securityProtocolOptions,
tls_ciphersuite_t(rawValue: TLS_AES_128_GCM_SHA256)!)
sec_protocol_options_set_verify_block(quicOptions.securityProtocolOptions, { _, _, sec_protocol_verify_complete in
sec_protocol_verify_complete(true)
}, .main)
return quicOptions
}
// Create a utility function to encode strings as preshared key data.
private static func stringToDispatchData(_ string: String) -> DispatchData? {
guard let stringData = string.data(using: .utf8) else {
return nil
}
let dispatchData = stringData.withUnsafeBytes {
DispatchData(bytes: $0)
}
return dispatchData
}
}
I get the following errors in the console
boringssl_session_handshake_incomplete(241) [C3:1][0x109d0c600] SSL library error boringssl_session_handshake_error_print(44) [C3:1][0x109d0c600] Error: 4459057536:error:100000ae:SSL routines:OPENSSL_internal:NO_CERTIFICATE_SET:/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls13_server.cc:882: boringssl_session_handshake_incomplete(241) [C4:1][0x109d0d200] SSL library error boringssl_session_handshake_error_print(44) [C4:1][0x109d0d200] Error: 4459057536:error:100000ae:SSL routines:OPENSSL_internal:NO_CERTIFICATE_SET:/Library/Caches/com.apple.xbs/Sources/boringssl/ssl/tls13_server.cc:882: nw_endpoint_flow_failed_with_error [C3 fe80::1884:2662:90ca:b011%en0.65328 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], scoped, ipv4, dns, uses wifi)] already failing, returning nw_endpoint_flow_failed_with_error [C4 192.168.0.98:65396 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], scoped, ipv4, dns, uses wifi)] already failing, returning quic_crypto_connection_state_handler [C1:1] [2ae0263d7dc186c7-] TLS error -9858 (state failed) nw_connection_copy_connected_local_endpoint_block_invoke [C3] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection nw_connection_copy_connected_remote_endpoint_block_invoke [C3] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection nw_connection_copy_protocol_metadata_internal_block_invoke [C3] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection quic_crypto_connection_state_handler [C2:1] [84fdc1e910f59f0a-] TLS error -9858 (state failed) nw_connection_copy_connected_local_endpoint_block_invoke [C4] Client called nw_connection_copy_connected_local_endpoint on unconnected nw_connection nw_connection_copy_connected_remote_endpoint_block_invoke [C4] Client called nw_connection_copy_connected_remote_endpoint on unconnected nw_connection nw_connection_copy_protocol_metadata_internal_block_invoke [C4] Client called nw_connection_copy_protocol_metadata_internal on unconnected nw_connection
Am I missing some configuration? I noticed with the working code that uses TCP and TLS that there is an NWParameters initializer that accepts tls options and tcp option but there isnt one that accepts tls and quic.
Thank you for any help :)
The obvious path forward here won’t work. You’re trying to combine two features:
-
QUIC
-
TLS-PSK
The issue is that QUIC requires TLS 1.3 but we only support TLS-PSK with TLS 1.2.
Thus, to work with QUIC you need to use standard TLS, that is, your server must have a digital identity. It’s possible to do that in a peer-to-peer environment, but it’s not trivial.
The best path forward depends on the nature of your product and where you are in its development. If you’re just getting started and want to explore QUIC, I recommend that you hard code a digital identity into your server. That’ll get QUIC working, and allow you to verify that it offers the features that you need. If you then decide that you really do want to use QUIC, we can talk about how to generate a digital identity for your server.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"