diff options
author | Ander Juaristi <a@juaristi.eus> | 2017-11-05 18:29:27 +0100 |
---|---|---|
committer | Ander Juaristi <a@juaristi.eus> | 2017-11-06 21:33:02 +0100 |
commit | aab6a8664a6ae707f45c9c2ff47d957f5d83d044 (patch) | |
tree | a3788cb4b8935431403ebae0ef4c2be8a02eecef | |
parent | 90a81a94da76bdfb33f9ba19ee68a1cc803421a2 (diff) | |
download | gnutls-aab6a8664a6ae707f45c9c2ff47d957f5d83d044.tar.gz |
Incremental handshake hash buildertmp-draft-ietf-tls-tls13-21-ajuaristi-incremental-hash-buffer-2
Signed-off-by: Ander Juaristi <a@juaristi.eus>
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/handshake-msg.c | 234 | ||||
-rw-r--r-- | lib/handshake-msg.h | 28 | ||||
-rw-r--r-- | lib/handshake.c | 176 | ||||
-rw-r--r-- | lib/handshake.h | 1 | ||||
-rw-r--r-- | lib/hello_ext.c | 15 | ||||
-rw-r--r-- | lib/hello_ext.h | 4 | ||||
-rw-r--r-- | lib/tls13/encrypted_extensions.c | 2 |
8 files changed, 312 insertions, 150 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 808e1bd350..c49ae3ac2e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -66,7 +66,7 @@ SRP_COBJECTS = srp.c PSK_COBJECTS = psk.c COBJECTS = range.c record.c compress.c debug.c cipher.c handshake-tls13.c \ - mbuffers.c buffers.c handshake.c num.c errors.c dh.c kx.c \ + mbuffers.c buffers.c handshake.c handshake-msg.c num.c errors.c dh.c kx.c \ priority.c hash_int.c cipher_int.c session.c db.c x509_b64.c \ hello_ext.c auth.c sslv2_compat.c datum.c session_pack.c mpi.c \ pk.c cert.c global.c constate.c anon_cred.c pkix_asn1_tab.c gnutls_asn1_tab.c \ diff --git a/lib/handshake-msg.c b/lib/handshake-msg.c new file mode 100644 index 0000000000..f64282d6dc --- /dev/null +++ b/lib/handshake-msg.c @@ -0,0 +1,234 @@ +#include "gnutls_int.h" +#include "handshake.h" +#include "handshake-msg.h" +#include "mbuffers.h" + +#define CHECK_SIZE(ll) \ + if ((session->internals.max_handshake_data_buffer_size > 0) && \ + (((ll) + session->internals.handshake_hash_buffer.length) > \ + session->internals.max_handshake_data_buffer_size)) { \ + _gnutls_debug_log("Handshake buffer length is %u (max: %u)\n", (unsigned)((ll) + session->internals.handshake_hash_buffer.length), (unsigned)session->internals.max_handshake_data_buffer_size); \ + return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE); \ + } + +struct handshake_msg_st +{ + gnutls_handshake_description_t type; + size_t committed_bytes; + gnutls_buffer_st buf; +}; + +int _gnutls_handshake_msg_init(struct handshake_msg_st **out, + gnutls_handshake_description_t type, + gnutls_session_t session) +{ + struct handshake_msg_st *hs = _gnutls_calloc(1, sizeof(struct handshake_msg_st)); + + hs->type = type; + _gnutls_buffer_init(&hs->buf); + + *out = hs; + return GNUTLS_E_SUCCESS; +} + +void _gnutls_handshake_msg_deinit(struct handshake_msg_st **hs) +{ + _gnutls_buffer_clear(&(*hs)->buf); + gnutls_free(*hs); + *hs = NULL; +} + +/* This function add the handshake headers and the + * handshake data to the handshake hash buffers. Needed + * for the finished messages calculations. + */ +int +_gnutls_handshake_hash_add_recvd(gnutls_session_t session, + gnutls_handshake_description_t recv_type, + uint8_t * header, uint16_t header_size, + uint8_t * dataptr, uint32_t datalen) +{ + int ret; + const version_entry_st *vers = get_version(session); + + if (unlikely(vers == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if ((vers->id != GNUTLS_DTLS0_9 && + recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) || + recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST) + return 0; + + CHECK_SIZE(header_size + datalen); + + session->internals.handshake_hash_buffer_prev_len = + session->internals.handshake_hash_buffer.length; + + if (vers->id != GNUTLS_DTLS0_9) { + ret = + _gnutls_buffer_append_data(&session->internals. + handshake_hash_buffer, + header, header_size); + if (ret < 0) + return gnutls_assert_val(ret); + } + if (datalen > 0) { + ret = + _gnutls_buffer_append_data(&session->internals. + handshake_hash_buffer, + dataptr, datalen); + if (ret < 0) + return gnutls_assert_val(ret); + } + + /* save the size until client KX. That is because the TLS + * session hash is calculated up to this message. + */ + if (recv_type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE) + session->internals.handshake_hash_buffer_client_kx_len = + session->internals.handshake_hash_buffer.length; + if (recv_type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_CLIENT) + session->internals.handshake_hash_buffer_server_finished_len = + session->internals.handshake_hash_buffer.length; + + return 0; +} + +static int +handshake_hash_add_sent(gnutls_session_t session, + gnutls_handshake_description_t type, + uint8_t *dataptr, uint32_t datalen, + gnutls_buffer_st *dst_buf) +{ + int ret; + const version_entry_st *vers = get_version(session); + + if (unlikely(vers == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) { + CHECK_SIZE(datalen); + + if (vers->id == GNUTLS_DTLS0_9) { + /* Old DTLS doesn't include the header in the MAC */ + if (datalen < 12) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + dataptr += 12; + datalen -= 12; + + if (datalen == 0) + return 0; + } + + ret = + _gnutls_buffer_append_data(dst_buf, + dataptr, datalen); + if (ret < 0) + return gnutls_assert_val(ret); + } + + return GNUTLS_E_SUCCESS; +} + +/* This function will store the handshake message we sent. + */ +int +_gnutls_handshake_hash_add_sent(gnutls_session_t session, + gnutls_handshake_description_t type, + uint8_t * dataptr, uint32_t datalen) +{ + int ret; + + /* We don't check for GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST because it + * is not sent via that channel. + */ + if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) { + ret = handshake_hash_add_sent(session, type, + dataptr, datalen, + &session->internals.handshake_hash_buffer); + if (ret < 0) + return gnutls_assert_val(ret); + + if (type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE) + session->internals.handshake_hash_buffer_client_kx_len = + session->internals.handshake_hash_buffer.length; + if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER) + session->internals.handshake_hash_buffer_server_finished_len = + session->internals.handshake_hash_buffer.length; + + return 0; + } + + return 0; +} + +static int +handshake_commit(gnutls_session_t session, + struct handshake_msg_st *hs, + uint8_t *data, uint32_t datasize, + size_t head_skip_bytes, size_t header_length) +{ + int ret; + size_t to_commit, offset, prev_committed_bytes; + + /* This message is not taken into account for the hash */ + if (hs->type == GNUTLS_HANDSHAKE_HELLO_REQUEST) + return GNUTLS_E_SUCCESS; + + prev_committed_bytes = hs->committed_bytes; + + if (datasize > hs->committed_bytes) { + to_commit = datasize - hs->committed_bytes - head_skip_bytes; + offset = hs->committed_bytes + head_skip_bytes; + + ret = handshake_hash_add_sent(session, hs->type, + data + offset, to_commit, + &hs->buf); + if (ret < 0) + return gnutls_assert_val(ret); + + hs->committed_bytes += to_commit; + } + + if (hs->committed_bytes > prev_committed_bytes) { + /* Update type field */ + hs->buf.data[0] = (uint8_t) hs->type; + /* Update size field */ + _gnutls_write_uint24(hs->committed_bytes - header_length, + &hs->buf.data[1]); + } + + return GNUTLS_E_SUCCESS; +} + +int _gnutls_handshake_msg_commit_from_buffer(gnutls_session_t session, + struct handshake_msg_st *hs, + gnutls_buffer_st *buf, + size_t head_skip_bytes, + size_t header_length) +{ + uint8_t *data; + uint32_t datasize; + + if (!hs || !buf) + return GNUTLS_E_INTERNAL_ERROR; + + data = buf->data; + datasize = buf->length; + + return handshake_commit(session, hs, + data, datasize, + head_skip_bytes, header_length); +} + +int _gnutls_handshake_msg_copy(struct handshake_msg_st *hs, + gnutls_buffer_st *dst_buf) +{ + if (!hs || !dst_buf) + return GNUTLS_E_INTERNAL_ERROR; + + _gnutls_buffer_init(dst_buf); + return _gnutls_buffer_append_data(dst_buf, hs->buf.data, hs->buf.length); +} diff --git a/lib/handshake-msg.h b/lib/handshake-msg.h new file mode 100644 index 0000000000..b9de7af008 --- /dev/null +++ b/lib/handshake-msg.h @@ -0,0 +1,28 @@ +#ifndef HANDSHAKE_MSG_H +#define HANDSHAKE_MSG_H + +struct handshake_msg_st; + +int _gnutls_handshake_msg_init(struct handshake_msg_st **out, + gnutls_handshake_description_t type, + gnutls_session_t session); +void _gnutls_handshake_msg_deinit(struct handshake_msg_st **hs); + +int _gnutls_handshake_hash_add_sent(gnutls_session_t session, + gnutls_handshake_description_t type, + uint8_t * dataptr, uint32_t datalen); +int _gnutls_handshake_hash_add_recvd(gnutls_session_t session, + gnutls_handshake_description_t recv_type, + uint8_t *header, uint16_t header_size, + uint8_t *dataptr, uint32_t datalen); + +int _gnutls_handshake_msg_commit_from_buffer(gnutls_session_t session, + struct handshake_msg_st *hs, + gnutls_buffer_st *buf, + size_t head_skip_bytes, + size_t header_length); + +int _gnutls_handshake_msg_copy(struct handshake_msg_st *hs, + gnutls_buffer_st *dst_buf); + +#endif diff --git a/lib/handshake.c b/lib/handshake.c index da0f41cc05..315267f08f 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -34,6 +34,7 @@ #include "mbuffers.h" #include "kx.h" #include "handshake.h" +#include "handshake-msg.h" #include "num.h" #include "hash_int.h" #include "db.h" @@ -87,17 +88,6 @@ handshake_hash_buffer_empty(gnutls_session_t session) } static int -handshake_hash_add_recvd(gnutls_session_t session, - gnutls_handshake_description_t recv_type, - uint8_t * header, uint16_t header_size, - uint8_t * dataptr, uint32_t datalen); - -static int -handshake_hash_add_sent(gnutls_session_t session, - gnutls_handshake_description_t type, - uint8_t * dataptr, uint32_t datalen); - -static int recv_hello_verify_request(gnutls_session_t session, uint8_t * data, int datalen); @@ -1036,10 +1026,9 @@ int _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel, gnutls_handshake_description_t type) { - int ret; + int pos = 0, ret; uint8_t *data; uint32_t datasize, i_datasize; - int pos = 0; if (bufel == NULL) { /* we are resuming a previously interrupted @@ -1047,7 +1036,6 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel, */ ret = _gnutls_handshake_io_write_flush(session); return ret; - } /* first run */ @@ -1081,16 +1069,12 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel, session, _gnutls_handshake2str(type), (long) datasize); - /* Here we keep the handshake messages in order to hash them... - */ - if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) - if ((ret = - handshake_hash_add_sent(session, type, data, - datasize)) < 0) { - gnutls_assert(); - _mbuffer_xfree(&bufel); - return ret; - } + ret = _gnutls_handshake_hash_add_sent(session, type, data, datasize); + if (ret < 0) { + gnutls_assert(); + _mbuffer_xfree(&bufel); + return ret; + } ret = call_hook_func(session, type, GNUTLS_HOOK_PRE, 0, _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel)); @@ -1144,122 +1128,6 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel, return ret; } -#define CHECK_SIZE(ll) \ - if ((session->internals.max_handshake_data_buffer_size > 0) && \ - (((ll) + session->internals.handshake_hash_buffer.length) > \ - session->internals.max_handshake_data_buffer_size)) { \ - _gnutls_debug_log("Handshake buffer length is %u (max: %u)\n", (unsigned)((ll) + session->internals.handshake_hash_buffer.length), (unsigned)session->internals.max_handshake_data_buffer_size); \ - return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE); \ - } - -/* This function add the handshake headers and the - * handshake data to the handshake hash buffers. Needed - * for the finished messages calculations. - */ -static int -handshake_hash_add_recvd(gnutls_session_t session, - gnutls_handshake_description_t recv_type, - uint8_t * header, uint16_t header_size, - uint8_t * dataptr, uint32_t datalen) -{ - int ret; - const version_entry_st *vers = get_version(session); - - if (unlikely(vers == NULL)) - return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); - - if ((vers->id != GNUTLS_DTLS0_9 && - recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) || - recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST) - return 0; - - CHECK_SIZE(header_size + datalen); - - session->internals.handshake_hash_buffer_prev_len = - session->internals.handshake_hash_buffer.length; - - if (vers->id != GNUTLS_DTLS0_9) { - ret = - _gnutls_buffer_append_data(&session->internals. - handshake_hash_buffer, - header, header_size); - if (ret < 0) - return gnutls_assert_val(ret); - } - if (datalen > 0) { - ret = - _gnutls_buffer_append_data(&session->internals. - handshake_hash_buffer, - dataptr, datalen); - if (ret < 0) - return gnutls_assert_val(ret); - } - - /* save the size until client KX. That is because the TLS - * session hash is calculated up to this message. - */ - if (recv_type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE) - session->internals.handshake_hash_buffer_client_kx_len = - session->internals.handshake_hash_buffer.length; - if (recv_type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_CLIENT) - session->internals.handshake_hash_buffer_server_finished_len = - session->internals.handshake_hash_buffer.length; - - return 0; -} - -/* This function will store the handshake message we sent. - */ -static int -handshake_hash_add_sent(gnutls_session_t session, - gnutls_handshake_description_t type, - uint8_t * dataptr, uint32_t datalen) -{ - int ret; - const version_entry_st *vers = get_version(session); - - if (unlikely(vers == NULL)) - return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); - - /* We don't check for GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST because it - * is not sent via that channel. - */ - if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) { - CHECK_SIZE(datalen); - - if (vers->id == GNUTLS_DTLS0_9) { - /* Old DTLS doesn't include the header in the MAC */ - if (datalen < 12) { - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; - } - dataptr += 12; - datalen -= 12; - - if (datalen == 0) - return 0; - } - - ret = - _gnutls_buffer_append_data(&session->internals. - handshake_hash_buffer, - dataptr, datalen); - if (ret < 0) - return gnutls_assert_val(ret); - - if (type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE) - session->internals.handshake_hash_buffer_client_kx_len = - session->internals.handshake_hash_buffer.length; - if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER) - session->internals.handshake_hash_buffer_server_finished_len = - session->internals.handshake_hash_buffer.length; - - return 0; - } - - return 0; -} - /* This function will receive handshake messages of the given types, * and will pass the message to the right place in order to be processed. * E.g. for the SERVER_HELLO message (if it is expected), it will be @@ -1292,7 +1160,7 @@ _gnutls_recv_handshake(gnutls_session_t session, goto cleanup; } - ret = handshake_hash_add_recvd(session, hsk.htype, + ret = _gnutls_handshake_hash_add_recvd(session, hsk.htype, hsk.header, hsk.header_size, hsk.data.data, hsk.data.length); @@ -1707,6 +1575,8 @@ static int send_client_hello(gnutls_session_t session, int again) unsigned add_sr_scsv = 0; uint8_t session_id_len = session->internals.resumed_security_parameters.session_id_size; + struct handshake_msg_st *hs = NULL; + gnutls_handshake_description_t hs_type = GNUTLS_HANDSHAKE_CLIENT_HELLO; /* note that rehandshake is different than resuming */ @@ -1714,6 +1584,10 @@ static int send_client_hello(gnutls_session_t session, int again) rehandshake = 1; if (again == 0) { + ret = _gnutls_handshake_msg_init(&hs, hs_type, session); + if (ret < 0) + return gnutls_assert_val(ret); + ret = _gnutls_buffer_init_handshake_mbuffer(&extdata); if (ret < 0) return gnutls_assert_val(ret); @@ -1852,6 +1726,16 @@ static int send_client_hello(gnutls_session_t session, int again) goto cleanup; } + /* Set the first chunk of send data */ + if (!IS_DTLS(session)) { + ret = _gnutls_handshake_msg_commit_from_buffer(session, hs, + &extdata, sizeof(mbuffer_st), HANDSHAKE_HEADER_SIZE(session)); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + /* Generate and copy TLS extensions. */ if (session->internals.priorities->no_extensions == 0) { @@ -1864,7 +1748,7 @@ static int send_client_hello(gnutls_session_t session, int again) ret = _gnutls_gen_hello_extensions(session, &extdata, GNUTLS_EXT_FLAG_CLIENT_HELLO, - type); + type, hs); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -1872,13 +1756,15 @@ static int send_client_hello(gnutls_session_t session, int again) } bufel = _gnutls_buffer_to_mbuffer(&extdata); + _gnutls_handshake_msg_deinit(&hs); } return - _gnutls_send_handshake(session, bufel, - GNUTLS_HANDSHAKE_CLIENT_HELLO); + _gnutls_send_handshake(session, bufel, hs_type); cleanup: + if (hs) + _gnutls_handshake_msg_deinit(&hs); _gnutls_buffer_clear(&extdata); return ret; } @@ -1972,7 +1858,7 @@ static int send_server_hello(gnutls_session_t session, int again) (session->internals.resumed == RESUME_TRUE) ? GNUTLS_EXT_MANDATORY : - GNUTLS_EXT_ANY); + GNUTLS_EXT_ANY, NULL); if (ret < 0) { gnutls_assert(); goto fail; diff --git a/lib/handshake.h b/lib/handshake.h index 79ac19eb7a..68d3f0d2c7 100644 --- a/lib/handshake.h +++ b/lib/handshake.h @@ -26,6 +26,7 @@ #include "errors.h" #include "record.h" +#include "handshake-msg.h" #define IMED_RET( str, ret, allow_alert) do { \ if (ret < 0) { \ diff --git a/lib/hello_ext.c b/lib/hello_ext.c index 55bfd6df64..f2006bc0c7 100644 --- a/lib/hello_ext.c +++ b/lib/hello_ext.c @@ -29,6 +29,7 @@ #include "gnutls_int.h" #include "hello_ext.h" #include "errors.h" +#include "handshake-msg.h" #include "ext/max_record.h" #include <ext/server_name.h> #include <ext/srp.h> @@ -307,7 +308,8 @@ int _gnutls_gen_hello_extensions(gnutls_session_t session, gnutls_buffer_st * buf, gnutls_ext_flags_t msg, - gnutls_ext_parse_type_t parse_type) + gnutls_ext_parse_type_t parse_type, + struct handshake_msg_st *hs) { int pos, ret; size_t i; @@ -345,10 +347,19 @@ _gnutls_gen_hello_extensions(gnutls_session_t session, if (ret < 0) return gnutls_assert_val(ret); - if (ret > 0) + if (ret > 0) { _gnutls_handshake_log ("EXT[%p]: Sending extension %s/%d (%d bytes)\n", session, ctx.ext->name, (int)ctx.ext->tls_id, ret-4); + + /* Update handshake send data buffer (if provided) */ + if (hs && !IS_DTLS(session)) { + ret = _gnutls_handshake_msg_commit_from_buffer(session, hs, + buf, sizeof(mbuffer_st), HANDSHAKE_HEADER_SIZE(session)); + if (ret < 0) + return gnutls_assert_val(ret); + } + } } ret = _gnutls_extv_append_final(buf, pos); diff --git a/lib/hello_ext.h b/lib/hello_ext.h index 13c0515588..9df67b2279 100644 --- a/lib/hello_ext.h +++ b/lib/hello_ext.h @@ -24,6 +24,7 @@ #define GNUTLS_EXTENSIONS_H #include <gnutls/gnutls.h> +#include "handshake-msg.h" /* Functions for hello extension parsing. */ @@ -34,7 +35,8 @@ int _gnutls_parse_hello_extensions(gnutls_session_t session, int _gnutls_gen_hello_extensions(gnutls_session_t session, gnutls_buffer_st * extdata, gnutls_ext_flags_t msg, - gnutls_ext_parse_type_t); + gnutls_ext_parse_type_t parse_type, + struct handshake_msg_st *hs); int _gnutls_hello_ext_init(void); void _gnutls_hello_ext_deinit(void); diff --git a/lib/tls13/encrypted_extensions.c b/lib/tls13/encrypted_extensions.c index ba63b95337..395919c6a8 100644 --- a/lib/tls13/encrypted_extensions.c +++ b/lib/tls13/encrypted_extensions.c @@ -58,7 +58,7 @@ int _gnutls13_send_encrypted_extensions(gnutls_session_t session, unsigned again if (ret < 0) return gnutls_assert_val(ret); - ret = _gnutls_gen_hello_extensions(session, &buf, GNUTLS_EXT_FLAG_EE, GNUTLS_EXT_ANY); + ret = _gnutls_gen_hello_extensions(session, &buf, GNUTLS_EXT_FLAG_EE, GNUTLS_EXT_ANY, NULL); if (ret < 0) { gnutls_assert(); goto cleanup; |