diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-07-18 13:48:13 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-11-14 15:00:32 +0100 |
commit | 8b421ba60de6a3ddf0cc1258b083fa062da12f11 (patch) | |
tree | 736dd9f1c8ec356b909822c78b1480858e8ebedf | |
parent | bdd613d9d57be2ea4a33620cb500a7f8c651e1cd (diff) | |
download | gnutls-8b421ba60de6a3ddf0cc1258b083fa062da12f11.tar.gz |
handshake: introduced TLS 1.3 handshake client state machine outline
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/debug.c | 22 | ||||
-rw-r--r-- | lib/gnutls_int.h | 4 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 182 | ||||
-rw-r--r-- | lib/handshake.c | 63 | ||||
-rw-r--r-- | lib/handshake.h | 35 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 5 |
7 files changed, 262 insertions, 51 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 67b670d45b..389fe4e3d4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -65,7 +65,7 @@ SRP_COBJECTS = srp.c PSK_COBJECTS = psk.c -COBJECTS = range.c record.c compress.c debug.c cipher.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 \ 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 \ diff --git a/lib/debug.c b/lib/debug.c index 252f2470a6..6a6aa1c94c 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -84,56 +84,46 @@ const char type) { switch (type) { + case GNUTLS_HANDSHAKE_END_OF_EARLY_DATA: + return "END OF EARLY DATA"; + case GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST: + return "HELLO RETRY REQUEST"; case GNUTLS_HANDSHAKE_HELLO_REQUEST: return "HELLO REQUEST"; - break; case GNUTLS_HANDSHAKE_CLIENT_HELLO: return "CLIENT HELLO"; - break; #ifdef ENABLE_SSL2 case GNUTLS_HANDSHAKE_CLIENT_HELLO_V2: return "SSL2 CLIENT HELLO"; - break; #endif case GNUTLS_HANDSHAKE_SERVER_HELLO: return "SERVER HELLO"; - break; case GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST: return "HELLO VERIFY REQUEST"; - break; case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: return "CERTIFICATE"; - break; + case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: + return "ENCRYPTED EXTENSIONS"; case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: return "SERVER KEY EXCHANGE"; - break; case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: return "CERTIFICATE REQUEST"; - break; case GNUTLS_HANDSHAKE_SERVER_HELLO_DONE: return "SERVER HELLO DONE"; - break; case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: return "CERTIFICATE VERIFY"; - break; case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE: return "CLIENT KEY EXCHANGE"; - break; case GNUTLS_HANDSHAKE_FINISHED: return "FINISHED"; - break; case GNUTLS_HANDSHAKE_SUPPLEMENTAL: return "SUPPLEMENTAL"; - break; case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS: return "CERTIFICATE STATUS"; - break; case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: return "NEW SESSION TICKET"; - break; case GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC: return "CHANGE CIPHER SPEC"; - break; default: return "Unknown Handshake packet"; } diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 008c99263d..dcbcfe4f65 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -238,7 +238,9 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2, STATE9, STATE10, STATE11, STATE12, STATE13, STATE14, STATE15, STATE16, STATE17, STATE18, STATE19, STATE20 = 20, STATE21, STATE22, - STATE30 = 30, STATE31, STATE40 = 40, STATE41, STATE50 = 50 + STATE30 = 30, STATE31, STATE40 = 40, STATE41, STATE50 = 50, + STATE100=100, STATE101, STATE102, STATE103, STATE104, + STATE105, STATE106, STATE107, STATE108, STATE109 } handshake_state_t; typedef enum bye_state_t { diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c new file mode 100644 index 0000000000..a61d1bfc45 --- /dev/null +++ b/lib/handshake-tls13.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +/* Functions that relate to the TLS handshake procedure. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "dh.h" +#include "debug.h" +#include "algorithms.h" +#include "cipher.h" +#include "buffers.h" +#include "mbuffers.h" +#include "kx.h" +#include "handshake.h" +#include "num.h" +#include "hash_int.h" +#include "db.h" +#include "hello_ext.h" +#include "supplemental.h" +#include "auth.h" +#include "sslv2_compat.h" +#include <auth/cert.h> +#include "constate.h" +#include <record.h> +#include <state.h> +#include <random.h> +#include <dtls.h> + +/* + * _gnutls13_handshake_client + * This function performs the client side of the handshake of the TLS/SSL protocol. + */ +int _gnutls13_handshake_client(gnutls_session_t session) +{ + int ret = 0; + + switch (STATE) { + case STATE100: + abort(); + STATE = STATE100; + IMED_RET("recv encrypted extensions", ret, 0); + /* fall through */ + case STATE101: + abort(); + STATE = STATE101; + IMED_RET("recv certificate request", ret, 0); + /* fall through */ + case STATE102: + abort(); + STATE = STATE102; + IMED_RET("recv certificate", ret, 0); + /* fall through */ + case STATE103: + abort(); + STATE = STATE103; + IMED_RET("recv server certificate verify", ret, 0); + /* fall through */ + case STATE104: + ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT); + STATE = STATE102; + if (ret < 0) + return gnutls_assert_val(ret); + FALLTHROUGH; + case STATE105: + abort(); + STATE = STATE105; + IMED_RET("recv finished", ret, 0); + /* fall through */ + case STATE106: + abort(); + STATE = STATE106; + IMED_RET("send certificate", ret, 0); + /* fall through */ + case STATE107: + abort(); + STATE = STATE107; + IMED_RET("send certificate verify", ret, 0); + /* fall through */ + case STATE108: + abort(); + STATE = STATE108; + IMED_RET("send finished", ret, 0); + + STATE = STATE0; + break; + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + + /* explicitly reset any false start flags */ + session->internals.recv_state = RECV_STATE_0; + + return 0; +} + +/* + * _gnutls13_handshake_server + * This function does the server stuff of the handshake protocol. + */ +int _gnutls13_handshake_server(gnutls_session_t session) +{ + int ret = 0; + + switch (STATE) { + case STATE100: + case STATE101: + abort(); + STATE = STATE101; + IMED_RET("send encrypted extensions", ret, 0); + /* fall through */ + case STATE102: + abort(); + STATE = STATE102; + IMED_RET("send certificate request", ret, 0); + /* fall through */ + case STATE103: + abort(); + STATE = STATE103; + IMED_RET("send certificate", ret, 0); + /* fall through */ + case STATE104: + abort(); + STATE = STATE104; + IMED_RET("send certificate verify", ret, 0); + /* fall through */ + case STATE105: + abort(); + STATE = STATE105; + IMED_RET("send finished", ret, 0); + /* fall through */ + case STATE106: + abort(); + STATE = STATE106; + IMED_RET("recv certificate", ret, 0); + /* fall through */ + case STATE107: + abort(); + STATE = STATE107; + IMED_RET("recv certificate verify", ret, 0); + /* fall through */ + case STATE108: + ret = _gnutls_run_verify_callback(session, GNUTLS_SERVER); + STATE = STATE108; + if (ret < 0) + return gnutls_assert_val(ret); + /* fall through */ + case STATE109: + abort(); + STATE = STATE109; + IMED_RET("recv finished", ret, 0); + /* fall through */ + + STATE = STATE0; + break; + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + + return 0; +} + diff --git a/lib/handshake.c b/lib/handshake.c index 11993dc259..fec06283e1 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -672,7 +672,7 @@ read_client_hello(gnutls_session_t session, uint8_t * data, * and initializing encryption. This is the first encrypted message * we send. */ -static int _gnutls_send_finished(gnutls_session_t session, int again) +int _gnutls_send_finished(gnutls_session_t session, int again) { mbuffer_st *bufel; uint8_t *data; @@ -757,7 +757,7 @@ static int _gnutls_send_finished(gnutls_session_t session, int again) /* This is to be called after sending our finished message. If everything * went fine we have negotiated a secure connection */ -static int _gnutls_recv_finished(gnutls_session_t session) +int _gnutls_recv_finished(gnutls_session_t session) { uint8_t data[MAX_VERIFY_DATA_SIZE], *vrfy; gnutls_buffer_st buf; @@ -1342,6 +1342,7 @@ _gnutls_recv_handshake(gnutls_session_t session, case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS: case GNUTLS_HANDSHAKE_FINISHED: + case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE: case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: @@ -2339,38 +2340,10 @@ gnutls_handshake_set_timeout(gnutls_session_t session, unsigned int ms) session->internals.handshake_timeout_ms = ms; } - -#define IMED_RET( str, ret, allow_alert) do { \ - if (ret < 0) { \ - /* EAGAIN and INTERRUPTED are always non-fatal */ \ - if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) \ - return ret; \ - if (ret == GNUTLS_E_GOT_APPLICATION_DATA && session->internals.initial_negotiation_completed != 0) \ - return ret; \ - if (session->internals.handshake_suspicious_loops < 16) { \ - if (ret == GNUTLS_E_LARGE_PACKET) { \ - session->internals.handshake_suspicious_loops++; \ - return ret; \ - } \ - /* a warning alert might interrupt handshake */ \ - if (allow_alert != 0 && ret==GNUTLS_E_WARNING_ALERT_RECEIVED) { \ - session->internals.handshake_suspicious_loops++; \ - return ret; \ - } \ - } \ - gnutls_assert(); \ - /* do not allow non-fatal errors at this point */ \ - if (gnutls_error_is_fatal(ret) == 0) ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); \ - session_invalidate(session); \ - _gnutls_handshake_hash_buffers_clear(session); \ - return ret; \ - } } while (0) - - /* Runs the certificate verification callback. * side is either GNUTLS_CLIENT or GNUTLS_SERVER. */ -static int run_verify_callback(gnutls_session_t session, unsigned int side) +int _gnutls_run_verify_callback(gnutls_session_t session, unsigned int side) { gnutls_certificate_credentials_t cred; int ret, type; @@ -2437,6 +2410,11 @@ static bool can_send_false_start(gnutls_session_t session) static int handshake_client(gnutls_session_t session) { int ret = 0; + const version_entry_st *ver; + + reset: + if (STATE >= STATE100) + return _gnutls13_handshake_client(session); switch (STATE) { case STATE0: @@ -2470,6 +2448,12 @@ static int handshake_client(gnutls_session_t session) IMED_RET("recv hello", ret, 1); /* fall through */ case STATE4: + ver = get_version(session); + if (ver->tls13_sem) { /* TLS 1.3 state machine */ + STATE = STATE100; + goto reset; + } + ret = _gnutls_ext_sr_verify(session); STATE = STATE4; IMED_RET("recv hello", ret, 0); @@ -2500,7 +2484,7 @@ static int handshake_client(gnutls_session_t session) #endif /* fall through */ case STATE8: - ret = run_verify_callback(session, GNUTLS_CLIENT); + ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT); STATE = STATE8; if (ret < 0) return gnutls_assert_val(ret); @@ -2840,6 +2824,11 @@ static int recv_handshake_final(gnutls_session_t session, int init) static int handshake_server(gnutls_session_t session) { int ret = 0; + const version_entry_st *ver; + + reset: + if (STATE >= STATE100) + return _gnutls13_handshake_server(session); switch (STATE) { case STATE0: @@ -2859,6 +2848,7 @@ static int handshake_server(gnutls_session_t session) IMED_RET("recv hello", ret, 1); /* fall through */ case STATE2: + ret = _gnutls_ext_sr_verify(session); STATE = STATE2; IMED_RET("recv hello", ret, 0); @@ -2867,6 +2857,13 @@ static int handshake_server(gnutls_session_t session) ret = send_server_hello(session, AGAIN(STATE3)); STATE = STATE3; IMED_RET("send hello", ret, 1); + + ver = get_version(session); + if (ver->tls13_sem) { /* TLS 1.3 state machine */ + STATE = STATE100; + goto reset; + } + /* fall through */ case STATE4: if (session->security_parameters.do_send_supplemental) { @@ -2943,7 +2940,7 @@ static int handshake_server(gnutls_session_t session) IMED_RET("recv client certificate", ret, 1); /* fall through */ case STATE12: - ret = run_verify_callback(session, GNUTLS_SERVER); + ret = _gnutls_run_verify_callback(session, GNUTLS_SERVER); STATE = STATE12; if (ret < 0) return gnutls_assert_val(ret); diff --git a/lib/handshake.h b/lib/handshake.h index 6934b0d6fd..90d82b8e9c 100644 --- a/lib/handshake.h +++ b/lib/handshake.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * Copyright (C) 2017 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -24,6 +25,33 @@ #define HANDSHAKE_H #include "errors.h" +#include "record.h" + +#define IMED_RET( str, ret, allow_alert) do { \ + if (ret < 0) { \ + /* EAGAIN and INTERRUPTED are always non-fatal */ \ + if (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) \ + return ret; \ + if (ret == GNUTLS_E_GOT_APPLICATION_DATA && session->internals.initial_negotiation_completed != 0) \ + return ret; \ + if (session->internals.handshake_suspicious_loops < 16) { \ + if (ret == GNUTLS_E_LARGE_PACKET) { \ + session->internals.handshake_suspicious_loops++; \ + return ret; \ + } \ + /* a warning alert might interrupt handshake */ \ + if (allow_alert != 0 && ret==GNUTLS_E_WARNING_ALERT_RECEIVED) { \ + session->internals.handshake_suspicious_loops++; \ + return ret; \ + } \ + } \ + gnutls_assert(); \ + /* do not allow non-fatal errors at this point */ \ + if (gnutls_error_is_fatal(ret) == 0) ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); \ + session_invalidate(session); \ + _gnutls_handshake_hash_buffers_clear(session); \ + return ret; \ + } } while (0) int _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel, gnutls_handshake_description_t type); @@ -88,4 +116,11 @@ int _gnutls_check_if_cert_hash_is_same(gnutls_session_t session, gnutls_certific #define EXPORTER_LABEL "exp master" #define RES_LABEL "res master" +int _gnutls_run_verify_callback(gnutls_session_t session, unsigned int side); +int _gnutls_recv_finished(gnutls_session_t session); +int _gnutls_send_finished(gnutls_session_t session, int again); + +int _gnutls13_handshake_client(gnutls_session_t session); +int _gnutls13_handshake_server(gnutls_session_t session); + #endif diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 206f9045ff..fd9a7285d9 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -486,6 +486,8 @@ typedef enum { * @GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST: DTLS Hello verify request. * @GNUTLS_HANDSHAKE_CLIENT_HELLO: Client hello. * @GNUTLS_HANDSHAKE_SERVER_HELLO: Server hello. + * @GNUTLS_HANDSHAKE_END_OF_EARLY_DATA: End of early data. + * @GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST: Hello retry request. * @GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: New session ticket. * @GNUTLS_HANDSHAKE_CERTIFICATE_PKT: Certificate packet. * @GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: Server key exchange. @@ -507,6 +509,9 @@ typedef enum { GNUTLS_HANDSHAKE_SERVER_HELLO = 2, GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST = 3, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET = 4, + GNUTLS_HANDSHAKE_END_OF_EARLY_DATA = 5, + GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST = 6, + GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS = 8, GNUTLS_HANDSHAKE_CERTIFICATE_PKT = 11, GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE = 12, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST = 13, |