]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
handshake: introduced TLS 1.3 handshake client state machine outline
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Tue, 18 Jul 2017 11:48:13 +0000 (13:48 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Feb 2018 14:29:34 +0000 (15:29 +0100)
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
lib/Makefile.am
lib/debug.c
lib/gnutls_int.h
lib/handshake-tls13.c [new file with mode: 0644]
lib/handshake.c
lib/handshake.h
lib/includes/gnutls/gnutls.h.in

index 8ec5bcdb80758901c8b6df49df92f537712b3cbb..913401427359f1845de88436fc3e8b24b694dc30 100644 (file)
@@ -65,7 +65,7 @@ SRP_COBJECTS = srp.c
 
 PSK_COBJECTS = psk.c
 
-COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h     \
+COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h 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 \
index 252f2470a69eb98d9d5868f05605e9fa54cfb59f..6a6aa1c94c859426786db78633f0ba2057112aeb 100644 (file)
@@ -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";
        }
index a010400a5262c98030e0611f1fefe86b1916bf46..b1daa83084af3e000e73e2348afac1f7709ac0d0 100644 (file)
@@ -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 (file)
index 0000000..a61d1bf
--- /dev/null
@@ -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;
+}
+
index 11993dc259e431568ddb20582bf838f816c0da7c..fec06283e16af6aebb21af218d2e0a0fc2964691 100644 (file)
@@ -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);
index 6934b0d6fd12321d7941e131bda986f9cd89d18c..90d82b8e9c750e10e05d9a49652fd5541f76c52d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2000-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
  *
  * Author: Nikos Mavrogiannopoulos
  *
 #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
index 53a17fc5cd7dc4c13188eb9d56ac94acefc25b41..02b1e4d5ab231f71d89838ba7463436c343bbf4c 100644 (file)
@@ -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,