mirror of
https://github.com/ipxe/ipxe
synced 2026-10-07 15:05:42 +03:00
[tls] Add support for TLS version 1.3 HelloRetryRequest
When the server does not want to use the key exchange algorithm offered in our ClientHello, it may respond with a HelloRetryRequest that specifies a new key exchange algorithm. Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
+55
-18
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
Reference in New Issue
Block a user