summaryrefslogtreecommitdiffstats
path: root/lib/tls13
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tls13')
-rw-r--r--lib/tls13/anti_replay.c266
-rw-r--r--lib/tls13/anti_replay.h31
-rw-r--r--lib/tls13/certificate.c662
-rw-r--r--lib/tls13/certificate.h29
-rw-r--r--lib/tls13/certificate_request.c393
-rw-r--r--lib/tls13/certificate_request.h31
-rw-r--r--lib/tls13/certificate_verify.c241
-rw-r--r--lib/tls13/certificate_verify.h29
-rw-r--r--lib/tls13/early_data.c107
-rw-r--r--lib/tls13/early_data.h30
-rw-r--r--lib/tls13/encrypted_extensions.c75
-rw-r--r--lib/tls13/encrypted_extensions.h29
-rw-r--r--lib/tls13/finished.c183
-rw-r--r--lib/tls13/finished.h33
-rw-r--r--lib/tls13/hello_retry.c195
-rw-r--r--lib/tls13/hello_retry.h28
-rw-r--r--lib/tls13/key_update.c217
-rw-r--r--lib/tls13/key_update.h29
-rw-r--r--lib/tls13/post_handshake.c252
-rw-r--r--lib/tls13/psk_ext_parser.c111
-rw-r--r--lib/tls13/psk_ext_parser.h58
-rw-r--r--lib/tls13/session_ticket.c496
-rw-r--r--lib/tls13/session_ticket.h50
23 files changed, 3575 insertions, 0 deletions
diff --git a/lib/tls13/anti_replay.c b/lib/tls13/anti_replay.c
new file mode 100644
index 0000000..f8333c5
--- /dev/null
+++ b/lib/tls13/anti_replay.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "db.h"
+#include "system.h"
+#include "tls13/anti_replay.h"
+
+/* The default time window in milliseconds; RFC8446 suggests the order
+ * of ten seconds is sufficient for the clients on the Internet. */
+#define DEFAULT_WINDOW_MS 10000
+
+struct gnutls_anti_replay_st {
+ uint32_t window;
+ struct timespec start_time;
+ gnutls_db_add_func db_add_func;
+ void *db_ptr;
+};
+
+/**
+ * gnutls_anti_replay_init:
+ * @anti_replay: is a pointer to #gnutls_anti_replay_t type
+ *
+ * This function will allocate and initialize the @anti_replay context
+ * to be usable for detect replay attacks. The context can then be
+ * attached to a @gnutls_session_t with
+ * gnutls_anti_replay_enable().
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.6.5
+ **/
+int
+gnutls_anti_replay_init(gnutls_anti_replay_t *anti_replay)
+{
+ *anti_replay = gnutls_calloc(1, sizeof(struct gnutls_anti_replay_st));
+ if (!*anti_replay)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ (*anti_replay)->window = DEFAULT_WINDOW_MS;
+
+ gnutls_gettime(&(*anti_replay)->start_time);
+
+ return 0;
+}
+
+/**
+ * gnutls_anti_replay_set_window:
+ * @anti_replay: is a #gnutls_anti_replay_t type.
+ * @window: is the time window recording ClientHello, in milliseconds
+ *
+ * Sets the time window used for ClientHello recording. In order to
+ * protect against replay attacks, the server records ClientHello
+ * messages within this time period from the last update, and
+ * considers it a replay when a ClientHello outside of the period; if
+ * a ClientHello arrives within this period, the server checks the
+ * database and detects duplicates.
+ *
+ * For the details of the algorithm, see RFC 8446, section 8.2.
+ *
+ * Since: 3.6.5
+ */
+void
+gnutls_anti_replay_set_window(gnutls_anti_replay_t anti_replay,
+ unsigned int window)
+{
+ anti_replay->window = window;
+}
+
+/**
+ * gnutls_anti_replay_deinit:
+ * @anti_replay: is a #gnutls_anti_replay type
+ *
+ * This function will deinitialize all resources occupied by the given
+ * anti-replay context.
+ *
+ * Since: 3.6.5
+ **/
+void
+gnutls_anti_replay_deinit(gnutls_anti_replay_t anti_replay)
+{
+ gnutls_free(anti_replay);
+}
+
+/**
+ * gnutls_anti_replay_enable:
+ * @session: is a #gnutls_session_t type.
+ * @anti_replay: is a #gnutls_anti_replay_t type.
+ *
+ * Request that the server should use anti-replay mechanism.
+ *
+ * Since: 3.6.5
+ **/
+void
+gnutls_anti_replay_enable(gnutls_session_t session,
+ gnutls_anti_replay_t anti_replay)
+{
+ if (unlikely(session->security_parameters.entity != GNUTLS_SERVER)) {
+ gnutls_assert();
+ return;
+ }
+
+ session->internals.anti_replay = anti_replay;
+}
+
+int
+_gnutls_anti_replay_check(gnutls_anti_replay_t anti_replay,
+ uint32_t client_ticket_age,
+ struct timespec *ticket_creation_time,
+ gnutls_datum_t *id)
+{
+ struct timespec now;
+ time_t window;
+ uint32_t server_ticket_age, diff;
+ gnutls_datum_t key = { NULL, 0 };
+ gnutls_datum_t entry = { NULL, 0 };
+ unsigned char key_buffer[MAX_HASH_SIZE + 12];
+ unsigned char entry_buffer[12]; /* magic + timestamp + expire_time */
+ unsigned char *p;
+ int ret;
+
+ if (unlikely(id->size > MAX_HASH_SIZE))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ gnutls_gettime(&now);
+ server_ticket_age = timespec_sub_ms(&now, ticket_creation_time);
+
+ /* It shouldn't be possible that the server's view of ticket
+ * age is smaller than the client's view.
+ */
+ if (unlikely(server_ticket_age < client_ticket_age))
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ /* If ticket is created before recording has started, discard
+ * reject early data.
+ */
+ if (_gnutls_timespec_cmp(ticket_creation_time,
+ &anti_replay->start_time) < 0) {
+ _gnutls_handshake_log("anti_replay: ticket is created before recording has started\n");
+ return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED);
+ }
+
+ /* If certain amount of time (window) has elapsed, rollover
+ * the recording.
+ */
+ diff = timespec_sub_ms(&now, &anti_replay->start_time);
+ if (diff > anti_replay->window)
+ gnutls_gettime(&anti_replay->start_time);
+
+ /* If expected_arrival_time is out of window, reject early
+ * data.
+ */
+ if (server_ticket_age - client_ticket_age > anti_replay->window) {
+ _gnutls_handshake_log("anti_replay: server ticket age: %u, client ticket age: %u\n",
+ server_ticket_age,
+ client_ticket_age);
+ return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED);
+ }
+
+ /* Check if the ClientHello is stored in the database.
+ */
+ if (!anti_replay->db_add_func)
+ return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED);
+
+ /* Create a key for database lookup, prefixing window start
+ * time to ID. Note that this shouldn't clash with session ID
+ * used in TLS 1.2, because such IDs are 32 octets, while here
+ * the key becomes 44+ octets.
+ */
+ p = key_buffer;
+ _gnutls_write_uint32((uint64_t) anti_replay->start_time.tv_sec >> 32, p);
+ p += 4;
+ _gnutls_write_uint32(anti_replay->start_time.tv_sec & 0xFFFFFFFF, p);
+ p += 4;
+ _gnutls_write_uint32(anti_replay->start_time.tv_nsec, p);
+ p += 4;
+ memcpy(p, id->data, id->size);
+ p += id->size;
+ key.data = key_buffer;
+ key.size = p - key_buffer;
+
+ /* Create an entry to be stored on database if the lookup
+ * failed. This is formatted so that
+ * gnutls_db_check_entry_expire_time() work.
+ */
+ p = entry_buffer;
+ _gnutls_write_uint32(PACKED_SESSION_MAGIC, p);
+ p += 4;
+ _gnutls_write_uint32(now.tv_sec, p);
+ p += 4;
+ window = anti_replay->window / 1000;
+ _gnutls_write_uint32(window, p);
+ p += 4;
+ entry.data = entry_buffer;
+ entry.size = p - entry_buffer;
+
+ ret = anti_replay->db_add_func(anti_replay->db_ptr,
+ (uint64_t)now.tv_sec+(uint64_t)window, &key, &entry);
+ if (ret < 0) {
+ _gnutls_handshake_log("anti_replay: duplicate ClientHello found\n");
+ return gnutls_assert_val(GNUTLS_E_EARLY_DATA_REJECTED);
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_anti_replay_set_ptr:
+ * @anti_replay: is a #gnutls_anti_replay_t type.
+ * @ptr: is the pointer
+ *
+ * Sets the pointer that will be provided to db add function
+ * as the first argument.
+ **/
+void gnutls_anti_replay_set_ptr(gnutls_anti_replay_t anti_replay, void *ptr)
+{
+ anti_replay->db_ptr = ptr;
+}
+
+/**
+ * gnutls_anti_replay_set_add_function:
+ * @anti_replay: is a #gnutls_anti_replay_t type.
+ * @add_func: is the function.
+ *
+ * Sets the function that will be used to store an entry if it is not
+ * already present in the resumed sessions database. This function returns 0
+ * if the entry is successfully stored, and a negative error code
+ * otherwise. In particular, if the entry is found in the database,
+ * it returns %GNUTLS_E_DB_ENTRY_EXISTS.
+ *
+ * The arguments to the @add_func are:
+ * - %ptr: the pointer set with gnutls_anti_replay_set_ptr()
+ * - %exp_time: the expiration time of the entry
+ * - %key: a pointer to the key
+ * - %data: a pointer to data to store
+ *
+ * The data set by this function can be examined using
+ * gnutls_db_check_entry_expire_time() and gnutls_db_check_entry_time().
+ *
+ * Since: 3.6.5
+ **/
+void
+gnutls_anti_replay_set_add_function(gnutls_anti_replay_t anti_replay,
+ gnutls_db_add_func add_func)
+{
+ anti_replay->db_add_func = add_func;
+}
diff --git a/lib/tls13/anti_replay.h b/lib/tls13/anti_replay.h
new file mode 100644
index 0000000..87726c3
--- /dev/null
+++ b/lib/tls13/anti_replay.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_ANTI_REPLAY_H
+#define GNUTLS_LIB_TLS13_ANTI_REPLAY_H
+
+int _gnutls_anti_replay_check(gnutls_anti_replay_t,
+ uint32_t client_ticket_age,
+ struct timespec *ticket_creation_time,
+ gnutls_datum_t *id);
+
+#endif /* GNUTLS_LIB_TLS13_ANTI_REPLAY_H */
diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c
new file mode 100644
index 0000000..9792629
--- /dev/null
+++ b/lib/tls13/certificate.c
@@ -0,0 +1,662 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "compress.h"
+#include "errors.h"
+#include "extv.h"
+#include "handshake.h"
+#include "tls13/certificate.h"
+#include "auth/cert.h"
+#include "mbuffers.h"
+#include "ext/compress_certificate.h"
+#include "ext/status_request.h"
+
+static int parse_cert_extension(void *ctx, unsigned tls_id, const uint8_t *data, unsigned data_size);
+static int parse_cert_list(gnutls_session_t session, uint8_t * data, size_t data_size);
+static int compress_certificate(gnutls_buffer_st * buf, unsigned cert_pos_mark,
+ gnutls_compression_method_t comp_method);
+static int decompress_certificate(gnutls_buffer_st * buf);
+
+int _gnutls13_recv_certificate(gnutls_session_t session)
+{
+ int ret, err, decompress_cert = 0;
+ gnutls_buffer_st buf;
+ unsigned optional = 0;
+
+ if (!session->internals.initial_negotiation_completed &&
+ session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER) {
+ /* if we didn't request a certificate, there will not be any */
+ if (session->internals.send_cert_req == 0)
+ return 0;
+
+ if (session->internals.send_cert_req != GNUTLS_CERT_REQUIRE)
+ optional = 1;
+ }
+
+ ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_PKT, 0, &buf);
+ if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) {
+ /* check if we received compressed certificate */
+ err = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT, 0, &buf);
+ if (err >= 0) {
+ decompress_cert = 1;
+ ret = err;
+ }
+ }
+ if (ret < 0) {
+ if (ret == GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET && session->internals.send_cert_req)
+ return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
+
+ return gnutls_assert_val(ret);
+ }
+
+ if (buf.length == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto cleanup;
+ }
+
+ if (decompress_cert) {
+ ret = decompress_certificate(&buf);
+ if (ret < 0) {
+ gnutls_assert();
+ gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_CERTIFICATE);
+ goto cleanup;
+ }
+ }
+
+ if (session->internals.initial_negotiation_completed &&
+ session->internals.post_handshake_cr_context.size > 0) {
+ gnutls_datum_t context;
+
+ /* verify whether the context matches */
+ ret = _gnutls_buffer_pop_datum_prefix8(&buf, &context);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (context.size != session->internals.post_handshake_cr_context.size ||
+ memcmp(context.data, session->internals.post_handshake_cr_context.data,
+ context.size) != 0) {
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ if (buf.data[0] != 0) {
+ /* The context field must be empty during handshake */
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto cleanup;
+ }
+
+ /* buf.length is positive */
+ buf.data++;
+ buf.length--;
+ }
+
+ _gnutls_handshake_log("HSK[%p]: parsing certificate message\n", session);
+
+ ret = parse_cert_list(session, buf.data, buf.length);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) {
+ if (optional)
+ ret = 0;
+ else if (session->security_parameters.entity ==
+ GNUTLS_SERVER)
+ ret = GNUTLS_E_CERTIFICATE_REQUIRED;
+ }
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ session->internals.hsk_flags |= HSK_CRT_VRFY_EXPECTED;
+
+ ret = 0;
+cleanup:
+
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+struct ocsp_req_ctx_st {
+ gnutls_pcert_st *pcert;
+ unsigned cert_index;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t cred;
+};
+
+static
+int append_status_request(void *_ctx, gnutls_buffer_st *buf)
+{
+ struct ocsp_req_ctx_st *ctx = _ctx;
+ gnutls_session_t session = ctx->session;
+ int ret;
+ gnutls_datum_t resp;
+ unsigned free_resp = 0;
+
+ assert(session->internals.selected_ocsp_func != NULL ||
+ session->internals.selected_ocsp_length != 0);
+
+ /* The global ocsp callback function can only be used to return
+ * a single certificate request */
+ if (session->internals.selected_ocsp_length == 1 && ctx->cert_index != 0)
+ return 0;
+
+ if (session->internals.selected_ocsp_length > 0) {
+ if (ctx->cert_index < session->internals.selected_ocsp_length) {
+ if ((session->internals.selected_ocsp[ctx->cert_index].exptime != 0 &&
+ gnutls_time(0) >= session->internals.selected_ocsp[ctx->cert_index].exptime) ||
+ session->internals.selected_ocsp[ctx->cert_index].response.data == NULL) {
+ return 0;
+ }
+
+ resp.data = session->internals.selected_ocsp[ctx->cert_index].response.data;
+ resp.size = session->internals.selected_ocsp[ctx->cert_index].response.size;
+ ret = 0;
+ } else {
+ return 0;
+ }
+ } else if (session->internals.selected_ocsp_func) {
+ if (ctx->cert_index == 0) {
+ ret = session->internals.selected_ocsp_func(session, session->internals.selected_ocsp_func_ptr, &resp);
+ free_resp = 1;
+ } else {
+ return 0;
+ }
+ } else
+ return 0;
+
+ if (ret == GNUTLS_E_NO_CERTIFICATE_STATUS || resp.data == 0) {
+ return 0;
+ } else if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_buffer_append_data(buf, "\x01", 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(buf, 24, resp.data, resp.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ if (free_resp)
+ gnutls_free(resp.data);
+ return ret;
+}
+
+int _gnutls13_send_certificate(gnutls_session_t session, unsigned again)
+{
+ int ret, compress_cert;
+ gnutls_pcert_st *apr_cert_list = NULL;
+ gnutls_privkey_t apr_pkey = NULL;
+ int apr_cert_list_length = 0;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ unsigned pos_mark, ext_pos_mark, cert_pos_mark;
+ unsigned i;
+ struct ocsp_req_ctx_st ctx;
+ gnutls_certificate_credentials_t cred;
+ gnutls_compression_method_t comp_method;
+ gnutls_handshake_description_t h_type;
+
+ comp_method = gnutls_compress_certificate_get_selected_method(session);
+ compress_cert = comp_method != GNUTLS_COMP_UNKNOWN;
+ h_type = compress_cert ? GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT
+ : GNUTLS_HANDSHAKE_CERTIFICATE_PKT;
+
+ if (again == 0) {
+ if (!session->internals.initial_negotiation_completed &&
+ session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER &&
+ session->internals.resumed)
+ return 0;
+
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+ if (cred == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ }
+
+ if (session->security_parameters.entity == GNUTLS_CLIENT &&
+ !(session->internals.hsk_flags & HSK_CRT_ASKED)) {
+ return 0;
+ }
+
+ ret = _gnutls_get_selected_cert(session, &apr_cert_list,
+ &apr_cert_list_length, &apr_pkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ cert_pos_mark = buf.length;
+
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8,
+ session->internals.post_handshake_cr_context.data,
+ session->internals.post_handshake_cr_context.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ } else {
+ ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ /* mark total size */
+ pos_mark = buf.length;
+ ret = _gnutls_buffer_append_prefix(&buf, 24, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ for (i=0;i<(unsigned)apr_cert_list_length;i++) {
+ ret = _gnutls_buffer_append_data_prefix(&buf, 24,
+ apr_cert_list[i].cert.data,
+ apr_cert_list[i].cert.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+#ifdef ENABLE_OCSP
+ if ((session->internals.selected_ocsp_length > 0 ||
+ session->internals.selected_ocsp_func) &&
+ (((session->internals.hsk_flags & HSK_OCSP_REQUESTED) && IS_SERVER(session)) ||
+ ((session->internals.hsk_flags & HSK_CLIENT_OCSP_REQUESTED) && !IS_SERVER(session)))) {
+ /* append status response if available */
+ ret = _gnutls_extv_append_init(&buf);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ ext_pos_mark = ret;
+
+ ctx.pcert = &apr_cert_list[i];
+ ctx.cert_index = i;
+ ctx.session = session;
+ ctx.cred = cred;
+ ret = _gnutls_extv_append(&buf, STATUS_REQUEST_TLS_ID,
+ &ctx, append_status_request);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_extv_append_final(&buf, ext_pos_mark, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else
+#endif
+ {
+ ret = _gnutls_buffer_append_prefix(&buf, 16, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+ }
+
+ _gnutls_write_uint24(buf.length-pos_mark-3, &buf.data[pos_mark]);
+
+ if (compress_cert) {
+ ret = compress_certificate(&buf, cert_pos_mark, comp_method);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ return _gnutls_send_handshake(session, bufel, h_type);
+
+ cleanup:
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+typedef struct crt_cert_ctx_st {
+ gnutls_session_t session;
+ gnutls_datum_t *ocsp;
+ unsigned idx;
+} crt_cert_ctx_st;
+
+static int parse_cert_extension(void *_ctx, unsigned tls_id, const uint8_t *data, unsigned data_size)
+{
+ crt_cert_ctx_st *ctx = _ctx;
+ gnutls_session_t session = ctx->session;
+ int ret;
+
+ if (tls_id == STATUS_REQUEST_TLS_ID) {
+#ifdef ENABLE_OCSP
+ if (!_gnutls_hello_ext_is_present(session, ext_mod_status_request.gid)) {
+ gnutls_assert();
+ goto unexpected;
+ }
+
+ _gnutls_handshake_log("Found OCSP response on cert %d\n", ctx->idx);
+
+ ret = _gnutls_parse_ocsp_response(session, data, data_size, ctx->ocsp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+#endif
+ } else {
+ goto unexpected;
+ }
+
+ return 0;
+
+ unexpected:
+ _gnutls_debug_log("received unexpected certificate extension (%d)\n", (int)tls_id);
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
+}
+
+static int
+parse_cert_list(gnutls_session_t session, uint8_t * data, size_t data_size)
+{
+ int ret;
+ size_t len;
+ uint8_t *p = data;
+ cert_auth_info_t info;
+ gnutls_certificate_credentials_t cred;
+ size_t size;
+ int i;
+ unsigned npeer_certs, npeer_ocsp, j;
+ crt_cert_ctx_st ctx;
+ gnutls_datum_t *peer_certs = NULL;
+ gnutls_datum_t *peer_ocsp = NULL;
+ unsigned nentries = 0;
+
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+ if (cred == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ }
+
+ if ((ret =
+ _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE,
+ sizeof(cert_auth_info_st), 1)) < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (data == NULL || data_size == 0) {
+ /* no certificate was sent */
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+ }
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+
+ DECR_LEN(data_size, 3);
+ size = _gnutls_read_uint24(p);
+ p += 3;
+
+ if (size != data_size)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ if (size == 0)
+ return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
+
+ i = data_size;
+
+ while (i > 0) {
+ DECR_LEN(data_size, 3);
+ len = _gnutls_read_uint24(p);
+ if (len == 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ DECR_LEN(data_size, len);
+ p += len + 3;
+ i -= len + 3;
+
+ DECR_LEN(data_size, 2);
+ len = _gnutls_read_uint16(p);
+ DECR_LEN(data_size, len);
+
+ i -= len + 2;
+ p += len + 2;
+
+ nentries++;
+ }
+
+ if (data_size != 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ /* this is unnecessary - keeping to avoid a regression due to a re-org
+ * of the loop above */
+ if (nentries == 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ npeer_ocsp = 0;
+ npeer_certs = 0;
+
+ /* Ok we now allocate the memory to hold the
+ * certificate list
+ */
+ peer_certs = gnutls_calloc(nentries, sizeof(gnutls_datum_t));
+ if (peer_certs == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ peer_ocsp = gnutls_calloc(nentries, sizeof(gnutls_datum_t));
+ if (peer_ocsp == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ p = data+3;
+
+ /* Now we start parsing the list (again).
+ * We don't use DECR_LEN since the list has
+ * been parsed before.
+ */
+
+ ctx.session = session;
+
+ for (j = 0; j < nentries; j++) {
+ len = _gnutls_read_uint24(p);
+ p += 3;
+
+ ret = _gnutls_set_datum(&peer_certs[j], p, len);
+ if (ret < 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_CERTIFICATE_ERROR;
+ goto cleanup;
+ }
+ npeer_certs++;
+
+ p += len;
+
+ len = _gnutls_read_uint16(p);
+
+ ctx.ocsp = &peer_ocsp[j];
+ ctx.idx = j;
+
+ ret = _gnutls_extv_parse(&ctx, parse_cert_extension, p, len+2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ p += len+2;
+ npeer_ocsp++;
+ }
+
+ /* The OCSP entries match the certificate entries, although
+ * the contents of each OCSP entry may be NULL.
+ */
+ for(j=0;j<info->ncerts;j++)
+ gnutls_free(info->raw_certificate_list[j].data);
+ gnutls_free(info->raw_certificate_list);
+
+ for(j=0;j<info->nocsp;j++)
+ gnutls_free(info->raw_ocsp_list[j].data);
+ gnutls_free(info->raw_ocsp_list);
+
+
+ info->raw_certificate_list = peer_certs;
+ info->ncerts = npeer_certs;
+
+ info->raw_ocsp_list = peer_ocsp;
+ info->nocsp = npeer_ocsp;
+
+ return 0;
+
+ cleanup:
+ for(j=0;j<npeer_certs;j++)
+ gnutls_free(peer_certs[j].data);
+
+ for(j=0;j<npeer_ocsp;j++)
+ gnutls_free(peer_ocsp[j].data);
+ gnutls_free(peer_certs);
+ gnutls_free(peer_ocsp);
+ return ret;
+
+}
+
+static int
+compress_certificate(gnutls_buffer_st * buf, unsigned cert_pos_mark,
+ gnutls_compression_method_t comp_method)
+{
+ int ret, method_num;
+ size_t comp_bound;
+ gnutls_datum_t plain, comp = { NULL, 0 };
+
+ method_num = _gnutls_compress_certificate_method2num(comp_method);
+ if (method_num == GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ plain.data = buf->data + cert_pos_mark;
+ plain.size = buf->length - cert_pos_mark;
+
+ comp_bound = _gnutls_compress_bound(comp_method, plain.size);
+ if (comp_bound == 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ comp.data = gnutls_malloc(comp_bound);
+ if (comp.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ ret = _gnutls_compress(comp_method, comp.data, comp_bound, plain.data, plain.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ comp.size = ret;
+
+ buf->length = cert_pos_mark;
+ ret = _gnutls_buffer_append_prefix(buf, 16, method_num);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ ret = _gnutls_buffer_append_prefix(buf, 24, plain.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ ret = _gnutls_buffer_append_data_prefix(buf, 24, comp.data, comp.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+cleanup:
+ gnutls_free(comp.data);
+ return ret;
+}
+
+static int
+decompress_certificate(gnutls_buffer_st * buf)
+{
+ int ret;
+ size_t method_num, plain_exp_len;
+ gnutls_datum_t comp, plain = { NULL, 0 };
+ gnutls_compression_method_t comp_method;
+
+ ret = _gnutls_buffer_pop_prefix16(buf, &method_num, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ comp_method = _gnutls_compress_certificate_num2method(method_num);
+
+ ret = _gnutls_buffer_pop_prefix24(buf, &plain_exp_len, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_pop_datum_prefix24(buf, &comp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ plain.data = gnutls_malloc(plain_exp_len);
+ if (plain.data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ ret = _gnutls_decompress(comp_method, plain.data, plain_exp_len, comp.data, comp.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ plain.size = ret;
+
+ if (plain.size != plain_exp_len) {
+ gnutls_assert();
+ ret = GNUTLS_E_DECOMPRESSION_FAILED;
+ goto cleanup;
+ }
+
+ _gnutls_buffer_clear(buf);
+ ret = _gnutls_buffer_append_data(buf, plain.data, plain.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+cleanup:
+ gnutls_free(plain.data);
+ return ret;
+}
diff --git a/lib/tls13/certificate.h b/lib/tls13/certificate.h
new file mode 100644
index 0000000..6f46cc2
--- /dev/null
+++ b/lib/tls13/certificate.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_CERTIFICATE_H
+#define GNUTLS_LIB_TLS13_CERTIFICATE_H
+
+int _gnutls13_recv_certificate(gnutls_session_t session);
+int _gnutls13_send_certificate(gnutls_session_t session, unsigned again);
+
+#endif /* GNUTLS_LIB_TLS13_CERTIFICATE_H */
diff --git a/lib/tls13/certificate_request.c b/lib/tls13/certificate_request.c
new file mode 100644
index 0000000..b613cab
--- /dev/null
+++ b/lib/tls13/certificate_request.c
@@ -0,0 +1,393 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "extv.h"
+#include "handshake.h"
+#include "tls13/certificate_request.h"
+#include "ext/compress_certificate.h"
+#include "ext/signature.h"
+#include "ext/status_request.h"
+#include "mbuffers.h"
+#include "algorithms.h"
+#include "auth/cert.h"
+
+/* for tlist dereference */
+#include "x509/verify-high.h"
+
+#define EXTID_CERTIFICATE_AUTHORITIES 47
+
+typedef struct crt_req_ctx_st {
+ gnutls_session_t session;
+ unsigned got_sig_algo;
+ gnutls_pk_algorithm_t pk_algos[MAX_ALGOS];
+ unsigned pk_algos_length;
+ const uint8_t *rdn; /* pointer inside the message buffer */
+ unsigned rdn_size;
+} crt_req_ctx_st;
+
+static unsigned is_algo_in_list(gnutls_pk_algorithm_t algo, gnutls_pk_algorithm_t *list, unsigned list_size)
+{
+ unsigned j;
+
+ for (j=0;j<list_size;j++) {
+ if (list[j] == algo)
+ return 1;
+ }
+ return 0;
+}
+
+static
+int parse_cert_extension(void *_ctx, unsigned tls_id, const uint8_t *data, unsigned data_size)
+{
+ crt_req_ctx_st *ctx = _ctx;
+ gnutls_session_t session = ctx->session;
+ unsigned v;
+ int ret;
+
+ /* Decide which certificate to use if the signature algorithms extension
+ * is present.
+ */
+ if (tls_id == ext_mod_sig.tls_id) {
+ const version_entry_st *ver = get_version(session);
+ const gnutls_sign_entry_st *se;
+ /* signature algorithms; let's use it to decide the certificate to use */
+ unsigned i;
+
+ if (ctx->got_sig_algo)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
+
+ ctx->got_sig_algo = 1;
+
+ if (data_size < 2)
+ return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
+
+ v = _gnutls_read_uint16(data);
+ if (v != data_size-2)
+ return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
+
+ data += 2;
+ data_size -= 2;
+
+ ret = _gnutls_sign_algorithm_parse_data(session, data, data_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* The APIs to retrieve a client certificate accept the public
+ * key algorithms instead of signatures. Get the public key algorithms
+ * from the signatures.
+ */
+ for (i=0;i<(unsigned)data_size;i+=2) {
+ se = _gnutls_tls_aid_to_sign_entry(data[i], data[i+1], ver);
+ if (se == NULL)
+ continue;
+
+ if (ctx->pk_algos_length >= sizeof(ctx->pk_algos)/sizeof(ctx->pk_algos[0]))
+ break;
+
+ if (is_algo_in_list(se->pk, ctx->pk_algos, ctx->pk_algos_length))
+ continue;
+
+ ctx->pk_algos[ctx->pk_algos_length++] = se->pk;
+ }
+#ifdef ENABLE_OCSP
+ } else if (tls_id == ext_mod_status_request.tls_id) {
+ if (data_size != 0)
+ return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
+
+ /* we are now allowed to send OCSP staples */
+ session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
+#endif
+ } else if (tls_id == EXTID_CERTIFICATE_AUTHORITIES) {
+ if (data_size < 3) {
+ return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
+ }
+
+ v = _gnutls_read_uint16(data);
+ if (v != data_size-2)
+ return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
+
+ ctx->rdn = data+2;
+ ctx->rdn_size = v;
+ } else if (tls_id == ext_mod_compress_certificate.tls_id) {
+ ret = _gnutls_compress_certificate_recv_params(session,
+ data,
+ data_size);
+ if (ret < 0) {
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ return 0;
+}
+
+int _gnutls13_recv_certificate_request_int(gnutls_session_t session, gnutls_buffer_st *buf)
+{
+ int ret;
+ crt_req_ctx_st ctx;
+ gnutls_pcert_st *apr_cert_list;
+ gnutls_privkey_t apr_pkey;
+ int apr_cert_list_length;
+
+ _gnutls_handshake_log("HSK[%p]: parsing certificate request\n", session);
+
+ if (unlikely(session->security_parameters.entity == GNUTLS_SERVER))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* if initial negotiation is complete, this is a post-handshake auth */
+ if (!session->internals.initial_negotiation_completed) {
+ if (buf->data[0] != 0) {
+ /* The context field must be empty during handshake */
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
+
+ /* buf->length is positive */
+ buf->data++;
+ buf->length--;
+ } else {
+ gnutls_datum_t context;
+
+ ret = _gnutls_buffer_pop_datum_prefix8(buf, &context);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ gnutls_free(session->internals.post_handshake_cr_context.data);
+ ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
+ context.data, context.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.session = session;
+
+ ret = _gnutls_extv_parse(&ctx, parse_cert_extension, buf->data, buf->length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* The "signature_algorithms" extension MUST be specified */
+ if (!ctx.got_sig_algo)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
+
+ session->internals.hsk_flags |= HSK_CRT_ASKED;
+
+ ret = _gnutls_select_client_cert(session, ctx.rdn, ctx.rdn_size,
+ ctx.pk_algos, ctx.pk_algos_length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_get_selected_cert(session, &apr_cert_list,
+ &apr_cert_list_length, &apr_pkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (apr_cert_list_length > 0) {
+ gnutls_sign_algorithm_t algo;
+
+ algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0, GNUTLS_KX_UNKNOWN);
+ if (algo == GNUTLS_SIGN_UNKNOWN) {
+ _gnutls_handshake_log("HSK[%p]: rejecting client auth because of no suitable signature algorithm\n", session);
+ _gnutls_selected_certs_deinit(session);
+ return gnutls_assert_val(0);
+ }
+
+ gnutls_sign_algorithm_set_client(session, algo);
+ }
+
+ return 0;
+}
+
+int _gnutls13_recv_certificate_request(gnutls_session_t session)
+{
+ int ret;
+ gnutls_buffer_st buf;
+
+ if (!session->internals.initial_negotiation_completed &&
+ session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
+ if (unlikely(session->security_parameters.entity != GNUTLS_CLIENT))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST, 1, &buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* if not received */
+ if (buf.length == 0) {
+ _gnutls_buffer_clear(&buf);
+ return 0;
+ }
+
+ ret = _gnutls13_recv_certificate_request_int(session, &buf);
+
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+static
+int write_certificate_authorities(void *ctx, gnutls_buffer_st *buf)
+{
+ gnutls_session_t session = ctx;
+ gnutls_certificate_credentials_t cred;
+
+ if (session->internals.ignore_rdn_sequence != 0)
+ return 0;
+
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+ if (cred == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ }
+
+ if (cred->tlist->x509_rdn_sequence.size == 0)
+ return 0;
+
+ return
+ _gnutls_buffer_append_data_prefix(buf, 16,
+ cred->
+ tlist->x509_rdn_sequence.
+ data,
+ cred->
+ tlist->x509_rdn_sequence.
+ size);
+}
+
+static int append_empty_ext(void *ctx, gnutls_buffer_st *buf)
+{
+ return GNUTLS_E_INT_RET_0;
+}
+
+int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again)
+{
+ gnutls_certificate_credentials_t cred;
+ int ret;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ unsigned init_pos;
+
+ if (again == 0) {
+ unsigned char rnd[12];
+
+ if (!session->internals.initial_negotiation_completed &&
+ session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
+ if (session->internals.send_cert_req == 0)
+ return 0;
+
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+ if (cred == NULL)
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (session->internals.initial_negotiation_completed) { /* reauth */
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, rnd, sizeof(rnd));
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ gnutls_free(session->internals.post_handshake_cr_context.data);
+ ret = _gnutls_set_datum(&session->internals.post_handshake_cr_context,
+ rnd, sizeof(rnd));
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8,
+ session->internals.post_handshake_cr_context.data,
+ session->internals.post_handshake_cr_context.size);
+ } else {
+ ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
+ }
+
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_extv_append_init(&buf);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ init_pos = ret;
+
+ ret = _gnutls_extv_append(&buf, ext_mod_sig.tls_id, session,
+ (extv_append_func)_gnutls_sign_algorithm_write_params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_extv_append(&buf, EXTID_CERTIFICATE_AUTHORITIES, session,
+ write_certificate_authorities);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+#ifdef ENABLE_OCSP
+ /* We always advertise our support for OCSP stapling */
+ ret = _gnutls_extv_append(&buf, ext_mod_status_request.tls_id, session,
+ append_empty_ext);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ session->internals.hsk_flags |= HSK_CLIENT_OCSP_REQUESTED;
+#endif
+
+ ret = _gnutls_extv_append(&buf, ext_mod_compress_certificate.tls_id, session,
+ (extv_append_func)_gnutls_compress_certificate_send_params);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_extv_append_final(&buf, init_pos, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+
+ session->internals.hsk_flags |= HSK_CRT_REQ_SENT;
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST);
+
+ cleanup:
+ _gnutls_buffer_clear(&buf);
+ return ret;
+
+}
+
diff --git a/lib/tls13/certificate_request.h b/lib/tls13/certificate_request.h
new file mode 100644
index 0000000..40ac9f2
--- /dev/null
+++ b/lib/tls13/certificate_request.h
@@ -0,0 +1,31 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_CERTIFICATE_REQUEST_H
+#define GNUTLS_LIB_TLS13_CERTIFICATE_REQUEST_H
+
+int _gnutls13_recv_certificate_request(gnutls_session_t session);
+int _gnutls13_recv_certificate_request_int(gnutls_session_t session, gnutls_buffer_st *buf);
+
+int _gnutls13_send_certificate_request(gnutls_session_t session, unsigned again);
+
+#endif /* GNUTLS_LIB_TLS13_CERTIFICATE_REQUEST_H */
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
new file mode 100644
index 0000000..45ff6fa
--- /dev/null
+++ b/lib/tls13/certificate_verify.c
@@ -0,0 +1,241 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "handshake.h"
+#include "auth/cert.h"
+#include "ext/signature.h"
+#include "algorithms.h"
+#include "tls13-sig.h"
+#include "mbuffers.h"
+#include "tls13/certificate_verify.h"
+
+#define SRV_CTX "TLS 1.3, server CertificateVerify"
+static const gnutls_datum_t srv_ctx = {
+ (void*)SRV_CTX, sizeof(SRV_CTX)-1
+};
+
+#define CLI_CTX "TLS 1.3, client CertificateVerify"
+static const gnutls_datum_t cli_ctx = {
+ (void*)CLI_CTX, sizeof(CLI_CTX)-1
+};
+
+int _gnutls13_recv_certificate_verify(gnutls_session_t session)
+{
+ int ret;
+ gnutls_buffer_st buf;
+ const gnutls_sign_entry_st *se;
+ gnutls_datum_t sig_data;
+ gnutls_certificate_credentials_t cred;
+ unsigned vflags;
+ gnutls_pcert_st peer_cert;
+ cert_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ bool server = 0;
+ gnutls_certificate_type_t cert_type;
+
+ memset(&peer_cert, 0, sizeof(peer_cert));
+
+ /* this message is only expected if we have received
+ * a certificate message */
+ if (!(session->internals.hsk_flags & HSK_CRT_VRFY_EXPECTED))
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ server = 1;
+
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+ if (unlikely(cred == NULL))
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ if (unlikely(info == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY, 0, &buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_handshake_log("HSK[%p]: Parsing certificate verify\n", session);
+
+ if (buf.length < 2) {
+ gnutls_assert();
+ ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ goto cleanup;
+ }
+
+ se = _gnutls_tls_aid_to_sign_entry(buf.data[0], buf.data[1], get_version(session));
+ if (se == NULL) {
+ _gnutls_handshake_log("Found unsupported signature (%d.%d)\n", (int)buf.data[0], (int)buf.data[1]);
+ ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ goto cleanup;
+ }
+
+ if (server)
+ gnutls_sign_algorithm_set_client(session, se->id);
+ else
+ gnutls_sign_algorithm_set_server(session, se->id);
+
+ buf.data+=2;
+ buf.length-=2;
+
+ /* we check during verification whether the algorithm is enabled */
+
+ ret = _gnutls_buffer_pop_datum_prefix16(&buf, &sig_data);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (sig_data.size == 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto cleanup;
+ }
+
+ /* We verify the certificate of the peer. Therefore we need to
+ * retrieve the negotiated certificate type for the peer. */
+ cert_type = get_certificate_type(session, GNUTLS_CTYPE_PEERS);
+
+ /* Verify the signature */
+ ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ vflags = cred->verify_flags | session->internals.additional_verify_flags;
+
+ ret = _gnutls13_handshake_verify_data(session, vflags, &peer_cert,
+ server?(&cli_ctx):(&srv_ctx),
+ &sig_data, se);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (buf.length > 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ goto cleanup;
+ }
+
+ ret = 0;
+ cleanup:
+ gnutls_pcert_deinit(&peer_cert);
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)
+{
+ int ret;
+ gnutls_pcert_st *apr_cert_list;
+ gnutls_privkey_t apr_pkey;
+ int apr_cert_list_length;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ gnutls_datum_t sig = {NULL, 0};
+ gnutls_sign_algorithm_t algo;
+ const gnutls_sign_entry_st *se;
+ bool server = 0;
+
+ if (again == 0) {
+ if (!session->internals.initial_negotiation_completed &&
+ session->internals.hsk_flags & HSK_PSK_SELECTED)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER &&
+ session->internals.resumed)
+ return 0;
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ server = 1;
+
+ ret = _gnutls_get_selected_cert(session, &apr_cert_list,
+ &apr_cert_list_length, &apr_pkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (apr_cert_list_length == 0) {
+ if (server) {
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ } else {
+ /* for client, this means either we
+ * didn't get a cert request or we are
+ * declining authentication; in either
+ * case we don't send a cert verify */
+ return 0;
+ }
+ }
+
+ if (server) {
+ algo = _gnutls_session_get_sign_algo(session, &apr_cert_list[0], apr_pkey, 0, GNUTLS_KX_UNKNOWN);
+ if (algo == GNUTLS_SIGN_UNKNOWN)
+ return gnutls_assert_val(GNUTLS_E_INCOMPATIBLE_SIG_WITH_KEY);
+
+ gnutls_sign_algorithm_set_server(session, algo);
+ } else {
+ /* for client, signature algorithm is already
+ * determined from Certificate Request */
+ algo = gnutls_sign_algorithm_get_client(session);
+ if (unlikely(algo == GNUTLS_SIGN_UNKNOWN))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ se = _gnutls_sign_to_entry(algo);
+
+ ret = _gnutls13_handshake_sign_data(session, &apr_cert_list[0], apr_pkey,
+ server?(&srv_ctx):(&cli_ctx),
+ &sig, se);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data(&buf, se->aid.id, 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(&buf, 16, sig.data, sig.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+
+ gnutls_free(sig.data);
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY);
+
+ cleanup:
+ gnutls_free(sig.data);
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
diff --git a/lib/tls13/certificate_verify.h b/lib/tls13/certificate_verify.h
new file mode 100644
index 0000000..9d4ce31
--- /dev/null
+++ b/lib/tls13/certificate_verify.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_CERTIFICATE_VERIFY_H
+#define GNUTLS_LIB_TLS13_CERTIFICATE_VERIFY_H
+
+int _gnutls13_recv_certificate_verify(gnutls_session_t session);
+int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again);
+
+#endif /* GNUTLS_LIB_TLS13_CERTIFICATE_VERIFY_H */
diff --git a/lib/tls13/early_data.c b/lib/tls13/early_data.c
new file mode 100644
index 0000000..3d565d5
--- /dev/null
+++ b/lib/tls13/early_data.c
@@ -0,0 +1,107 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "handshake.h"
+#include "tls13/early_data.h"
+
+int _gnutls13_send_early_data(gnutls_session_t session)
+{
+ int ret;
+
+ if (!(session->security_parameters.entity == GNUTLS_CLIENT &&
+ session->internals.hsk_flags & HSK_EARLY_DATA_IN_FLIGHT))
+ return 0;
+
+ while (session->internals.early_data_presend_buffer.length > 0) {
+ ret =
+ gnutls_record_send(session,
+ session->internals.
+ early_data_presend_buffer.data,
+ session->internals.
+ early_data_presend_buffer.
+ length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.early_data_presend_buffer.data += ret;
+ session->internals.early_data_presend_buffer.length -= ret;
+ }
+
+
+ return 0;
+}
+
+int _gnutls13_send_end_of_early_data(gnutls_session_t session, unsigned again)
+{
+ int ret;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+
+ if (!(session->security_parameters.entity == GNUTLS_CLIENT &&
+ session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED))
+ return 0;
+
+ if (session->internals.flags & GNUTLS_NO_END_OF_EARLY_DATA) {
+ return 0;
+ }
+
+ if (again == 0) {
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_END_OF_EARLY_DATA);
+}
+
+int _gnutls13_recv_end_of_early_data(gnutls_session_t session)
+{
+ int ret;
+ gnutls_buffer_st buf;
+
+ if (!(session->security_parameters.entity == GNUTLS_SERVER &&
+ session->internals.hsk_flags & HSK_EARLY_DATA_ACCEPTED))
+ return 0;
+
+ if (!(session->internals.flags & GNUTLS_NO_END_OF_EARLY_DATA)) {
+ ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_END_OF_EARLY_DATA, 0, &buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (buf.length != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ goto cleanup;
+ }
+ }
+
+ session->internals.hsk_flags &= ~HSK_EARLY_DATA_IN_FLIGHT;
+
+ ret = 0;
+cleanup:
+
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
diff --git a/lib/tls13/early_data.h b/lib/tls13/early_data.h
new file mode 100644
index 0000000..6fef12c
--- /dev/null
+++ b/lib/tls13/early_data.h
@@ -0,0 +1,30 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_EARLY_DATA_H
+#define GNUTLS_LIB_TLS13_EARLY_DATA_H
+
+int _gnutls13_send_end_of_early_data(gnutls_session_t session, unsigned again);
+int _gnutls13_recv_end_of_early_data(gnutls_session_t session);
+int _gnutls13_send_early_data(gnutls_session_t session);
+
+#endif /* GNUTLS_LIB_TLS13_EARLY_DATA_H */
diff --git a/lib/tls13/encrypted_extensions.c b/lib/tls13/encrypted_extensions.c
new file mode 100644
index 0000000..84c328f
--- /dev/null
+++ b/lib/tls13/encrypted_extensions.c
@@ -0,0 +1,75 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "hello_ext.h"
+#include "handshake.h"
+#include "mbuffers.h"
+#include "tls13/encrypted_extensions.h"
+
+int _gnutls13_recv_encrypted_extensions(gnutls_session_t session)
+{
+ int ret;
+ gnutls_buffer_st buf;
+
+ ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS, 0, &buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_handshake_log("HSK[%p]: parsing encrypted extensions\n", session);
+ ret = _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_EE, GNUTLS_EXT_ANY,
+ buf.data, buf.length);
+ _gnutls_buffer_clear(&buf);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+int _gnutls13_send_encrypted_extensions(gnutls_session_t session, unsigned again)
+{
+ int ret;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+
+ if (again == 0) {
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_gen_hello_extensions(session, &buf, GNUTLS_EXT_FLAG_EE, GNUTLS_EXT_ANY);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS);
+
+ cleanup:
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
diff --git a/lib/tls13/encrypted_extensions.h b/lib/tls13/encrypted_extensions.h
new file mode 100644
index 0000000..2c7cf75
--- /dev/null
+++ b/lib/tls13/encrypted_extensions.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_ENCRYPTED_EXTENSIONS_H
+#define GNUTLS_LIB_TLS13_ENCRYPTED_EXTENSIONS_H
+
+int _gnutls13_recv_encrypted_extensions(gnutls_session_t session);
+int _gnutls13_send_encrypted_extensions(gnutls_session_t session, unsigned again);
+
+#endif /* GNUTLS_LIB_TLS13_ENCRYPTED_EXTENSIONS_H */
diff --git a/lib/tls13/finished.c b/lib/tls13/finished.c
new file mode 100644
index 0000000..ec646e6
--- /dev/null
+++ b/lib/tls13/finished.c
@@ -0,0 +1,183 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "handshake.h"
+#include "tls13/finished.h"
+#include "mem.h"
+#include "mbuffers.h"
+#include "secrets.h"
+
+int _gnutls13_compute_finished(const mac_entry_st *prf,
+ const uint8_t *base_key,
+ gnutls_buffer_st *handshake_hash_buffer,
+ void *out)
+{
+ int ret;
+ uint8_t fkey[MAX_HASH_SIZE];
+ uint8_t ts_hash[MAX_HASH_SIZE];
+
+ ret = _tls13_expand_secret2(prf,
+ "finished", 8,
+ NULL, 0,
+ base_key,
+ prf->output_size, fkey);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_hash_fast(MAC_TO_DIG(prf->id),
+ handshake_hash_buffer->data,
+ handshake_hash_buffer->length,
+ ts_hash);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_hmac_fast(prf->id,
+ fkey, prf->output_size,
+ ts_hash, prf->output_size,
+ out);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+int _gnutls13_recv_finished(gnutls_session_t session)
+{
+ int ret;
+ gnutls_buffer_st buf;
+ uint8_t verifier[MAX_HASH_SIZE];
+ const uint8_t *base_key;
+ unsigned hash_size;
+
+ if (unlikely(session->security_parameters.prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ hash_size = session->security_parameters.prf->output_size;
+
+ if (!session->internals.initial_negotiation_completed) {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.hs_skey;
+ else
+ base_key = session->key.proto.tls13.hs_ckey;
+ } else {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.ap_skey;
+ else
+ base_key = session->key.proto.tls13.ap_ckey;
+ }
+
+ ret = _gnutls13_compute_finished(session->security_parameters.prf,
+ base_key,
+ &session->internals.handshake_hash_buffer,
+ verifier);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_recv_handshake(session, GNUTLS_HANDSHAKE_FINISHED, 0, &buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_handshake_log("HSK[%p]: parsing finished\n", session);
+
+ if (buf.length != hash_size) {
+ gnutls_assert();
+ ret = GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ goto cleanup;
+ }
+
+
+#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+# warning This is unsafe for production builds
+#else
+ if (gnutls_memcmp(verifier, buf.data, buf.length) != 0) {
+ gnutls_assert();
+ ret = GNUTLS_E_ERROR_IN_FINISHED_PACKET;
+ goto cleanup;
+ }
+#endif
+
+ ret = 0;
+cleanup:
+
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+int _gnutls13_send_finished(gnutls_session_t session, unsigned again)
+{
+ int ret;
+ uint8_t verifier[MAX_HASH_SIZE];
+ mbuffer_st *bufel = NULL;
+ const uint8_t *base_key;
+ unsigned hash_size;
+
+ if (again == 0) {
+ if (unlikely(session->security_parameters.prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ hash_size = session->security_parameters.prf->output_size;
+
+ if (!session->internals.initial_negotiation_completed) {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.hs_ckey;
+ else
+ base_key = session->key.proto.tls13.hs_skey;
+ } else {
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ base_key = session->key.proto.tls13.ap_ckey;
+ else
+ base_key = session->key.proto.tls13.ap_skey;
+ }
+
+ ret = _gnutls13_compute_finished(session->security_parameters.prf,
+ base_key,
+ &session->internals.handshake_hash_buffer,
+ verifier);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_handshake_log("HSK[%p]: sending finished\n", session);
+
+ bufel = _gnutls_handshake_alloc(session, hash_size);
+ if (bufel == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ _mbuffer_set_udata_size(bufel, 0);
+ ret = _mbuffer_append_data(bufel, verifier, hash_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_FINISHED);
+
+cleanup:
+ _mbuffer_xfree(&bufel);
+ return ret;
+}
diff --git a/lib/tls13/finished.h b/lib/tls13/finished.h
new file mode 100644
index 0000000..cf475b2
--- /dev/null
+++ b/lib/tls13/finished.h
@@ -0,0 +1,33 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_FINISHED_H
+#define GNUTLS_LIB_TLS13_FINISHED_H
+
+int _gnutls13_compute_finished(const mac_entry_st *prf,
+ const uint8_t *base_key,
+ gnutls_buffer_st *handshake_hash_buffer,
+ void *out);
+int _gnutls13_recv_finished(gnutls_session_t session);
+int _gnutls13_send_finished(gnutls_session_t session, unsigned again);
+
+#endif /* GNUTLS_LIB_TLS13_FINISHED_H */
diff --git a/lib/tls13/hello_retry.c b/lib/tls13/hello_retry.c
new file mode 100644
index 0000000..dd4cba5
--- /dev/null
+++ b/lib/tls13/hello_retry.c
@@ -0,0 +1,195 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "hello_ext.h"
+#include "handshake.h"
+#include "tls13/hello_retry.h"
+#include "auth/cert.h"
+#include "mbuffers.h"
+#include "state.h"
+
+int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again)
+{
+ int ret;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ const version_entry_st *ver;
+ const uint8_t vbuf[2] = {0x03, 0x03};
+
+ if (again == 0) {
+ ver = get_version(session);
+ if (unlikely(ver == NULL || session->security_parameters.cs == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_append_data(&buf, vbuf, 2);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_buffer_append_data(&buf,
+ HRR_RANDOM,
+ GNUTLS_RANDOM_SIZE);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8,
+ session->security_parameters.session_id,
+ session->security_parameters.session_id_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data(&buf, session->security_parameters.cs->id, 2);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* compression */
+ ret = _gnutls_buffer_append_prefix(&buf, 8, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_gen_hello_extensions(session, &buf,
+ GNUTLS_EXT_FLAG_HRR,
+ GNUTLS_EXT_ANY);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* reset extensions sent by this session to allow re-sending them */
+ session->internals.used_exts = 0;
+
+ reset_binders(session);
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST);
+
+ cleanup:
+ _gnutls_buffer_clear(&buf);
+ return ret;
+}
+
+int
+_gnutls13_recv_hello_retry_request(gnutls_session_t session,
+ gnutls_buffer_st *buf)
+{
+ int ret;
+ uint8_t tmp[2];
+ const gnutls_cipher_suite_entry_st *cs;
+ const mac_entry_st *prf;
+ gnutls_datum_t session_id;
+ uint8_t random[GNUTLS_RANDOM_SIZE];
+
+ /* only under TLS 1.3 */
+ if (IS_DTLS(session))
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+ if (session->internals.hsk_flags & HSK_HRR_RECEIVED)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET);
+
+ session->internals.hsk_flags |= HSK_HRR_RECEIVED;
+
+ /* version */
+ ret = _gnutls_buffer_pop_data(buf, tmp, 2);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ if (unlikely(tmp[0] != 0x03 || tmp[1] != 0x03))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+
+ ret = _gnutls_buffer_pop_data(buf, random, GNUTLS_RANDOM_SIZE);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ if (memcmp(random, HRR_RANDOM, GNUTLS_RANDOM_SIZE) != 0) {
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
+
+ ret = _gnutls_buffer_pop_datum_prefix8(buf, &session_id);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ /* read ciphersuites */
+ ret = _gnutls_buffer_pop_data(buf, tmp, 2);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ cs = ciphersuite_to_entry(tmp);
+ if (unlikely(cs == NULL))
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_SUITE);
+
+ _gnutls_handshake_log("EXT[%p]: Hello Retry Request with %s\n", session, cs->name);
+ memcpy(session->internals.hrr_cs, cs->id, 2);
+
+ prf = mac_to_entry(cs->prf);
+ if (unlikely(prf == NULL))
+ return gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_SUITE);
+
+ /* compression */
+ ret = _gnutls_buffer_pop_data(buf, tmp, 1);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ if (unlikely(tmp[0] != 0))
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ ret = _gnutls13_handshake_hash_buffers_synth(session, prf, 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (buf->length <= 2) {
+ /* no extensions present */
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_EXTENSIONS_LENGTH);
+ }
+
+ /* figure version first */
+ ret =
+ _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_HRR,
+ GNUTLS_EXT_VERSION_NEG,
+ buf->data, buf->length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* parse the rest of extensions */
+ ret = _gnutls_parse_hello_extensions(session, GNUTLS_EXT_FLAG_HRR, GNUTLS_EXT_ANY,
+ buf->data, buf->length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.used_exts = 0;
+
+ return 0;
+}
diff --git a/lib/tls13/hello_retry.h b/lib/tls13/hello_retry.h
new file mode 100644
index 0000000..373670f
--- /dev/null
+++ b/lib/tls13/hello_retry.h
@@ -0,0 +1,28 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_HELLO_RETRY_H
+#define GNUTLS_LIB_TLS13_HELLO_RETRY_H
+
+int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again);
+
+#endif /* GNUTLS_LIB_TLS13_HELLO_RETRY_H */
diff --git a/lib/tls13/key_update.c b/lib/tls13/key_update.c
new file mode 100644
index 0000000..c6f6e0a
--- /dev/null
+++ b/lib/tls13/key_update.c
@@ -0,0 +1,217 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "handshake.h"
+#include "tls13/key_update.h"
+#include "mem.h"
+#include "mbuffers.h"
+#include "secrets.h"
+
+#define KEY_UPDATES_WINDOW 1000
+#define KEY_UPDATES_PER_WINDOW 8
+
+static int update_keys(gnutls_session_t session, hs_stage_t stage)
+{
+ int ret;
+
+ ret = _tls13_update_secret(session, session->key.proto.tls13.temp_secret,
+ session->key.proto.tls13.temp_secret_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ _gnutls_epoch_bump(session);
+ ret = _gnutls_epoch_dup(session, EPOCH_READ_CURRENT);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* If we send a key update during early start, only update our
+ * write keys */
+ if (session->internals.recv_state == RECV_STATE_EARLY_START) {
+ ret = _tls13_write_connection_state_init(session, stage);
+ } else {
+ ret = _tls13_connection_state_init(session, stage);
+ }
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+int _gnutls13_recv_key_update(gnutls_session_t session, gnutls_buffer_st *buf)
+{
+ int ret;
+ struct timespec now;
+
+ if (buf->length != 1)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+ gnutls_gettime(&now);
+
+ /* Roll over the counter if the time window has elapsed */
+ if (session->internals.key_update_count == 0 ||
+ timespec_sub_ms(&now, &session->internals.last_key_update) >
+ KEY_UPDATES_WINDOW) {
+ session->internals.last_key_update = now;
+ session->internals.key_update_count = 0;
+ }
+
+ if (unlikely(++session->internals.key_update_count >
+ KEY_UPDATES_PER_WINDOW)) {
+ _gnutls_debug_log("reached maximum number of key updates per %d milliseconds (%d)\n",
+ KEY_UPDATES_WINDOW,
+ KEY_UPDATES_PER_WINDOW);
+ return gnutls_assert_val(GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS);
+ }
+
+ _gnutls_epoch_gc(session);
+
+ _gnutls_handshake_log("HSK[%p]: received TLS 1.3 key update (%u)\n",
+ session, (unsigned)buf->data[0]);
+
+ switch(buf->data[0]) {
+ case 0:
+ /* peer updated its key, not requested our key update */
+ ret = update_keys(session, STAGE_UPD_PEERS);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ break;
+ case 1:
+ if (session->internals.hsk_flags & HSK_KEY_UPDATE_ASKED) {
+ /* if we had asked a key update we shouldn't get this
+ * reply */
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ }
+
+ /* peer updated its key, requested our key update */
+ ret = update_keys(session, STAGE_UPD_PEERS);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* we mark that a key update is schedule, and it
+ * will be performed prior to sending the next application
+ * message.
+ */
+ if (session->internals.rsend_state == RECORD_SEND_NORMAL)
+ session->internals.rsend_state = RECORD_SEND_KEY_UPDATE_1;
+ else if (session->internals.rsend_state == RECORD_SEND_CORKED)
+ session->internals.rsend_state = RECORD_SEND_CORKED_TO_KU;
+
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
+
+ session->internals.hsk_flags &= ~(unsigned)(HSK_KEY_UPDATE_ASKED);
+
+ return 0;
+}
+
+int _gnutls13_send_key_update(gnutls_session_t session, unsigned again, unsigned flags /* GNUTLS_KU_* */)
+{
+ int ret;
+ mbuffer_st *bufel = NULL;
+ uint8_t val;
+
+ if (again == 0) {
+ if (flags & GNUTLS_KU_PEER) {
+ /* mark that we asked a key update to prevent an
+ * infinite ping pong when receiving the reply */
+ session->internals.hsk_flags |= HSK_KEY_UPDATE_ASKED;
+ val = 0x01;
+ } else {
+ val = 0x00;
+ }
+
+ _gnutls_handshake_log("HSK[%p]: sending key update (%u)\n", session, (unsigned)val);
+
+ bufel = _gnutls_handshake_alloc(session, 1);
+ if (bufel == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ _mbuffer_set_udata_size(bufel, 0);
+ ret = _mbuffer_append_data(bufel, &val, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ }
+
+ return _gnutls_send_handshake(session, bufel, GNUTLS_HANDSHAKE_KEY_UPDATE);
+
+cleanup:
+ _mbuffer_xfree(&bufel);
+ return ret;
+}
+
+/**
+ * gnutls_session_key_update:
+ * @session: is a #gnutls_session_t type.
+ * @flags: zero of %GNUTLS_KU_PEER
+ *
+ * This function will update/refresh the session keys when the
+ * TLS protocol is 1.3 or better. The peer is notified of the
+ * update by sending a message, so this function should be
+ * treated similarly to gnutls_record_send() --i.e., it may
+ * return %GNUTLS_E_AGAIN or %GNUTLS_E_INTERRUPTED.
+ *
+ * When this flag %GNUTLS_KU_PEER is specified, this function
+ * in addition to updating the local keys, will ask the peer to
+ * refresh its keys too.
+ *
+ * If the negotiated version is not TLS 1.3 or better this
+ * function will return %GNUTLS_E_INVALID_REQUEST.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code.
+ *
+ * Since: 3.6.3
+ **/
+int gnutls_session_key_update(gnutls_session_t session, unsigned flags)
+{
+ int ret;
+ const version_entry_st *vers = get_version(session);
+
+ if (!vers->tls13_sem)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ ret =
+ _gnutls13_send_key_update(session, AGAIN(STATE150), flags);
+ STATE = STATE150;
+
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ STATE = STATE0;
+
+ _gnutls_epoch_gc(session);
+
+ /* it was completely sent, update the keys */
+ ret = update_keys(session, STAGE_UPD_OURS);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
diff --git a/lib/tls13/key_update.h b/lib/tls13/key_update.h
new file mode 100644
index 0000000..4a0123a
--- /dev/null
+++ b/lib/tls13/key_update.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_KEY_UPDATE_H
+#define GNUTLS_LIB_TLS13_KEY_UPDATE_H
+
+int _gnutls13_recv_key_update(gnutls_session_t session, gnutls_buffer_st *buf);
+int _gnutls13_send_key_update(gnutls_session_t session, unsigned again, unsigned flags);
+
+#endif /* GNUTLS_LIB_TLS13_KEY_UPDATE_H */
diff --git a/lib/tls13/post_handshake.c b/lib/tls13/post_handshake.c
new file mode 100644
index 0000000..97ca287
--- /dev/null
+++ b/lib/tls13/post_handshake.c
@@ -0,0 +1,252 @@
+/*
+ * 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 <https://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>
+#include "tls13/certificate_request.h"
+#include "tls13/certificate_verify.h"
+#include "tls13/certificate.h"
+#include "tls13/finished.h"
+
+#undef AGAIN
+#define AGAIN(x) ((x)==(REAUTH_STATE))
+
+/*
+ * _gnutls13_reauth_client
+ * This function performs the client side of the post-handshake authentication
+ */
+static
+int _gnutls13_reauth_client(gnutls_session_t session)
+{
+ int ret = 0;
+ size_t tmp;
+
+ if (!session->internals.initial_negotiation_completed)
+ return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE);
+
+ if (!(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (session->internals.reauth_buffer.length == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ switch (REAUTH_STATE) {
+ case REAUTH_STATE0:
+
+ /* restore handshake transcript */
+ _gnutls_buffer_reset(&session->internals.handshake_hash_buffer);
+ ret = gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
+ session->internals.post_handshake_hash_buffer.data,
+ session->internals.post_handshake_hash_buffer.length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* append the previously received certificate request message, to the
+ * transcript. */
+ ret = gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
+ session->internals.reauth_buffer.data,
+ session->internals.reauth_buffer.length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.handshake_hash_buffer_prev_len = session->internals.handshake_hash_buffer.length;
+
+ /* skip the reauth buffer handshake message headers */
+ ret = _gnutls_buffer_pop_prefix32(&session->internals.reauth_buffer, &tmp, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ FALLTHROUGH;
+ case REAUTH_STATE1:
+ ret = _gnutls13_recv_certificate_request_int(session,
+ &session->internals.reauth_buffer);
+ REAUTH_STATE = REAUTH_STATE1;
+ IMED_RET("recv certificate request", ret, 0);
+ FALLTHROUGH;
+ case REAUTH_STATE2:
+ ret = _gnutls13_send_certificate(session, AGAIN(REAUTH_STATE2));
+ REAUTH_STATE = REAUTH_STATE2;
+ IMED_RET("send certificate", ret, 0);
+ FALLTHROUGH;
+ case REAUTH_STATE3:
+ ret = _gnutls13_send_certificate_verify(session, AGAIN(REAUTH_STATE3));
+ REAUTH_STATE = REAUTH_STATE3;
+ IMED_RET("send certificate verify", ret, 0);
+ FALLTHROUGH;
+ case REAUTH_STATE4:
+ ret = _gnutls13_send_finished(session, AGAIN(REAUTH_STATE4));
+ REAUTH_STATE = REAUTH_STATE4;
+ IMED_RET("send finished", ret, 0);
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ _gnutls_handshake_hash_buffers_clear(session);
+ _gnutls_buffer_reset(&session->internals.reauth_buffer);
+ REAUTH_STATE = REAUTH_STATE0;
+
+ return 0;
+}
+
+/*
+ * _gnutls13_reauth_server
+ * This function does the server stuff of the post-handshake authentication.
+ */
+static
+int _gnutls13_reauth_server(gnutls_session_t session)
+{
+ int ret = 0;
+
+ if (session->security_parameters.post_handshake_auth == 0 ||
+ (!(session->internals.flags & GNUTLS_POST_HANDSHAKE_AUTH)))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (session->internals.send_cert_req == 0) {
+ _gnutls_debug_log("You need to call gnutls_certificate_server_set_request to enable post handshake auth\n");
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ switch (REAUTH_STATE) {
+ case REAUTH_STATE0:
+ /* restore handshake transcript */
+ _gnutls_buffer_reset(&session->internals.handshake_hash_buffer);
+ ret = gnutls_buffer_append_data(&session->internals.handshake_hash_buffer,
+ session->internals.post_handshake_hash_buffer.data,
+ session->internals.post_handshake_hash_buffer.length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.handshake_hash_buffer_prev_len = session->internals.handshake_hash_buffer.length;
+
+ FALLTHROUGH;
+ case REAUTH_STATE1:
+ ret = _gnutls13_send_certificate_request(session, AGAIN(REAUTH_STATE1));
+ REAUTH_STATE = REAUTH_STATE1;
+ IMED_RET("send certificate request", ret, 0);
+ FALLTHROUGH;
+ case REAUTH_STATE2:
+ /* here we should tolerate application data */
+ ret = _gnutls13_recv_certificate(session);
+ REAUTH_STATE = REAUTH_STATE2;
+ IMED_RET("recv certificate", ret, 0);
+ FALLTHROUGH;
+ case REAUTH_STATE3:
+ ret = _gnutls13_recv_certificate_verify(session);
+ REAUTH_STATE = REAUTH_STATE3;
+ IMED_RET("recv certificate verify", ret, 0);
+ FALLTHROUGH;
+ case REAUTH_STATE4:
+ ret = _gnutls_run_verify_callback(session, GNUTLS_CLIENT);
+ REAUTH_STATE = REAUTH_STATE4;
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ FALLTHROUGH;
+ case REAUTH_STATE5:
+ ret = _gnutls13_recv_finished(session);
+ REAUTH_STATE = REAUTH_STATE5;
+ IMED_RET("recv finished", ret, 0);
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ _gnutls_handshake_hash_buffers_clear(session);
+ REAUTH_STATE = REAUTH_STATE0;
+
+ return 0;
+}
+
+/**
+ * gnutls_reauth:
+ * @session: is a #gnutls_session_t type.
+ * @flags: must be zero
+ *
+ * This function performs the post-handshake authentication
+ * for TLS 1.3. The post-handshake authentication is initiated by the server
+ * by calling this function. Clients respond when %GNUTLS_E_REAUTH_REQUEST
+ * has been seen while receiving data.
+ *
+ * The non-fatal errors expected by this function are:
+ * %GNUTLS_E_INTERRUPTED, %GNUTLS_E_AGAIN, as well as
+ * %GNUTLS_E_GOT_APPLICATION_DATA when called on server side.
+ *
+ * The former two interrupt the authentication procedure due to the transport
+ * layer being interrupted, and the latter because there were pending data prior
+ * to peer initiating the re-authentication. The server should read/process that
+ * data as unauthenticated and retry calling gnutls_reauth().
+ *
+ * When this function is called under TLS1.2 or earlier or the peer didn't
+ * advertise post-handshake auth, it always fails with
+ * %GNUTLS_E_INVALID_REQUEST. The verification of the received peers certificate
+ * is delegated to the session or credentials verification callbacks. A
+ * server can check whether post handshake authentication is supported
+ * by the client by checking the session flags with gnutls_session_get_flags().
+ *
+ * Prior to calling this function in server side, the function
+ * gnutls_certificate_server_set_request() must be called setting expectations
+ * for the received certificate (request or require). If none are set
+ * this function will return with %GNUTLS_E_INVALID_REQUEST.
+ *
+ * Note that post handshake authentication is available irrespective
+ * of the initial negotiation type (PSK or certificate). In all cases
+ * however, certificate credentials must be set to the session prior
+ * to calling this function.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on a successful authentication, otherwise a negative error code.
+ **/
+int gnutls_reauth(gnutls_session_t session, unsigned int flags)
+{
+ const version_entry_st *vers = get_version(session);
+
+ if (unlikely(!vers->tls13_sem))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ return _gnutls13_reauth_server(session);
+ else
+ return _gnutls13_reauth_client(session);
+}
diff --git a/lib/tls13/psk_ext_parser.c b/lib/tls13/psk_ext_parser.c
new file mode 100644
index 0000000..33ebc04
--- /dev/null
+++ b/lib/tls13/psk_ext_parser.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017-2018 Free Software Foundation, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Author: Ander Juaristi, 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "tls13/psk_ext_parser.h"
+
+/* Returns GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE when no identities
+ * are present, or 0, on success.
+ */
+int _gnutls13_psk_ext_parser_init(psk_ext_parser_st *p,
+ const unsigned char *data, size_t len)
+{
+ if (!p || !data || !len)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ memset(p, 0, sizeof(*p));
+
+ DECR_LEN(len, 2);
+ p->identities_len = _gnutls_read_uint16(data);
+ data += 2;
+
+ if (p->identities_len == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ p->identities_data = (unsigned char *) data;
+
+ DECR_LEN(len, p->identities_len);
+ data += p->identities_len;
+
+ DECR_LEN(len, 2);
+ p->binders_len = _gnutls_read_uint16(data);
+ data += 2;
+
+ p->binders_data = data;
+ DECR_LEN(len, p->binders_len);
+
+ return 0;
+}
+
+/* Extract PSK identity and move to the next iteration.
+ *
+ * Returns GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE when no more identities
+ * are present, or 0, on success.
+ */
+int _gnutls13_psk_ext_iter_next_identity(psk_ext_iter_st *iter,
+ struct psk_st *psk)
+{
+ if (iter->identities_len == 0)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ DECR_LEN(iter->identities_len, 2);
+ psk->identity.size = _gnutls_read_uint16(iter->identities_data);
+ if (psk->identity.size == 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ iter->identities_data += 2;
+ psk->identity.data = (void*)iter->identities_data;
+
+ DECR_LEN(iter->identities_len, psk->identity.size);
+ iter->identities_data += psk->identity.size;
+
+ DECR_LEN(iter->identities_len, 4);
+ psk->ob_ticket_age = _gnutls_read_uint32(iter->identities_data);
+ iter->identities_data += 4;
+
+ return 0;
+}
+
+/* Extract PSK binder and move to the next iteration.
+ *
+ * Returns GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE when no more identities
+ * are present, or 0, on success.
+ */
+int _gnutls13_psk_ext_iter_next_binder(psk_ext_iter_st *iter,
+ gnutls_datum_t *binder)
+{
+ if (iter->binders_len == 0)
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+ DECR_LEN(iter->binders_len, 1);
+ binder->size = *iter->binders_data;
+ if (binder->size == 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ iter->binders_data++;
+ binder->data = (uint8_t *)iter->binders_data;
+ DECR_LEN(iter->binders_len, binder->size);
+ iter->binders_data += binder->size;
+
+ return 0;
+}
diff --git a/lib/tls13/psk_ext_parser.h b/lib/tls13/psk_ext_parser.h
new file mode 100644
index 0000000..f46b211
--- /dev/null
+++ b/lib/tls13/psk_ext_parser.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ * Author: Ander Juaristi
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_PSK_EXT_PARSER_H
+#define GNUTLS_LIB_TLS13_PSK_EXT_PARSER_H
+
+struct psk_ext_parser_st {
+ const unsigned char *identities_data;
+ size_t identities_len;
+
+ const unsigned char *binders_data;
+ size_t binders_len;
+};
+
+typedef struct psk_ext_parser_st psk_ext_parser_st;
+typedef struct psk_ext_parser_st psk_ext_iter_st;
+
+struct psk_st {
+ /* constant values */
+ gnutls_datum_t identity;
+ uint32_t ob_ticket_age;
+};
+
+int _gnutls13_psk_ext_parser_init(psk_ext_parser_st *p,
+ const unsigned char *data, size_t len);
+
+inline static
+void _gnutls13_psk_ext_iter_init(psk_ext_iter_st *iter,
+ const psk_ext_parser_st *p)
+{
+ memcpy(iter, p, sizeof(*p));
+}
+
+int _gnutls13_psk_ext_iter_next_identity(psk_ext_iter_st *iter,
+ struct psk_st *psk);
+int _gnutls13_psk_ext_iter_next_binder(psk_ext_iter_st *iter,
+ gnutls_datum_t *binder);
+
+#endif /* GNUTLS_LIB_TLS13_PSK_EXT_PARSER_H */
diff --git a/lib/tls13/session_ticket.c b/lib/tls13/session_ticket.c
new file mode 100644
index 0000000..909ab05
--- /dev/null
+++ b/lib/tls13/session_ticket.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Ander Juaristi
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "extv.h"
+#include "handshake.h"
+#include "mbuffers.h"
+#include "ext/pre_shared_key.h"
+#include "ext/session_ticket.h"
+#include "ext/early_data.h"
+#include "auth/cert.h"
+#include "tls13/session_ticket.h"
+#include "session_pack.h"
+#include "db.h"
+
+static int
+pack_ticket(gnutls_session_t session, tls13_ticket_st *ticket, gnutls_datum_t *packed)
+{
+ uint8_t *p;
+ gnutls_datum_t state;
+ int ret;
+
+ ret = _gnutls_session_pack(session, &state);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ packed->size = 2 + 4 + 4 +
+ 1 + ticket->prf->output_size +
+ 1 + ticket->nonce_size + 2 + state.size + 12;
+
+ packed->data = gnutls_malloc(packed->size);
+ if (!packed->data) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto cleanup;
+ }
+
+ p = packed->data;
+
+ _gnutls_write_uint16(ticket->prf->id, p);
+ p += 2;
+ _gnutls_write_uint32(ticket->age_add, p);
+ p += 4;
+ _gnutls_write_uint32(ticket->lifetime, p);
+ p += 4;
+ *p = ticket->prf->output_size;
+ p += 1;
+ memcpy(p, ticket->resumption_master_secret, ticket->prf->output_size);
+ p += ticket->prf->output_size;
+ *p = ticket->nonce_size;
+
+ p += 1;
+ memcpy(p, ticket->nonce, ticket->nonce_size);
+ p += ticket->nonce_size;
+
+ _gnutls_write_uint16(state.size, p);
+ p += 2;
+
+ memcpy(p, state.data, state.size);
+ p += state.size;
+
+ _gnutls_write_uint32((uint64_t) ticket->creation_time.tv_sec >> 32, p);
+ p += 4;
+ _gnutls_write_uint32(ticket->creation_time.tv_sec & 0xFFFFFFFF, p);
+ p += 4;
+ _gnutls_write_uint32(ticket->creation_time.tv_nsec, p);
+
+ ret = 0;
+
+ cleanup:
+ gnutls_free(state.data);
+ return ret;
+}
+
+static int
+unpack_ticket(gnutls_session_t session, gnutls_datum_t *packed, tls13_ticket_st *data)
+{
+ uint32_t age_add, lifetime;
+ struct timespec creation_time;
+ uint8_t resumption_master_secret[MAX_HASH_SIZE];
+ size_t resumption_master_secret_size;
+ uint8_t nonce[UINT8_MAX];
+ size_t nonce_size;
+ gnutls_datum_t state;
+ gnutls_mac_algorithm_t kdf;
+ const mac_entry_st *prf;
+ uint8_t *p;
+ size_t len;
+ uint64_t v;
+ int ret;
+
+ if (unlikely(packed == NULL || data == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ memset(data, 0, sizeof(*data));
+
+ p = packed->data;
+ len = packed->size;
+
+ DECR_LEN(len, 2);
+ kdf = _gnutls_read_uint16(p);
+ p += 2;
+
+ /* Check if the MAC ID we got is valid */
+ prf = _gnutls_mac_to_entry(kdf);
+ if (prf == NULL)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ /* Read the ticket age add and the ticket lifetime */
+ DECR_LEN(len, 4);
+ age_add = _gnutls_read_uint32(p);
+ p += 4;
+
+ DECR_LEN(len, 4);
+ lifetime = _gnutls_read_uint32(p);
+ p += 4;
+
+ /*
+ * Check if the whole ticket is large enough,
+ * and read the resumption master secret
+ */
+ DECR_LEN(len, 1);
+ resumption_master_secret_size = *p;
+ p += 1;
+
+ /* Check if the size of resumption_master_secret matches the PRF */
+ if (resumption_master_secret_size != prf->output_size)
+ return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+
+ DECR_LEN(len, resumption_master_secret_size);
+ memcpy(resumption_master_secret, p, resumption_master_secret_size);
+ p += resumption_master_secret_size;
+
+ /* Read the ticket nonce */
+ DECR_LEN(len, 1);
+ nonce_size = *p;
+ p += 1;
+
+ DECR_LEN(len, nonce_size);
+ memcpy(nonce, p, nonce_size);
+ p += nonce_size;
+
+ DECR_LEN(len, 2);
+ state.size = _gnutls_read_uint16(p);
+ p += 2;
+
+ DECR_LEN(len, state.size);
+ state.data = p;
+ p += state.size;
+
+ DECR_LEN(len, 12);
+ v = _gnutls_read_uint32(p);
+ p += 4;
+ creation_time.tv_sec = (v << 32) | _gnutls_read_uint32(p);
+ p += 4;
+ creation_time.tv_nsec = _gnutls_read_uint32(p);
+
+ ret = _gnutls_session_unpack(session, &state);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* No errors - Now return all the data to the caller */
+ data->prf = prf;
+ memcpy(data->resumption_master_secret, resumption_master_secret,
+ resumption_master_secret_size);
+ memcpy(data->nonce, nonce, nonce_size);
+ data->nonce_size = nonce_size;
+ data->age_add = age_add;
+ data->lifetime = lifetime;
+ memcpy(&data->creation_time, &creation_time, sizeof(struct timespec));
+
+ return 0;
+}
+
+static int
+generate_session_ticket(gnutls_session_t session, tls13_ticket_st *ticket)
+{
+ int ret;
+ gnutls_datum_t packed = { NULL, 0 };
+ struct timespec now;
+ tls13_ticket_st ticket_data;
+
+ gnutls_gettime(&now);
+ if (session->internals.resumed) {
+ /* If we are resuming ensure that we don't extend the lifetime
+ * of the ticket past the original session expiration time */
+ if (now.tv_sec >= session->security_parameters.timestamp + session->internals.expire_time)
+ return GNUTLS_E_INT_RET_0; /* don't send ticket */
+ else
+ ticket->lifetime = session->security_parameters.timestamp +
+ session->internals.expire_time - now.tv_sec;
+ } else {
+ /* Set ticket lifetime to the default expiration time */
+ ticket->lifetime = session->internals.expire_time;
+ }
+
+ /* Generate a random 32-bit ticket nonce */
+ ticket->nonce_size = 4;
+
+ if ((ret = gnutls_rnd(GNUTLS_RND_NONCE,
+ ticket->nonce, ticket->nonce_size)) < 0)
+ return gnutls_assert_val(ret);
+
+ if ((ret = gnutls_rnd(GNUTLS_RND_NONCE, &ticket->age_add, sizeof(uint32_t))) < 0)
+ return gnutls_assert_val(ret);
+ /* This is merely to produce the same binder value on
+ * different endian architectures. */
+#ifdef WORDS_BIGENDIAN
+ ticket->age_add = bswap_32(ticket->age_add);
+#endif
+
+ ticket->prf = session->security_parameters.prf;
+
+ /* Encrypt the ticket and place the result in ticket->ticket */
+ ticket_data.lifetime = ticket->lifetime;
+ ticket_data.age_add = ticket->age_add;
+ memcpy(&ticket_data.creation_time, &now, sizeof(struct timespec));
+ memcpy(ticket_data.nonce, ticket->nonce, ticket->nonce_size);
+ ticket_data.nonce_size = ticket->nonce_size;
+ ticket_data.prf = ticket->prf;
+ memcpy(&ticket_data.resumption_master_secret,
+ session->key.proto.tls13.ap_rms,
+ ticket->prf->output_size);
+
+ ret = pack_ticket(session, &ticket_data, &packed);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_encrypt_session_ticket(session, &packed, &ticket->ticket);
+ _gnutls_free_datum(&packed);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
+
+static int append_nst_extension(void *ctx, gnutls_buffer_st *buf)
+{
+ gnutls_session_t session = ctx;
+ int ret;
+
+ if (!(session->internals.flags & GNUTLS_ENABLE_EARLY_DATA))
+ return 0;
+
+ ret = _gnutls_buffer_append_prefix(buf, 32,
+ session->security_parameters.
+ max_early_data_size);
+ if (ret < 0)
+ gnutls_assert();
+
+ return ret;
+}
+
+int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned nr, unsigned again)
+{
+ int ret = 0;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ tls13_ticket_st ticket;
+ unsigned i;
+
+ /* Client does not send a NewSessionTicket */
+ if (unlikely(session->security_parameters.entity == GNUTLS_CLIENT))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ /* Session resumption has not been enabled */
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
+ return gnutls_assert_val(0);
+
+ /* If we received the psk_key_exchange_modes extension which
+ * does not have overlap with the server configuration, don't
+ * send a session ticket */
+ if (session->internals.hsk_flags & HSK_PSK_KE_MODE_INVALID)
+ return gnutls_assert_val(0);
+
+ if (again == 0) {
+ for (i=0;i<nr;i++) {
+ unsigned init_pos;
+
+ memset(&ticket, 0, sizeof(tls13_ticket_st));
+ bufel = NULL;
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = generate_session_ticket(session, &ticket);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_INT_RET_0) {
+ ret = gnutls_assert_val(0);
+ goto cleanup;
+ }
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_prefix(&buf, 32, ticket.lifetime);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_prefix(&buf, 32, ticket.age_add);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* append ticket_nonce */
+ ret = _gnutls_buffer_append_data_prefix(&buf, 8, ticket.nonce, ticket.nonce_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* append ticket */
+ ret = _gnutls_buffer_append_data_prefix(&buf, 16, ticket.ticket.data, ticket.ticket.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_free_datum(&ticket.ticket);
+
+ /* append extensions */
+ ret = _gnutls_extv_append_init(&buf);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ init_pos = ret;
+
+ ret = _gnutls_extv_append(&buf, ext_mod_early_data.tls_id, session,
+ (extv_append_func)append_nst_extension);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_extv_append_final(&buf, init_pos, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+
+ ret = _gnutls_send_handshake2(session, bufel,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET, 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT;
+ }
+ }
+
+ ret = _gnutls_handshake_io_write_flush(session);
+
+ return ret;
+
+cleanup:
+ _gnutls_free_datum(&ticket.ticket);
+ _mbuffer_xfree(&bufel);
+ _gnutls_buffer_clear(&buf);
+
+ return ret;
+}
+
+static int parse_nst_extension(void *ctx, unsigned tls_id, const unsigned char *data, unsigned data_size)
+{
+ gnutls_session_t session = ctx;
+ if (tls_id == ext_mod_early_data.tls_id) {
+ if (data_size < 4)
+ return gnutls_assert_val(GNUTLS_E_TLS_PACKET_DECODING_ERROR);
+ session->security_parameters.max_early_data_size =
+ _gnutls_read_uint32(data);
+ }
+ return 0;
+}
+
+int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf)
+{
+ int ret;
+ uint8_t value;
+ tls13_ticket_st *ticket = &session->internals.tls13_ticket;
+ gnutls_datum_t t;
+ size_t val;
+
+ if (unlikely(buf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ _gnutls_free_datum(&ticket->ticket);
+ memset(ticket, 0, sizeof(tls13_ticket_st));
+
+ _gnutls_handshake_log("HSK[%p]: parsing session ticket message\n", session);
+
+ /* ticket_lifetime */
+ ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ ticket->lifetime = val;
+
+ /* ticket_age_add */
+ ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ ticket->age_add = val;
+
+ /* ticket_nonce */
+ ret = _gnutls_buffer_pop_prefix8(buf, &value, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ticket->nonce_size = value;
+ ret = _gnutls_buffer_pop_data(buf, ticket->nonce, ticket->nonce_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* ticket */
+ ret = _gnutls_buffer_pop_datum_prefix16(buf, &t);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ gnutls_free(ticket->ticket.data);
+ ret = _gnutls_set_datum(&ticket->ticket, t.data, t.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Extensions */
+ ret = _gnutls_extv_parse(session, parse_nst_extension, buf->data, buf->length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Record the ticket arrival time */
+ gnutls_gettime(&ticket->arrival_time);
+
+ return 0;
+}
+
+/*
+ * Parse the ticket in 'data' and return the resumption master secret
+ * and the KDF ID associated to it.
+ */
+int _gnutls13_unpack_session_ticket(gnutls_session_t session,
+ gnutls_datum_t *data,
+ tls13_ticket_st *ticket_data)
+{
+ int ret;
+ gnutls_datum_t decrypted = { NULL, 0 };
+
+ if (unlikely(data == NULL || ticket_data == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (!session->key.stek_initialized) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ /* Check MAC and decrypt ticket */
+ ret = _gnutls_decrypt_session_ticket(session, data, &decrypted);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Return ticket parameters */
+ ret = unpack_ticket(session, &decrypted, ticket_data);
+ _gnutls_free_datum(&decrypted);
+ if (ret < 0)
+ return ret;
+
+ ret = _gnutls_check_resumed_params(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+}
diff --git a/lib/tls13/session_ticket.h b/lib/tls13/session_ticket.h
new file mode 100644
index 0000000..db0be15
--- /dev/null
+++ b/lib/tls13/session_ticket.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Ander Juaristi
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_TLS13_SESSION_TICKET_H
+#define GNUTLS_LIB_TLS13_SESSION_TICKET_H
+
+int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf);
+int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned nr, unsigned again);
+
+static inline bool
+_gnutls13_can_send_session_ticket(gnutls_session_t session)
+{
+ return session->key.stek_initialized &&
+ !(session->internals.flags & GNUTLS_NO_TICKETS);
+}
+
+int _gnutls13_unpack_session_ticket(gnutls_session_t session,
+ gnutls_datum_t *data,
+ tls13_ticket_st *ticket_data);
+
+inline static
+void tls13_ticket_deinit(tls13_ticket_st *ticket)
+{
+ zeroize_temp_key(&ticket->resumption_master_secret,
+ sizeof(ticket->resumption_master_secret));
+
+ _gnutls_free_datum(&ticket->ticket);
+ memset(ticket, 0, sizeof(tls13_ticket_st));
+}
+
+#endif /* GNUTLS_LIB_TLS13_SESSION_TICKET_H */