summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-07-18 13:48:13 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2017-10-10 14:10:22 +0200
commit830a36d396910ac07374cd0e15295f7d1f3abd3a (patch)
tree81e88a062e98aee3cb4fb6e2f9436d49d4e5d5fb
parent88fcb14644bfed461305491770fe79bd6255ae21 (diff)
downloadgnutls-830a36d396910ac07374cd0e15295f7d1f3abd3a.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.am2
-rw-r--r--lib/debug.c22
-rw-r--r--lib/gnutls_int.h4
-rw-r--r--lib/handshake-tls13.c182
-rw-r--r--lib/handshake.c63
-rw-r--r--lib/handshake.h35
-rw-r--r--lib/includes/gnutls/gnutls.h.in5
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,