summaryrefslogtreecommitdiffstats
path: root/lib/session_pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/session_pack.c')
-rw-r--r--lib/session_pack.c1204
1 files changed, 1204 insertions, 0 deletions
diff --git a/lib/session_pack.c b/lib/session_pack.c
new file mode 100644
index 0000000..fa9a0fc
--- /dev/null
+++ b/lib/session_pack.c
@@ -0,0 +1,1204 @@
+/*
+ * Copyright (C) 2000-2012 Free Software Foundation, Inc.
+ * 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/>
+ *
+ */
+
+/* Contains functions that are supposed to pack and unpack session data,
+ * before and after they are sent to the database backend.
+ */
+
+#include "gnutls_int.h"
+#ifdef ENABLE_SRP
+#include <auth/srp_kx.h>
+#endif
+#ifdef ENABLE_PSK
+#include <auth/psk.h>
+#endif
+#include <auth/anon.h>
+#include <auth/cert.h>
+#include "errors.h"
+#include <auth.h>
+#include <session_pack.h>
+#include <datum.h>
+#include <num.h>
+#include <hello_ext.h>
+#include <constate.h>
+#include <algorithms.h>
+#include <state.h>
+#include <db.h>
+#include "tls13/session_ticket.h"
+
+static int pack_certificate_auth_info(gnutls_session_t,
+ gnutls_buffer_st * packed_session);
+static int unpack_certificate_auth_info(gnutls_session_t,
+ gnutls_buffer_st * packed_session);
+
+static int unpack_srp_auth_info(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+static int pack_srp_auth_info(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+
+static int unpack_psk_auth_info(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+static int pack_psk_auth_info(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+
+static int unpack_anon_auth_info(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+static int pack_anon_auth_info(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+
+static int unpack_security_parameters(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+static int pack_security_parameters(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+static int tls13_unpack_security_parameters(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+static int tls13_pack_security_parameters(gnutls_session_t session,
+ gnutls_buffer_st * packed_session);
+
+
+/* Since auth_info structures contain malloced data, this function
+ * is required in order to pack these structures in a vector in
+ * order to store them to the DB.
+ *
+ * packed_session will contain the session data.
+ *
+ * The data will be in a platform independent format.
+ */
+int
+_gnutls_session_pack(gnutls_session_t session,
+ gnutls_datum_t * packed_session)
+{
+ int ret;
+ gnutls_buffer_st sb;
+ uint8_t id;
+
+ if (packed_session == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ _gnutls_buffer_init(&sb);
+
+
+ id = gnutls_auth_get_type(session);
+
+ BUFFER_APPEND_NUM(&sb, PACKED_SESSION_MAGIC);
+ BUFFER_APPEND_NUM(&sb, session->security_parameters.timestamp);
+ BUFFER_APPEND_NUM(&sb, session->internals.expire_time);
+ BUFFER_APPEND(&sb, &id, 1);
+
+ switch (id) {
+#ifdef ENABLE_SRP
+ case GNUTLS_CRD_SRP:
+ ret = pack_srp_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ break;
+#endif
+#ifdef ENABLE_PSK
+ case GNUTLS_CRD_PSK:
+ ret = pack_psk_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ break;
+#endif
+#ifdef ENABLE_ANON
+ case GNUTLS_CRD_ANON:
+ ret = pack_anon_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ break;
+#endif
+ case GNUTLS_CRD_CERTIFICATE:
+ ret = pack_certificate_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ break;
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto fail;
+
+ }
+
+ /* Auth_info structures copied. Now copy security_parameters_st.
+ * packed_session must have allocated space for the security parameters.
+ */
+ ret = pack_security_parameters(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
+
+ if (session->security_parameters.pversion->tls13_sem) {
+ ret = tls13_pack_security_parameters(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ /* Extensions are re-negotiated in a resumed session under TLS 1.3 */
+ if (!session->security_parameters.pversion->tls13_sem) {
+ ret = _gnutls_hello_ext_pack(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
+
+ return _gnutls_buffer_to_datum(&sb, packed_session, 0);
+
+ fail:
+ _gnutls_buffer_clear(&sb);
+ return ret;
+}
+
+
+/* Load session data from a buffer.
+ */
+int
+_gnutls_session_unpack(gnutls_session_t session,
+ const gnutls_datum_t * packed_session)
+{
+ int ret;
+ gnutls_buffer_st sb;
+ uint32_t magic;
+ uint32_t expire_time;
+ uint8_t id;
+
+ _gnutls_buffer_init(&sb);
+
+ if (packed_session == NULL || packed_session->size == 0) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ ret =
+ _gnutls_buffer_append_data(&sb, packed_session->data,
+ packed_session->size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ if (session->key.auth_info != NULL) {
+ _gnutls_free_auth_info(session);
+ }
+
+ BUFFER_POP_NUM(&sb, magic);
+ if (magic != PACKED_SESSION_MAGIC) {
+ ret = gnutls_assert_val(GNUTLS_E_DB_ERROR);
+ goto error;
+ }
+
+ BUFFER_POP_NUM(&sb,
+ session->internals.resumed_security_parameters.
+ timestamp);
+ BUFFER_POP_NUM(&sb, expire_time);
+ (void) expire_time;
+ BUFFER_POP(&sb, &id, 1);
+
+ switch (id) {
+#ifdef ENABLE_SRP
+ case GNUTLS_CRD_SRP:
+ ret = unpack_srp_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ break;
+#endif
+#ifdef ENABLE_PSK
+ case GNUTLS_CRD_PSK:
+ ret = unpack_psk_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ break;
+#endif
+#ifdef ENABLE_ANON
+ case GNUTLS_CRD_ANON:
+ ret = unpack_anon_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ break;
+#endif
+ case GNUTLS_CRD_CERTIFICATE:
+ ret = unpack_certificate_auth_info(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ break;
+ default:
+ gnutls_assert();
+ ret = GNUTLS_E_INTERNAL_ERROR;
+ goto error;
+
+ }
+
+ /* Auth_info structures copied. Now copy security_parameters_st.
+ * packed_session must have allocated space for the security parameters.
+ */
+ ret = unpack_security_parameters(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (session->internals.resumed_security_parameters.pversion->tls13_sem) {
+ /* 'prf' will not be NULL at this point, else unpack_security_parameters() would have failed */
+ ret = tls13_unpack_security_parameters(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
+ if (!session->internals.resumed_security_parameters.pversion->tls13_sem) {
+ ret = _gnutls_hello_ext_unpack(session, &sb);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
+ ret = 0;
+
+ error:
+ _gnutls_buffer_clear(&sb);
+
+ return ret;
+}
+
+/*
+ * If we're using TLS 1.3 semantics, we might have TLS 1.3-specific data.
+ * Format:
+ * 4 bytes the total length
+ * 4 bytes the ticket lifetime
+ * 4 bytes the ticket age add value
+ * 1 byte the ticket nonce length
+ * x bytes the ticket nonce
+ * 4 bytes the ticket length
+ * x bytes the ticket
+ * 1 bytes the resumption master secret length
+ * x bytes the resumption master secret
+ * 12 bytes the ticket arrival time
+ * 4 bytes the max early data size
+ *
+ * We only store that info if we received a TLS 1.3 NewSessionTicket at some point.
+ * If we didn't receive any NST then we cannot resume a TLS 1.3 session and hence
+ * its nonsense to store all that info.
+ */
+static int
+tls13_pack_security_parameters(gnutls_session_t session, gnutls_buffer_st *ps)
+{
+ int ret = 0;
+ uint32_t length = 0;
+ size_t length_pos;
+ tls13_ticket_st *ticket = &session->internals.tls13_ticket;
+
+ length_pos = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+
+ if (ticket->ticket.data != NULL) {
+ BUFFER_APPEND_NUM(ps, ticket->lifetime);
+ length += 4;
+ BUFFER_APPEND_NUM(ps, ticket->age_add);
+ length += 4;
+ BUFFER_APPEND_PFX1(ps,
+ ticket->nonce,
+ ticket->nonce_size);
+ length += (1 + ticket->nonce_size);
+ BUFFER_APPEND_PFX4(ps,
+ ticket->ticket.data,
+ ticket->ticket.size);
+ length += (4 + ticket->ticket.size);
+ BUFFER_APPEND_PFX1(ps,
+ ticket->resumption_master_secret,
+ ticket->prf->output_size);
+ length += (1 + ticket->prf->output_size);
+ BUFFER_APPEND_TS(ps, ticket->arrival_time);
+ length += 12;
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.
+ max_early_data_size);
+ length += 4;
+
+ /* Overwrite the length field */
+ _gnutls_write_uint32(length, ps->data + length_pos);
+ }
+
+ return ret;
+}
+
+static int
+tls13_unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st *ps)
+{
+ uint32_t ttl_len;
+ tls13_ticket_st *ticket = &session->internals.tls13_ticket;
+ gnutls_datum_t t;
+ int ret = 0;
+
+ BUFFER_POP_NUM(ps, ttl_len);
+
+ if (ttl_len > 0) {
+ BUFFER_POP_NUM(ps, ticket->lifetime);
+ BUFFER_POP_NUM(ps, ticket->age_add);
+
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0 || t.size > sizeof(ticket->nonce)) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ ticket->nonce_size = t.size;
+ memcpy(ticket->nonce, t.data, t.size);
+
+ BUFFER_POP_DATUM(ps, &ticket->ticket);
+
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0 || t.size > sizeof(ticket->resumption_master_secret)) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ memcpy(ticket->resumption_master_secret, t.data, t.size);
+
+ if (unlikely(session->internals.resumed_security_parameters.prf == NULL ||
+ session->internals.resumed_security_parameters.prf->output_size != t.size))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ticket->prf = session->internals.resumed_security_parameters.prf;
+
+ BUFFER_POP_TS(ps, ticket->arrival_time);
+ BUFFER_POP_NUM(ps,
+ session->security_parameters.
+ max_early_data_size);
+ }
+
+error:
+ return ret;
+}
+
+/* Format:
+ * 1 byte the credentials type
+ * 4 bytes the size of the whole structure
+ * DH stuff
+ * 2 bytes the size of secret key in bits
+ * 4 bytes the size of the prime
+ * x bytes the prime
+ * 4 bytes the size of the generator
+ * x bytes the generator
+ * 4 bytes the size of the public key
+ * x bytes the public key
+ * RSA stuff
+ * 4 bytes the size of the modulus
+ * x bytes the modulus
+ * 4 bytes the size of the exponent
+ * x bytes the exponent
+ * CERTIFICATES
+ * 4 bytes the length of the certificate list
+ * 4 bytes the size of first certificate
+ * x bytes the certificate
+ * and so on...
+ */
+static int
+pack_certificate_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ unsigned int i;
+ int cur_size, ret;
+ cert_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ int size_offset;
+
+ size_offset = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+ cur_size = ps->length;
+
+ if (info) {
+
+ BUFFER_APPEND_NUM(ps, info->dh.secret_bits);
+ BUFFER_APPEND_PFX4(ps, info->dh.prime.data,
+ info->dh.prime.size);
+ BUFFER_APPEND_PFX4(ps, info->dh.generator.data,
+ info->dh.generator.size);
+ BUFFER_APPEND_PFX4(ps, info->dh.public_key.data,
+ info->dh.public_key.size);
+
+ BUFFER_APPEND_NUM(ps, info->ncerts);
+
+ for (i = 0; i < info->ncerts; i++) {
+ BUFFER_APPEND_PFX4(ps,
+ info->raw_certificate_list[i].
+ data,
+ info->raw_certificate_list[i].
+ size);
+ }
+
+ BUFFER_APPEND_NUM(ps, info->nocsp);
+
+ for (i = 0; i < info->nocsp; i++) {
+ BUFFER_APPEND_PFX4(ps,
+ info->raw_ocsp_list[i].
+ data,
+ info->raw_ocsp_list[i].
+ size);
+ }
+ }
+
+ /* write the real size */
+ _gnutls_write_uint32(ps->length - cur_size,
+ ps->data + size_offset);
+
+ return 0;
+}
+
+
+/* Upack certificate info.
+ */
+static int
+unpack_certificate_auth_info(gnutls_session_t session,
+ gnutls_buffer_st * ps)
+{
+ int ret;
+ unsigned int i = 0, j = 0;
+ size_t pack_size;
+ cert_auth_info_t info = NULL;
+ unsigned cur_ncerts = 0;
+ unsigned cur_nocsp = 0;
+
+ BUFFER_POP_NUM(ps, pack_size);
+
+ if (pack_size == 0)
+ return 0; /* nothing to be done */
+
+ /* client and server have the same auth_info here
+ */
+ ret =
+ _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE,
+ sizeof(cert_auth_info_st), 1);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ BUFFER_POP_NUM(ps, info->dh.secret_bits);
+
+ BUFFER_POP_DATUM(ps, &info->dh.prime);
+ BUFFER_POP_DATUM(ps, &info->dh.generator);
+ BUFFER_POP_DATUM(ps, &info->dh.public_key);
+
+ BUFFER_POP_NUM(ps, info->ncerts);
+
+ if (info->ncerts > 0) {
+ info->raw_certificate_list =
+ gnutls_calloc(info->ncerts, sizeof(gnutls_datum_t));
+ if (info->raw_certificate_list == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto error;
+ }
+ }
+
+ for (i = 0; i < info->ncerts; i++) {
+ BUFFER_POP_DATUM(ps, &info->raw_certificate_list[i]);
+ cur_ncerts++;
+ }
+
+ /* read OCSP responses */
+ BUFFER_POP_NUM(ps, info->nocsp);
+
+ if (info->nocsp > 0) {
+ info->raw_ocsp_list =
+ gnutls_calloc(info->nocsp, sizeof(gnutls_datum_t));
+ if (info->raw_ocsp_list == NULL) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto error;
+ }
+ }
+
+ for (i = 0; i < info->nocsp; i++) {
+ BUFFER_POP_DATUM(ps, &info->raw_ocsp_list[i]);
+ cur_nocsp++;
+ }
+
+ return 0;
+
+ error:
+ if (info) {
+ _gnutls_free_datum(&info->dh.prime);
+ _gnutls_free_datum(&info->dh.generator);
+ _gnutls_free_datum(&info->dh.public_key);
+
+ for (j = 0; j < cur_ncerts; j++)
+ _gnutls_free_datum(&info->raw_certificate_list[j]);
+
+ for (j = 0; j < cur_nocsp; j++)
+ _gnutls_free_datum(&info->raw_ocsp_list[j]);
+
+ gnutls_free(info->raw_certificate_list);
+ gnutls_free(info->raw_ocsp_list);
+ }
+
+ return ret;
+
+}
+
+#ifdef ENABLE_SRP
+/* Packs the SRP session authentication data.
+ */
+
+/* Format:
+ * 1 byte the credentials type
+ * 4 bytes the size of the SRP username (x)
+ * x bytes the SRP username
+ */
+static int
+pack_srp_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ srp_server_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_SRP);
+ int len, ret;
+ int size_offset;
+ size_t cur_size;
+ const char *username = NULL;
+
+ if (info) {
+ if (info->username) {
+ username = info->username;
+ len = strlen(info->username) + 1; /* include the terminating null */
+ } else {
+ username = "\0";
+ len = 1;
+ }
+ } else
+ len = 0;
+
+ size_offset = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+ cur_size = ps->length;
+
+ BUFFER_APPEND_PFX4(ps, username, len);
+
+ /* write the real size */
+ _gnutls_write_uint32(ps->length - cur_size,
+ ps->data + size_offset);
+
+ return 0;
+}
+
+
+static int
+unpack_srp_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ size_t username_size;
+ int ret;
+ srp_server_auth_info_t info;
+
+ BUFFER_POP_NUM(ps, username_size);
+ if (username_size > MAX_USERNAME_SIZE + 1)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_auth_info_init(session, GNUTLS_CRD_SRP,
+ sizeof(srp_server_auth_info_st), 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_SRP);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ gnutls_free(info->username);
+ if (username_size == 0) {
+ info->username = NULL;
+ } else {
+ info->username = gnutls_malloc(username_size);
+ if (info->username == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ BUFFER_POP(ps, info->username, username_size);
+
+ ret = 0;
+
+ error:
+ return ret;
+}
+#endif
+
+
+#ifdef ENABLE_ANON
+/* Packs the ANON session authentication data.
+ */
+
+/* Format:
+ * 1 byte the credentials type
+ * 4 bytes the size of the whole structure
+ * 2 bytes the size of secret key in bits
+ * 4 bytes the size of the prime
+ * x bytes the prime
+ * 4 bytes the size of the generator
+ * x bytes the generator
+ * 4 bytes the size of the public key
+ * x bytes the public key
+ */
+static int
+pack_anon_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ int cur_size, ret;
+ anon_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
+ int size_offset;
+
+ size_offset = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+ cur_size = ps->length;
+
+ if (info) {
+ BUFFER_APPEND_NUM(ps, info->dh.secret_bits);
+ BUFFER_APPEND_PFX4(ps, info->dh.prime.data,
+ info->dh.prime.size);
+ BUFFER_APPEND_PFX4(ps, info->dh.generator.data,
+ info->dh.generator.size);
+ BUFFER_APPEND_PFX4(ps, info->dh.public_key.data,
+ info->dh.public_key.size);
+ }
+
+ /* write the real size */
+ _gnutls_write_uint32(ps->length - cur_size,
+ ps->data + size_offset);
+
+ return 0;
+}
+
+
+static int
+unpack_anon_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ int ret;
+ size_t pack_size;
+ anon_auth_info_t info = NULL;
+
+ BUFFER_POP_NUM(ps, pack_size);
+
+ if (pack_size == 0)
+ return 0; /* nothing to be done */
+
+ /* client and server have the same auth_info here
+ */
+ ret =
+ _gnutls_auth_info_init(session, GNUTLS_CRD_ANON,
+ sizeof(anon_auth_info_st), 1);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_ANON);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ BUFFER_POP_NUM(ps, info->dh.secret_bits);
+
+ BUFFER_POP_DATUM(ps, &info->dh.prime);
+ BUFFER_POP_DATUM(ps, &info->dh.generator);
+ BUFFER_POP_DATUM(ps, &info->dh.public_key);
+
+ return 0;
+
+ error:
+ if (info) {
+ _gnutls_free_datum(&info->dh.prime);
+ _gnutls_free_datum(&info->dh.generator);
+ _gnutls_free_datum(&info->dh.public_key);
+ }
+
+ return ret;
+}
+#endif /* ANON */
+
+#ifdef ENABLE_PSK
+/* Packs the PSK session authentication data.
+ */
+
+/* Format:
+ * 1 byte the credentials type
+ * 4 bytes the size of the whole structure
+ *
+ * 4 bytes the size of the PSK username (x)
+ * x bytes the PSK username
+ * 2 bytes the size of secret key in bits
+ * 4 bytes the size of the prime
+ * x bytes the prime
+ * 4 bytes the size of the generator
+ * x bytes the generator
+ * 4 bytes the size of the public key
+ * x bytes the public key
+ */
+static int
+pack_psk_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ psk_auth_info_t info;
+ int username_len;
+ int hint_len, ret;
+ int size_offset;
+ size_t cur_size;
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ username_len = info->username_len;
+ hint_len = info->hint_len + 1; /* include the terminating null */
+
+ size_offset = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+ cur_size = ps->length;
+
+ BUFFER_APPEND_PFX4(ps, info->username, username_len);
+ BUFFER_APPEND_PFX4(ps, info->hint ? info->hint : "\0", hint_len);
+
+ BUFFER_APPEND_NUM(ps, info->dh.secret_bits);
+ BUFFER_APPEND_PFX4(ps, info->dh.prime.data, info->dh.prime.size);
+ BUFFER_APPEND_PFX4(ps, info->dh.generator.data,
+ info->dh.generator.size);
+ BUFFER_APPEND_PFX4(ps, info->dh.public_key.data,
+ info->dh.public_key.size);
+
+ /* write the real size */
+ _gnutls_write_uint32(ps->length - cur_size,
+ ps->data + size_offset);
+ return 0;
+}
+
+static int
+unpack_psk_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ size_t username_size, hint_size;
+ int ret;
+ psk_auth_info_t info;
+ unsigned pack_size;
+
+ ret =
+ _gnutls_auth_info_init(session, GNUTLS_CRD_PSK,
+ sizeof(psk_auth_info_st), 1);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ if (info == NULL)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ BUFFER_POP_NUM(ps, pack_size);
+ if (pack_size == 0)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ BUFFER_POP_NUM(ps, username_size);
+ if (username_size > MAX_USERNAME_SIZE)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ gnutls_free(info->username);
+ info->username = gnutls_malloc(username_size + 1);
+ if (info->username == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ BUFFER_POP(ps, info->username, username_size);
+ info->username[username_size] = 0;
+ info->username_len = username_size;
+
+ /* hint_size includes the terminating null */
+ BUFFER_POP_NUM(ps, hint_size);
+ if (hint_size > MAX_USERNAME_SIZE + 1)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ gnutls_free(info->hint);
+ info->hint = gnutls_malloc(hint_size);
+ if (info->hint == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ BUFFER_POP(ps, info->hint, hint_size);
+ info->hint_len = hint_size - 1;
+
+ BUFFER_POP_NUM(ps, info->dh.secret_bits);
+
+ BUFFER_POP_DATUM(ps, &info->dh.prime);
+ BUFFER_POP_DATUM(ps, &info->dh.generator);
+ BUFFER_POP_DATUM(ps, &info->dh.public_key);
+
+ ret = 0;
+
+ error:
+ _gnutls_free_datum(&info->dh.prime);
+ _gnutls_free_datum(&info->dh.generator);
+ _gnutls_free_datum(&info->dh.public_key);
+
+ return ret;
+}
+#endif
+
+
+/* Packs the security parameters.
+ */
+static int
+pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+
+ int ret;
+ int size_offset;
+ size_t cur_size;
+
+ if (session->security_parameters.epoch_read
+ != session->security_parameters.epoch_write &&
+ !(session->internals.hsk_flags & HSK_EARLY_START_USED)) {
+ gnutls_assert();
+ return GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE;
+ }
+
+ ret = _gnutls_epoch_get(session, EPOCH_READ_CURRENT, NULL);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ /* move after the auth info stuff.
+ */
+ size_offset = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+ cur_size = ps->length;
+
+ BUFFER_APPEND_NUM(ps, session->security_parameters.entity);
+ BUFFER_APPEND_NUM(ps, session->security_parameters.prf->id);
+
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.client_auth_type);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.server_auth_type);
+
+ BUFFER_APPEND(ps, &session->security_parameters.session_id_size,
+ 1);
+ BUFFER_APPEND(ps, session->security_parameters.session_id,
+ session->security_parameters.session_id_size);
+
+ BUFFER_APPEND_NUM(ps, session->security_parameters.pversion->id);
+
+ BUFFER_APPEND_NUM(ps, session->security_parameters.client_ctype);
+ BUFFER_APPEND_NUM(ps, session->security_parameters.server_ctype);
+
+ BUFFER_APPEND(ps, session->security_parameters.cs->id, 2);
+
+ /* if we are under TLS 1.3 do not pack keys or params negotiated using an extension
+ * they are not necessary */
+ if (!session->security_parameters.pversion->tls13_sem) {
+ BUFFER_APPEND_PFX1(ps, session->security_parameters.master_secret,
+ GNUTLS_MASTER_SIZE);
+ BUFFER_APPEND_PFX1(ps, session->security_parameters.client_random,
+ GNUTLS_RANDOM_SIZE);
+ BUFFER_APPEND_PFX1(ps, session->security_parameters.server_random,
+ GNUTLS_RANDOM_SIZE);
+
+ /* reset max_record_recv_size if it was negotiated
+ * using the record_size_limit extension */
+ if (session->internals.hsk_flags & HSK_RECORD_SIZE_LIMIT_NEGOTIATED) {
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.
+ max_user_record_send_size);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.
+ max_user_record_recv_size);
+ } else {
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.
+ max_record_recv_size);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.
+ max_record_send_size);
+ }
+
+ if (session->security_parameters.grp) {
+ BUFFER_APPEND_NUM(ps, session->security_parameters.grp->id);
+ } else {
+ BUFFER_APPEND_NUM(ps, 0);
+ }
+
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.server_sign_algo);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.client_sign_algo);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.ext_master_secret);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.etm);
+ }
+
+
+ _gnutls_write_uint32(ps->length - cur_size,
+ ps->data + size_offset);
+
+ return 0;
+}
+
+static int
+unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
+{
+ size_t pack_size;
+ int ret;
+ unsigned version;
+ gnutls_datum_t t;
+ time_t timestamp;
+ uint8_t cs[2];
+
+ BUFFER_POP_NUM(ps, pack_size);
+
+ if (pack_size == 0)
+ return GNUTLS_E_INVALID_REQUEST;
+
+ timestamp =
+ session->internals.resumed_security_parameters.timestamp;
+ memset(&session->internals.resumed_security_parameters, 0,
+ sizeof(session->internals.resumed_security_parameters));
+ session->internals.resumed_security_parameters.timestamp =
+ timestamp;
+
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ entity);
+
+ BUFFER_POP_NUM(ps, version);
+ session->internals.resumed_security_parameters.prf = mac_to_entry(version);
+ if (session->internals.resumed_security_parameters.prf == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ client_auth_type);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ server_auth_type);
+
+ BUFFER_POP(ps,
+ &session->internals.resumed_security_parameters.
+ session_id_size, 1);
+
+ BUFFER_POP(ps,
+ session->internals.resumed_security_parameters.
+ session_id,
+ session->internals.resumed_security_parameters.
+ session_id_size);
+
+ BUFFER_POP_NUM(ps, version);
+ session->internals.resumed_security_parameters.pversion =
+ version_to_entry(version);
+ if (session->internals.resumed_security_parameters.pversion ==
+ NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ client_ctype);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ server_ctype);
+
+ BUFFER_POP(ps, cs, 2);
+ session->internals.resumed_security_parameters.cs = ciphersuite_to_entry(cs);
+ if (session->internals.resumed_security_parameters.cs == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (!session->internals.resumed_security_parameters.pversion->tls13_sem) {
+ /* master secret */
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ if (t.size == GNUTLS_MASTER_SIZE)
+ memcpy(session->internals.resumed_security_parameters.master_secret, t.data, t.size);
+
+ /* client random */
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ if (t.size == GNUTLS_RANDOM_SIZE)
+ memcpy(session->internals.resumed_security_parameters.client_random, t.data, t.size);
+
+ /* server random */
+ ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
+ if (ret < 0) {
+ ret = GNUTLS_E_PARSING_ERROR;
+ gnutls_assert();
+ goto error;
+ }
+ if (t.size == GNUTLS_RANDOM_SIZE)
+ memcpy(session->internals.resumed_security_parameters.server_random, t.data, t.size);
+
+
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ max_record_send_size);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ max_record_recv_size);
+
+ BUFFER_POP_NUM(ps, ret);
+ session->internals.resumed_security_parameters.grp = _gnutls_id_to_group(ret);
+ /* it can be null */
+
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ server_sign_algo);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ client_sign_algo);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ ext_master_secret);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ etm);
+
+ if (session->internals.resumed_security_parameters.
+ max_record_recv_size == 0
+ || session->internals.resumed_security_parameters.
+ max_record_send_size == 0) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ }
+
+ ret = 0;
+
+ error:
+ return ret;
+}
+
+/**
+ * gnutls_session_set_premaster:
+ * @session: is a #gnutls_session_t type.
+ * @entity: GNUTLS_SERVER or GNUTLS_CLIENT
+ * @version: the TLS protocol version
+ * @kx: the key exchange method
+ * @cipher: the cipher
+ * @mac: the MAC algorithm
+ * @comp: the compression method (ignored)
+ * @master: the master key to use
+ * @session_id: the session identifier
+ *
+ * This function sets the premaster secret in a session. This is
+ * a function intended for exceptional uses. Do not use this
+ * function unless you are implementing a legacy protocol.
+ * Use gnutls_session_set_data() instead.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
+ * an error code is returned.
+ **/
+int
+gnutls_session_set_premaster(gnutls_session_t session, unsigned int entity,
+ gnutls_protocol_t version,
+ gnutls_kx_algorithm_t kx,
+ gnutls_cipher_algorithm_t cipher,
+ gnutls_mac_algorithm_t mac,
+ gnutls_compression_method_t comp,
+ const gnutls_datum_t * master,
+ const gnutls_datum_t * session_id)
+{
+ int ret;
+ uint8_t cs[2];
+
+ memset(&session->internals.resumed_security_parameters, 0,
+ sizeof(session->internals.resumed_security_parameters));
+
+ session->internals.resumed_security_parameters.entity = entity;
+
+ ret =
+ _gnutls_cipher_suite_get_id(kx, cipher, mac, cs);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.resumed_security_parameters.cs = ciphersuite_to_entry(cs);
+ if (session->internals.resumed_security_parameters.cs == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ session->internals.resumed_security_parameters.client_ctype =
+ DEFAULT_CERT_TYPE;
+ session->internals.resumed_security_parameters.server_ctype =
+ DEFAULT_CERT_TYPE;
+ session->internals.resumed_security_parameters.pversion =
+ version_to_entry(version);
+ if (session->internals.resumed_security_parameters.pversion ==
+ NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (session->internals.resumed_security_parameters.pversion->selectable_prf)
+ session->internals.resumed_security_parameters.prf = mac_to_entry(session->internals.resumed_security_parameters.cs->prf);
+ else
+ session->internals.resumed_security_parameters.prf = mac_to_entry(GNUTLS_MAC_MD5_SHA1);
+ if (session->internals.resumed_security_parameters.prf == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ if (master->size != GNUTLS_MASTER_SIZE)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ memcpy(session->internals.resumed_security_parameters.
+ master_secret, master->data, master->size);
+
+ if (session_id->size > GNUTLS_MAX_SESSION_ID)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ session->internals.resumed_security_parameters.session_id_size =
+ session_id->size;
+ memcpy(session->internals.resumed_security_parameters.session_id,
+ session_id->data, session_id->size);
+
+ session->internals.resumed_security_parameters.
+ max_record_send_size =
+ session->internals.resumed_security_parameters.
+ max_record_recv_size = DEFAULT_MAX_RECORD_SIZE;
+
+ session->internals.resumed_security_parameters.timestamp =
+ gnutls_time(0);
+
+ session->internals.resumed_security_parameters.grp = 0;
+
+ session->internals.resumed_security_parameters.post_handshake_auth = 0;
+
+ session->internals.premaster_set = 1;
+
+ return 0;
+}