From 30f05b19d3bad0fb0b223f6b0c5c68bb667c3a5a Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Fri, 13 Jan 2017 09:19:10 +0000 Subject: [PATCH] Create the NewSessionTicket message in TLSv1.3 Reviewed-by: Rich Salz (Merged from https://github.com/openssl/openssl/pull/2259) --- ssl/statem/statem_clnt.c | 2 +- ssl/statem/statem_lib.c | 41 ++++++++++++++++++++++------------- ssl/statem/statem_locl.h | 2 +- ssl/statem/statem_srvr.c | 46 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 70 insertions(+), 21 deletions(-) diff --git a/ssl/statem/statem_clnt.c b/ssl/statem/statem_clnt.c index 35082ae572..563daf946f 100644 --- a/ssl/statem/statem_clnt.c +++ b/ssl/statem/statem_clnt.c @@ -602,7 +602,7 @@ WORK_STATE ossl_statem_client_pre_work(SSL *s, WORK_STATE wst) break; case TLS_ST_OK: - return tls_finish_handshake(s, wst); + return tls_finish_handshake(s, wst, 1); } return WORK_FINISHED_CONTINUE; diff --git a/ssl/statem/statem_lib.c b/ssl/statem/statem_lib.c index c81c012488..ad1466f9a9 100644 --- a/ssl/statem/statem_lib.c +++ b/ssl/statem/statem_lib.c @@ -821,7 +821,12 @@ unsigned long ssl3_output_cert_chain(SSL *s, WPACKET *pkt, CERT_PKEY *cpk, return 1; } -WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst) +/* + * Tidy up after the end of a handshake. In the case of SCTP this may result + * in NBIO events. If |clearbufs| is set then init_buf and the wbio buffer is + * freed up as well. + */ +WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst, int clearbufs) { void (*cb) (const SSL *ssl, int type, int val) = NULL; @@ -834,28 +839,27 @@ WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst) } #endif - /* clean a few things up */ - ssl3_cleanup_key_block(s); - - if (!SSL_IS_DTLS(s)) { - /* - * We don't do this in DTLS because we may still need the init_buf - * in case there are any unexpected retransmits - */ - BUF_MEM_free(s->init_buf); - s->init_buf = NULL; + if (clearbufs) { + if (!SSL_IS_DTLS(s)) { + /* + * We don't do this in DTLS because we may still need the init_buf + * in case there are any unexpected retransmits + */ + BUF_MEM_free(s->init_buf); + s->init_buf = NULL; + } + ssl_free_wbio_buffer(s); + s->init_num = 0; } - ssl_free_wbio_buffer(s); - - s->init_num = 0; - if (s->statem.cleanuphand) { /* skipped if we just sent a HelloRequest */ s->renegotiate = 0; s->new_session = 0; s->statem.cleanuphand = 0; + ssl3_cleanup_key_block(s); + if (s->server) { ssl_update_cache(s, SSL_SESS_CACHE_SERVER); @@ -887,6 +891,13 @@ WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst) } } + /* + * If we've not cleared the buffers its because we've got more work to do, + * so continue. + */ + if (!clearbufs) + return WORK_FINISHED_CONTINUE; + return WORK_FINISHED_STOP; } diff --git a/ssl/statem/statem_locl.h b/ssl/statem/statem_locl.h index be3167ce54..1eb971f736 100644 --- a/ssl/statem/statem_locl.h +++ b/ssl/statem/statem_locl.h @@ -107,7 +107,7 @@ __owur int tls_construct_change_cipher_spec(SSL *s, WPACKET *pkt); __owur int dtls_construct_change_cipher_spec(SSL *s, WPACKET *pkt); __owur int tls_construct_finished(SSL *s, WPACKET *pkt); -__owur WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst); +__owur WORK_STATE tls_finish_handshake(SSL *s, WORK_STATE wst, int clearbufs); __owur WORK_STATE dtls_wait_for_dry(SSL *s); /* some client-only functions */ diff --git a/ssl/statem/statem_srvr.c b/ssl/statem/statem_srvr.c index cb080aa10f..6b3d4f7749 100644 --- a/ssl/statem/statem_srvr.c +++ b/ssl/statem/statem_srvr.c @@ -439,6 +439,18 @@ static WRITE_TRAN ossl_statem_server13_write_transition(SSL *s) return WRITE_TRAN_FINISHED; case TLS_ST_SR_FINISHED: + /* + * Technically we have finished the handshake at this point, but we're + * going to remain "in_init" for now and write out the session ticket + * immediately. + * TODO(TLS1.3): Perhaps we need to be able to control this behaviour + * and give the application the opportunity to delay sending the + * session ticket? + */ + st->hand_state = TLS_ST_SW_SESSION_TICKET; + return WRITE_TRAN_CONTINUE; + + case TLS_ST_SW_SESSION_TICKET: st->hand_state = TLS_ST_OK; ossl_statem_set_in_init(s, 0); return WRITE_TRAN_CONTINUE; @@ -626,7 +638,14 @@ WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst) return WORK_FINISHED_CONTINUE; case TLS_ST_SW_SESSION_TICKET: - if (SSL_IS_DTLS(s)) { + if (SSL_IS_TLS13(s)) { + /* + * Actually this is the end of the handshake, but we're going + * straight into writing the session ticket out. So we finish off + * the handshake, but keep the various buffers active. + */ + return tls_finish_handshake(s, wst, 0); + } if (SSL_IS_DTLS(s)) { /* * We're into the last flight. We don't retransmit the last flight * unless we need to, so we don't use the timer @@ -653,7 +672,7 @@ WORK_STATE ossl_statem_server_pre_work(SSL *s, WORK_STATE wst) return WORK_FINISHED_CONTINUE; case TLS_ST_OK: - return tls_finish_handshake(s, wst); + return tls_finish_handshake(s, wst, 1); } return WORK_FINISHED_CONTINUE; @@ -788,6 +807,11 @@ WORK_STATE ossl_statem_server_post_work(SSL *s, WORK_STATE wst) return WORK_ERROR; } break; + + case TLS_ST_SW_SESSION_TICKET: + if (SSL_IS_TLS13(s) && statem_flush(s) != 1) + return WORK_MORE_A; + break; } return WORK_FINISHED_CONTINUE; @@ -3219,8 +3243,12 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt) SSL_CTX *tctx = s->initial_ctx; unsigned char iv[EVP_MAX_IV_LENGTH]; unsigned char key_name[TLSEXT_KEYNAME_LENGTH]; - int iv_len; + int iv_len, al = SSL_AD_INTERNAL_ERROR; size_t macoffset, macendoffset; + union { + unsigned char age_add_c[sizeof(uint32_t)]; + uint32_t age_add; + } age_add_u; /* get session encoding length */ slen_full = i2d_SSL_SESSION(s->session, NULL); @@ -3313,12 +3341,18 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt) sizeof(tctx->ext.tick_key_name)); } + if (SSL_IS_TLS13(s) && RAND_bytes(age_add_u.age_add_c, + sizeof(age_add_u)) <= 0) + goto err; + /* * Ticket lifetime hint (advisory only): We leave this unspecified * for resumed session (for simplicity), and guess that tickets for * new sessions will live as long as their sessions. */ if (!WPACKET_put_bytes_u32(pkt, s->hit ? 0 : s->session->timeout) + || (SSL_IS_TLS13(s) + && !WPACKET_put_bytes_u32(pkt, age_add_u.age_add)) /* Now the actual ticket data */ || !WPACKET_start_sub_packet_u16(pkt) || !WPACKET_get_total_written(pkt, &macoffset) @@ -3345,7 +3379,11 @@ int tls_construct_new_session_ticket(SSL *s, WPACKET *pkt) || hlen > EVP_MAX_MD_SIZE || !WPACKET_allocate_bytes(pkt, hlen, &macdata2) || macdata1 != macdata2 - || !WPACKET_close(pkt)) { + || !WPACKET_close(pkt) + || (SSL_IS_TLS13(s) + && !tls_construct_extensions(s, pkt, + EXT_TLS1_3_NEW_SESSION_TICKET, + NULL, 0, &al))) { SSLerr(SSL_F_TLS_CONSTRUCT_NEW_SESSION_TICKET, ERR_R_INTERNAL_ERROR); goto err; } -- 2.25.1