Specifying minimum TLS version for Network.framework framers

Hi,

I'm working on a Network.framework protocol using the C interface, for a wire protocol which starts plaintext then negotiates to a TLS connection, but I'm having problems specifying the minimum TLS version (on both Big Sur 11.2.3 and Monterey 12.1).

Thanks to some very handy answers (in particular, here and here, as well as the WWDC 2019 session 712), I've managed to get the plaintext portion working well. However, when I try to prepend a TLS connection to the framer, I seem to lose the ability to adjust the minimum TLS version. Since the server in question supports TLS versions 1.2 and 1.3 only, I'm getting the following error:

[boringssl] boringssl_context_handle_fatal_alert(1763) [C[C5.1:1]:1][0x11e8a5350] read alert, level: fatal, description: protocol version
[boringssl] boringssl_session_handshake_incomplete(90) [C[C5.1:1]:1][0x11e8a5350] SSL library error
[boringssl] boringssl_session_handshake_error_print(41) [C[C5.1:1]:1][0x11e8a5350] Error: 4320212808:error:1000042e:SSL routines:OPENSSL_internal:TLSV1_ALERT_PROTOCOL_VERSION:/AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/boringssl/boringssl-351.40.2/ssl/tls_record.cc:592:SSL alert number 70
[boringssl] nw_protocol_boringssl_handshake_negotiate_proceed(767) [C[C5.1:1]:1][0x11e8a5350] handshake failed at state 12288: not completed

This is despite the following code to establish the TLS options:

// This is called from inside the input handler, when we receive a packet instructing to change to TLS
- (void) configureTLS: (nw_framer_t) framer
{
	// Setup the TLS options:
	nw_protocol_options_t tlsOptions = nw_tls_create_options();
	
	// Set the various security options:
	sec_protocol_options_t secOptions = nw_tls_copy_sec_protocol_options( tlsOptions );
	sec_protocol_options_set_max_tls_protocol_version( secOptions, tls_protocol_version_TLSv13 );
	sec_protocol_options_set_min_tls_protocol_version( secOptions, tls_protocol_version_TLSv12 );
	sec_protocol_options_append_tls_ciphersuite_group( secOptions, tls_ciphersuite_group_default );
	sec_protocol_options_set_peer_authentication_required( secOptions, false );
	
	// Add TLS to the protocol stack:	
	nw_framer_prepend_application_protocol( framer, tlsOptions );
	
    // PRepare for use:
	nw_framer_pass_through_input( framer );
	nw_framer_pass_through_output( framer );
    nw_framer_mark_ready( framer );
}

I've sadly not seen any similar errors in my travels so far (other than this, which doesn't seem to help. Any hints how I can force the minimum TLS version?

Answered by simwilson in 702144022

It took half a day with Wireshark (as well as a bit of digging through the boringssl and server source code, and a whole lot of googling) in order to discover that it was my fault!

After my method -configureTLS: above was called, I was calling nw_framer_write_output_data(..) to send the next necessary packet in the server handshake once a TLS connection is established. This was somehow sending the data after the server's "Hello Retry Request, Change Cipher Spec" packet, rather than after the TLS handshake had completed, and thus was causing the invalid version problem (due to the server incorrectly interpreting the packet it received).

The solution: wait until the state changes to ready, then send my application's startup packet.

The "missing methods" nw_framer_pass_through_*() make the encryption/decryption of data through the TLS protocol seem quite magical, but now I'm left with raw data being output on the callback for nw_framer_set_output_handler(), since we now bypass my application protocol's input/output methods completely, so I might see if I can implement a second application protocol on top of the TLS protocol to handle this...

Thanks!

As far as I know, and it's been awhile since I've dug into this, NWConnection does not support upgrading a connection mid-stream to TLS. Now, as you mentioned, it looks like others have been able to implement this using other application protocol stacks, so there may be some advancement on this front. If you get stuck on this, open up a TSI and I can allocate some time into looking deeper into this situation.

Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Accepted Answer

It took half a day with Wireshark (as well as a bit of digging through the boringssl and server source code, and a whole lot of googling) in order to discover that it was my fault!

After my method -configureTLS: above was called, I was calling nw_framer_write_output_data(..) to send the next necessary packet in the server handshake once a TLS connection is established. This was somehow sending the data after the server's "Hello Retry Request, Change Cipher Spec" packet, rather than after the TLS handshake had completed, and thus was causing the invalid version problem (due to the server incorrectly interpreting the packet it received).

The solution: wait until the state changes to ready, then send my application's startup packet.

The "missing methods" nw_framer_pass_through_*() make the encryption/decryption of data through the TLS protocol seem quite magical, but now I'm left with raw data being output on the callback for nw_framer_set_output_handler(), since we now bypass my application protocol's input/output methods completely, so I might see if I can implement a second application protocol on top of the TLS protocol to handle this...

Thanks!

Specifying minimum TLS version for Network.framework framers
 
 
Q