Skip to content

Commit

Permalink
tls: issue HelloRetryRequest to work around missing key shares
Browse files Browse the repository at this point in the history
  • Loading branch information
riptl authored and ripatel-fd committed Dec 19, 2024
1 parent 2b890f9 commit 055cb19
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 14 deletions.
1 change: 0 additions & 1 deletion deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,6 @@ install_openssl () {
no-comp \
no-ct \
no-des \
no-dh \
no-dsa \
no-dtls \
no-dtls1-method \
Expand Down
2 changes: 1 addition & 1 deletion src/waltz/tls/Local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ endif
# Uncomment this to test against quictls. Upstream OpenSSL does not
# support this test.
#ifdef FD_HAS_OPENSSL
#$(call make-unit-test,test_tls_openssl,test_tls_openssl,fd_quic fd_tls fd_ballet fd_util)
#$(call make-unit-test,test_tls_openssl,test_tls_openssl,fd_quic fd_tls fd_ballet fd_util,-lssl -lcrypto)
#$(call run-unit-test,test_tls_openssl)
#endif
104 changes: 103 additions & 1 deletion src/waltz/tls/fd_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,91 @@ fd_tls_server_handshake( fd_tls_t const * server,
}
}

static long
fd_tls_server_hs_retry( fd_tls_t const * server,
fd_tls_estate_srv_t * handshake,
fd_tls_client_hello_t const * ch,
uchar const ch1_hash[32] ) {

if( FD_UNLIKELY( handshake->hello_retry ) ) {
/* Already retried but still no X25519 share */
return fd_tls_alert( &handshake->base, FD_TLS_ALERT_ILLEGAL_PARAMETER, FD_TLS_REASON_SENDMSG_FAIL );
}
handshake->hello_retry = 1;

/* Message buffer */
# define MSG_BUFSZ 512UL
uchar msg_buf[ MSG_BUFSZ ];

/* Transcript hasher (RetryHelloRequest variation)
https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.1 */
fd_sha256_t transcript; fd_sha256_init( &transcript );
uchar const transcript_prefix[] = { 254, 0x00, 0x00, 32 };
fd_sha256_append( &transcript, transcript_prefix, sizeof(transcript_prefix) );
fd_sha256_append( &transcript, ch1_hash, 32 );

/* Create HelloRetryRequest message */

ulong server_hello_sz;

do {
uchar * wire = msg_buf;
uchar * const wire_end = msg_buf + MSG_BUFSZ;

/* Leave space for message header */

void * hdr_ptr = wire;
wire += sizeof(fd_tls_msg_hdr_t);
fd_tls_msg_hdr_t hdr = { .type = FD_TLS_MSG_SERVER_HELLO };

/* Construct server hello */

fd_tls_server_hello_t sh = {
.cipher_suite = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256,
.key_share = { .has_x25519 = 1 },
.session_id = ch->session_id,
};
memcpy( sh.key_share.x25519, server->kex_public_key, 32UL );

/* Encode server hello */

long encode_res = fd_tls_encode_hello_retry_request( &sh, wire, (ulong)(wire_end-wire) );
if( FD_UNLIKELY( encode_res<0L ) )
return fd_tls_alert( &handshake->base, (uint)(-encode_res), FD_TLS_REASON_SH_ENCODE );
wire += (ulong)encode_res;

hdr.sz = fd_uint_to_tls_u24( (uint)encode_res );
fd_tls_encode_msg_hdr( &hdr, hdr_ptr, sizeof(fd_tls_msg_hdr_t) );
server_hello_sz = (ulong)(wire - msg_buf);
} while(0);

/* Call back with HelloRetryRequest */

if( FD_UNLIKELY( !server->sendmsg_fn(
handshake,
msg_buf, server_hello_sz,
FD_TLS_LEVEL_INITIAL,
/* flush */ 1 ) ) )
return fd_tls_alert( &handshake->base, FD_TLS_ALERT_INTERNAL_ERROR, FD_TLS_REASON_SENDMSG_FAIL );

/* Record HelloRetryRequest in transcript hash */

fd_sha256_append( &transcript, msg_buf, server_hello_sz );

/* Finish up ********************************************************/

/* Store transcript hash state */

fd_tls_transcript_store( &handshake->transcript, &transcript );

/* Done */

handshake->base.state = FD_TLS_HS_START;

# undef MSG_BUFSZ
return (long)0L;
}

/* fd_tls_server_hs_start is invoked in response to the initial
ClientHello. We send back several messages in response, including
- the ServerHello, completing cryptographic negotiation
Expand Down Expand Up @@ -335,7 +420,12 @@ fd_tls_server_hs_start( fd_tls_t const * const server,
uchar msg_buf[ MSG_BUFSZ ];

/* Transcript hasher */
fd_sha256_t transcript; fd_sha256_init( &transcript );
fd_sha256_t transcript;
if( handshake->hello_retry ) {
fd_tls_transcript_load( &handshake->transcript, &transcript );
} else {
fd_sha256_init( &transcript );
}

/* Read client hello ************************************************/

Expand Down Expand Up @@ -405,6 +495,16 @@ fd_tls_server_hs_start( fd_tls_t const * const server,

fd_sha256_append( &transcript, record, read_sz );

/* Retry if key share is missing */

if( !ch.key_share.has_x25519 ) {
uchar ch1_hash[ 32 ];
fd_sha256_fini( &transcript, ch1_hash );
long rc = fd_tls_server_hs_retry( server, handshake, &ch, ch1_hash );
/**/ rc = fd_long_if( rc>=0, (long)read_sz, rc );
return rc;
}

/* Respond with server hello ****************************************/

/* Create server random */
Expand Down Expand Up @@ -1699,6 +1799,8 @@ fd_tls_reason_cstr( uint reason ) {
return "unsupported cryptographic parameters (fd_tls only supports TLS 1.3, X25519, Ed25519, AES-128-GCM)";
case FD_TLS_REASON_CH_NO_QUIC:
return "client does not support QUIC (missing QUIC transport params)";
case FD_TLS_REASON_CH_RETRY_KS:
return "client didn't provide an X25519 key share even after a RetryHelloRequest";
case FD_TLS_REASON_X25519_FAIL:
return "X25519 key exchange failed";
case FD_TLS_REASON_NO_X509:
Expand Down
1 change: 1 addition & 0 deletions src/waltz/tls/fd_tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ typedef struct fd_tls fd_tls_t;
#define FD_TLS_REASON_CH_ENCODE (104) /* failed to encode ClientHello */
#define FD_TLS_REASON_CH_CRYPTO_NEG (105) /* ClientHello crypto negotiation failed */
#define FD_TLS_REASON_CH_NO_QUIC (106) /* Missing QUIC transport params in ClientHello */
#define FD_TLS_REASON_CH_RETRY_KS (107) /* ClientHello still missing key share after a retry */

#define FD_TLS_REASON_SH_EXPECTED (201) /* wanted ServerHello, got another msg type */
#define FD_TLS_REASON_SH_PARSE (203) /* failed to parse ServerHello */
Expand Down
1 change: 1 addition & 0 deletions src/waltz/tls/fd_tls_estate.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ struct fd_tls_estate_srv {
uchar server_cert_rpk : 1; /* 0: X.509 1: raw public key */
uchar client_cert : 1; /* 0: no client auth 1: client cert */
uchar client_cert_rpk : 1; /* 0: X.509 1: raw public key */
uchar hello_retry : 1;

fd_tls_transcript_t transcript;
uchar client_hs_secret[32];
Expand Down
65 changes: 58 additions & 7 deletions src/waltz/tls/fd_tls_proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

typedef struct fd_tls_u24 tls_u24; /* code generator helper */

/* hello_retry_magic is the RFC 8446 hardcoded value of the 'random' field of a RetryHelloRequest */
static uchar const hello_retry_magic[ 32 ] =
{ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C };

#define FD_TLS_ENCODE_EXT_BEGIN( type ) \
do { \
int valid = 1; \
Expand Down Expand Up @@ -282,14 +289,9 @@ fd_tls_decode_server_hello( fd_tls_server_hello_t * out,
if( FD_UNLIKELY( cipher_suite != FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256 ) )
return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;

/* Middlebox compatibility for HelloRetryRequest */
/* Reject HelloRetryRequest (we only support X25519) */

static uchar const special_random[ 32 ] =
{ 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11,
0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91,
0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E,
0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C };
if( FD_UNLIKELY( 0==memcmp( out->random, special_random, 32 ) ) )
if( FD_UNLIKELY( 0==memcmp( out->random, hello_retry_magic, 32 ) ) )
return -(long)FD_TLS_ALERT_ILLEGAL_PARAMETER;

/* Read extensions */
Expand Down Expand Up @@ -408,6 +410,55 @@ fd_tls_encode_server_hello( fd_tls_server_hello_t const * in,
return (long)( wire_laddr - (ulong)wire );
}

long
fd_tls_encode_hello_retry_request( fd_tls_server_hello_t const * in,
uchar * wire,
ulong wire_sz ) {

ulong wire_laddr = (ulong)wire;

ushort legacy_version = FD_TLS_VERSION_TLS12;
uchar legacy_session_id_sz = (uchar)in->session_id.bufsz;
ushort cipher_suite = FD_TLS_CIPHER_SUITE_AES_128_GCM_SHA256;
uchar legacy_compression_method = 0;

# define FIELDS( FIELD ) \
FIELD( 0, &legacy_version, ushort, 1 ) \
FIELD( 1, hello_retry_magic, uchar, 32UL ) \
FIELD( 2, &legacy_session_id_sz, uchar, 1 ) \
FIELD( 3, in->session_id.buf, uchar, legacy_session_id_sz ) \
FIELD( 4, &cipher_suite, ushort, 1 ) \
FIELD( 5, &legacy_compression_method, uchar, 1 )
FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
# undef FIELDS

/* Encode extensions */

ushort * extension_tot_sz = FD_TLS_SKIP_FIELD( ushort );
ulong extension_start = wire_laddr;

ushort ext_supported_versions_ext_type = FD_TLS_EXT_SUPPORTED_VERSIONS;
ushort ext_supported_versions[1] = { FD_TLS_VERSION_TLS13 };
ushort ext_supported_versions_ext_sz = sizeof(ext_supported_versions);

ushort ext_key_share_ext_type = FD_TLS_EXT_KEY_SHARE;
ushort ext_key_share_ext_sz = sizeof(ushort);
ushort ext_key_share_group = FD_TLS_GROUP_X25519;

# define FIELDS( FIELD ) \
FIELD( 0, &ext_supported_versions_ext_type, ushort, 1 ) \
FIELD( 1, &ext_supported_versions_ext_sz, ushort, 1 ) \
FIELD( 2, ext_supported_versions, ushort, 1 ) \
FIELD( 3, &ext_key_share_ext_type, ushort, 1 ) \
FIELD( 4, &ext_key_share_ext_sz, ushort, 1 ) \
FIELD( 5, &ext_key_share_group, ushort, 1 )
FD_TLS_ENCODE_STATIC_BATCH( FIELDS )
# undef FIELDS

*extension_tot_sz = fd_ushort_bswap( (ushort)( (ulong)wire_laddr - extension_start ) );
return (long)( wire_laddr - (ulong)wire );
}

long
fd_tls_decode_enc_ext( fd_tls_enc_ext_t * const out,
uchar const * const wire,
Expand Down
5 changes: 5 additions & 0 deletions src/waltz/tls/fd_tls_proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,11 @@ fd_tls_encode_server_hello( fd_tls_server_hello_t const * in,
uchar * wire,
ulong wire_sz );

long
fd_tls_encode_hello_retry_request( fd_tls_server_hello_t const * in,
uchar * wire,
ulong wire_sz );

long
fd_tls_decode_enc_ext( fd_tls_enc_ext_t * out,
uchar const * wire,
Expand Down
2 changes: 1 addition & 1 deletion src/waltz/tls/test_tls_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ fd_tls_test_sign( void * ctx ) {

/* Test record transport */

#define TEST_RECORD_BUFSZ (1024UL)
#define TEST_RECORD_BUFSZ (4096UL)
struct test_record {
uint level;
uchar buf[ TEST_RECORD_BUFSZ ];
Expand Down
15 changes: 12 additions & 3 deletions src/waltz/tls/test_tls_openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ _fdtls_sendmsg( void const * handshake,
ulong record_sz,
uint encryption_level,
int flush ) {
(void)handshake; (void)flush;
(void)handshake; (void)flush;
test_record_log( record, record_sz, !!_is_ossl_to_fd );
test_record_send( &_fdtls_out, encryption_level, record, record_sz );
return 1;
Expand Down Expand Up @@ -195,7 +195,7 @@ _ossl_info( SSL const * ssl,
int type,
int val ) {
(void)ssl; (void)type; (void)val;
FD_LOG_DEBUG(( "OpenSSL info: type=%#x val=%d", type, val ));
FD_LOG_DEBUG(( "OpenSSL info: type=%#x val=%d", (uint)type, val ));
if( (type&SSL_CB_LOOP)==SSL_CB_LOOP )
FD_LOG_INFO(( "OpenSSL state: %s", SSL_state_string_long( ssl ) ));
}
Expand Down Expand Up @@ -312,8 +312,13 @@ test_server( SSL_CTX * ctx ) {
int res = SSL_do_handshake( ssl );
FD_TEST( SSL_get_error( ssl, res )==SSL_ERROR_WANT_READ );

/* ServerHello, EncryptedExtensions, Certificate, CertificateVerify, server Finished */
/* RetryHelloRequest OR ServerHello, EncryptedExtensions, Certificate, CertificateVerify, server Finished */
_fd_server_respond( server, hs );
if( hs->base.state==FD_TLS_HS_START ) {
/* In case of RetryHelloRequest */
_ossl_respond( ssl );
_fd_server_respond( server, hs );
}

_ossl_respond( ssl );

Expand Down Expand Up @@ -475,6 +480,10 @@ main( int argc,
SSL_CTX_set_alpn_protos( ctx, (uchar const *)"\xasolana-tpu", 11UL );
SSL_CTX_set_alpn_select_cb( ctx, _ossl_alpn_select, NULL );

/* Test server with and without RetryHelloRequest */
FD_TEST( 1==SSL_CTX_set1_groups_list( ctx, "ffdhe8192:X25519" ) );
test_server( ctx );
FD_TEST( 1==SSL_CTX_set1_groups_list( ctx, "X25519" ) );
test_server( ctx );

/* Test client with and without cert */
Expand Down

0 comments on commit 055cb19

Please sign in to comment.