The branch master has been updated via 395cc5cdbef001c9886719bd31dbe48bad839b5c (commit) via 9a5198808ae0dffd4459039bd3fc96fcfc3eeaf1 (commit) via 94ed2c6739754d13306fe510bb8bc19c2ad42749 (commit) via 5a8e54d9dc99dcc54b10e78ba0901e185fd2f77d (commit) via 323f212aa792904b7312d22f6107e9546a41faa4 (commit) via 2ee1271d8ff95d6a5036b37f7f03e1ae14436eeb (commit) via ef7daaf915d7e0b7b48027f9ac4d47493adef0bb (commit) via 0f1e51ea115beef8a5fdd80d5a6c13ee289f980a (commit) via c87386a2cd586368a61d86ede03319f910d050f4 (commit) via d7c42d71ba407a4b3c26ed58263ae225976bbac3 (commit) via bcec335856233cbcea4d96e3d43e1b43b8fe4182 (commit) via d6d0bcddd9e7e16f413b307df4256f349e1d02cf (commit) via b1834ad781ee445f5f580e5dcf4792b96ae08d1d (commit) via d2c27a28c068188c1bda5109d228d94f868d06af (commit) from 78e09b53a40729f5e99829ccc733b592bd22fea1 (commit)
- Log ----------------------------------------------------------------- commit 395cc5cdbef001c9886719bd31dbe48bad839b5c Author: Matt Caswell <m...@openssl.org> Date: Tue Nov 15 17:50:48 2016 +0000 Fix a typo in a comment Reviewed-by: Rich Salz <rs...@openssl.org> commit 9a5198808ae0dffd4459039bd3fc96fcfc3eeaf1 Author: Matt Caswell <m...@openssl.org> Date: Tue Nov 15 17:50:08 2016 +0000 Move getting the curvelist for client and server out of the loop No need to continually get the list of supported curves for the client and server. Just do it once. Reviewed-by: Rich Salz <rs...@openssl.org> commit 94ed2c6739754d13306fe510bb8bc19c2ad42749 Author: Matt Caswell <m...@openssl.org> Date: Mon Nov 14 14:53:31 2016 +0000 Fixed various style issues in the key_share code Numerous style issues as well as references to TLS1_3_VERSION instead of SSL_IS_TLS13(s) Reviewed-by: Rich Salz <rs...@openssl.org> commit 5a8e54d9dc99dcc54b10e78ba0901e185fd2f77d Author: Matt Caswell <m...@openssl.org> Date: Thu Nov 3 18:51:28 2016 +0000 Add some tests for the key_share extension Reviewed-by: Rich Salz <rs...@openssl.org> commit 323f212aa792904b7312d22f6107e9546a41faa4 Author: Matt Caswell <m...@openssl.org> Date: Fri Nov 4 09:49:16 2016 +0000 Check key_exchange data length is not 0 Reviewed-by: Rich Salz <rs...@openssl.org> commit 2ee1271d8ff95d6a5036b37f7f03e1ae14436eeb Author: Matt Caswell <m...@openssl.org> Date: Fri Nov 4 00:07:50 2016 +0000 Ensure the whole key_share extension is well formatted Reviewed-by: Rich Salz <rs...@openssl.org> commit ef7daaf915d7e0b7b48027f9ac4d47493adef0bb Author: Matt Caswell <m...@openssl.org> Date: Thu Nov 3 18:50:41 2016 +0000 Validate that the provided key_share is in supported_groups Reviewed-by: Rich Salz <rs...@openssl.org> commit 0f1e51ea115beef8a5fdd80d5a6c13ee289f980a Author: Matt Caswell <m...@openssl.org> Date: Wed Nov 2 15:03:56 2016 +0000 Start using the key_share data to derive the PMS The previous commits put in place the logic to exchange key_share data. We now need to do something with that information. In <= TLSv1.2 the equivalent of the key_share extension is the ServerKeyExchange and ClientKeyExchange messages. With key_share those two messages are no longer necessary. The commit removes the SKE and CKE messages from the TLSv1.3 state machine. TLSv1.3 is completely different to TLSv1.2 in the messages that it sends and the transitions that are allowed. Therefore, rather than extend the existing <=TLS1.2 state transition functions, we create a whole new set for TLSv1.3. Intially these are still based on the TLSv1.2 ones, but over time they will be amended. The new TLSv1.3 transitions remove SKE and CKE completely. There's also some cleanup for some stuff which is not relevant to TLSv1.3 and is easy to remove, e.g. the DTLS support (we're not doing DTLSv1.3 yet) and NPN. I also disable EXTMS for TLSv1.3. Using it was causing some added complexity, so rather than fix it I removed it, since eventually it will not be needed anyway. Reviewed-by: Rich Salz <rs...@openssl.org> commit c87386a2cd586368a61d86ede03319f910d050f4 Author: Matt Caswell <m...@openssl.org> Date: Thu Nov 3 15:05:27 2016 +0000 Add a TLS version consistency check during session resumption This is a temporary fix for while we are still using the old session resumption logic in the TLSv1.3 code. Due to differences in EXTMS support we can't resume a <=TLSv1.2 session in a TLSv1.3 connection (the EXTMS consistency check causes the connection to abort). This causes test failures. Ultimately we will rewrite the session resumption logic for TLSv1.3 so this problem will go away. But until then we need a quick fix to keep the tests happy. Reviewed-by: Rich Salz <rs...@openssl.org> commit d7c42d71ba407a4b3c26ed58263ae225976bbac3 Author: Matt Caswell <m...@openssl.org> Date: Tue Nov 1 14:09:19 2016 +0000 Add processing of the key_share received in the ServerHello Reviewed-by: Rich Salz <rs...@openssl.org> commit bcec335856233cbcea4d96e3d43e1b43b8fe4182 Author: Matt Caswell <m...@openssl.org> Date: Tue Nov 1 13:24:02 2016 +0000 Add key_share info to the ServerHello Reviewed-by: Rich Salz <rs...@openssl.org> commit d6d0bcddd9e7e16f413b307df4256f349e1d02cf Author: Matt Caswell <m...@openssl.org> Date: Tue Nov 1 11:17:10 2016 +0000 Update the trace code to know about the key_share extension Reviewed-by: Rich Salz <rs...@openssl.org> commit b1834ad781ee445f5f580e5dcf4792b96ae08d1d Author: Matt Caswell <m...@openssl.org> Date: Tue Nov 1 10:53:32 2016 +0000 Add the key_share processing to the server side At the moment the server doesn't yet do anything with this information. We still need to send the server's key_share info back to the client. That will happen in subsequent commits. Reviewed-by: Rich Salz <rs...@openssl.org> commit d2c27a28c068188c1bda5109d228d94f868d06af Author: Matt Caswell <m...@openssl.org> Date: Mon Oct 31 14:49:52 2016 +0000 Generate the key_shares extension on the client side In this commit we just generate the extension on the client side, but don't yet do anything with it. Subsequent commits, will add the server side capability. At the moment we hard code a single key_share. In the future we should make this configurable. Reviewed-by: Rich Salz <rs...@openssl.org> ----------------------------------------------------------------------- Summary of changes: include/openssl/ssl.h | 6 + include/openssl/tls1.h | 1 + ssl/s3_lib.c | 8 +- ssl/ssl_err.c | 8 + ssl/ssl_locl.h | 5 +- ssl/ssl_sess.c | 17 ++ ssl/statem/statem_clnt.c | 201 ++++++++++++++- ssl/statem/statem_srvr.c | 224 ++++++++++++++++- ssl/t1_enc.c | 8 +- ssl/t1_lib.c | 459 +++++++++++++++++++++++++++++++--- ssl/t1_trce.c | 32 ++- test/recipes/70-test_key_share.t | 326 ++++++++++++++++++++++++ test/recipes/70-test_sslsessiontick.t | 2 +- test/recipes/70-test_tlsextms.t | 50 ++-- test/recipes/80-test_ssl_new.t | 3 +- test/ssl-tests/08-npn.conf | 22 ++ test/ssl-tests/08-npn.conf.in | 33 ++- util/TLSProxy/Message.pm | 2 + 18 files changed, 1326 insertions(+), 81 deletions(-) create mode 100755 test/recipes/70-test_key_share.t diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index b61a992..66f7d8c 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2074,6 +2074,7 @@ int ERR_load_SSL_strings(void); /* Error codes for the SSL functions. */ /* Function codes. */ +# define SSL_F_ADD_CLIENT_KEY_SHARE_EXT 438 # define SSL_F_CHECK_SUITEB_CIPHER_LIST 331 # define SSL_F_CT_MOVE_SCTS 345 # define SSL_F_CT_STRICT 349 @@ -2099,10 +2100,13 @@ int ERR_load_SSL_strings(void); # define SSL_F_DTLS_GET_REASSEMBLED_MESSAGE 370 # define SSL_F_DTLS_PROCESS_HELLO_VERIFY 386 # define SSL_F_OPENSSL_INIT_SSL 342 +# define SSL_F_OSSL_STATEM_CLIENT13_READ_TRANSITION 436 # define SSL_F_OSSL_STATEM_CLIENT_CONSTRUCT_MESSAGE 430 # define SSL_F_OSSL_STATEM_CLIENT_READ_TRANSITION 417 +# define SSL_F_OSSL_STATEM_SERVER13_READ_TRANSITION 437 # define SSL_F_OSSL_STATEM_SERVER_CONSTRUCT_MESSAGE 431 # define SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION 418 +# define SSL_F_PROCESS_KEY_SHARE_EXT 439 # define SSL_F_READ_STATE_MACHINE 352 # define SSL_F_SSL3_CHANGE_CIPHER_STATE 129 # define SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM 130 @@ -2313,6 +2317,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_BAD_ECPOINT 306 # define SSL_R_BAD_HANDSHAKE_LENGTH 332 # define SSL_R_BAD_HELLO_REQUEST 105 +# define SSL_R_BAD_KEY_SHARE 108 # define SSL_R_BAD_LENGTH 271 # define SSL_R_BAD_PACKET_LENGTH 115 # define SSL_R_BAD_PROTOCOL_VERSION_NUMBER 116 @@ -2427,6 +2432,7 @@ int ERR_load_SSL_strings(void); # define SSL_R_NO_SHARED_CIPHER 193 # define SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS 376 # define SSL_R_NO_SRTP_PROFILES 359 +# define SSL_R_NO_SUITABLE_KEY_SHARE 101 # define SSL_R_NO_VALID_SCTS 216 # define SSL_R_NO_VERIFY_COOKIE_CALLBACK 403 # define SSL_R_NULL_SSL_CTX 195 diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h index 1fd5788..b2d3057 100644 --- a/include/openssl/tls1.h +++ b/include/openssl/tls1.h @@ -174,6 +174,7 @@ extern "C" { # define TLSEXT_TYPE_session_ticket 35 /* As defined for TLS1.3 */ +# define TLSEXT_TYPE_key_share 40 # define TLSEXT_TYPE_supported_versions 43 /* Temporary extension type */ diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c index 2439167..bcc0f9e 100644 --- a/ssl/s3_lib.c +++ b/ssl/s3_lib.c @@ -4068,7 +4068,7 @@ EVP_PKEY *ssl_generate_pkey_curve(int id) #endif /* Derive premaster or master secret for ECDH/DH */ -int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey) +int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, int genmaster) { int rv = 0; unsigned char *pms = NULL; @@ -4093,12 +4093,12 @@ int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey) if (EVP_PKEY_derive(pctx, pms, &pmslen) <= 0) goto err; - if (s->server) { - /* For server generate master secret and discard premaster */ + if (genmaster) { + /* Generate master secret and discard premaster */ rv = ssl_generate_master_secret(s, pms, pmslen, 1); pms = NULL; } else { - /* For client just save premaster secret */ + /* Save premaster secret */ s->s3->tmp.pms = pms; s->s3->tmp.pmslen = pmslen; pms = NULL; diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c index 6c438fe..235a53c 100644 --- a/ssl/ssl_err.c +++ b/ssl/ssl_err.c @@ -19,6 +19,7 @@ # define ERR_REASON(reason) ERR_PACK(ERR_LIB_SSL,0,reason) static ERR_STRING_DATA SSL_str_functs[] = { + {ERR_FUNC(SSL_F_ADD_CLIENT_KEY_SHARE_EXT), "add_client_key_share_ext"}, {ERR_FUNC(SSL_F_CHECK_SUITEB_CIPHER_LIST), "check_suiteb_cipher_list"}, {ERR_FUNC(SSL_F_CT_MOVE_SCTS), "ct_move_scts"}, {ERR_FUNC(SSL_F_CT_STRICT), "ct_strict"}, @@ -49,14 +50,19 @@ static ERR_STRING_DATA SSL_str_functs[] = { "dtls_get_reassembled_message"}, {ERR_FUNC(SSL_F_DTLS_PROCESS_HELLO_VERIFY), "dtls_process_hello_verify"}, {ERR_FUNC(SSL_F_OPENSSL_INIT_SSL), "OPENSSL_init_ssl"}, + {ERR_FUNC(SSL_F_OSSL_STATEM_CLIENT13_READ_TRANSITION), + "ossl_statem_client13_read_transition"}, {ERR_FUNC(SSL_F_OSSL_STATEM_CLIENT_CONSTRUCT_MESSAGE), "ossl_statem_client_construct_message"}, {ERR_FUNC(SSL_F_OSSL_STATEM_CLIENT_READ_TRANSITION), "ossl_statem_client_read_transition"}, + {ERR_FUNC(SSL_F_OSSL_STATEM_SERVER13_READ_TRANSITION), + "ossl_statem_server13_read_transition"}, {ERR_FUNC(SSL_F_OSSL_STATEM_SERVER_CONSTRUCT_MESSAGE), "ossl_statem_server_construct_message"}, {ERR_FUNC(SSL_F_OSSL_STATEM_SERVER_READ_TRANSITION), "ossl_statem_server_read_transition"}, + {ERR_FUNC(SSL_F_PROCESS_KEY_SHARE_EXT), "process_key_share_ext"}, {ERR_FUNC(SSL_F_READ_STATE_MACHINE), "read_state_machine"}, {ERR_FUNC(SSL_F_SSL3_CHANGE_CIPHER_STATE), "ssl3_change_cipher_state"}, {ERR_FUNC(SSL_F_SSL3_CHECK_CERT_AND_ALGORITHM), @@ -342,6 +348,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = { {ERR_REASON(SSL_R_BAD_ECPOINT), "bad ecpoint"}, {ERR_REASON(SSL_R_BAD_HANDSHAKE_LENGTH), "bad handshake length"}, {ERR_REASON(SSL_R_BAD_HELLO_REQUEST), "bad hello request"}, + {ERR_REASON(SSL_R_BAD_KEY_SHARE), "bad key share"}, {ERR_REASON(SSL_R_BAD_LENGTH), "bad length"}, {ERR_REASON(SSL_R_BAD_PACKET_LENGTH), "bad packet length"}, {ERR_REASON(SSL_R_BAD_PROTOCOL_VERSION_NUMBER), @@ -488,6 +495,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = { {ERR_REASON(SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS), "no shared signature algorithms"}, {ERR_REASON(SSL_R_NO_SRTP_PROFILES), "no srtp profiles"}, + {ERR_REASON(SSL_R_NO_SUITABLE_KEY_SHARE), "no suitable key share"}, {ERR_REASON(SSL_R_NO_VALID_SCTS), "no valid scts"}, {ERR_REASON(SSL_R_NO_VERIFY_COOKIE_CALLBACK), "no verify cookie callback"}, diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h index 105a487..88b2f8b 100644 --- a/ssl/ssl_locl.h +++ b/ssl/ssl_locl.h @@ -1299,6 +1299,8 @@ typedef struct ssl3_state_st { /* For clients: peer temporary key */ # if !defined(OPENSSL_NO_EC) || !defined(OPENSSL_NO_DH) + /* The group_id for the DH/ECDH key */ + unsigned int group_id; EVP_PKEY *peer_tmp; # endif @@ -1877,7 +1879,8 @@ __owur int ssl_fill_hello_random(SSL *s, int server, unsigned char *field, __owur int ssl_generate_master_secret(SSL *s, unsigned char *pms, size_t pmslen, int free_pms); __owur EVP_PKEY *ssl_generate_pkey(EVP_PKEY *pm); -__owur int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey); +__owur int ssl_derive(SSL *s, EVP_PKEY *privkey, EVP_PKEY *pubkey, + int genmaster); __owur EVP_PKEY *ssl_dh_to_pkey(DH *dh); __owur const SSL_CIPHER *ssl3_get_cipher_by_char(const unsigned char *p); diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index 825e706..47dbf85 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -588,6 +588,23 @@ int ssl_get_prev_session(SSL *s, CLIENTHELLO_MSG *hello) goto err; } + /* + * TODO(TLS1.3): This is temporary, because TLSv1.3 resumption is completely + * different. For now though we're still using the old resumption logic, so + * to avoid test failures we need this. Remove this code! + * + * Check TLS version consistency. We can't resume <=TLSv1.2 session if we + * have negotiated TLSv1.3, and vice versa. + */ + if (!SSL_IS_DTLS(s) + && ((ret->ssl_version <= TLS1_2_VERSION + && s->version >=TLS1_3_VERSION) + || (ret->ssl_version >= TLS1_3_VERSION + && s->version <= TLS1_2_VERSION))) { + /* Continue but do not resume */ + goto err; + } + /* Check extended master secret extension consistency */ if (ret->flags & SSL_SESS_FLAG_EXTMS) { /* If old session includes extms, but new does not: abort handshake */ diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index e0d53fe..f89d317 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -108,19 +108,135 @@ static int key_exchange_expected(SSL *s) /* * ossl_statem_client_read_transition() encapsulates the logic for the allowed + * handshake state transitions when a TLS1.3 client is reading messages from the + * server. The message type that the server has sent is provided in |mt|. The + * current state is in |s->statem.hand_state|. + * + * Return values are 1 for success (transition allowed) and 0 on error + * (transition not allowed) + */ +static int ossl_statem_client13_read_transition(SSL *s, int mt) +{ + OSSL_STATEM *st = &s->statem; + + /* + * TODO(TLS1.3): This is still based on the TLSv1.2 state machine. Over time + * we will update this to look more like real TLSv1.3 + */ + + /* + * Note: There is no case for TLS_ST_CW_CLNT_HELLO, because we haven't + * yet negotiated TLSv1.3 at that point so that is handled by + * ossl_statem_client_read_transition() + */ + + switch (st->hand_state) { + default: + break; + + case TLS_ST_CR_SRVR_HELLO: + if (s->hit) { + if (s->tlsext_ticket_expected) { + if (mt == SSL3_MT_NEWSESSION_TICKET) { + st->hand_state = TLS_ST_CR_SESSION_TICKET; + return 1; + } + } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_CR_CHANGE; + return 1; + } + } else { + if (mt == SSL3_MT_CERTIFICATE) { + st->hand_state = TLS_ST_CR_CERT; + return 1; + } + } + break; + + case TLS_ST_CR_CERT: + /* + * The CertificateStatus message is optional even if + * |tlsext_status_expected| is set + */ + if (s->tlsext_status_expected && mt == SSL3_MT_CERTIFICATE_STATUS) { + st->hand_state = TLS_ST_CR_CERT_STATUS; + return 1; + } + /* Fall through */ + + case TLS_ST_CR_CERT_STATUS: + if (mt == SSL3_MT_CERTIFICATE_REQUEST) { + if (cert_req_allowed(s)) { + st->hand_state = TLS_ST_CR_CERT_REQ; + return 1; + } + goto err; + } + /* Fall through */ + + case TLS_ST_CR_CERT_REQ: + if (mt == SSL3_MT_SERVER_DONE) { + st->hand_state = TLS_ST_CR_SRVR_DONE; + return 1; + } + break; + + case TLS_ST_CW_FINISHED: + if (s->tlsext_ticket_expected) { + if (mt == SSL3_MT_NEWSESSION_TICKET) { + st->hand_state = TLS_ST_CR_SESSION_TICKET; + return 1; + } + } else if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_CR_CHANGE; + return 1; + } + break; + + case TLS_ST_CR_SESSION_TICKET: + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_CR_CHANGE; + return 1; + } + break; + + case TLS_ST_CR_CHANGE: + if (mt == SSL3_MT_FINISHED) { + st->hand_state = TLS_ST_CR_FINISHED; + return 1; + } + break; + } + + err: + /* No valid transition found */ + ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_UNEXPECTED_MESSAGE); + SSLerr(SSL_F_OSSL_STATEM_CLIENT13_READ_TRANSITION, + SSL_R_UNEXPECTED_MESSAGE); + return 0; +} + +/* + * ossl_statem_client_read_transition() encapsulates the logic for the allowed * handshake state transitions when the client is reading messages from the * server. The message type that the server has sent is provided in |mt|. The * current state is in |s->statem.hand_state|. * - * Return values are: - * 1: Success (transition allowed) - * 0: Error (transition not allowed) + * Return values are 1 for success (transition allowed) and 0 on error + * (transition not allowed) */ int ossl_statem_client_read_transition(SSL *s, int mt) { OSSL_STATEM *st = &s->statem; int ske_expected; + /* + * Note that after a ClientHello we don't know what version we are going + * to negotiate yet, so we don't take this branch until later + */ + if (s->method->version == TLS1_3_VERSION) + return ossl_statem_client13_read_transition(s, mt); + switch (st->hand_state) { default: break; @@ -271,13 +387,83 @@ int ossl_statem_client_read_transition(SSL *s, int mt) } /* - * client_write_transition() works out what handshake state to move to next - * when the client is writing messages to be sent to the server. + * ossl_statem_client13_write_transition() works out what handshake state to + * move to next when the TLSv1.3 client is writing messages to be sent to the + * server. + */ +static WRITE_TRAN ossl_statem_client13_write_transition(SSL *s) +{ + OSSL_STATEM *st = &s->statem; + + /* + * TODO(TLS1.3): This is still based on the TLSv1.2 state machine. Over time + * we will update this to look more like real TLSv1.3 + */ + + /* + * Note: There are no cases for TLS_ST_BEFORE or TLS_ST_CW_CLNT_HELLO, + * because we haven't negotiated TLSv1.3 yet at that point. They are + * handled by ossl_statem_client_write_transition(). + */ + switch (st->hand_state) { + default: + /* Shouldn't happen */ + return WRITE_TRAN_ERROR; + + case TLS_ST_CR_SRVR_DONE: + st->hand_state = (s->s3->tmp.cert_req != 0) ? TLS_ST_CW_CERT + : TLS_ST_CW_CHANGE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_CERT: + /* If a non-empty Certificate we also send CertificateVerify */ + st->hand_state = (s->s3->tmp.cert_req == 1) ? TLS_ST_CW_CERT_VRFY + : TLS_ST_CW_CHANGE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_CERT_VRFY: + st->hand_state = TLS_ST_CW_CHANGE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_CHANGE: + st->hand_state = TLS_ST_CW_FINISHED; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_CW_FINISHED: + if (s->hit) { + st->hand_state = TLS_ST_OK; + ossl_statem_set_in_init(s, 0); + return WRITE_TRAN_CONTINUE; + } + return WRITE_TRAN_FINISHED; + + case TLS_ST_CR_FINISHED: + if (s->hit) { + st->hand_state = TLS_ST_CW_CHANGE; + return WRITE_TRAN_CONTINUE; + } + st->hand_state = TLS_ST_OK; + ossl_statem_set_in_init(s, 0); + return WRITE_TRAN_CONTINUE; + } +} + +/* + * ossl_statem_client_write_transition() works out what handshake state to + * move to next when the client is writing messages to be sent to the server. */ WRITE_TRAN ossl_statem_client_write_transition(SSL *s) { OSSL_STATEM *st = &s->statem; + /* + * Note that immediately before/after a ClientHello we don't know what + * version we are going to negotiate yet, so we don't take this branch until + * later + */ + if (s->method->version == TLS1_3_VERSION) + return ossl_statem_client13_write_transition(s); + switch (st->hand_state) { default: /* Shouldn't happen */ @@ -2282,7 +2468,7 @@ static int tls_construct_cke_dhe(SSL *s, WPACKET *pkt, int *al) ckey = ssl_generate_pkey(skey); dh_clnt = EVP_PKEY_get0_DH(ckey); - if (dh_clnt == NULL || ssl_derive(s, ckey, skey) == 0) + if (dh_clnt == NULL || ssl_derive(s, ckey, skey, 0) == 0) goto err; /* send off the data */ @@ -2318,7 +2504,7 @@ static int tls_construct_cke_ecdhe(SSL *s, WPACKET *pkt, int *al) ckey = ssl_generate_pkey(skey); - if (ssl_derive(s, ckey, skey) == 0) { + if (ssl_derive(s, ckey, skey, 0) == 0) { SSLerr(SSL_F_TLS_CONSTRUCT_CKE_ECDHE, ERR_R_EVP_LIB); goto err; } @@ -2626,6 +2812,7 @@ int tls_construct_client_verify(SSL *s, WPACKET *pkt) SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_VERIFY, ERR_R_INTERNAL_ERROR); goto err; } + if (SSL_USE_SIGALGS(s)&& !tls12_get_sigandhash(pkt, pkey, md)) { SSLerr(SSL_F_TLS_CONSTRUCT_CLIENT_VERIFY, ERR_R_INTERNAL_ERROR); goto err; diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index 142c637..3c4d6ee 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -68,19 +68,105 @@ static STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, int *al); /* - * server_read_transition() encapsulates the logic for the allowed handshake - * state transitions when the server is reading messages from the client. The - * message type that the client has sent is provided in |mt|. The current state - * is in |s->statem.hand_state|. + * ossl_statem_server13_read_transition() encapsulates the logic for the allowed + * handshake state transitions when a TLSv1.3 server is reading messages from + * the client. The message type that the client has sent is provided in |mt|. + * The current state is in |s->statem.hand_state|. * - * Valid return values are: - * 1: Success (transition allowed) - * 0: Error (transition not allowed) + * Return values are 1 for success (transition allowed) and 0 on error + * (transition not allowed) + */ +static int ossl_statem_server13_read_transition(SSL *s, int mt) +{ + OSSL_STATEM *st = &s->statem; + + /* + * TODO(TLS1.3): This is still based on the TLSv1.2 state machine. Over time + * we will update this to look more like real TLSv1.3 + */ + + /* + * Note: There is no case for TLS_ST_BEFORE because at that stage we have + * not negotiated TLSv1.3 yet, so that case is handled by + * ossl_statem_server_read_transition() + */ + switch (st->hand_state) { + default: + break; + + case TLS_ST_SW_SRVR_DONE: + if (s->s3->tmp.cert_request) { + if (mt == SSL3_MT_CERTIFICATE) { + st->hand_state = TLS_ST_SR_CERT; + return 1; + } + } else { + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_SR_CHANGE; + return 1; + } + } + break; + + case TLS_ST_SR_CERT: + if (s->session->peer == NULL) { + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_SR_CHANGE; + return 1; + } + } else { + if (mt == SSL3_MT_CERTIFICATE_VERIFY) { + st->hand_state = TLS_ST_SR_CERT_VRFY; + return 1; + } + } + break; + + case TLS_ST_SR_CERT_VRFY: + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_SR_CHANGE; + return 1; + } + break; + + case TLS_ST_SR_CHANGE: + if (mt == SSL3_MT_FINISHED) { + st->hand_state = TLS_ST_SR_FINISHED; + return 1; + } + break; + + case TLS_ST_SW_FINISHED: + if (mt == SSL3_MT_CHANGE_CIPHER_SPEC) { + st->hand_state = TLS_ST_SR_CHANGE; + return 1; + } + break; + } + + /* No valid transition found */ + ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_UNEXPECTED_MESSAGE); + SSLerr(SSL_F_OSSL_STATEM_SERVER13_READ_TRANSITION, + SSL_R_UNEXPECTED_MESSAGE); + return 0; +} + +/* + * ossl_statem_server_read_transition() encapsulates the logic for the allowed + * handshake state transitions when the server is reading messages from the + * client. The message type that the client has sent is provided in |mt|. The + * current state is in |s->statem.hand_state|. + * + * Return values are 1 for success (transition allowed) and 0 on error + * (transition not allowed) */ int ossl_statem_server_read_transition(SSL *s, int mt) { OSSL_STATEM *st = &s->statem; + if (s->method->version == TLS1_3_VERSION) + return ossl_statem_server13_read_transition(s, mt); + switch (st->hand_state) { default: break; @@ -304,13 +390,108 @@ static int send_certificate_request(SSL *s) } /* - * server_write_transition() works out what handshake state to move to next - * when the server is writing messages to be sent to the client. + * ossl_statem_server13_write_transition() works out what handshake state to + * move to next when a TLSv1.3 server is writing messages to be sent to the + * client. + */ +static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) +{ + OSSL_STATEM *st = &s->statem; + + /* + * TODO(TLS1.3): This is still based on the TLSv1.2 state machine. Over time + * we will update this to look more like real TLSv1.3 + */ + + /* + * No case for TLS_ST_BEFORE, because at that stage we have not negotiated + * TLSv1.3 yet, so that is handled by ossl_statem_server_write_transition() + */ + + switch (st->hand_state) { + default: + /* Shouldn't happen */ + return WRITE_TRAN_ERROR; + + case TLS_ST_SR_CLNT_HELLO: + st->hand_state = TLS_ST_SW_SRVR_HELLO; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_SRVR_HELLO: + if (s->hit) + st->hand_state = s->tlsext_ticket_expected + ? TLS_ST_SW_SESSION_TICKET : TLS_ST_SW_CHANGE; + else + st->hand_state = TLS_ST_SW_CERT; + + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_CERT: + if (s->tlsext_status_expected) { + st->hand_state = TLS_ST_SW_CERT_STATUS; + return WRITE_TRAN_CONTINUE; + } + /* Fall through */ + + case TLS_ST_SW_CERT_STATUS: + if (send_certificate_request(s)) { + st->hand_state = TLS_ST_SW_CERT_REQ; + return WRITE_TRAN_CONTINUE; + } + /* Fall through */ + + case TLS_ST_SW_CERT_REQ: + st->hand_state = TLS_ST_SW_SRVR_DONE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_SRVR_DONE: + return WRITE_TRAN_FINISHED; + + case TLS_ST_SR_FINISHED: + if (s->hit) { + st->hand_state = TLS_ST_OK; + ossl_statem_set_in_init(s, 0); + return WRITE_TRAN_CONTINUE; + } + + st->hand_state = s->tlsext_ticket_expected ? TLS_ST_SW_SESSION_TICKET + : TLS_ST_SW_CHANGE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_SESSION_TICKET: + st->hand_state = TLS_ST_SW_CHANGE; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_CHANGE: + st->hand_state = TLS_ST_SW_FINISHED; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_FINISHED: + if (s->hit) + return WRITE_TRAN_FINISHED; + + st->hand_state = TLS_ST_OK; + ossl_statem_set_in_init(s, 0); + return WRITE_TRAN_CONTINUE; + } +} + +/* + * ossl_statem_server_write_transition() works out what handshake state to move + * to next when the server is writing messages to be sent to the client. */ WRITE_TRAN ossl_statem_server_write_transition(SSL *s) { OSSL_STATEM *st = &s->statem; + /* + * Note that before the ClientHello we don't know what version we are going + * to negotiate yet, so we don't take this branch until later + */ + + if (s->method->version == TLS1_3_VERSION) + return ossl_statem_server13_write_transition(s); + switch (st->hand_state) { default: /* Shouldn't happen */ @@ -1263,6 +1444,15 @@ MSG_PROCESS_RETURN tls_process_client_hello(SSL *s, PACKET *pkt) goto err; } + /* Check we've got a key_share for TLSv1.3 */ + if (s->version == TLS1_3_VERSION && s->s3->peer_tmp == NULL && !s->hit) { + /* No suitable share */ + /* TODO(TLS1.3): Send a HelloRetryRequest */ + al = SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_TLS_PROCESS_CLIENT_HELLO, SSL_R_NO_SUITABLE_KEY_SHARE); + goto f_err; + } + /* * Check if we want to use external pre-shared secret for this handshake * for not reused session only. We need to generate server_random before @@ -2307,7 +2497,7 @@ static int tls_process_cke_dhe(SSL *s, PACKET *pkt, int *al) goto err; } - if (ssl_derive(s, skey, ckey) == 0) { + if (ssl_derive(s, skey, ckey, 1) == 0) { *al = SSL_AD_INTERNAL_ERROR; SSLerr(SSL_F_TLS_PROCESS_CKE_DHE, ERR_R_INTERNAL_ERROR); goto err; @@ -2367,7 +2557,7 @@ static int tls_process_cke_ecdhe(SSL *s, PACKET *pkt, int *al) } } - if (ssl_derive(s, skey, ckey) == 0) { + if (ssl_derive(s, skey, ckey, 1) == 0) { *al = SSL_AD_INTERNAL_ERROR; SSLerr(SSL_F_TLS_PROCESS_CKE_ECDHE, ERR_R_INTERNAL_ERROR); goto err; @@ -2767,6 +2957,7 @@ MSG_PROCESS_RETURN tls_process_cert_verify(SSL *s, PACKET *pkt) al = SSL_AD_INTERNAL_ERROR; goto f_err; } + #ifdef SSL_DEBUG fprintf(stderr, "Using client verify alg %s\n", EVP_MD_name(md)); #endif @@ -2922,6 +3113,17 @@ MSG_PROCESS_RETURN tls_process_client_certificate(SSL *s, PACKET *pkt) sk_X509_pop_free(s->session->peer_chain, X509_free); s->session->peer_chain = sk; + + /* + * Freeze the handshake buffer. For <TLS1.3 we do this after the CKE + * message + */ + if (SSL_IS_TLS13(s) && !ssl3_digest_cached_records(s, 1)) { + al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_TLS_PROCESS_CLIENT_CERTIFICATE, ERR_R_INTERNAL_ERROR); + goto f_err; + } + /* * Inconsistency alert: cert_chain does *not* include the peer's own * certificate, while we do include it in statem_clnt.c diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c index 8d1e350..37cd25d 100644 --- a/ssl/t1_enc.c +++ b/ssl/t1_enc.c @@ -474,7 +474,13 @@ size_t tls1_final_finish_mac(SSL *s, const char *str, size_t slen, int tls1_generate_master_secret(SSL *s, unsigned char *out, unsigned char *p, size_t len, size_t *secret_size) { - if (s->session->flags & SSL_SESS_FLAG_EXTMS) { + /* + * TODO(TLS1.3): We haven't implemented TLS1.3 key derivation yet. For now + * we will just force no use of EMS (which adds complications around the + * handshake hash). This will need to be removed later + */ + if ((s->session->flags & SSL_SESS_FLAG_EXTMS) + && SSL_IS_TLS13(s)) { unsigned char hash[EVP_MAX_MD_SIZE * 2]; size_t hashlen; /* diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 338334f..74022ee 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1026,9 +1026,13 @@ static int tls1_check_duplicate_extensions(const PACKET *packet) int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) { #ifndef OPENSSL_NO_EC - /* See if we support any ECC ciphersuites */ + const unsigned char *pcurves = NULL; + size_t num_curves = 0; int using_ecc = 0; - if (s->version >= TLS1_VERSION || SSL_IS_DTLS(s)) { + + /* See if we support any ECC ciphersuites */ + if ((s->version >= TLS1_VERSION && s->version <= TLS1_2_VERSION) + || SSL_IS_DTLS(s)) { int i; unsigned long alg_k, alg_a; STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(s); @@ -1044,6 +1048,18 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) break; } } + } else if (SSL_IS_TLS13(s)) { + /* + * TODO(TLS1.3): We always use ECC for TLSv1.3 at the moment. This will + * change if we implement DH key shares + */ + using_ecc = 1; + } +#else + if (SSL_IS_TLS13(s)) { + /* Shouldn't happen! */ + SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); + return 0; } #endif @@ -1102,8 +1118,8 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) /* * Add TLS extension ECPointFormats to the ClientHello message */ - const unsigned char *pcurves, *pformats; - size_t num_curves, num_formats; + const unsigned char *pformats, *pcurvestmp; + size_t num_formats; size_t i; tls1_get_formatlist(s, &pformats, &num_formats); @@ -1126,6 +1142,7 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); return 0; } + pcurvestmp = pcurves; if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_groups) /* Sub-packet for supported_groups extension */ @@ -1135,10 +1152,10 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) return 0; } /* Copy curve ID if supported */ - for (i = 0; i < num_curves; i++, pcurves += 2) { + for (i = 0; i < num_curves; i++, pcurvestmp += 2) { if (tls_curve_allowed(s, pcurves, SSL_SECOP_CURVE_SUPPORTED)) { - if (!WPACKET_put_bytes_u8(pkt, pcurves[0]) - || !WPACKET_put_bytes_u8(pkt, pcurves[1])) { + if (!WPACKET_put_bytes_u8(pkt, pcurvestmp[0]) + || !WPACKET_put_bytes_u8(pkt, pcurvestmp[1])) { SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); return 0; @@ -1349,8 +1366,13 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) return 0; } + /* TLS1.3 specific extensions */ if (SSL_IS_TLS13(s)) { int min_version, max_version, reason, currv; + size_t i, sharessent = 0; + + /* TODO(TLS1.3): Should we add this extension for versions < TLS1.3? */ + /* supported_versions extension */ if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_supported_versions) || !WPACKET_start_sub_packet_u16(pkt) || !WPACKET_start_sub_packet_u8(pkt)) { @@ -1384,6 +1406,79 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); return 0; } + + + /* key_share extension */ + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share) + /* Extension data sub-packet */ + || !WPACKET_start_sub_packet_u16(pkt) + /* KeyShare list sub-packet */ + || !WPACKET_start_sub_packet_u16(pkt)) { + SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* + * TODO(TLS1.3): Make the number of key_shares sent configurable. For + * now, just send one + */ + for (i = 0; i < num_curves && sharessent < 1; i++, pcurves += 2) { + unsigned char *encodedPoint = NULL; + unsigned int curve_id = 0; + EVP_PKEY *key_share_key = NULL; + size_t encodedlen; + + if (!tls_curve_allowed(s, pcurves, SSL_SECOP_CURVE_SUPPORTED)) + continue; + + if (s->s3->tmp.pkey != NULL) { + /* Shouldn't happen! */ + SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, + ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Generate a key for this key_share */ + curve_id = (pcurves[0] << 8) | pcurves[1]; + key_share_key = ssl_generate_pkey_curve(curve_id); + if (key_share_key == NULL) { + SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_EVP_LIB); + return 0; + } + + /* Encode the public key. */ + encodedlen = EVP_PKEY_get1_tls_encodedpoint(key_share_key, + &encodedPoint); + if (encodedlen == 0) { + SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_EC_LIB); + EVP_PKEY_free(key_share_key); + return 0; + } + + /* Create KeyShareEntry */ + if (!WPACKET_put_bytes_u16(pkt, curve_id) + || !WPACKET_sub_memcpy_u16(pkt, encodedPoint, encodedlen)) { + SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, + ERR_R_INTERNAL_ERROR); + EVP_PKEY_free(key_share_key); + OPENSSL_free(encodedPoint); + return 0; + } + + /* + * TODO(TLS1.3): When changing to send more than one key_share we're + * going to need to be able to save more than one EVP_PKEY. For now + * we reuse the existing tmp.pkey + */ + s->s3->group_id = curve_id; + s->s3->tmp.pkey = key_share_key; + sharessent++; + OPENSSL_free(encodedPoint); + } + if (!WPACKET_close(pkt) || !WPACKET_close(pkt)) { + SSLerr(SSL_F_SSL_ADD_CLIENTHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); + return 0; + } } /* @@ -1421,6 +1516,59 @@ int ssl_add_clienthello_tlsext(SSL *s, WPACKET *pkt, int *al) return 1; } +/* + * Add the key_share extension. + * + * Returns 1 on success or 0 on failure. + */ +static int add_client_key_share_ext(SSL *s, WPACKET *pkt, int *al) +{ + unsigned char *encodedPoint; + size_t encoded_pt_len = 0; + EVP_PKEY *ckey = s->s3->peer_tmp, *skey = NULL; + + if (ckey == NULL) { + SSLerr(SSL_F_ADD_CLIENT_KEY_SHARE_EXT, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!WPACKET_put_bytes_u16(pkt, TLSEXT_TYPE_key_share) + || !WPACKET_start_sub_packet_u16(pkt) + || !WPACKET_put_bytes_u16(pkt, s->s3->group_id)) { + SSLerr(SSL_F_ADD_CLIENT_KEY_SHARE_EXT, ERR_R_INTERNAL_ERROR); + return 0; + } + + skey = ssl_generate_pkey(ckey); + + /* Generate encoding of server key */ + encoded_pt_len = EVP_PKEY_get1_tls_encodedpoint(skey, &encodedPoint); + if (encoded_pt_len == 0) { + SSLerr(SSL_F_ADD_CLIENT_KEY_SHARE_EXT, ERR_R_EC_LIB); + EVP_PKEY_free(skey); + return 0; + } + + if (!WPACKET_sub_memcpy_u16(pkt, encodedPoint, encoded_pt_len) + || !WPACKET_close(pkt)) { + SSLerr(SSL_F_ADD_CLIENT_KEY_SHARE_EXT, ERR_R_INTERNAL_ERROR); + EVP_PKEY_free(skey); + OPENSSL_free(encodedPoint); + return 0; + } + OPENSSL_free(encodedPoint); + + /* This causes the crypto state to be updated based on the derived keys */ + s->s3->tmp.pkey = skey; + if (ssl_derive(s, skey, ckey, 1) == 0) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_ADD_CLIENT_KEY_SHARE_EXT, ERR_R_INTERNAL_ERROR); + return 0; + } + + return 1; +} + int ssl_add_serverhello_tlsext(SSL *s, WPACKET *pkt, int *al) { #ifndef OPENSSL_NO_NEXTPROTONEG @@ -1553,6 +1701,10 @@ int ssl_add_serverhello_tlsext(SSL *s, WPACKET *pkt, int *al) } } #endif + + if (SSL_IS_TLS13(s) && !s->hit && !add_client_key_share_ext(s, pkt, al)) + return 0; + if (!custom_ext_add(s, 1, pkt, al)) { SSLerr(SSL_F_SSL_ADD_SERVERHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); return 0; @@ -1743,6 +1895,198 @@ static void ssl_check_for_safari(SSL *s, const CLIENTHELLO_MSG *hello) } #endif /* !OPENSSL_NO_EC */ + +/* + * Process the supported_groups extension if present. Returns success if the + * extension is absent, or if it has been successfully processed. + * + * Returns 1 on success or 0 on failure + */ +static int tls_process_supported_groups(SSL *s, CLIENTHELLO_MSG *hello) +{ +#ifndef OPENSSL_NO_EC + PACKET supported_groups_list; + RAW_EXTENSION *suppgroups = tls_get_extension_by_type(hello->pre_proc_exts, + hello->num_extensions, + TLSEXT_TYPE_supported_groups); + + if (suppgroups == NULL) + return 1; + + /* Each group is 2 bytes and we must have at least 1. */ + if (!PACKET_as_length_prefixed_2(&suppgroups->data, + &supported_groups_list) + || PACKET_remaining(&supported_groups_list) == 0 + || (PACKET_remaining(&supported_groups_list) % 2) != 0) { + return 0; + } + + if (!s->hit + && !PACKET_memdup(&supported_groups_list, + &s->session->tlsext_supportedgroupslist, + &s->session->tlsext_supportedgroupslist_length)) { + return 0; + } +#endif + return 1; +} + +/* + * Checks a list of |groups| to determine if the |group_id| is in it. If it is + * and |checkallow| is 1 then additionally check if the group is allowed to be + * used. Returns 1 if the group is in the list (and allowed if |checkallow| is + * 1) or 0 otherwise. + */ +static int check_in_list(SSL *s, unsigned int group_id, + const unsigned char *groups, size_t num_groups, + int checkallow) +{ + size_t i; + + if (groups == NULL || num_groups == 0) + return 0; + + for (i = 0; i < num_groups; i++, groups += 2) { + unsigned int share_id = (groups[0] << 8) | (groups[1]); + + if (group_id == share_id + && (!checkallow || tls_curve_allowed(s, groups, + SSL_SECOP_CURVE_CHECK))) { + break; + } + } + + /* If i == num_groups then not in the list */ + return i < num_groups; +} + +/* + * Process a key_share extension received in the ClientHello. |pkt| contains + * the raw PACKET data for the extension. Returns 1 on success or 0 on failure. + * If a failure occurs then |*al| is set to an appropriate alert value. + */ +static int process_key_share_ext(SSL *s, PACKET *pkt, int *al) +{ + unsigned int group_id; + PACKET key_share_list, encoded_pt; + const unsigned char *clntcurves, *srvrcurves; + size_t clnt_num_curves, srvr_num_curves; + int group_nid, found = 0; + unsigned int curve_flags; + + /* Sanity check */ + if (s->s3->peer_tmp != NULL) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!PACKET_as_length_prefixed_2(pkt, &key_share_list)) { + *al = SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, + SSL_R_LENGTH_MISMATCH); + return 0; + } + + /* Get our list of supported curves */ + if (!tls1_get_curvelist(s, 0, &srvrcurves, &srvr_num_curves)) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, + ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Get the clients list of supported curves */ + if (!tls1_get_curvelist(s, 1, &clntcurves, &clnt_num_curves)) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, + ERR_R_INTERNAL_ERROR); + return 0; + } + + while (PACKET_remaining(&key_share_list) > 0) { + if (!PACKET_get_net_2(&key_share_list, &group_id) + || !PACKET_get_length_prefixed_2(&key_share_list, &encoded_pt) + || PACKET_remaining(&encoded_pt) == 0) { + *al = SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, + SSL_R_LENGTH_MISMATCH); + return 0; + } + + /* + * If we already found a suitable key_share we loop through the + * rest to verify the structure, but don't process them. + */ + if (found) + continue; + + /* Check if this share is in supported_groups sent from client */ + if (!check_in_list(s, group_id, clntcurves, clnt_num_curves, 0)) { + *al = SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, + SSL_R_BAD_KEY_SHARE); + return 0; + } + + /* Check if this share is for a group we can use */ + if (!check_in_list(s, group_id, srvrcurves, srvr_num_curves, 1)) { + /* Share not suitable */ + continue; + } + + group_nid = tls1_ec_curve_id2nid(group_id, &curve_flags); + + if (group_nid == 0) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, + SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS); + return 0; + } + + if ((curve_flags & TLS_CURVE_TYPE) == TLS_CURVE_CUSTOM) { + /* Can happen for some curves, e.g. X25519 */ + EVP_PKEY *key = EVP_PKEY_new(); + + if (key == NULL || !EVP_PKEY_set_type(key, group_nid)) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, ERR_R_EVP_LIB); + EVP_PKEY_free(key); + return 0; + } + s->s3->peer_tmp = key; + } else { + /* Set up EVP_PKEY with named curve as parameters */ + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (pctx == NULL + || EVP_PKEY_paramgen_init(pctx) <= 0 + || EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, + group_nid) <= 0 + || EVP_PKEY_paramgen(pctx, &s->s3->peer_tmp) <= 0) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, ERR_R_EVP_LIB); + EVP_PKEY_CTX_free(pctx); + return 0; + } + EVP_PKEY_CTX_free(pctx); + pctx = NULL; + } + s->s3->group_id = group_id; + + if (!EVP_PKEY_set1_tls_encodedpoint(s->s3->peer_tmp, + PACKET_data(&encoded_pt), + PACKET_remaining(&encoded_pt))) { + *al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_PROCESS_KEY_SHARE_EXT, SSL_R_BAD_ECPOINT); + return 0; + } + + found = 1; + } + + return 1; +} + /* * Loop through all remaining ClientHello extensions that we collected earlier * and haven't already processed. For each one parse it and update the SSL @@ -1792,6 +2136,15 @@ static int ssl_scan_clienthello_tlsext(SSL *s, CLIENTHELLO_MSG *hello, int *al) s->srtp_profile = NULL; /* + * We process the supported_groups extension first so that is done before + * we get to key_share which needs to use the information in it. + */ + if (!tls_process_supported_groups(s, hello)) { + *al = TLS1_AD_INTERNAL_ERROR; + return 0; + } + + /* * We parse all extensions to ensure the ClientHello is well-formed but, * unless an extension specifies otherwise, we ignore extensions upon * resumption. @@ -1932,26 +2285,6 @@ static int ssl_scan_clienthello_tlsext(SSL *s, CLIENTHELLO_MSG *hello, int *al) return 0; } } - } else if (currext->type == TLSEXT_TYPE_supported_groups) { - PACKET supported_groups_list; - - /* Each group is 2 bytes and we must have at least 1. */ - if (!PACKET_as_length_prefixed_2(&currext->data, - &supported_groups_list) - || PACKET_remaining(&supported_groups_list) == 0 - || (PACKET_remaining(&supported_groups_list) % 2) != 0) { - return 0; - } - - if (!s->hit) { - if (!PACKET_memdup(&supported_groups_list, - &s->session->tlsext_supportedgroupslist, - &s-> - session->tlsext_supportedgroupslist_length)) { - *al = TLS1_AD_INTERNAL_ERROR; - return 0; - } - } } #endif /* OPENSSL_NO_EC */ else if (currext->type == TLSEXT_TYPE_session_ticket) { @@ -2106,8 +2439,13 @@ static int ssl_scan_clienthello_tlsext(SSL *s, CLIENTHELLO_MSG *hello, int *al) } #endif else if (currext->type == TLSEXT_TYPE_encrypt_then_mac - && !(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC)) + && !(s->options & SSL_OP_NO_ENCRYPT_THEN_MAC)) { s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC; + } else if (currext->type == TLSEXT_TYPE_key_share + && SSL_IS_TLS13(s) && !s->hit + && !process_key_share_ext(s, &currext->data, al)) { + return 0; + } /* * Note: extended master secret extension handled in * tls_check_client_ems_support() @@ -2396,16 +2734,70 @@ static int ssl_scan_serverhello_tlsext(SSL *s, PACKET *pkt, int *al) s->s3->tmp.new_cipher->algorithm_mac != SSL_AEAD && s->s3->tmp.new_cipher->algorithm_enc != SSL_RC4) s->s3->flags |= TLS1_FLAGS_ENCRYPT_THEN_MAC; - } else if (type == TLSEXT_TYPE_extended_master_secret) { + } else if (type == TLSEXT_TYPE_extended_master_secret && + (SSL_IS_DTLS(s) || !SSL_IS_TLS13(s))) { s->s3->flags |= TLS1_FLAGS_RECEIVED_EXTMS; if (!s->hit) s->session->flags |= SSL_SESS_FLAG_EXTMS; - } + } else if (type == TLSEXT_TYPE_key_share + && SSL_IS_TLS13(s)) { + unsigned int group_id; + PACKET encoded_pt; + EVP_PKEY *ckey = s->s3->tmp.pkey, *skey = NULL; + + /* Sanity check */ + if (ckey == NULL) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!PACKET_get_net_2(&spkt, &group_id)) { + *al = SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT, + SSL_R_LENGTH_MISMATCH); + return 0; + } + + if (group_id != s->s3->group_id) { + /* + * This isn't for the group that we sent in the original + * key_share! + */ + *al = SSL_AD_HANDSHAKE_FAILURE; + SSLerr(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT, + SSL_R_BAD_KEY_SHARE); + return 0; + } + + if (!PACKET_as_length_prefixed_2(&spkt, &encoded_pt) + || PACKET_remaining(&encoded_pt) == 0) { + *al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT, + SSL_R_LENGTH_MISMATCH); + return 0; + } + + skey = ssl_generate_pkey(ckey); + if (!EVP_PKEY_set1_tls_encodedpoint(skey, PACKET_data(&encoded_pt), + PACKET_remaining(&encoded_pt))) { + *al = SSL_AD_DECODE_ERROR; + SSLerr(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT, SSL_R_BAD_ECPOINT); + return 0; + } + + if (ssl_derive(s, ckey, skey, 1) == 0) { + *al = SSL_AD_INTERNAL_ERROR; + SSLerr(SSL_F_SSL_SCAN_SERVERHELLO_TLSEXT, ERR_R_INTERNAL_ERROR); + EVP_PKEY_free(skey); + return 0; + } + EVP_PKEY_free(skey); /* * If this extension type was not otherwise handled, but matches a * custom_cli_ext_record, then send it to the c callback */ - else if (custom_ext_parse(s, 0, type, data, size, al) <= 0) + } else if (custom_ext_parse(s, 0, type, data, size, al) <= 0) return 0; } @@ -2839,7 +3231,7 @@ int tls_get_ticket_from_client(SSL *s, CLIENTHELLO_MSG *hello, /* * Sets the extended master secret flag if the extension is present in the - * ClientHello + * ClientHello and we can support it * Returns: * 1 on success * 0 on error @@ -2850,7 +3242,8 @@ int tls_check_client_ems_support(SSL *s, const CLIENTHELLO_MSG *hello) s->s3->flags &= ~TLS1_FLAGS_RECEIVED_EXTMS; - if (s->version <= SSL3_VERSION) + if (!SSL_IS_DTLS(s) && (s->version < TLS1_VERSION + || s->version > TLS1_2_VERSION)) return 1; emsext = tls_get_extension_by_type(hello->pre_proc_exts, diff --git a/ssl/t1_trce.c b/ssl/t1_trce.c index 2e94c65..42cf2be 100644 --- a/ssl/t1_trce.c +++ b/ssl/t1_trce.c @@ -447,6 +447,7 @@ static ssl_trace_tbl ssl_exts_tbl[] = { {TLSEXT_TYPE_client_authz, "client_authz"}, {TLSEXT_TYPE_server_authz, "server_authz"}, {TLSEXT_TYPE_cert_type, "cert_type"}, + {TLSEXT_TYPE_key_share, "key_share"}, {TLSEXT_TYPE_supported_groups, "supported_groups"}, {TLSEXT_TYPE_ec_point_formats, "ec_point_formats"}, {TLSEXT_TYPE_srp, "srp"}, @@ -645,7 +646,7 @@ static int ssl_print_signature(BIO *bio, int indent, SSL *s, static int ssl_print_extension(BIO *bio, int indent, int server, int extype, const unsigned char *ext, size_t extlen) { - size_t xlen; + size_t xlen, share_len; BIO_indent(bio, indent, 80); BIO_printf(bio, "extension_type=%s(%d), length=%d\n", ssl_trace_str(extype, ssl_exts_tbl), extype, (int)extlen); @@ -718,6 +719,35 @@ static int ssl_print_extension(BIO *bio, int indent, int server, int extype, ssl_print_hex(bio, indent + 4, "ticket", ext, extlen); break; + case TLSEXT_TYPE_key_share: + if (extlen < 2) + return 0; + if (server) { + xlen = extlen; + } else { + xlen = (ext[0] << 8) | ext[1]; + if (extlen != xlen + 2) + return 0; + ext += 2; + } + for (; xlen > 0; ext += share_len, xlen -= share_len) { + int group_id; + + if (xlen < 4) + return 0; + group_id = (ext[0] << 8) | ext[1]; + share_len = (ext[2] << 8) | ext[3]; + ext += 4; + xlen -= 4; + if (xlen < share_len) + return 0; + BIO_indent(bio, indent + 4, 80); + BIO_printf(bio, "NamedGroup: %s\n", + ssl_trace_str(group_id, ssl_groups_tbl)); + ssl_print_hex(bio, indent + 4, "key_exchange: ", ext, share_len); + } + break; + case TLSEXT_TYPE_supported_versions: if (extlen < 1) return 0; diff --git a/test/recipes/70-test_key_share.t b/test/recipes/70-test_key_share.t new file mode 100755 index 0000000..380b1a8 --- /dev/null +++ b/test/recipes/70-test_key_share.t @@ -0,0 +1,326 @@ +#! /usr/bin/env perl +# Copyright 2015-2016 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the OpenSSL license (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use strict; +use OpenSSL::Test qw/:DEFAULT cmdstr srctop_file bldtop_dir/; +use OpenSSL::Test::Utils; +use TLSProxy::Proxy; +use File::Temp qw(tempfile); + +use constant { + LOOK_ONLY => 0, + EMPTY_EXTENSION => 1, + MISSING_EXTENSION => 2, + NO_ACCEPTABLE_KEY_SHARES => 3, + NON_PREFERRED_KEY_SHARE => 4, + ACCEPTABLE_AT_END => 5, + NOT_IN_SUPPORTED_GROUPS => 6, + GROUP_ID_TOO_SHORT => 7, + KEX_LEN_MISMATCH => 8, + ZERO_LEN_KEX_DATA => 9, + TRAILING_DATA => 10, + SELECT_X25519 => 11 +}; + +use constant { + CLIENT_TO_SERVER => 1, + SERVER_TO_CLIENT => 2 +}; + + +use constant { + X25519 => 0x1d, + P_256 => 0x17 +}; + +my $testtype; +my $direction; +my $selectedgroupid; + +my $test_name = "test_key_share"; +setup($test_name); + +plan skip_all => "TLSProxy isn't usable on $^O" + if $^O =~ /^(VMS|MSWin32)$/; + +plan skip_all => "$test_name needs the dynamic engine feature enabled" + if disabled("engine") || disabled("dynamic-engine"); + +plan skip_all => "$test_name needs the sock feature enabled" + if disabled("sock"); + +plan skip_all => "$test_name needs TLS1.3 enabled" + if disabled("tls1_3"); + +$ENV{OPENSSL_ia32cap} = '~0x200000200000000'; + +my $proxy = TLSProxy::Proxy->new( + undef, + cmdstr(app(["openssl"]), display => 1), + srctop_file("apps", "server.pem"), + (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) +); + +#We assume that test_ssl_new and friends will test the happy path for this, +#so we concentrate on the less common scenarios + +#Test 1: An empty key_shares extension should not succeed +$testtype = EMPTY_EXTENSION; +$direction = CLIENT_TO_SERVER; +$proxy->filter(\&modify_key_shares_filter); +$proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; +plan tests => 17; +#TODO(TLS1.3): Actually this should succeed after a HelloRetryRequest - but +#we've not implemented that yet, so for now we look for a fail +ok(TLSProxy::Message->fail(), "Empty key_shares"); + +#Test 2: A missing key_shares extension should not succeed +$proxy->clear(); +$testtype = MISSING_EXTENSION; +$proxy->start(); +#TODO(TLS1.3): As above this should really succeed after a HelloRetryRequest, +#but we look for fail for now +ok(TLSProxy::Message->fail(), "Missing key_shares extension"); + +#Test 3: No acceptable key_shares should fail +$proxy->clear(); +$testtype = NO_ACCEPTABLE_KEY_SHARES; +$proxy->start(); +#TODO(TLS1.3): Again this should go around the loop of a HelloRetryRequest but +#we fail for now +ok(TLSProxy::Message->fail(), "No acceptable key_shares"); + +#Test 4: A non preferred but acceptable key_share should succeed +$proxy->clear(); +$proxy->filter(undef); +$proxy->clientflags("-curves P-256"); +$proxy->start(); +ok(TLSProxy::Message->success(), "Non preferred key_share"); +$proxy->filter(\&modify_key_shares_filter); + +#Test 5: An acceptable key_share after a list of non-acceptable ones should +#succeed +$proxy->clear(); +$testtype = ACCEPTABLE_AT_END; +$proxy->start(); +ok(TLSProxy::Message->success(), "Acceptable key_share at end of list"); + +#Test 6: An acceptable key_share but for a group not in supported_groups should +#fail +$proxy->clear(); +$testtype = NOT_IN_SUPPORTED_GROUPS; +$proxy->start(); +ok(TLSProxy::Message->fail(), "Acceptable key_share not in supported_groups"); + +#Test 7: Too short group_id should fail +$proxy->clear(); +$testtype = GROUP_ID_TOO_SHORT; +$proxy->start(); +ok(TLSProxy::Message->fail(), "Group id too short"); + +#Test 8: key_exchange length mismatch should fail +$proxy->clear(); +$testtype = KEX_LEN_MISMATCH; +$proxy->start(); +ok(TLSProxy::Message->fail(), "key_exchange length mismatch"); + +#Test 9: Zero length key_exchange should fail +$proxy->clear(); +$testtype = ZERO_LEN_KEX_DATA; +$proxy->start(); +ok(TLSProxy::Message->fail(), "zero length key_exchange data"); + +#Test 10: Trailing data on key_share list should fail +$proxy->clear(); +$testtype = TRAILING_DATA; +$proxy->start(); +ok(TLSProxy::Message->fail(), "key_share list trailing data"); + +#Test 11: Multiple acceptable key_shares - we choose the first one +$proxy->clear(); +$direction = SERVER_TO_CLIENT; +$testtype = LOOK_ONLY; +$proxy->clientflags("-curves P-256:X25519"); +$proxy->start(); +ok(TLSProxy::Message->success() && ($selectedgroupid == P_256), + "Multiple acceptable key_shares"); + +#Test 12: Multiple acceptable key_shares - we choose the first one (part 2) +$proxy->clear(); +$proxy->clientflags("-curves X25519:P-256"); +$proxy->start(); +ok(TLSProxy::Message->success() && ($selectedgroupid == X25519), + "Multiple acceptable key_shares (part 2)"); + +#Test 13: Server sends key_share that wasn't offerred should fail +$proxy->clear(); +$testtype = SELECT_X25519; +$proxy->clientflags("-curves P-256"); +$proxy->start(); +ok(TLSProxy::Message->fail(), "Non offered key_share"); + +#Test 14: Too short group_id in ServerHello should fail +$proxy->clear(); +$testtype = GROUP_ID_TOO_SHORT; +$proxy->start(); +ok(TLSProxy::Message->fail(), "Group id too short in ServerHello"); + +#Test 15: key_exchange length mismatch in ServerHello should fail +$proxy->clear(); +$testtype = KEX_LEN_MISMATCH; +$proxy->start(); +ok(TLSProxy::Message->fail(), "key_exchange length mismatch in ServerHello"); + +#Test 16: Zero length key_exchange in ServerHello should fail +$proxy->clear(); +$testtype = ZERO_LEN_KEX_DATA; +$proxy->start(); +ok(TLSProxy::Message->fail(), "zero length key_exchange data in ServerHello"); + +#Test 17: Trailing data on key_share in ServerHello should fail +$proxy->clear(); +$testtype = TRAILING_DATA; +$proxy->start(); +ok(TLSProxy::Message->fail(), "key_share trailing data in ServerHello"); + + +sub modify_key_shares_filter +{ + my $proxy = shift; + + # We're only interested in the initial ClientHello + if (($direction == CLIENT_TO_SERVER && $proxy->flight != 0) + || ($direction == SERVER_TO_CLIENT && $proxy->flight != 1)) { + return; + } + + foreach my $message (@{$proxy->message_list}) { + if ($message->mt == TLSProxy::Message::MT_CLIENT_HELLO + && $direction == CLIENT_TO_SERVER) { + my $ext; + my $suppgroups; + + #Setup supported groups to include some unrecognised groups + $suppgroups = pack "C8", + 0x00, 0x06, #List Length + 0xff, 0xfe, #Non existing group 1 + 0xff, 0xff, #Non existing group 2 + 0x00, 0x1d; #x25519 + + if ($testtype == EMPTY_EXTENSION) { + $ext = pack "C2", + 0x00, 0x00; + } elsif ($testtype == NO_ACCEPTABLE_KEY_SHARES) { + $ext = pack "C12", + 0x00, 0x0a, #List Length + 0xff, 0xfe, #Non existing group 1 + 0x00, 0x01, 0xff, #key_exchange data + 0xff, 0xff, #Non existing group 2 + 0x00, 0x01, 0xff; #key_exchange data + } elsif ($testtype == ACCEPTABLE_AT_END) { + $ext = pack "C11H64", + 0x00, 0x29, #List Length + 0xff, 0xfe, #Non existing group 1 + 0x00, 0x01, 0xff, #key_exchange data + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + "155155B95269ED5C87EAA99C2EF5A593". + "EDF83495E80380089F831B94D14B1421"; #key_exchange data + } elsif ($testtype == NOT_IN_SUPPORTED_GROUPS) { + $suppgroups = pack "C4", + 0x00, 0x02, #List Length + 0x00, 0xfe; #Non existing group 1 + } elsif ($testtype == GROUP_ID_TOO_SHORT) { + $ext = pack "C6H64C1", + 0x00, 0x25, #List Length + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + "155155B95269ED5C87EAA99C2EF5A593". + "EDF83495E80380089F831B94D14B1421"; #key_exchange data + 0x00; #Group id too short + } elsif ($testtype == KEX_LEN_MISMATCH) { + $ext = pack "C8", + 0x00, 0x06, #List Length + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + 0x15, 0x51; #Only two bytes of data, but length should be 32 + } elsif ($testtype == ZERO_LEN_KEX_DATA) { + $ext = pack "C10H64", + 0x00, 0x28, #List Length + 0xff, 0xfe, #Non existing group 1 + 0x00, 0x00, #zero length key_exchange data is invalid + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + "155155B95269ED5C87EAA99C2EF5A593". + "EDF83495E80380089F831B94D14B1421"; #key_exchange data + } elsif ($testtype == TRAILING_DATA) { + $ext = pack "C6H64C1", + 0x00, 0x24, #List Length + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + "155155B95269ED5C87EAA99C2EF5A593". + "EDF83495E80380089F831B94D14B1421", #key_exchange data + 0x00; #Trailing garbage + } + + $message->set_extension( + TLSProxy::Message::EXT_SUPPORTED_GROUPS, $suppgroups); + + if ($testtype == MISSING_EXTENSION) { + $message->delete_extension( + TLSProxy::Message::EXT_KEY_SHARE); + } elsif ($testtype != NOT_IN_SUPPORTED_GROUPS) { + $message->set_extension( + TLSProxy::Message::EXT_KEY_SHARE, $ext); + } + + $message->repack(); + } elsif ($message->mt == TLSProxy::Message::MT_SERVER_HELLO + && $direction == SERVER_TO_CLIENT) { + my $ext; + my $key_share = + ${$message->extension_data}{TLSProxy::Message::EXT_KEY_SHARE}; + $selectedgroupid = unpack("n", $key_share); + + if ($testtype == LOOK_ONLY) { + return; + } + if ($testtype == SELECT_X25519) { + $ext = pack "C4H64", + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + "155155B95269ED5C87EAA99C2EF5A593". + "EDF83495E80380089F831B94D14B1421"; #key_exchange data + } elsif ($testtype == GROUP_ID_TOO_SHORT) { + $ext = pack "C1", + 0x00; + } elsif ($testtype == KEX_LEN_MISMATCH) { + $ext = pack "C6", + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + 0x15, 0x51; #Only two bytes of data, but length should be 32 + } elsif ($testtype == ZERO_LEN_KEX_DATA) { + $ext = pack "C4", + 0x00, 0x1d, #x25519 + 0x00, 0x00, #zero length key_exchange data is invalid + } elsif ($testtype == TRAILING_DATA) { + $ext = pack "C4H64C1", + 0x00, 0x1d, #x25519 + 0x00, 0x20, #key_exchange data length + "155155B95269ED5C87EAA99C2EF5A593". + "EDF83495E80380089F831B94D14B1421", #key_exchange data + 0x00; #Trailing garbage + } + $message->set_extension( TLSProxy::Message::EXT_KEY_SHARE, $ext); + + $message->repack(); + } + } +} + + diff --git a/test/recipes/70-test_sslsessiontick.t b/test/recipes/70-test_sslsessiontick.t index 89ef12f..0c29ec7 100755 --- a/test/recipes/70-test_sslsessiontick.t +++ b/test/recipes/70-test_sslsessiontick.t @@ -229,7 +229,7 @@ sub checkmessages($$$$$$) $shellotickext = 1; } } - } elsif ($message->mt == TLSProxy::Message::MT_CLIENT_KEY_EXCHANGE) { + } elsif ($message->mt == TLSProxy::Message::MT_CERTIFICATE) { #Must be doing a full handshake $fullhand = 1; } elsif ($message->mt == TLSProxy::Message::MT_NEW_SESSION_TICKET) { diff --git a/test/recipes/70-test_tlsextms.t b/test/recipes/70-test_tlsextms.t index 1248594..dc6cf75 100644 --- a/test/recipes/70-test_tlsextms.t +++ b/test/recipes/70-test_tlsextms.t @@ -24,8 +24,8 @@ plan skip_all => "$test_name needs the dynamic engine feature enabled" plan skip_all => "$test_name needs the sock feature enabled" if disabled("sock"); -plan skip_all => "$test_name needs TLS enabled" - if alldisabled(available_protocols("tls")); +plan skip_all => "$test_name needs TLSv1.0, TLSv1.1 or TLSv1.2 enabled" + if disabled("tls1") && disabled("tls1_1") && disabled("tls1_2"); $ENV{OPENSSL_ia32cap} = '~0x200000200000000'; @@ -46,14 +46,19 @@ my $proxy = TLSProxy::Proxy->new( (!$ENV{HARNESS_ACTIVE} || $ENV{HARNESS_VERBOSE}) ); +#Note that EXTMS is only relevant for <TLS1.3 + #Test 1: By default server and client should send extended master secret # extension. #Expected result: ClientHello extension seen; ServerHello extension seen # Full handshake setrmextms(0, 0); +$proxy->clientflags("-no_tls1_3"); $proxy->start() or plan skip_all => "Unable to start up Proxy for tests"; -plan tests => 9; +my $numtests = 9; +$numtests++ if (!disabled("tls1_3")); +plan tests => $numtests; checkmessages(1, "Default extended master secret test", 1, 1, 1); #Test 2: If client omits extended master secret extension, server should too. @@ -62,6 +67,7 @@ checkmessages(1, "Default extended master secret test", 1, 1, 1); clearall(); setrmextms(1, 0); +$proxy->clientflags("-no_tls1_3"); $proxy->start(); checkmessages(2, "No client extension extended master secret test", 0, 0, 1); @@ -69,7 +75,7 @@ checkmessages(2, "No client extension extended master secret test", 0, 0, 1); # Expected result: same as test 1. clearall(); -$proxy->clientflags("-no_ticket"); +$proxy->clientflags("-no_ticket -no_tls1_3"); setrmextms(0, 0); $proxy->start(); checkmessages(3, "No ticket extended master secret test", 1, 1, 1); @@ -78,10 +84,10 @@ checkmessages(3, "No ticket extended master secret test", 1, 1, 1); # Expected result: same as test 2. clearall(); -$proxy->clientflags("-no_ticket"); +$proxy->clientflags("-no_ticket -no_tls1_3"); setrmextms(1, 0); $proxy->start(); -checkmessages(2, "No ticket, no client extension extended master secret test", 0, 0, 1); +checkmessages(4, "No ticket, no client extension extended master secret test", 0, 0, 1); #Test 5: Session resumption extended master secret test # @@ -92,10 +98,10 @@ clearall(); setrmextms(0, 0); (undef, my $session) = tempfile(); $proxy->serverconnects(2); -$proxy->clientflags("-sess_out ".$session); +$proxy->clientflags("-no_tls1_3 -sess_out ".$session); $proxy->start(); $proxy->clearClient(); -$proxy->clientflags("-sess_in ".$session); +$proxy->clientflags("-no_tls1_3 -sess_in ".$session); $proxy->clientstart(); checkmessages(5, "Session resumption extended master secret test", 1, 1, 0); unlink $session; @@ -109,10 +115,10 @@ clearall(); setrmextms(1, 0); (undef, $session) = tempfile(); $proxy->serverconnects(2); -$proxy->clientflags("-sess_out ".$session); +$proxy->clientflags("-no_tls1_3 -sess_out ".$session); $proxy->start(); $proxy->clearClient(); -$proxy->clientflags("-sess_in ".$session); +$proxy->clientflags("-no_tls1_3 -sess_in ".$session); setrmextms(0, 0); $proxy->clientstart(); checkmessages(6, "Session resumption extended master secret test", 1, 1, 1); @@ -126,10 +132,10 @@ clearall(); setrmextms(0, 0); (undef, $session) = tempfile(); $proxy->serverconnects(2); -$proxy->clientflags("-sess_out ".$session); +$proxy->clientflags("-no_tls1_3 -sess_out ".$session); $proxy->start(); $proxy->clearClient(); -$proxy->clientflags("-sess_in ".$session); +$proxy->clientflags("-no_tls1_3 -sess_in ".$session); setrmextms(1, 0); $proxy->clientstart(); ok(TLSProxy::Message->fail(), "Client inconsistent session resumption"); @@ -143,10 +149,10 @@ clearall(); setrmextms(0, 0); (undef, $session) = tempfile(); $proxy->serverconnects(2); -$proxy->clientflags("-sess_out ".$session); +$proxy->clientflags("-no_tls1_3 -sess_out ".$session); $proxy->start(); $proxy->clearClient(); -$proxy->clientflags("-sess_in ".$session); +$proxy->clientflags("-no_tls1_3 -sess_in ".$session); setrmextms(0, 1); $proxy->clientstart(); ok(TLSProxy::Message->fail(), "Server inconsistent session resumption 1"); @@ -160,15 +166,27 @@ clearall(); setrmextms(0, 1); (undef, $session) = tempfile(); $proxy->serverconnects(2); -$proxy->clientflags("-sess_out ".$session); +$proxy->clientflags("-no_tls1_3 -sess_out ".$session); $proxy->start(); $proxy->clearClient(); -$proxy->clientflags("-sess_in ".$session); +$proxy->clientflags("-no_tls1_3 -sess_in ".$session); setrmextms(0, 0); $proxy->clientstart(); ok(TLSProxy::Message->fail(), "Server inconsistent session resumption 2"); unlink $session; +#Test 10: In TLS1.3 we should not negotiate extended master secret +#Expected result: ClientHello extension seen; ServerHello extension not seen +# TLS1.3 handshake (will appear as abbreviated handshake +# because of no CKE message) +if (!disabled("tls1_3")) { + clearall(); + setrmextms(0, 0); + $proxy->start(); + checkmessages(10, "TLS1.3 extended master secret test", 1, 0, 0); +} + + sub extms_filter { my $proxy = shift; diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t index d89aa3c..65d2be9 100644 --- a/test/recipes/80-test_ssl_new.t +++ b/test/recipes/80-test_ssl_new.t @@ -62,7 +62,8 @@ my %conf_dependent_tests = ( # conditions. my %skip = ( "07-dtls-protocol-version.conf" => $no_dtls, - "08-npn.conf" => $no_tls || $no_npn, + "08-npn.conf" => (disabled("tls1") && disabled("tls1_1") + && disabled("tls1_2")) || $no_npn, "10-resumption.conf" => disabled("tls1_1") || disabled("tls1_2"), "11-dtls_resumption.conf" => disabled("dtls1") || disabled("dtls1_2"), "12-ct.conf" => $no_tls || $no_ct || $no_ec, diff --git a/test/ssl-tests/08-npn.conf b/test/ssl-tests/08-npn.conf index 9115ef4..f38b3f6 100644 --- a/test/ssl-tests/08-npn.conf +++ b/test/ssl-tests/08-npn.conf @@ -38,6 +38,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [0-npn-simple-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -69,6 +70,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [1-npn-client-finds-match-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -100,6 +102,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [2-npn-client-honours-server-pref-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -131,6 +134,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [3-npn-client-first-pref-on-mismatch-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -162,6 +166,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [4-npn-no-server-support-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -188,6 +193,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [5-npn-no-client-support-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -220,6 +226,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [6-npn-with-sni-no-context-switch-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -264,6 +271,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [7-npn-with-sni-context-switch-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -308,6 +316,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [8-npn-selected-sni-server-supports-npn-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -351,6 +360,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [9-npn-selected-sni-server-does-not-support-npn-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -384,6 +394,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [10-alpn-preferred-over-npn-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -423,6 +434,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [11-sni-npn-preferred-over-alpn-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -464,6 +476,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [12-npn-simple-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -506,6 +519,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [13-npn-server-switch-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -546,11 +560,13 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [14-npn-client-switch-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer [14-npn-client-switch-resumption-resume-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -596,6 +612,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [15-npn-client-first-pref-on-mismatch-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -641,6 +658,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [16-npn-no-server-support-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -676,11 +694,13 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [17-npn-no-client-support-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer [17-npn-no-client-support-resumption-resume-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -721,6 +741,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [18-alpn-preferred-over-npn-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer @@ -768,6 +789,7 @@ PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem [19-npn-used-if-alpn-not-supported-resumption-client] CipherString = DEFAULT +MaxProtocol = TLSv1.2 VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem VerifyMode = Peer diff --git a/test/ssl-tests/08-npn.conf.in b/test/ssl-tests/08-npn.conf.in index 8a1f4ec..b5df13d 100644 --- a/test/ssl-tests/08-npn.conf.in +++ b/test/ssl-tests/08-npn.conf.in @@ -7,14 +7,13 @@ # https://www.openssl.org/source/license.html -## Test version negotiation +## Test NPN. Note that NPN is only supported up to TLSv1.2 use strict; use warnings; package ssltests; - our @tests = ( { name => "npn-simple", @@ -27,6 +26,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedNPNProtocol" => "foo", @@ -43,6 +43,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo,bar", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedNPNProtocol" => "bar", @@ -59,6 +60,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo,bar", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedNPNProtocol" => "bar", @@ -75,6 +77,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo,bar", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedNPNProtocol" => "foo", @@ -82,11 +85,12 @@ our @tests = ( }, { name => "npn-no-server-support", - server => { }, + server => {}, client => { extra => { "NPNProtocols" => "foo", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedNPNProtocol" => undef, @@ -99,7 +103,9 @@ our @tests = ( "NPNProtocols" => "foo", }, }, - client => { }, + client => { + "MaxProtocol" => "TLSv1.2" + }, test => { "ExpectedNPNProtocol" => undef, }, @@ -122,6 +128,7 @@ our @tests = ( "NPNProtocols" => "foo,bar", "ServerName" => "server1", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedServerName" => "server1", @@ -146,6 +153,7 @@ our @tests = ( "NPNProtocols" => "foo,bar", "ServerName" => "server2", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedServerName" => "server2", @@ -169,6 +177,7 @@ our @tests = ( "NPNProtocols" => "foo,bar", "ServerName" => "server2", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedServerName" => "server2", @@ -189,6 +198,7 @@ our @tests = ( "NPNProtocols" => "foo,bar", "ServerName" => "server2", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedServerName" => "server2", @@ -208,6 +218,7 @@ our @tests = ( "ALPNProtocols" => "foo", "NPNProtocols" => "bar", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedALPNProtocol" => "foo", @@ -233,6 +244,7 @@ our @tests = ( "ALPNProtocols" => "foo", "NPNProtocols" => "bar", }, + "MaxProtocol" => "TLSv1.2" }, test => { "ExpectedALPNProtocol" => undef, @@ -251,6 +263,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo", }, + "MaxProtocol" => "TLSv1.2" }, test => { "HandshakeMode" => "Resume", @@ -274,6 +287,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo,bar,baz", }, + "MaxProtocol" => "TLSv1.2" }, test => { "HandshakeMode" => "Resume", @@ -292,11 +306,13 @@ our @tests = ( extra => { "NPNProtocols" => "foo,baz", }, + "MaxProtocol" => "TLSv1.2" }, resume_client => { extra => { "NPNProtocols" => "bar,baz", }, + "MaxProtocol" => "TLSv1.2" }, test => { "HandshakeMode" => "Resume", @@ -320,6 +336,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo,bar", }, + "MaxProtocol" => "TLSv1.2" }, test => { "HandshakeMode" => "Resume", @@ -339,6 +356,7 @@ our @tests = ( extra => { "NPNProtocols" => "foo", }, + "MaxProtocol" => "TLSv1.2" }, test => { "HandshakeMode" => "Resume", @@ -357,8 +375,11 @@ our @tests = ( extra => { "NPNProtocols" => "foo", }, + "MaxProtocol" => "TLSv1.2" + }, + resume_client => { + "MaxProtocol" => "TLSv1.2" }, - resume_client => { }, test => { "HandshakeMode" => "Resume", "ResumptionExpected" => "Yes", @@ -383,6 +404,7 @@ our @tests = ( "ALPNProtocols" => "foo", "NPNProtocols" => "bar,baz", }, + "MaxProtocol" => "TLSv1.2" }, test => { "HandshakeMode" => "Resume", @@ -409,6 +431,7 @@ our @tests = ( "ALPNProtocols" => "foo", "NPNProtocols" => "bar,baz", }, + "MaxProtocol" => "TLSv1.2" }, test => { "HandshakeMode" => "Resume", diff --git a/util/TLSProxy/Message.pm b/util/TLSProxy/Message.pm index 8a14dea..6bf5a72 100644 --- a/util/TLSProxy/Message.pm +++ b/util/TLSProxy/Message.pm @@ -59,10 +59,12 @@ my %message_type = ( use constant { EXT_STATUS_REQUEST => 5, + EXT_SUPPORTED_GROUPS => 10, EXT_ENCRYPT_THEN_MAC => 22, EXT_EXTENDED_MASTER_SECRET => 23, EXT_SESSION_TICKET => 35, EXT_SUPPORTED_VERSIONS => 43, + EXT_KEY_SHARE => 40, # This extension does not exist and isn't recognised by OpenSSL. # We use it to test handling of duplicate extensions. EXT_DUPLICATE_EXTENSION => 1234 _____ openssl-commits mailing list To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-commits