diff --git a/src/crypto/tlsfmt.c b/src/crypto/tlsfmt.c index 91fd09abe..60aea534b 100644 --- a/src/crypto/tlsfmt.c +++ b/src/crypto/tlsfmt.c @@ -64,6 +64,10 @@ static const char * tls_map_name ( const uint8_t *map ) { return "KeyShareClientHello"; } else if ( map == tls_key_share_entry_map ) { return "KeyShareEntry"; + } else if ( map == tls_key_share_hello_retry_request_map ) { + return "KeyShareHelloRetryRequest"; + } else if ( map == tls_key_share_server_hello_map ) { + return "KeyShareServerHello"; } else if ( map == tls_max_fragment_length_map ) { return "MaxFragmentLength"; } else if ( map == tls_named_group_list_map ) { @@ -689,6 +693,7 @@ TLS_DESCR_MAPPING ( tls_client_hello ) = { TLS_VAR16 ( tls_client_hello, TLS_VERSION_BASE, suites ), TLS_VAR08 ( tls_client_hello, TLS_VERSION_BASE, compression ), TLS_EXT16 ( tls_client_hello, TLS_VERSION_BASE, ext ), + TLS_EXTND ( tls_client_hello, TLS_COOKIE, ext.cookie ), TLS_EXTND ( tls_client_hello, TLS_EXTENDED_MASTER_SECRET, ext.ems ), TLS_EXTND ( tls_client_hello, TLS_MAX_FRAGMENT_LENGTH, ext.frag ), TLS_EXTND ( tls_client_hello, TLS_NAMED_GROUP, ext.groups ), @@ -754,6 +759,20 @@ TLS_DESCR_MAPPING ( tls_key_share_entry ) = { TLS_EXTRA ( tls_key_share_entry, TLS_VERSION_BASE, next ), }; +/** KeyShareHelloRetryRequest descriptor mapping */ +TLS_DESCR_MAPPING ( tls_key_share_hello_retry_request ) = { + TLS_MAPSZ ( tls_key_share_hello_retry_request ), + TLS_FIXED ( tls_key_share_hello_retry_request, TLS_VERSION_BASE, + group ), +}; + +/** KeyShareServerHello descriptor mapping */ +TLS_DESCR_MAPPING ( tls_key_share_server_hello ) = { + TLS_MAPSZ ( tls_key_share_server_hello ), + TLS_FIXED ( tls_key_share_server_hello, TLS_VERSION_BASE, group ), + TLS_VAR16 ( tls_key_share_server_hello, TLS_VERSION_BASE, public ), +}; + /** MaxFragmentLength descriptor mapping */ TLS_DESCR_MAPPING ( tls_max_fragment_length ) = { TLS_MAPSZ ( tls_max_fragment_length ), @@ -799,6 +818,7 @@ TLS_DESCR_MAPPING ( tls_server_hello ) = { TLS_EXTND ( tls_server_hello, TLS_EXTENDED_MASTER_SECRET, ext.ems ), TLS_EXTND ( tls_server_hello, TLS_SUPPORTED_VERSIONS, ext.supver ), TLS_EXTND ( tls_server_hello, TLS_KEY_SHARE, ext.key ), + TLS_EXTND ( tls_server_hello, TLS_COOKIE, ext.cookie ), }; /** ServerHello descriptor mapping */ diff --git a/src/include/ipxe/tls.h b/src/include/ipxe/tls.h index 6a1363274..3a750d393 100644 --- a/src/include/ipxe/tls.h +++ b/src/include/ipxe/tls.h @@ -188,6 +188,9 @@ union tls_server_random { /* TLS supported versions extension */ #define TLS_SUPPORTED_VERSIONS 43 +/* TLS cookie extension */ +#define TLS_COOKIE 44 + /* TLS pre-shared key modes extension */ #define TLS_PSK_MODES 45 @@ -504,6 +507,8 @@ struct tls_connection { int extended_master_secret; /** Verification data */ struct tls_verify_data verify; + /** Cookie */ + struct tls_cursor cookie; /** Secure channel */ struct secure_channel channel; diff --git a/src/include/ipxe/tlsfmt.h b/src/include/ipxe/tlsfmt.h index e8096978e..7dc30b76e 100644 --- a/src/include/ipxe/tlsfmt.h +++ b/src/include/ipxe/tlsfmt.h @@ -428,6 +428,8 @@ struct tls_client_hello { struct { /** All extensions */ struct tls_cursor all; + /** Cookie extension */ + struct tls_cursor cookie; /** Extended master secret extension */ struct tls_cursor ems; /** Maximum fragment length extension */ @@ -522,6 +524,32 @@ struct tls_key_share_entry { struct tls_cursor next; }; +/** KeyShareHelloRetryRequest descriptor */ +struct tls_key_share_hello_retry_request { + /** Named group */ + uint16_t __attribute__ (( aligned ( 1 ) )) *group; +}; + +/** KeyShareServerHello descriptor */ +struct tls_key_share_server_hello { + /** Named group */ + uint16_t __attribute__ (( aligned ( 1 ) )) *group; + /** Public key */ + struct tls_cursor public; +}; + +/** KeyShareServerHello/KeyShareHelloRetryRequest combined descriptor */ +union tls_key_share_server { + /** Named group (present in both) */ + uint16_t __attribute__ (( aligned ( 1 ) )) *group; + /** KeyShareHelloRetryRequest descriptor */ + struct tls_key_share_hello_retry_request hrr; + /** KeyShareServerHello descriptor */ + struct tls_key_share_server_hello hello; + /** Raw pointer/length array */ + union tls_ptr_len desc[0]; +}; + /** MaxFragmentLength descriptor */ struct tls_max_fragment_length { /** Maximum fragment length */ @@ -593,6 +621,8 @@ struct tls_server_hello { struct tls_cursor supver; /** Key share extension */ struct tls_cursor key; + /** Cookie extension */ + struct tls_cursor cookie; } ext; }; @@ -762,6 +792,8 @@ extern TLS_DESCR_MAPPING ( tls_extension ); extern TLS_DESCR_MAPPING ( tls_hello_request ); extern TLS_DESCR_MAPPING ( tls_key_share_client_hello ); extern TLS_DESCR_MAPPING ( tls_key_share_entry ); +extern TLS_DESCR_MAPPING ( tls_key_share_hello_retry_request ); +extern TLS_DESCR_MAPPING ( tls_key_share_server_hello ); extern TLS_DESCR_MAPPING ( tls_max_fragment_length ); extern TLS_DESCR_MAPPING ( tls_named_group_list ); extern TLS_DESCR_MAPPING ( tls_new_session_ticket ); diff --git a/src/net/tls.c b/src/net/tls.c index aa22f7c4e..6bbc2359d 100644 --- a/src/net/tls.c +++ b/src/net/tls.c @@ -186,14 +186,14 @@ FILE_SECBOOT ( PERMITTED ); #define EINFO_EPROTO_CIPHER_CHANGE \ __einfo_uniqify ( EINFO_EPROTO, 0x02, \ "Illegal cipher change" ) -#define EPROTO_KEY_SHARE __einfo_error ( EINFO_EPROTO_KEY_SHARE ) -#define EINFO_EPROTO_KEY_SHARE \ - __einfo_uniqify ( EINFO_EPROTO, 0x03, \ - "Multiple key shares offered" ) #define EPROTO_VALIDATION __einfo_error ( EINFO_EPROTO_VALIDATION ) #define EINFO_EPROTO_VALIDATION \ __einfo_uniqify ( EINFO_EPROTO, 0x04, \ "Certificate validation already in progress" ) +#define EPROTO_RETRY __einfo_error ( EINFO_EPROTO_RETRY ) +#define EINFO_EPROTO_RETRY \ + __einfo_uniqify ( EINFO_EPROTO, 0x05, \ + "Illegal retry request" ) /* Avoid dragging in RSA support unconditionally */ struct pubkey_algorithm rsa_algorithm __attribute__ (( weak )); @@ -201,6 +201,19 @@ struct pubkey_algorithm rsa_algorithm __attribute__ (( weak )); /** List of TLS session */ static LIST_HEAD ( tls_sessions ); +/** ServerHello downgrade magic value */ +static const uint8_t tls_downgrade_magic[7] = TLS_SERVER_DOWNGRADE_MAGIC; + +/** HelloRetryRequest magic value */ +static const struct tls_random tls_hrr_magic = { + .bytes = { + 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 + }, +}; + static void tls_tx_resume_all ( struct tls_session *session ); static struct io_buffer * tls_alloc_iob ( struct tls_connection *tls, size_t len ); @@ -407,6 +420,7 @@ static void free_tls ( struct refcnt *refcnt ) { zfree ( tls->new_ticket.data ); tls_clear_digest ( tls ); zfree ( tls->verify.dynamic ); + zfree ( tls->cookie.data ); tls_clear_cipher ( tls, &tls->tx.cipherspec ); tls_clear_cipher ( tls, &tls->rx.cipherspec ); list_for_each_entry_safe ( iobuf, tmp, &tls->rx.data, list ) { @@ -2058,6 +2072,10 @@ static int tls_client_hello ( struct tls_connection *tls, /* Prepare MaxFragmentLength extension */ ext->frag.len = sizeof ( *frag ); + /* Prepare Cookie extension */ + ext->cookie.data = tls->cookie.data; + ext->cookie.len = tls->cookie.len; + /* Prepare RenegotiationInfo extension */ reneg->verify.data = tls->verify.client; reneg->verify.len = tls->verify.len; @@ -2680,17 +2698,18 @@ static int tls_new_hello_request ( struct tls_connection *tls, */ static int tls_new_server_hello ( struct tls_connection *tls, const struct tls_cursor *cursor ) { - static const uint8_t downgrade_magic[7] = TLS_SERVER_DOWNGRADE_MAGIC; struct tls_session *session = tls->session; struct tls_named_group *group; struct tls_server_hello hello; struct tls_renegotiation_info reneg; struct tls_supported_version supver; - struct tls_key_share_entry key; + union tls_key_share_server key; union tls_server_random *random; + const uint8_t *key_map; uint16_t version; uint16_t suite; size_t verify_len; + int retry; int rc; /* Parse ServerHello structure */ @@ -2720,16 +2739,22 @@ static int tls_new_server_hello ( struct tls_connection *tls, } /* Parse KeyShareEntry, if present */ - if ( ( rc = tls_parse_opt ( tls_key_share_entry, tls->version, - &hello.ext.key, &key ) ) != 0 ) { - DBGC ( tls, "TLS %p could not parse KeyShareEntry: %s\n", - tls, strerror ( rc ) ); + retry = ( memcmp ( random, &tls_hrr_magic, + sizeof ( *random ) ) == 0 ); + key_map = ( retry ? tls_key_share_hello_retry_request_map : + tls_key_share_server_hello_map ); + if ( ( rc = tls_parse_opt_map ( key_map, tls->version, + &hello.ext.key, key.desc ) ) != 0 ) { + DBGC ( tls, "TLS %p could not parse KeyShare%s: %s\n", + tls, ( retry ? "HelloRetryRequest" : "ServerHello" ), + strerror ( rc ) ); return rc; } - if ( key.next.len ) { - DBGC ( tls, "TLS %p has multiple KeyShareEntry structures\n", - tls ); - return -EPROTO_KEY_SHARE; + + /* Refuse repeated retries */ + if ( retry && ( tls->suite != &tls_cipher_suite_null ) ) { + DBGC ( tls, "TLS %p refusing repeated retry request\n", tls ); + return -EPROTO_RETRY; } /* Handle secure renegotiation */ @@ -2767,7 +2792,7 @@ static int tls_new_server_hello ( struct tls_connection *tls, /* Check for downgrade attacks */ if ( ( TLS_VERSION_TLS_1_1 < TLS_VERSION_MAX ) && ( tls->version < TLS_VERSION_MAX ) && - ( memcmp ( random->downgrade.magic, downgrade_magic, + ( memcmp ( random->downgrade.magic, tls_downgrade_magic, sizeof ( random->downgrade.magic ) ) == 0 ) && ( ( random->downgrade.version + TLS_VERSION_TLS_1_1 ) < TLS_VERSION_MAX ) ) { @@ -2823,14 +2848,26 @@ static int tls_new_server_hello ( struct tls_connection *tls, } /* Agree shared key, if applicable */ - if ( key.public.data && + if ( ( ! retry ) && key.hello.public.data && ( ( rc = tls_key_agree ( tls, tls->group, - &key.public ) ) != 0 ) ) { + &key.hello.public ) ) != 0 ) ) { return rc; } + /* Record cookie, if any */ + if ( ( rc = tls_copy ( &hello.ext.cookie, &tls->cookie ) ) != 0 ) + return rc; + + /* Retry ClientHello , if applicable */ + if ( is_pending ( &tls->client.negotiation ) && retry ) { + tlskey_message ( &tls->key ); + tls->tx.pending = TLS_TX_CLIENT_HELLO; + tls_tx_resume ( tls ); + DBGC ( tls, "TLS %p retrying hello\n", tls ); + } + /* Schedule change to handshake traffic keys, if applicable */ - if ( tls_version ( tls, TLS_VERSION_TLS_1_3 ) ) { + if ( tls_version ( tls, TLS_VERSION_TLS_1_3 ) && ( ! retry ) ) { tls->tx.cipherspec.pending = &tls_handshake; tls->rx.cipherspec.pending = &tls_handshake; } diff --git a/src/tests/tlsfmt_test.c b/src/tests/tlsfmt_test.c index 46088c5f7..76c024c81 100644 --- a/src/tests/tlsfmt_test.c +++ b/src/tests/tlsfmt_test.c @@ -450,6 +450,8 @@ static void tlsfmt_test_exec ( void ) { map_ok ( tls_hello_request ); map_ok ( tls_key_share_client_hello ); map_ok ( tls_key_share_entry ); + map_ok ( tls_key_share_hello_retry_request ); + map_ok ( tls_key_share_server_hello ); map_ok ( tls_max_fragment_length ); map_ok ( tls_named_group_list ); map_ok ( tls_new_session_ticket );