summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2018-05-26 05:34:20 +0000
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2018-05-26 05:34:20 +0000
commit76e5e8708cf045b9a79d413f97b3eb93710c5c44 (patch)
tree84fde5ffa940dd064c3bb14f25071bd38cf8e545
parent9fe41ad5c2a34ecbbe3697f320fbb10168f8abd9 (diff)
parente154be8af0f1ee7e9e21924302e0f71d817fa675 (diff)
downloadgnutls-76e5e8708cf045b9a79d413f97b3eb93710c5c44.tar.gz
Merge branch 'tmp-session-resumption2' into 'master'
TLS 1.3 session resumption Closes #441 and #290 See merge request gnutls/gnutls!638
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--NEWS1
-rw-r--r--doc/Makefile.am2
-rw-r--r--doc/cha-gtls-app.texi18
-rw-r--r--doc/cha-upgrade.texi9
-rw-r--r--doc/manpages/Makefile.am1
-rw-r--r--lib/algorithms/ciphersuites.c5
-rw-r--r--lib/auth.c53
-rw-r--r--lib/cert-session.c9
-rw-r--r--lib/constate.c42
-rw-r--r--lib/constate.h2
-rw-r--r--lib/db.c10
-rw-r--r--lib/ext/pre_shared_key.c511
-rw-r--r--lib/ext/pre_shared_key.h5
-rw-r--r--lib/ext/psk_ke_modes.c62
-rw-r--r--lib/ext/session_ticket.c519
-rw-r--r--lib/ext/session_ticket.h7
-rw-r--r--lib/gnutls_int.h86
-rw-r--r--lib/handshake-tls13.c94
-rw-r--r--lib/handshake.c274
-rw-r--r--lib/handshake.h6
-rw-r--r--lib/hello_ext.c2
-rw-r--r--lib/includes/gnutls/gnutls.h.in6
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/priority.c2
-rw-r--r--lib/session.c71
-rw-r--r--lib/session_pack.c388
-rw-r--r--lib/state.c36
-rw-r--r--lib/state.h2
-rw-r--r--lib/tls13/certificate.c3
-rw-r--r--lib/tls13/certificate_verify.c3
-rw-r--r--lib/tls13/finished.c11
-rw-r--r--lib/tls13/finished.h1
-rw-r--r--lib/tls13/hello_retry.c3
-rw-r--r--lib/tls13/session_ticket.c373
-rw-r--r--lib/tls13/session_ticket.h30
-rw-r--r--m4/hooks.m416
-rw-r--r--src/serv.c4
-rw-r--r--src/socket.c2
-rw-r--r--symbols.last1
-rw-r--r--tests/Makefile.am13
-rw-r--r--tests/psk-file.c5
-rw-r--r--tests/resume.c209
-rw-r--r--tests/scripts/common.sh11
-rw-r--r--tests/session-tickets-missing.c41
-rwxr-xr-xtests/suite/testcompat-tls13-openssl.sh38
-rw-r--r--tests/tls13/key_update.c53
-rw-r--r--tests/tls13/post-handshake-with-cert-ticket.c360
48 files changed, 2528 insertions, 875 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1b4085048e..abdec0a1ff 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -97,7 +97,7 @@ minimal.Fedora.x86_64:
--disable-doc --disable-dtls-srtp-support --disable-alpn-support --disable-tests
--disable-heartbeat-support --disable-srp-authentication --disable-psk-authentication
--disable-anon-authentication --disable-dhe --disable-ecdhe
- --disable-ocsp --disable-session-tickets --disable-non-suiteb-curves --with-included-unistring
+ --disable-ocsp --disable-non-suiteb-curves --with-included-unistring
--disable-nls --disable-libdane --without-p11-kit --without-tpm
--disable-ssl3-support --disable-ssl2-support --disable-doc --enable-openssl-compatibility
--disable-gcc-warnings
diff --git a/NEWS b/NEWS
index 707ada24e1..53c9601d21 100644
--- a/NEWS
+++ b/NEWS
@@ -60,6 +60,7 @@ See the end for copying conditions.
** API and ABI modifications:
gnutls_fips140_set_mode: Added
gnutls_session_key_update: Added
+gnutls_session_ticket_send: Added
gnutls_ext_get_current_msg: Added
gnutls_reauth: Added
gnutls_ocsp_status_request_get2: Added
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 468fc1427a..ca2c2c30a5 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1877,6 +1877,8 @@ FUNCS += functions/gnutls_session_ticket_enable_server
FUNCS += functions/gnutls_session_ticket_enable_server.short
FUNCS += functions/gnutls_session_ticket_key_generate
FUNCS += functions/gnutls_session_ticket_key_generate.short
+FUNCS += functions/gnutls_session_ticket_send
+FUNCS += functions/gnutls_session_ticket_send.short
FUNCS += functions/gnutls_set_default_priority
FUNCS += functions/gnutls_set_default_priority.short
FUNCS += functions/gnutls_sign_algorithm_get
diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi
index 05e1e03a49..5ecb3d7382 100644
--- a/doc/cha-gtls-app.texi
+++ b/doc/cha-gtls-app.texi
@@ -1648,11 +1648,13 @@ using the priority functions, at least the algorithms used in the last session.
@subsubheading Server side
-In order to support resumption a server can store
-the session security parameters in a local database or by using session
-tickets (see @ref{Session tickets}) to delegate storage to the client. Because
-session tickets might not be supported by all clients, servers
-could combine the two methods.
+Under TLS 1.2, in order to support resumption a server can either store
+the session security parameters in a local database or use session
+tickets (see @ref{Session tickets}) to delegate storage to the client.
+A server enabling both session tickets and a storage for session data
+would use session tickets when clients support it and the storage otherwise.
+
+Under TLS1.3 a server can only send session tickets.
A storing server needs to specify callback functions to store, retrieve and delete session data. These can be
registered with the functions below. The stored sessions in the database can be checked using @funcref{gnutls_db_check_entry}
@@ -1661,7 +1663,7 @@ for expiration.
@showfuncD{gnutls_db_set_retrieve_function,gnutls_db_set_store_function,gnutls_db_set_ptr,gnutls_db_set_remove_function}
@showfuncA{gnutls_db_check_entry}
-A server utilizing tickets should generate ticket encryption
+A server supporting session tickets must generate ticket encryption
and authentication keys using @funcref{gnutls_session_ticket_key_generate}.
Those keys should be associated with the GnuTLS session using
@funcref{gnutls_session_ticket_enable_server}, and should be rotated regularly
@@ -1672,8 +1674,8 @@ if revealed could be used to decrypt all previous sessions.
@showfuncdesc{gnutls_session_ticket_key_generate}
@showfuncdesc{gnutls_session_resumption_requested}
-A server enabling both session tickets and a storage for session data
-would use session tickets when clients support it and the storage otherwise.
+The expiration time for session resumption, either in tickets or stored data
+is set using @funcref{gnutls_db_set_cache_expiration}.
@node Certificate verification
@subsection Certificate verification
diff --git a/doc/cha-upgrade.texi b/doc/cha-upgrade.texi
index 337331e356..a5819cf8dd 100644
--- a/doc/cha-upgrade.texi
+++ b/doc/cha-upgrade.texi
@@ -225,4 +225,13 @@ which can be accessed directly via @funcref{gnutls_session_key_update} and @func
@item @funcref{gnutls_pkcs11_privkey_generate3}, @funcref{gnutls_pkcs11_copy_secret_key}, @funcref{gnutls_pkcs11_copy_x509_privkey2}
@tab These functions no longer create an exportable key by default; they require the flag @code{GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_SENSITIVE} to do so.
+@item @funcref{gnutls_db_set_retrieve_function}, @funcref{gnutls_db_set_store_function}, @funcref{gnutls_db_set_remove_function}
+@tab These functions are no longer relevant under TLS 1.3; resumption under
+TLS 1.3 is done via session tickets, c.f. @funcref{gnutls_session_ticket_enable_server}.
+
+@item @funcref{gnutls_session_get_data2}, @funcref{gnutls_session_get_data}
+@tab These functions may introduce a slight delay under TLS 1.3 for few
+milliseconds. Check output of @funcref{gnutls_session_get_flags} for GNUTLS_SFLAGS_SESSION_TICKET
+before calling this function to avoid delays.
+
@end multitable
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index 981407e970..78d29df813 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -734,6 +734,7 @@ APIMANS += gnutls_session_supplemental_register.3
APIMANS += gnutls_session_ticket_enable_client.3
APIMANS += gnutls_session_ticket_enable_server.3
APIMANS += gnutls_session_ticket_key_generate.3
+APIMANS += gnutls_session_ticket_send.3
APIMANS += gnutls_set_default_priority.3
APIMANS += gnutls_sign_algorithm_get.3
APIMANS += gnutls_sign_algorithm_get_client.3
diff --git a/lib/algorithms/ciphersuites.c b/lib/algorithms/ciphersuites.c
index dbfcbb0c90..7b24757468 100644
--- a/lib/algorithms/ciphersuites.c
+++ b/lib/algorithms/ciphersuites.c
@@ -1435,6 +1435,7 @@ static unsigned kx_is_ok(gnutls_session_t session, gnutls_kx_algorithm_t kx, uns
return 1;
}
+/* Called on server-side only */
int
_gnutls_figure_common_ciphersuite(gnutls_session_t session,
const ciphersuite_list_st *peer_clist,
@@ -1485,7 +1486,7 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session,
/* if we have selected PSK, we need a ciphersuites which matches
* the selected binder */
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- if (session->key.proto.tls13.binder_prf->id != session->internals.priorities->cs.entry[j]->prf)
+ if (session->key.binders[0].prf->id != session->internals.priorities->cs.entry[j]->prf)
continue;
} else if (cred_type == GNUTLS_CRD_CERTIFICATE) {
ret = _gnutls_server_select_cert(session, peer_clist->entry[i]);
@@ -1528,7 +1529,7 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session,
/* if we have selected PSK, we need a ciphersuites which matches
* the selected binder */
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- if (session->key.proto.tls13.binder_prf->id != session->internals.priorities->cs.entry[j]->prf)
+ if (session->key.binders[0].prf->id != session->internals.priorities->cs.entry[j]->prf)
break;
} else if (cred_type == GNUTLS_CRD_CERTIFICATE) {
ret = _gnutls_server_select_cert(session, peer_clist->entry[i]);
diff --git a/lib/auth.c b/lib/auth.c
index ca4425e67e..4bdedda38b 100644
--- a/lib/auth.c
+++ b/lib/auth.c
@@ -193,27 +193,18 @@ gnutls_credentials_get(gnutls_session_t session,
* %GNUTLS_KX_RSA, %GNUTLS_KX_DHE_RSA), the same function are to be
* used to access the authentication data.
*
+ * Note that on resumed sessions, this function returns the schema
+ * used in the original session authentication.
+ *
* Returns: The type of credentials for the current authentication
* schema, a #gnutls_credentials_type_t type.
**/
gnutls_credentials_type_t gnutls_auth_get_type(gnutls_session_t session)
{
-/* This is not the credentials we must set, but the authentication data
- * we get by the peer, so it should be reversed.
- */
- gnutls_kx_algorithm_t kx;
- int server =
- session->security_parameters.entity == GNUTLS_SERVER ? 0 : 1;
-
- if (!session->security_parameters.cs) {
- gnutls_assert();
- return 0;
- }
-
- kx = gnutls_kx_get(session);
-
- return
- _gnutls_map_kx_get_cred(kx, server);
+ if (session->security_parameters.entity == GNUTLS_SERVER)
+ return gnutls_auth_client_get_type(session);
+ else
+ return gnutls_auth_server_get_type(session);
}
/**
@@ -224,23 +215,16 @@ gnutls_credentials_type_t gnutls_auth_get_type(gnutls_session_t session)
* The returned information is to be used to distinguish the function used
* to access authentication data.
*
+ * Note that on resumed sessions, this function returns the schema
+ * used in the original session authentication.
+ *
* Returns: The type of credentials for the server authentication
* schema, a #gnutls_credentials_type_t type.
**/
gnutls_credentials_type_t
gnutls_auth_server_get_type(gnutls_session_t session)
{
- gnutls_kx_algorithm_t kx;
-
- if (!session->security_parameters.cs) {
- gnutls_assert();
- return 0;
- }
-
- kx = gnutls_kx_get(session);
-
- return
- _gnutls_map_kx_get_cred(kx, 1);
+ return session->security_parameters.server_auth_type;
}
/**
@@ -251,23 +235,16 @@ gnutls_auth_server_get_type(gnutls_session_t session)
* The returned information is to be used to distinguish the function used
* to access authentication data.
*
+ * Note that on resumed sessions, this function returns the schema
+ * used in the original session authentication.
+ *
* Returns: The type of credentials for the client authentication
* schema, a #gnutls_credentials_type_t type.
**/
gnutls_credentials_type_t
gnutls_auth_client_get_type(gnutls_session_t session)
{
- gnutls_kx_algorithm_t kx;
-
- if (!session->security_parameters.cs) {
- gnutls_assert();
- return 0;
- }
-
- kx = gnutls_kx_get(session);
-
- return
- _gnutls_map_kx_get_cred(kx, 0);
+ return session->security_parameters.client_auth_type;
}
diff --git a/lib/cert-session.c b/lib/cert-session.c
index dc3d9aebf8..f858308664 100644
--- a/lib/cert-session.c
+++ b/lib/cert-session.c
@@ -48,6 +48,11 @@
* The certificate is in raw (DER) format. No certificate
* list is being returned. Only the first certificate.
*
+ * This function returns the certificate that was sent in the current
+ * handshake. In subsequent resumed sessions this function will return
+ * %NULL. That differs from gnutls_certificate_get_peers() which always
+ * returns the peer's certificate used in the original session.
+ *
* Returns: a pointer to a #gnutls_datum_t containing our
* certificate, or %NULL in case of an error or if no certificate
* was used.
@@ -85,8 +90,8 @@ const gnutls_datum_t *gnutls_certificate_get_ours(gnutls_session_t session)
* are servers which violate this principle and thus on certain
* occasions this may be an unsorted list.
*
- * In case of OpenPGP keys a single key will be returned in raw
- * format.
+ * In resumed sessions, this function will return the peer's certificate
+ * list as used in the first/original session.
*
* Returns: a pointer to a #gnutls_datum_t containing the peer's
* certificates, or %NULL in case of an error or if no certificate
diff --git a/lib/constate.c b/lib/constate.c
index cc8b817715..cdf9ed6479 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -647,31 +647,37 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t
return 0;
}
-
-#define CPY_COMMON dst->entity = src->entity; \
- dst->cs = src->cs; \
- dst->grp = src->grp; \
- dst->prf = src->prf; \
- memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
- memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
- memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
- memcpy( dst->session_id, src->session_id, GNUTLS_MAX_SESSION_ID_SIZE); \
- dst->session_id_size = src->session_id_size; \
+/* This copies the session values which apply to subsequent/resumed
+ * sessions. Under TLS 1.3, these values are items which are not
+ * negotiated on the subsequent session. */
+#define CPY_COMMON(tls13_sem) \
+ if (!tls13_sem) { \
+ dst->cs = src->cs; \
+ memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
+ memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
+ memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
+ dst->ext_master_secret = src->ext_master_secret; \
+ dst->etm = src->etm; \
+ dst->max_record_recv_size = src->max_record_recv_size; \
+ dst->max_record_send_size = src->max_record_send_size; \
+ dst->prf = src->prf; \
+ dst->grp = src->grp; \
+ dst->pversion = src->pversion; \
+ memcpy( dst->session_id, src->session_id, GNUTLS_MAX_SESSION_ID_SIZE); \
+ dst->session_id_size = src->session_id_size; \
+ dst->timestamp = src->timestamp; \
+ } \
dst->cert_type = src->cert_type; \
- dst->timestamp = src->timestamp; \
- dst->ext_master_secret = src->ext_master_secret; \
- dst->etm = src->etm; \
- dst->max_record_recv_size = src->max_record_recv_size; \
- dst->max_record_send_size = src->max_record_send_size
+ dst->client_auth_type = src->client_auth_type; \
+ dst->server_auth_type = src->server_auth_type
-static void _gnutls_set_resumed_parameters(gnutls_session_t session)
+void _gnutls_set_resumed_parameters(gnutls_session_t session)
{
security_parameters_st *src =
&session->internals.resumed_security_parameters;
security_parameters_st *dst = &session->security_parameters;
- CPY_COMMON;
- dst->pversion = src->pversion;
+ CPY_COMMON(get_version(session)->tls13_sem);
}
/* Sets the current connection session to conform with the
diff --git a/lib/constate.h b/lib/constate.h
index 1d62edccfa..8a15400f5f 100644
--- a/lib/constate.h
+++ b/lib/constate.h
@@ -43,6 +43,8 @@ void _gnutls_epoch_gc(gnutls_session_t session);
void _gnutls_epoch_free(gnutls_session_t session,
record_parameters_st * state);
+void _gnutls_set_resumed_parameters(gnutls_session_t session);
+
int _tls13_connection_state_init(gnutls_session_t session, hs_stage_t stage);
static inline int _gnutls_epoch_is_valid(gnutls_session_t session,
diff --git a/lib/db.c b/lib/db.c
index 0bed7a5b98..38225d31f5 100644
--- a/lib/db.c
+++ b/lib/db.c
@@ -122,12 +122,18 @@ void *gnutls_db_get_ptr(gnutls_session_t session)
* @session: is a #gnutls_session_t type.
* @seconds: is the number of seconds.
*
- * Set the expiration time for resumed sessions. The default is 3600
- * (one hour) at the time of this writing.
+ * Set the expiration time for resumed sessions. The default is 21600
+ * (size hours) at the time of writing.
+ *
+ * The maximum value that can be set using this function is 604800
+ * (7 days).
+ *
**/
void gnutls_db_set_cache_expiration(gnutls_session_t session, int seconds)
{
session->internals.expire_time = seconds;
+ if (session->internals.expire_time > 604800)
+ session->internals.expire_time = 604800;
}
/**
diff --git a/lib/ext/pre_shared_key.c b/lib/ext/pre_shared_key.c
index 0d3468c40d..5c8a80c4a2 100644
--- a/lib/ext/pre_shared_key.c
+++ b/lib/ext/pre_shared_key.c
@@ -26,18 +26,53 @@
#include "secrets.h"
#include "tls13/psk_ext_parser.h"
#include "tls13/finished.h"
+#include "tls13/session_ticket.h"
#include "auth/psk_passwd.h"
+#include <ext/session_ticket.h>
#include <ext/pre_shared_key.h>
#include <assert.h>
static int
+compute_psk_from_ticket(const tls13_ticket_t *ticket, gnutls_datum_t *key)
+{
+ int ret;
+ char label[] = "resumption";
+
+ if (unlikely(ticket->prf == NULL || ticket->prf->output_size == 0))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ key->data = gnutls_malloc(ticket->prf->output_size);
+ if (!key->data) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ key->size = ticket->prf->output_size;
+
+ ret = _tls13_expand_secret2(ticket->prf,
+ label, sizeof(label)-1,
+ ticket->nonce, ticket->nonce_size,
+ ticket->resumption_master_secret,
+ key->size,
+ key->data);
+ if (ret < 0)
+ gnutls_assert();
+
+ return ret;
+}
+
+static int
compute_binder_key(const mac_entry_st *prf,
- const uint8_t *key, size_t keylen,
- void *out)
+ const uint8_t *key, size_t keylen,
+ bool resuming,
+ void *out)
{
int ret;
- char label[] = "ext binder";
- size_t label_len = sizeof(label) - 1;
+ const char ext_label[] = "ext binder";
+ const size_t ext_label_len = sizeof(ext_label) - 1;
+ const char res_label[] = "res binder";
+ const size_t res_label_len = sizeof(res_label) - 1;
+ const char *label = resuming ? res_label : ext_label;
+ size_t label_len = resuming ? res_label_len : ext_label_len;
uint8_t tmp_key[MAX_HASH_SIZE];
/* Compute HKDF-Extract(0, psk) */
@@ -46,11 +81,8 @@ compute_binder_key(const mac_entry_st *prf,
return ret;
/* Compute Derive-Secret(secret, label, transcript_hash) */
- ret = _tls13_derive_secret2(prf,
- label, label_len,
- NULL, 0,
- tmp_key,
- out);
+ ret = _tls13_derive_secret2(prf, label, label_len,
+ NULL, 0, tmp_key, out);
if (ret < 0)
return ret;
@@ -58,46 +90,71 @@ compute_binder_key(const mac_entry_st *prf,
}
static int
-compute_psk_binder(unsigned entity,
- const mac_entry_st *prf, unsigned binders_length, unsigned hash_size,
- int exts_length, int ext_offset,
- const gnutls_datum_t *psk, const gnutls_datum_t *client_hello,
- void *out)
+compute_psk_binder(gnutls_session_t session,
+ const mac_entry_st *prf, unsigned binders_length,
+ int exts_length, int ext_offset,
+ const gnutls_datum_t *psk, const gnutls_datum_t *client_hello,
+ bool resuming, void *out)
{
int ret;
- unsigned extensions_len_pos;
+ unsigned client_hello_pos, extensions_len_pos;
gnutls_buffer_st handshake_buf;
uint8_t binder_key[MAX_HASH_SIZE];
_gnutls_buffer_init(&handshake_buf);
- if (entity == GNUTLS_CLIENT) {
- ret = gnutls_buffer_append_data(&handshake_buf,
- (const void *) client_hello->data,
- client_hello->size);
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ if (session->internals.hsk_flags & HSK_HRR_RECEIVED) {
+ ret = gnutls_buffer_append_data(&handshake_buf,
+ (const void *) session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer.length);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
+ client_hello_pos = handshake_buf.length;
+ ret = gnutls_buffer_append_data(&handshake_buf, client_hello->data,
+ client_hello->size);
if (ret < 0) {
gnutls_assert();
goto error;
}
/* This is a ClientHello message */
- handshake_buf.data[0] = GNUTLS_HANDSHAKE_CLIENT_HELLO;
+ handshake_buf.data[client_hello_pos] = GNUTLS_HANDSHAKE_CLIENT_HELLO;
- /*
- * At this point we have not yet added the binders to the ClientHello,
+ /* At this point we have not yet added the binders to the ClientHello,
* but we have to overwrite the size field, pretending as if binders
* of the correct length were present.
*/
- _gnutls_write_uint24(handshake_buf.length + binders_length - 2, &handshake_buf.data[1]);
- _gnutls_write_uint16(handshake_buf.length + binders_length - ext_offset,
- &handshake_buf.data[ext_offset]);
-
- extensions_len_pos = handshake_buf.length - exts_length - 2;
+ _gnutls_write_uint24(handshake_buf.length - client_hello_pos + binders_length - 2, &handshake_buf.data[client_hello_pos + 1]);
+ _gnutls_write_uint16(handshake_buf.length - client_hello_pos + binders_length - ext_offset,
+ &handshake_buf.data[client_hello_pos + ext_offset]);
+ extensions_len_pos = handshake_buf.length - client_hello_pos - exts_length - 2;
_gnutls_write_uint16(exts_length + binders_length + 2,
- &handshake_buf.data[extensions_len_pos]);
+ &handshake_buf.data[client_hello_pos + extensions_len_pos]);
} else {
- if (unlikely(client_hello->size <= binders_length))
- return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ if (session->internals.hsk_flags & HSK_HRR_SENT) {
+ if (unlikely(session->internals.handshake_hash_buffer.length <= client_hello->size)) {
+ ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ goto error;
+ }
+
+ ret = gnutls_buffer_append_data(&handshake_buf,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer.length - client_hello->size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ }
+
+ if (unlikely(client_hello->size <= binders_length)) {
+ ret = gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ goto error;
+ }
ret = gnutls_buffer_append_data(&handshake_buf,
(const void *) client_hello->data,
@@ -109,7 +166,7 @@ compute_psk_binder(unsigned entity,
}
ret = compute_binder_key(prf,
- psk->data, psk->size,
+ psk->data, psk->size, resuming,
binder_key);
if (ret < 0) {
gnutls_assert();
@@ -117,7 +174,6 @@ compute_psk_binder(unsigned entity,
}
ret = _gnutls13_compute_finished(prf, binder_key,
- hash_size,
&handshake_buf,
out);
if (ret < 0) {
@@ -138,52 +194,155 @@ client_send_params(gnutls_session_t session,
{
int ret, ext_offset = 0;
uint8_t binder_value[MAX_HASH_SIZE];
- size_t length, pos;
- gnutls_datum_t username = {NULL, 0}, key = {NULL, 0}, client_hello;
- const mac_entry_st *prf = cred->binder_algo;
- unsigned hash_size = _gnutls_mac_get_algo_len(prf);
- int free_data;
-
- if (prf == NULL || hash_size == 0 || hash_size > 255)
- return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ size_t spos;
+ gnutls_datum_t username = {NULL, 0};
+ gnutls_datum_t user_key = {NULL, 0}, rkey = {NULL, 0};
+ gnutls_datum_t client_hello;
+ unsigned next_idx;
+ const mac_entry_st *prf_res = NULL;
+ const mac_entry_st *prf_psk = NULL;
+ time_t cur_time;
+ int ticket_age;
+ uint32_t ob_ticket_age;
+ int free_username = 0;
+ psk_auth_info_t info = NULL;
+ unsigned psk_id_len = 0;
+ unsigned binders_len, binders_pos;
+
+ if (((session->internals.flags & GNUTLS_NO_TICKETS) ||
+ session->internals.tls13_ticket.ticket.data == NULL) &&
+ (!cred || !_gnutls_have_psk_credentials(cred, session))) {
- /* Credentials but no username set - this extension is not applicable */
- if (!_gnutls_have_psk_credentials(cred))
return 0;
+ }
- ret = _gnutls_find_psk_key(session, cred, &username, &key, &free_data);
+ binders_len = 0;
+
+ /* placeholder to be filled later */
+ spos = extdata->length;
+ ret = _gnutls_buffer_append_prefix(extdata, 16, 0);
if (ret < 0)
return gnutls_assert_val(ret);
- if (username.size == 0 || username.size > UINT16_MAX) {
- ret = gnutls_assert_val(GNUTLS_E_INVALID_PASSWORD);
- goto cleanup;
- }
+ /* First, let's see if we have a session ticket to send */
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS) &&
+ session->internals.tls13_ticket.ticket.data != NULL) {
+ /* We found a session ticket */
+ if (unlikely(session->internals.tls13_ticket.prf == NULL)) {
+ _gnutls13_session_ticket_unset(session);
+ ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ goto cleanup;
+ }
- /* placeholder to be filled later */
- pos = extdata->length;
- ret = _gnutls_buffer_append_prefix(extdata, 16, 0);
- if (ret < 0) {
- gnutls_assert_val(ret);
- goto cleanup;
- }
+ prf_res = session->internals.tls13_ticket.prf;
- if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16,
- username.data, username.size)) < 0) {
- gnutls_assert();
- goto cleanup;
+ /* Check whether the ticket is stale */
+ cur_time = gnutls_time(0);
+ ticket_age = cur_time - session->internals.tls13_ticket.timestamp;
+ if (ticket_age < 0 || ticket_age > cur_time) {
+ gnutls_assert();
+ _gnutls13_session_ticket_unset(session);
+ goto ignore_ticket;
+ }
+
+ if ((unsigned int) ticket_age > session->internals.tls13_ticket.lifetime) {
+ _gnutls13_session_ticket_unset(session);
+ goto ignore_ticket;
+ }
+
+ ret = compute_psk_from_ticket(&session->internals.tls13_ticket, &rkey);
+ if (ret < 0) {
+ _gnutls13_session_ticket_unset(session);
+ goto ignore_ticket;
+ }
+
+ /* Calculate obfuscated ticket age, in milliseconds, mod 2^32 */
+ ob_ticket_age = ticket_age * 1000 + session->internals.tls13_ticket.age_add;
+
+ if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16,
+ session->internals.tls13_ticket.ticket.data,
+ session->internals.tls13_ticket.ticket.size)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Now append the obfuscated ticket age */
+ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, ob_ticket_age)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ psk_id_len += 6 + session->internals.tls13_ticket.ticket.size;
+ binders_len += 1 + _gnutls_mac_get_algo_len(prf_res);
}
- /* Now append the ticket age, which is always zero for out-of-band PSKs */
- if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) {
- gnutls_assert();
- goto cleanup;
+ ignore_ticket:
+ if (cred && _gnutls_have_psk_credentials(cred, session)) {
+ gnutls_datum_t tkey;
+
+ if (cred->binder_algo == NULL) {
+ gnutls_assert();
+ ret = gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
+ goto cleanup;
+ }
+
+ prf_psk = cred->binder_algo;
+
+ ret = _gnutls_find_psk_key(session, cred, &username, &tkey, &free_username);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ if (username.size == 0 || username.size > UINT16_MAX) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_PASSWORD);
+ goto cleanup;
+ }
+
+ if (!free_username) {
+ /* we need to copy the key */
+ ret = _gnutls_set_datum(&user_key, tkey.data, tkey.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ } else {
+ user_key.data = tkey.data;
+ user_key.size = tkey.size;
+ }
+
+ ret = _gnutls_auth_info_set(session, GNUTLS_CRD_PSK, sizeof(psk_auth_info_st), 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ info = _gnutls_get_auth_info(session, GNUTLS_CRD_PSK);
+ assert(info != NULL);
+
+ memcpy(info->username, username.data, username.size);
+ info->username[username.size] = 0;
+
+ if ((ret = _gnutls_buffer_append_data_prefix(extdata, 16,
+ username.data,
+ username.size)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Now append the obfuscated ticket age */
+ if ((ret = _gnutls_buffer_append_prefix(extdata, 32, 0)) < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ psk_id_len += 6 + username.size;
+ binders_len += 1 + _gnutls_mac_get_algo_len(prf_psk);
}
- /* Total length appended is the length of the data, plus six octets */
- length = (username.size + 6);
- _gnutls_write_uint16(length, &extdata->data[pos]);
+ _gnutls_write_uint16(psk_id_len, &extdata->data[spos]);
+ binders_pos = extdata->length-spos;
ext_offset = _gnutls_ext_get_extensions_offset(session);
/* Compute the binders. extdata->data points to the start
@@ -194,43 +353,89 @@ client_send_params(gnutls_session_t session,
client_hello.data = extdata->data+sizeof(mbuffer_st);
client_hello.size = extdata->length-sizeof(mbuffer_st);
- ret = compute_psk_binder(GNUTLS_CLIENT, prf,
- hash_size+1, hash_size, extdata->length-pos,
- ext_offset, &key, &client_hello,
- binder_value);
+ next_idx = 0;
+
+ ret = _gnutls_buffer_append_prefix(extdata, 16, binders_len);
if (ret < 0) {
- gnutls_assert();
+ gnutls_assert_val(ret);
goto cleanup;
}
- /* Associate the selected pre-shared key with the session */
- session->key.psk.data = key.data;
- session->key.psk.size = key.size;
- session->key.psk_needs_free = free_data;
- key.data = NULL;
- session->key.proto.tls13.binder_prf = prf;
+ if (prf_res && rkey.size > 0) {
+ ret = compute_psk_binder(session, prf_res,
+ binders_len, binders_pos,
+ ext_offset, &rkey, &client_hello, 1,
+ binder_value);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Associate the selected pre-shared key with the session */
+ gnutls_free(session->key.binders[next_idx].psk.data);
+ session->key.binders[next_idx].psk.data = rkey.data;
+ session->key.binders[next_idx].psk.size = rkey.size;
+ rkey.data = NULL;
- /* Now append the binders */
- ret = _gnutls_buffer_append_prefix(extdata, 16, hash_size+1);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
+ session->key.binders[next_idx].prf = prf_res;
+ session->key.binders[next_idx].resumption = 1;
+ session->key.binders[next_idx].idx = next_idx;
+
+ _gnutls_handshake_log("EXT[%p]: sent PSK resumption identity (%d)\n", session, next_idx);
+
+ next_idx++;
+
+ /* Add the binder */
+ ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_res->output_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT;
}
- /* Add the size of the binder (we only have one) */
- ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, hash_size);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
+ if (prf_psk && user_key.size > 0 && info) {
+ ret = compute_psk_binder(session, prf_psk,
+ binders_len, binders_pos,
+ ext_offset, &user_key, &client_hello, 0,
+ binder_value);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* Associate the selected pre-shared key with the session */
+ gnutls_free(session->key.binders[next_idx].psk.data);
+ session->key.binders[next_idx].psk.data = user_key.data;
+ session->key.binders[next_idx].psk.size = user_key.size;
+ user_key.data = NULL;
+
+ session->key.binders[next_idx].prf = prf_psk;
+ session->key.binders[next_idx].resumption = 0;
+ session->key.binders[next_idx].idx = next_idx;
+
+ _gnutls_handshake_log("EXT[%p]: sent PSK identity '%s' (%d)\n", session, info->username, next_idx);
+
+ next_idx++;
+
+ /* Add the binder */
+ ret = _gnutls_buffer_append_data_prefix(extdata, 8, binder_value, prf_psk->output_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
}
ret = 0;
cleanup:
- if (free_data) {
+ if (free_username)
_gnutls_free_datum(&username);
- _gnutls_free_temp_key_datum(&key);
- }
+
+ _gnutls_free_temp_key_datum(&user_key);
+ _gnutls_free_temp_key_datum(&rkey);
+
return ret;
}
@@ -243,7 +448,7 @@ server_send_params(gnutls_session_t session, gnutls_buffer_t extdata)
return 0;
ret = _gnutls_buffer_append_prefix(extdata, 16,
- session->key.proto.tls13.psk_index);
+ session->key.binders[0].idx);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -258,13 +463,16 @@ static int server_recv_params(gnutls_session_t session,
const mac_entry_st *prf;
gnutls_datum_t full_client_hello;
uint8_t binder_value[MAX_HASH_SIZE];
- int psk_index = -1;
+ int psk_index;
gnutls_datum_t binder_recvd = { NULL, 0 };
gnutls_datum_t key = {NULL, 0};
- unsigned hash_size, cand_index;
+ unsigned cand_index;
psk_ext_parser_st psk_parser;
struct psk_st psk;
psk_auth_info_t info;
+ tls13_ticket_t ticket_data;
+ int ticket_age;
+ bool resuming;
ret = _gnutls13_psk_ext_parser_init(&psk_parser, data, len);
if (ret < 0) {
@@ -273,25 +481,68 @@ static int server_recv_params(gnutls_session_t session,
return gnutls_assert_val(ret);
}
+ psk_index = -1;
+
while ((ret = _gnutls13_psk_ext_parser_next_psk(&psk_parser, &psk)) >= 0) {
- if (psk.ob_ticket_age == 0) {
- cand_index = ret;
+ cand_index = ret;
+ /* Is this a PSK? */
+ if (psk.ob_ticket_age == 0) {
/* _gnutls_psk_pwd_find_entry() expects 0-terminated identities */
if (psk.identity.size > 0 && psk.identity.size <= MAX_USERNAME_SIZE) {
char identity_str[psk.identity.size + 1];
+ prf = pskcred->binder_algo;
+
memcpy(identity_str, psk.identity.data, psk.identity.size);
identity_str[psk.identity.size] = 0;
+ /* this fails only on configuration errors; as such we always
+ * return its error code in that case */
ret = _gnutls_psk_pwd_find_entry(session, identity_str, &key);
if (ret < 0)
return gnutls_assert_val(ret);
psk_index = cand_index;
+ resuming = 0;
break;
}
}
+
+ /* Is this a session ticket? */
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS) &&
+ (ret = _gnutls13_unpack_session_ticket(session, &psk.identity, &ticket_data)) == 0) {
+ prf = ticket_data.prf;
+
+ if (!prf) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ /* Check whether ticket is stale or not */
+ ticket_age = psk.ob_ticket_age - ticket_data.age_add;
+ if (ticket_age < 0) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ if ((unsigned int) (ticket_age / 1000) > ticket_data.lifetime) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ ret = compute_psk_from_ticket(&ticket_data, &key);
+ if (ret < 0) {
+ tls13_ticket_deinit(&ticket_data);
+ continue;
+ }
+
+ tls13_ticket_deinit(&ticket_data);
+
+ psk_index = cand_index;
+ resuming = 1;
+ break;
+ }
}
if (psk_index < 0)
@@ -312,10 +563,8 @@ static int server_recv_params(gnutls_session_t session,
}
/* Compute the binder value for this PSK */
- prf = pskcred->binder_algo;
- hash_size = prf->output_size;
- ret = compute_psk_binder(GNUTLS_SERVER, prf, psk_parser.binder_len+2, hash_size, 0, 0,
- &key, &full_client_hello,
+ ret = compute_psk_binder(session, prf, psk_parser.binder_len+2, 0, 0,
+ &key, &full_client_hello, resuming,
binder_value);
if (ret < 0) {
gnutls_assert();
@@ -330,15 +579,15 @@ static int server_recv_params(gnutls_session_t session,
}
if (session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)
- _gnutls_handshake_log("EXT[%p]: Selected DHE-PSK mode\n", session);
+ _gnutls_handshake_log("EXT[%p]: selected DHE-PSK mode\n", session);
else {
reset_cand_groups(session);
- _gnutls_handshake_log("EXT[%p]: Selected PSK mode\n", session);
+ _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session);
}
/* save the username in psk_auth_info to make it available
* using gnutls_psk_server_get_username() */
- if (psk.ob_ticket_age == 0) {
+ if (!resuming) {
if (psk.identity.size >= sizeof(info->username)) {
gnutls_assert();
ret = GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
@@ -356,18 +605,21 @@ static int server_recv_params(gnutls_session_t session,
memcpy(info->username, psk.identity.data, psk.identity.size);
info->username[psk.identity.size] = 0;
- _gnutls_handshake_log("EXT[%p]: Selected PSK identity: %s\n", session, info->username);
+ _gnutls_handshake_log("EXT[%p]: selected PSK identity: %s (%d)\n", session, info->username, psk_index);
+ } else {
+ session->internals.resumed = RESUME_TRUE;
+ _gnutls_handshake_log("EXT[%p]: selected resumption PSK identity (%d)\n", session, psk_index);
}
session->internals.hsk_flags |= HSK_PSK_SELECTED;
/* Reference the selected pre-shared key */
- session->key.psk.data = key.data;
- session->key.psk.size = key.size;
- session->key.psk_needs_free = 1;
+ session->key.binders[0].psk.data = key.data;
+ session->key.binders[0].psk.size = key.size;
- session->key.proto.tls13.psk_index = psk_index;
- session->key.proto.tls13.binder_prf = prf;
+ session->key.binders[0].idx = psk_index;
+ session->key.binders[0].prf = prf;
+ session->key.binders[0].resumption = resuming;
return 0;
@@ -417,24 +669,19 @@ static int _gnutls_psk_send_params(gnutls_session_t session,
if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) {
cred = (gnutls_psk_client_credentials_t)
_gnutls_get_cred(session, GNUTLS_CRD_PSK);
- /* If there are no PSK credentials, this extension is not applicable,
- * so we return zero. */
- if (cred == NULL || !session->internals.priorities->have_psk)
- return 0;
+ }
- return client_send_params(session, extdata, cred);
- } else {
+ if ((session->internals.flags & GNUTLS_NO_TICKETS) && !session->internals.priorities->have_psk)
return 0;
- }
+
+ return client_send_params(session, extdata, cred);
} else {
vers = get_version(session);
if (!vers || !vers->tls13_sem)
return 0;
- cred = (gnutls_psk_client_credentials_t)
- _gnutls_get_cred(session, GNUTLS_CRD_PSK);
- if (cred == NULL || !session->internals.priorities->have_psk)
+ if ((session->internals.flags & GNUTLS_NO_TICKETS) && !session->internals.priorities->have_psk)
return 0;
if (session->internals.hsk_flags & HSK_PSK_KE_MODES_RECEIVED)
@@ -444,6 +691,15 @@ static int _gnutls_psk_send_params(gnutls_session_t session,
}
}
+static void swap_binders(gnutls_session_t session)
+{
+ struct binder_data_st tmp;
+
+ memcpy(&tmp, &session->key.binders[0], sizeof(struct binder_data_st));
+ memcpy(&session->key.binders[0], &session->key.binders[1], sizeof(struct binder_data_st));
+ memcpy(&session->key.binders[1], &tmp, sizeof(struct binder_data_st));
+}
+
/*
* Return values for this function:
* - 0 : Not applicable.
@@ -453,6 +709,7 @@ static int _gnutls_psk_send_params(gnutls_session_t session,
static int _gnutls_psk_recv_params(gnutls_session_t session,
const unsigned char *data, size_t len)
{
+ unsigned i;
gnutls_psk_server_credentials_t pskcred;
const version_entry_st *vers = get_version(session);
@@ -463,10 +720,22 @@ static int _gnutls_psk_recv_params(gnutls_session_t session,
if (session->internals.hsk_flags & HSK_PSK_KE_MODES_SENT) {
uint16_t selected_identity = _gnutls_read_uint16(data);
- if (selected_identity == 0) {
- _gnutls_handshake_log("EXT[%p]: Selected PSK mode\n", session);
- session->internals.hsk_flags |= HSK_PSK_SELECTED;
+ for (i=0;i<sizeof(session->key.binders)/sizeof(session->key.binders[0]);i++) {
+ if (session->key.binders[i].prf != NULL && session->key.binders[i].idx == selected_identity) {
+ if (session->key.binders[i].resumption) {
+ session->internals.resumed = RESUME_TRUE;
+ _gnutls_handshake_log("EXT[%p]: selected PSK-resumption mode\n", session);
+ } else {
+ _gnutls_handshake_log("EXT[%p]: selected PSK mode\n", session);
+ }
+
+ /* ensure that selected binder is set on (our) index zero */
+ if (i != 0)
+ swap_binders(session);
+ session->internals.hsk_flags |= HSK_PSK_SELECTED;
+ }
}
+
return 0;
} else {
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION);
@@ -483,7 +752,7 @@ static int _gnutls_psk_recv_params(gnutls_session_t session,
/* If there are no PSK credentials, this extension is not applicable,
* so we return zero. */
- if (pskcred == NULL)
+ if (pskcred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS))
return 0;
return server_recv_params(session, data, len, pskcred);
diff --git a/lib/ext/pre_shared_key.h b/lib/ext/pre_shared_key.h
index 25dd159f6e..2e830ff52e 100644
--- a/lib/ext/pre_shared_key.h
+++ b/lib/ext/pre_shared_key.h
@@ -3,13 +3,14 @@
#include "auth/psk.h"
#include <hello_ext.h>
+#include "tls13/session_ticket.h"
extern const hello_ext_entry_st ext_pre_shared_key;
inline static
-unsigned _gnutls_have_psk_credentials(const gnutls_psk_client_credentials_t cred)
+unsigned _gnutls_have_psk_credentials(const gnutls_psk_client_credentials_t cred, gnutls_session_t session)
{
- if (cred->get_function || cred->username.data)
+ if ((cred->get_function || cred->username.data) && session->internals.priorities->have_psk)
return 1;
else
return 0;
diff --git a/lib/ext/psk_ke_modes.c b/lib/ext/psk_ke_modes.c
index af136fd3ca..9c41d9e94a 100644
--- a/lib/ext/psk_ke_modes.c
+++ b/lib/ext/psk_ke_modes.c
@@ -28,17 +28,31 @@
#define PSK_KE 0
#define PSK_DHE_KE 1
-/*
- * We only support ECDHE-authenticated PSKs.
- * The client just sends a "psk_key_exchange_modes" extension
- * with the value one.
- */
+/* Relevant to client only */
+static bool
+psk_ke_modes_is_required(gnutls_session_t session)
+{
+ gnutls_psk_client_credentials_t cred;
+
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS) &&
+ session->internals.tls13_ticket.ticket.data != NULL)
+ return 1;
+
+ if (session->internals.priorities->have_psk) {
+ cred = (gnutls_psk_client_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_PSK);
+ if (cred && _gnutls_have_psk_credentials(cred, session))
+ return 1;
+ }
+
+ return 0;
+}
+
static int
psk_ke_modes_send_params(gnutls_session_t session,
gnutls_buffer_t extdata)
{
int ret;
- gnutls_psk_client_credentials_t cred;
const version_entry_st *vers;
uint8_t data[2];
unsigned pos, i;
@@ -46,19 +60,21 @@ psk_ke_modes_send_params(gnutls_session_t session,
unsigned have_psk = 0;
/* Server doesn't send psk_key_exchange_modes */
- if (session->security_parameters.entity == GNUTLS_SERVER ||
- !session->internals.priorities->have_psk)
+ if (session->security_parameters.entity == GNUTLS_SERVER)
return 0;
- cred = (gnutls_psk_client_credentials_t)
- _gnutls_get_cred(session, GNUTLS_CRD_PSK);
- if (cred == NULL || _gnutls_have_psk_credentials(cred) == 0)
+ if (!psk_ke_modes_is_required(session))
return 0;
vers = _gnutls_version_max(session);
if (!vers || !vers->tls13_sem)
return 0;
+ /* We send the list prioritized according to our preferences as a convention
+ * (used throughout the protocol), even if the protocol doesn't mandate that
+ * for this particular message. That way we can keep the TLS 1.2 semantics/
+ * prioritization when negotiating PSK or DHE-PSK. Receiving servers would
+ * very likely respect our prioritization if they parse the message serially. */
pos = 0;
for (i=0;i<session->internals.priorities->_kx.algorithms;i++) {
if (session->internals.priorities->_kx.priority[i] == GNUTLS_KX_PSK && !have_psk) {
@@ -78,6 +94,17 @@ psk_ke_modes_send_params(gnutls_session_t session,
break;
}
+ /* For session resumption we need to send at least one */
+ if (pos == 0) {
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
+ return 0;
+
+ data[pos++] = PSK_DHE_KE;
+ data[pos++] = PSK_KE;
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_DHE_PSK;
+ session->internals.hsk_flags |= HSK_PSK_KE_MODE_PSK;
+ }
+
ret = _gnutls_buffer_append_data_prefix(extdata, 8, data, pos);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -121,7 +148,7 @@ psk_ke_modes_recv_params(gnutls_session_t session,
}
cred = (gnutls_psk_server_credentials_t)_gnutls_get_cred(session, GNUTLS_CRD_PSK);
- if (cred == NULL) {
+ if (cred == NULL && (session->internals.flags & GNUTLS_NO_TICKETS)) {
session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
return gnutls_assert_val(0);
}
@@ -142,8 +169,12 @@ psk_ke_modes_recv_params(gnutls_session_t session,
break;
}
- if (session->internals.priorities->groups.size == 0 && psk_pos == MAX_POS)
- return gnutls_assert_val(0);
+ if (psk_pos == MAX_POS && dhpsk_pos == MAX_POS) {
+ if (!(session->internals.flags & GNUTLS_NO_TICKETS))
+ dhpsk_pos = 0;
+ else if (session->internals.priorities->groups.size == 0)
+ return gnutls_assert_val(0);
+ }
for (i=0;i<ke_modes_len;i++) {
DECR_LEN(len, 1);
@@ -172,10 +203,11 @@ psk_ke_modes_recv_params(gnutls_session_t session,
if ((session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK) ||
(session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK)) {
+
return 0;
} else {
session->internals.hsk_flags |= HSK_PSK_KE_MODE_INVALID;
- return 0;
+ return gnutls_assert_val(0);
}
}
diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c
index 69dc138a42..40bbe5b112 100644
--- a/lib/ext/session_ticket.c
+++ b/lib/ext/session_ticket.c
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2009-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2009-2018 Free Software Foundation, Inc.
*
- * Author: Daiki Ueno
+ * Author: Daiki Ueno, Ander Juaristi
*
* This file is part of GnuTLS.
*
@@ -36,15 +36,11 @@
#include <dtls.h>
#include "db.h"
-#ifdef ENABLE_SESSION_TICKETS
-
-#define KEY_NAME_SIZE 16
-#define CIPHER_KEY_SIZE 32
+/* They are restricted by TICKET_CIPHER_KEY_SIZE and TICKET_MAC_SECRET_SIZE */
#define CIPHER GNUTLS_CIPHER_AES_256_CBC
#define IV_SIZE 16
#define BLOCK_SIZE 16
-#define MAC_SECRET_SIZE 16
#define MAC_ALGO GNUTLS_MAC_SHA1
#define MAC_SIZE 20 /* HMAC-SHA1 */
@@ -74,29 +70,97 @@ const hello_ext_entry_st ext_mod_session_ticket = {
.cannot_be_overriden = 1
};
-#define SESSION_KEY_SIZE (KEY_NAME_SIZE+CIPHER_KEY_SIZE+MAC_SECRET_SIZE)
#define NAME_POS (0)
-#define KEY_POS (KEY_NAME_SIZE)
-#define MAC_SECRET_POS (KEY_NAME_SIZE+CIPHER_KEY_SIZE)
+#define KEY_POS (TICKET_KEY_NAME_SIZE)
+#define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE)
typedef struct {
- int session_ticket_enable;
- int session_ticket_renew;
-
uint8_t *session_ticket;
int session_ticket_len;
-
- uint8_t key[SESSION_KEY_SIZE];
} session_ticket_ext_st;
struct ticket_st {
- uint8_t key_name[KEY_NAME_SIZE];
+ uint8_t key_name[TICKET_KEY_NAME_SIZE];
uint8_t IV[IV_SIZE];
uint8_t *encrypted_state;
uint16_t encrypted_state_len;
uint8_t mac[MAC_SIZE];
};
+static void
+deinit_ticket(struct ticket_st *ticket)
+{
+ free(ticket->encrypted_state);
+}
+
+static int
+unpack_ticket(const gnutls_datum_t *ticket_data, struct ticket_st *ticket)
+{
+ const uint8_t * data = ticket_data->data;
+ ssize_t data_size = ticket_data->size;
+ const uint8_t *encrypted_state;
+
+ /* Format:
+ * Key name
+ * IV
+ * data length
+ * encrypted data
+ * MAC
+ */
+ DECR_LEN(data_size, TICKET_KEY_NAME_SIZE);
+ memcpy(ticket->key_name, data, TICKET_KEY_NAME_SIZE);
+ data += TICKET_KEY_NAME_SIZE;
+
+ DECR_LEN(data_size, IV_SIZE);
+ memcpy(ticket->IV, data, IV_SIZE);
+ data += IV_SIZE;
+
+ DECR_LEN(data_size, 2);
+ ticket->encrypted_state_len = _gnutls_read_uint16(data);
+ data += 2;
+
+ encrypted_state = data;
+
+ DECR_LEN(data_size, ticket->encrypted_state_len);
+ data += ticket->encrypted_state_len;
+
+ DECR_LEN(data_size, MAC_SIZE);
+ memcpy(ticket->mac, data, MAC_SIZE);
+
+ ticket->encrypted_state =
+ gnutls_malloc(ticket->encrypted_state_len);
+ if (!ticket->encrypted_state) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memcpy(ticket->encrypted_state, encrypted_state,
+ ticket->encrypted_state_len);
+
+ return 0;
+}
+
+static void
+pack_ticket(const struct ticket_st *ticket, gnutls_datum_t *ticket_data)
+{
+ uint8_t *p;
+
+ p = ticket_data->data;
+
+ memcpy(p, ticket->key_name, TICKET_KEY_NAME_SIZE);
+ p += TICKET_KEY_NAME_SIZE;
+
+ memcpy(p, ticket->IV, IV_SIZE);
+ p += IV_SIZE;
+
+ _gnutls_write_uint16(ticket->encrypted_state_len, p);
+ p += 2;
+
+ memcpy(p, ticket->encrypted_state, ticket->encrypted_state_len);
+ p += ticket->encrypted_state_len;
+
+ memcpy(p, ticket->mac, MAC_SIZE);
+}
+
static
int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket,
uint8_t * digest)
@@ -112,7 +176,7 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket,
return ret;
}
- _gnutls_mac(&digest_hd, ticket->key_name, KEY_NAME_SIZE);
+ _gnutls_mac(&digest_hd, ticket->key_name, TICKET_KEY_NAME_SIZE);
_gnutls_mac(&digest_hd, ticket->IV, IV_SIZE);
length16 = _gnutls_conv_uint16(ticket->encrypted_state_len);
_gnutls_mac(&digest_hd, &length16, 2);
@@ -123,121 +187,119 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket,
return 0;
}
-static int
-decrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
- struct ticket_st *ticket)
+int
+_gnutls_decrypt_session_ticket(gnutls_session_t session,
+ const gnutls_datum_t *ticket_data,
+ gnutls_datum_t *state)
{
cipher_hd_st cipher_hd;
- gnutls_datum_t key, IV, state, mac_secret;
+ gnutls_datum_t key, IV, mac_secret;
uint8_t cmac[MAC_SIZE];
- time_t timestamp = gnutls_time(0);
+ struct ticket_st ticket;
int ret;
- /* Check the integrity of ticket */
- mac_secret.data = (void *) &priv->key[MAC_SECRET_POS];
- mac_secret.size = MAC_SECRET_SIZE;
- ret = digest_ticket(&mac_secret, ticket, cmac);
+ ret = unpack_ticket(ticket_data, &ticket);
if (ret < 0)
- return gnutls_assert_val(ret);
-
- if (memcmp(ticket->mac, cmac, MAC_SIZE))
- return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ return ret;
- if (ticket->encrypted_state_len % BLOCK_SIZE != 0)
- return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ /* If the key name of the ticket does not match the one that we
+ hold, issue a new ticket. */
+ if (memcmp
+ (ticket.key_name, &session->key.session_ticket_key[NAME_POS],
+ TICKET_KEY_NAME_SIZE)) {
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto cleanup;
+ }
- /* Decrypt encrypted_state */
- key.data = (void *) &priv->key[KEY_POS];
- key.size = CIPHER_KEY_SIZE;
- IV.data = ticket->IV;
- IV.size = IV_SIZE;
- ret =
- _gnutls_cipher_init(&cipher_hd,
- cipher_to_entry(CIPHER),
- &key, &IV, 0);
+ /* Check the integrity of ticket */
+ mac_secret.data = (void *) &session->key.session_ticket_key[MAC_SECRET_POS];
+ mac_secret.size = TICKET_MAC_SECRET_SIZE;
+ ret = digest_ticket(&mac_secret, &ticket, cmac);
if (ret < 0) {
gnutls_assert();
- return ret;
+ goto cleanup;
}
- ret = _gnutls_cipher_decrypt(&cipher_hd, ticket->encrypted_state,
- ticket->encrypted_state_len);
- if (ret < 0) {
- gnutls_assert();
+
+ if (memcmp(ticket.mac, cmac, MAC_SIZE)) {
+ ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
goto cleanup;
}
- /* Unpack security parameters. */
- state.data = ticket->encrypted_state;
- state.size = ticket->encrypted_state_len;
- ret = _gnutls_session_unpack(session, &state);
- if (ret < 0) {
- gnutls_assert();
+ if (ticket.encrypted_state_len % BLOCK_SIZE != 0) {
+ ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
goto cleanup;
}
- if (timestamp -
- session->internals.resumed_security_parameters.timestamp >
- session->internals.expire_time
- || session->internals.resumed_security_parameters.timestamp >
- timestamp) {
+ /* Decrypt encrypted_state */
+ key.data = (void *) &session->key.session_ticket_key[KEY_POS];
+ key.size = TICKET_CIPHER_KEY_SIZE;
+ IV.data = ticket.IV;
+ IV.size = IV_SIZE;
+ ret =
+ _gnutls_cipher_init(&cipher_hd,
+ cipher_to_entry(CIPHER),
+ &key, &IV, 0);
+ if (ret < 0) {
gnutls_assert();
- ret = GNUTLS_E_EXPIRED;
goto cleanup;
}
- ret = _gnutls_check_resumed_params(session);
+ ret = _gnutls_cipher_decrypt(&cipher_hd, ticket.encrypted_state,
+ ticket.encrypted_state_len);
if (ret < 0) {
gnutls_assert();
- goto cleanup;
+ goto cleanup2;
}
- session->internals.resumed = RESUME_TRUE;
+ state->data = ticket.encrypted_state;
+ state->size = ticket.encrypted_state_len;
+
+ ticket.encrypted_state = NULL;
ret = 0;
-cleanup:
+
+cleanup2:
_gnutls_cipher_deinit(&cipher_hd);
+cleanup:
+ deinit_ticket(&ticket);
+
return ret;
}
-static int
-encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
- struct ticket_st *ticket)
+int
+_gnutls_encrypt_session_ticket(gnutls_session_t session,
+ const gnutls_datum_t *state,
+ gnutls_datum_t *ticket_data)
{
cipher_hd_st cipher_hd;
gnutls_datum_t key, IV;
- gnutls_datum_t state = {NULL,0}, encrypted_state = {NULL,0};
+ gnutls_datum_t encrypted_state = {NULL,0};
uint8_t iv[IV_SIZE];
gnutls_datum_t mac_secret;
- uint32_t t;
+ struct ticket_st ticket;
int ret;
- /* Pack security parameters. */
- ret = _gnutls_session_pack(session, &state);
- if (ret < 0) {
- gnutls_assert();
- return ret;
- }
-
- encrypted_state.size = ((state.size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
- encrypted_state.data = gnutls_calloc(1, encrypted_state.size);
- if (!encrypted_state.data) {
+ encrypted_state.size = ((state->size + BLOCK_SIZE - 1) / BLOCK_SIZE) * BLOCK_SIZE;
+ ticket_data->size = TICKET_KEY_NAME_SIZE + IV_SIZE + 2 +
+ encrypted_state.size + MAC_SIZE;
+ ticket_data->data = gnutls_calloc(1, ticket_data->size);
+ if (!ticket_data->data) {
gnutls_assert();
ret = GNUTLS_E_MEMORY_ERROR;
goto cleanup;
}
- memcpy(encrypted_state.data, state.data, state.size);
+ encrypted_state.data = ticket_data->data + TICKET_KEY_NAME_SIZE + IV_SIZE + 2;
+ memcpy(encrypted_state.data, state->data, state->size);
/* Encrypt state */
- key.data = (void *) &priv->key[KEY_POS];
- key.size = CIPHER_KEY_SIZE;
+ key.data = (void *) &session->key.session_ticket_key[KEY_POS];
+ key.size = TICKET_CIPHER_KEY_SIZE;
IV.data = iv;
IV.size = IV_SIZE;
- t = gnutls_time(0);
- memcpy(iv, &t, 4);
- ret = gnutls_rnd(GNUTLS_RND_NONCE, iv+4, IV_SIZE-4);
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, IV_SIZE);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -261,14 +323,14 @@ encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
/* Fill the ticket structure to compute MAC. */
- memcpy(ticket->key_name, &priv->key[NAME_POS], KEY_NAME_SIZE);
- memcpy(ticket->IV, IV.data, IV.size);
- ticket->encrypted_state_len = encrypted_state.size;
- ticket->encrypted_state = encrypted_state.data;
-
- mac_secret.data = &priv->key[MAC_SECRET_POS];
- mac_secret.size = MAC_SECRET_SIZE;
- ret = digest_ticket(&mac_secret, ticket, ticket->mac);
+ memcpy(ticket.key_name, &session->key.session_ticket_key[NAME_POS], TICKET_KEY_NAME_SIZE);
+ memcpy(ticket.IV, IV.data, IV.size);
+ ticket.encrypted_state_len = encrypted_state.size;
+ ticket.encrypted_state = encrypted_state.data;
+
+ mac_secret.data = &session->key.session_ticket_key[MAC_SECRET_POS];
+ mac_secret.size = TICKET_MAC_SECRET_SIZE;
+ ret = digest_ticket(&mac_secret, &ticket, ticket.mac);
if (ret < 0) {
gnutls_assert();
goto cleanup2;
@@ -276,107 +338,82 @@ encrypt_ticket(gnutls_session_t session, session_ticket_ext_st * priv,
encrypted_state.data = NULL;
+ pack_ticket(&ticket, ticket_data);
+
ret = 0;
cleanup2:
_gnutls_cipher_deinit(&cipher_hd);
cleanup:
- _gnutls_free_datum(&state);
_gnutls_free_datum(&encrypted_state);
return ret;
}
static int
+unpack_session(gnutls_session_t session, const gnutls_datum_t *state)
+{
+ int ret;
+ time_t timestamp = gnutls_time(0);
+
+ if (unlikely(!state))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _gnutls_session_unpack(session, state);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (timestamp -
+ session->internals.resumed_security_parameters.timestamp >
+ session->internals.expire_time
+ || session->internals.resumed_security_parameters.timestamp >
+ timestamp)
+ return gnutls_assert_val(GNUTLS_E_EXPIRED);
+
+ ret = _gnutls_check_resumed_params(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->internals.resumed = RESUME_TRUE;
+ return 0;
+}
+
+static int
session_ticket_recv_params(gnutls_session_t session,
const uint8_t * data, size_t _data_size)
{
+ gnutls_datum_t ticket_data;
+ gnutls_datum_t state;
ssize_t data_size = _data_size;
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
int ret;
- ret =
- _gnutls_hello_ext_get_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- &epriv);
- if (ret < 0) {
- return 0;
- }
- priv = epriv;
-
- if (!priv->session_ticket_enable)
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
return 0;
if (session->security_parameters.entity == GNUTLS_SERVER) {
- struct ticket_st ticket;
- const uint8_t *encrypted_state;
-
/* The client requested a new session ticket. */
if (data_size == 0) {
- priv->session_ticket_renew = 1;
- return 0;
- }
-
- /* Format:
- * Key name
- * IV
- * data length
- * encrypted data
- * MAC
- */
- DECR_LEN(data_size, KEY_NAME_SIZE);
- memcpy(ticket.key_name, data, KEY_NAME_SIZE);
- data += KEY_NAME_SIZE;
-
- /* If the key name of the ticket does not match the one that we
- hold, issue a new ticket. */
- if (memcmp
- (ticket.key_name, &priv->key[NAME_POS],
- KEY_NAME_SIZE)) {
- priv->session_ticket_renew = 1;
+ session->internals.session_ticket_renew = 1;
return 0;
}
- DECR_LEN(data_size, IV_SIZE);
- memcpy(ticket.IV, data, IV_SIZE);
- data += IV_SIZE;
-
- DECR_LEN(data_size, 2);
- ticket.encrypted_state_len = _gnutls_read_uint16(data);
- data += 2;
-
- encrypted_state = data;
-
- DECR_LEN(data_size, ticket.encrypted_state_len);
- data += ticket.encrypted_state_len;
-
- DECR_LEN(data_size, MAC_SIZE);
- memcpy(ticket.mac, data, MAC_SIZE);
+ ticket_data.data = (void *)data;
+ ticket_data.size = data_size;
+ if ((ret = _gnutls_decrypt_session_ticket(session, &ticket_data, &state)) == 0) {
+ ret = unpack_session(session, &state);
- ticket.encrypted_state =
- gnutls_malloc(ticket.encrypted_state_len);
- if (!ticket.encrypted_state) {
- gnutls_assert();
- return GNUTLS_E_MEMORY_ERROR;
+ _gnutls_free_datum(&state);
}
- memcpy(ticket.encrypted_state, encrypted_state,
- ticket.encrypted_state_len);
-
- ret = decrypt_ticket(session, priv, &ticket);
-
- gnutls_free(ticket.encrypted_state);
- ticket.encrypted_state = NULL;
if (ret < 0) {
- priv->session_ticket_renew = 1;
+ session->internals.session_ticket_renew = 1;
return 0;
}
} else { /* Client */
if (data_size == 0) {
- priv->session_ticket_renew = 1;
+ session->internals.session_ticket_renew = 1;
return 0;
}
}
@@ -395,18 +432,11 @@ session_ticket_send_params(gnutls_session_t session,
gnutls_ext_priv_data_t epriv;
int ret;
- ret =
- _gnutls_hello_ext_get_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- &epriv);
- if (ret >= 0)
- priv = epriv;
-
- if (priv == NULL || !priv->session_ticket_enable)
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
return 0;
if (session->security_parameters.entity == GNUTLS_SERVER) {
- if (priv && priv->session_ticket_renew) {
+ if (session->internals.session_ticket_renew) {
return GNUTLS_E_INT_RET_0;
}
} else {
@@ -422,7 +452,7 @@ session_ticket_send_params(gnutls_session_t session,
return GNUTLS_E_INT_RET_0;
/* previous data had session tickets disabled. Don't advertize. Ignore. */
- if (!priv->session_ticket_enable)
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
return 0;
if (priv->session_ticket_len > 0) {
@@ -458,7 +488,6 @@ session_ticket_pack(gnutls_ext_priv_data_t epriv, gnutls_buffer_st * ps)
BUFFER_APPEND_PFX4(ps, priv->session_ticket,
priv->session_ticket_len);
- BUFFER_APPEND_NUM(ps, priv->session_ticket_enable);
return 0;
}
@@ -480,7 +509,6 @@ session_ticket_unpack(gnutls_buffer_st * ps, gnutls_ext_priv_data_t * _priv)
BUFFER_POP_DATUM(ps, &ticket);
priv->session_ticket = ticket.data;
priv->session_ticket_len = ticket.size;
- BUFFER_POP_NUM(ps, priv->session_ticket_enable);
epriv = priv;
*_priv = epriv;
@@ -516,11 +544,11 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key)
* used. These limits do not affect this function as
* it does not generate a "key" but rather key material
* that includes nonces and other stuff. */
- key->data = gnutls_malloc(SESSION_KEY_SIZE);
+ key->data = gnutls_malloc(TICKET_MASTER_KEY_SIZE);
if (key->data == NULL)
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
- key->size = SESSION_KEY_SIZE;
+ key->size = TICKET_MASTER_KEY_SIZE;
ret = gnutls_rnd(GNUTLS_RND_RANDOM, key->data, key->size);
if (ret < 0) {
gnutls_free(key->data);
@@ -528,7 +556,7 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key)
}
return 0;
} else {
- return gnutls_key_generate(key, SESSION_KEY_SIZE);
+ return gnutls_key_generate(key, TICKET_MASTER_KEY_SIZE);
}
}
@@ -537,7 +565,8 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key)
* @session: is a #gnutls_session_t type.
*
* Request that the client should attempt session resumption using
- * SessionTicket.
+ * SessionTicket. This call is typically unnecessary as session
+ * tickets are enabled by default.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
* error code.
@@ -546,25 +575,12 @@ int gnutls_session_ticket_key_generate(gnutls_datum_t * key)
**/
int gnutls_session_ticket_enable_client(gnutls_session_t session)
{
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
-
if (!session) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
- priv = gnutls_calloc(1, sizeof(*priv));
- if (priv == NULL) {
- gnutls_assert();
- return GNUTLS_E_MEMORY_ERROR;
- }
- priv->session_ticket_enable = 1;
- epriv = priv;
-
- _gnutls_hello_ext_set_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- epriv);
+ session->internals.flags &= ~GNUTLS_NO_TICKETS;
return 0;
}
@@ -575,9 +591,13 @@ int gnutls_session_ticket_enable_client(gnutls_session_t session)
* @key: key to encrypt session parameters.
*
* Request that the server should attempt session resumption using
- * SessionTicket. @key must be initialized with
- * gnutls_session_ticket_key_generate(), and should be overwritten
- * using gnutls_memset() before being released.
+ * session tickets, i.e., by delegating storage to the client.
+ * @key must be initialized using gnutls_session_ticket_key_generate().
+ * To avoid leaking that key, use gnutls_memset() prior to
+ * releasing it.
+ *
+ * The default ticket expiration time can be overriden using
+ * gnutls_db_set_cache_expiration().
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
* error code.
@@ -588,55 +608,39 @@ int
gnutls_session_ticket_enable_server(gnutls_session_t session,
const gnutls_datum_t * key)
{
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
-
- if (!session || !key || key->size != SESSION_KEY_SIZE) {
+ if (!session || !key || key->size != TICKET_MASTER_KEY_SIZE) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
- priv = gnutls_calloc(1, sizeof(*priv));
- if (priv == NULL) {
- gnutls_assert();
- return GNUTLS_E_MEMORY_ERROR;
- }
- epriv = priv;
-
- memcpy(&priv->key, key->data, key->size);
- priv->session_ticket_enable = 1;
-
- _gnutls_hello_ext_set_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- epriv);
+ memcpy(session->key.session_ticket_key, key->data, key->size);
+ session->internals.flags &= ~GNUTLS_NO_TICKETS;
return 0;
}
+/*
+ * Return zero if session tickets haven't been enabled.
+ */
int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
{
mbuffer_st *bufel = NULL;
uint8_t *data = NULL, *p;
int data_size = 0;
int ret;
- struct ticket_st ticket;
- uint16_t ticket_len;
- session_ticket_ext_st *priv = NULL;
- gnutls_ext_priv_data_t epriv;
+ gnutls_datum_t state = { NULL, 0 };
uint16_t epoch_saved = session->security_parameters.epoch_write;
+ gnutls_datum_t ticket_data;
if (again == 0) {
- ret =
- _gnutls_hello_ext_get_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- &epriv);
- if (ret < 0)
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
return 0;
- priv = epriv;
-
- if (!priv->session_ticket_renew)
+ if (!session->internals.session_ticket_renew)
return 0;
+ _gnutls_handshake_log
+ ("HSK[%p]: sending session ticket\n", session);
+
/* XXX: Temporarily set write algorithms to be used.
_gnutls_write_connection_state_init() does this job, but it also
triggers encryption, while NewSessionTicket should not be
@@ -653,23 +657,28 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
session->security_parameters.epoch_write =
session->security_parameters.epoch_next;
- ret = encrypt_ticket(session, priv, &ticket);
- session->security_parameters.epoch_write = epoch_saved;
+ /* Pack security parameters. */
+ ret = _gnutls_session_pack(session, &state);
if (ret < 0) {
gnutls_assert();
return ret;
}
- ticket_len =
- KEY_NAME_SIZE + IV_SIZE + 2 +
- ticket.encrypted_state_len + MAC_SIZE;
+ /* Generate an encrypted ticket */
+ ret = _gnutls_encrypt_session_ticket(session, &state, &ticket_data);
+ session->security_parameters.epoch_write = epoch_saved;
+ _gnutls_free_datum(&state);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
bufel =
_gnutls_handshake_alloc(session,
- 4 + 2 + ticket_len);
+ 4 + 2 + ticket_data.size);
if (!bufel) {
gnutls_assert();
- gnutls_free(ticket.encrypted_state);
+ _gnutls_free_datum(&ticket_data);
return GNUTLS_E_MEMORY_ERROR;
}
@@ -679,33 +688,25 @@ int _gnutls_send_new_session_ticket(gnutls_session_t session, int again)
_gnutls_write_uint32(session->internals.expire_time, p);
p += 4;
- _gnutls_write_uint16(ticket_len, p);
- p += 2;
-
- memcpy(p, ticket.key_name, KEY_NAME_SIZE);
- p += KEY_NAME_SIZE;
-
- memcpy(p, ticket.IV, IV_SIZE);
- p += IV_SIZE;
-
- _gnutls_write_uint16(ticket.encrypted_state_len, p);
+ _gnutls_write_uint16(ticket_data.size, p);
p += 2;
- memcpy(p, ticket.encrypted_state, ticket.encrypted_state_len);
- gnutls_free(ticket.encrypted_state);
- p += ticket.encrypted_state_len;
+ memcpy(p, ticket_data.data, ticket_data.size);
+ p += ticket_data.size;
- memcpy(p, ticket.mac, MAC_SIZE);
- p += MAC_SIZE;
+ _gnutls_free_datum(&ticket_data);
data_size = p - data;
- session->internals.ticket_sent = 1;
+ session->internals.hsk_flags |= HSK_TLS12_TICKET_SENT;
}
return _gnutls_send_handshake(session, data_size ? bufel : NULL,
GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
}
+/*
+ * Return zero if session tickets haven't been enabled.
+ */
int _gnutls_recv_new_session_ticket(gnutls_session_t session)
{
uint8_t *p;
@@ -716,17 +717,9 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
session_ticket_ext_st *priv = NULL;
gnutls_ext_priv_data_t epriv;
- ret =
- _gnutls_hello_ext_get_priv(session,
- GNUTLS_EXTENSION_SESSION_TICKET,
- &epriv);
- if (ret < 0) {
- gnutls_assert();
+ if (session->internals.flags & GNUTLS_NO_TICKETS)
return 0;
- }
- priv = epriv;
-
- if (!priv->session_ticket_renew)
+ if (!session->internals.session_ticket_renew)
return 0;
/* This is the last flight and peer cannot be sure
@@ -764,15 +757,24 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
DECR_LENGTH_COM(data_size, ticket_len, ret =
GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
goto error);
+
+ priv = gnutls_calloc(1, sizeof(*priv));
+ if (!priv) {
+ gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
+ goto error;
+ }
priv->session_ticket =
gnutls_realloc_fast(priv->session_ticket, ticket_len);
if (!priv->session_ticket) {
+ gnutls_free(priv);
gnutls_assert();
ret = GNUTLS_E_MEMORY_ERROR;
goto error;
}
memcpy(priv->session_ticket, p, ticket_len);
priv->session_ticket_len = ticket_len;
+ epriv = priv;
/* Discard the current session ID. (RFC5077 3.4) */
ret =
@@ -782,17 +784,22 @@ int _gnutls_recv_new_session_ticket(gnutls_session_t session)
session_id_size);
if (ret < 0) {
gnutls_assert();
- gnutls_free(priv->session_ticket);
- priv->session_ticket = NULL;
+ session_ticket_deinit_data(epriv);
ret = GNUTLS_E_INTERNAL_ERROR;
goto error;
}
ret = 0;
+ _gnutls_handshake_log
+ ("HSK[%p]: received session ticket\n", session);
+ session->internals.hsk_flags |= HSK_TICKET_RECEIVED;
+
+ _gnutls_hello_ext_set_priv(session,
+ GNUTLS_EXTENSION_SESSION_TICKET,
+ epriv);
+
error:
_gnutls_buffer_clear(&buf);
return ret;
}
-
-#endif
diff --git a/lib/ext/session_ticket.h b/lib/ext/session_ticket.h
index c00c3f6b7e..4fad2d7a50 100644
--- a/lib/ext/session_ticket.h
+++ b/lib/ext/session_ticket.h
@@ -30,4 +30,11 @@ extern const hello_ext_entry_st ext_mod_session_ticket;
int _gnutls_send_new_session_ticket(gnutls_session_t session, int again);
int _gnutls_recv_new_session_ticket(gnutls_session_t session);
+int _gnutls_encrypt_session_ticket(gnutls_session_t session,
+ const gnutls_datum_t *state,
+ gnutls_datum_t *ticket_data);
+int _gnutls_decrypt_session_ticket(gnutls_session_t session,
+ const gnutls_datum_t *ticket_data,
+ gnutls_datum_t *state);
+
#endif
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 9c9fb1533a..367dbff83e 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2000-2016 Free Software Foundation, Inc.
- * Copyright (C) 2015-2017 Red Hat, Inc.
+ * Copyright (C) 2015-2018 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
@@ -120,6 +120,8 @@ typedef struct {
#define MAX_FILENAME 512
#define MAX_HASH_SIZE 64
+#define MAX_MAC_KEY_SIZE 64
+
#define MAX_CIPHER_BLOCK_SIZE 16
#define MAX_CIPHER_KEY_SIZE 32
@@ -158,7 +160,7 @@ typedef struct {
#define _GNUTLS_EXT_TLS_POST_CS 177
/* expire time for resuming sessions */
-#define DEFAULT_EXPIRE_TIME 3600
+#define DEFAULT_EXPIRE_TIME 21600
#define DEFAULT_HANDSHAKE_TIMEOUT_MS 40*1000
/* The EC group to be used when the extension
@@ -267,7 +269,7 @@ typedef enum handshake_state_t { STATE0 = 0, STATE1, STATE2,
STATE90=90, STATE91, STATE92, STATE93, STATE99=99,
STATE100=100, STATE101, STATE102, STATE103, STATE104,
STATE105, STATE106, STATE107, STATE108, STATE109, STATE110,
- STATE111,
+ STATE111, STATE112,
STATE150 /* key update */
} handshake_state_t;
@@ -275,11 +277,16 @@ typedef enum bye_state_t {
BYE_STATE0 = 0, BYE_STATE1, BYE_STATE2
} bye_state_t;
+typedef enum send_ticket_state_t {
+ TICKET_STATE0 = 0, TICKET_STATE1
+} send_ticket_state_t;
+
typedef enum reauth_state_t {
REAUTH_STATE0 = 0, REAUTH_STATE1, REAUTH_STATE2, REAUTH_STATE3,
REAUTH_STATE4, REAUTH_STATE5
} reauth_state_t;
+#define TICKET_STATE session->internals.ticket_state
#define BYE_STATE session->internals.bye_state
#define REAUTH_STATE session->internals.reauth_state
@@ -461,6 +468,23 @@ typedef struct auth_cred_st {
struct auth_cred_st *next;
} auth_cred_st;
+/* session ticket definitions */
+#define TICKET_MASTER_KEY_SIZE (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE+TICKET_MAC_SECRET_SIZE)
+#define TICKET_KEY_NAME_SIZE 16
+#define TICKET_CIPHER_KEY_SIZE 32
+#define TICKET_MAC_SECRET_SIZE 16
+
+struct binder_data_st {
+ const struct mac_entry_st *prf; /* non-null if this struct is set */
+ gnutls_datum_t psk;
+
+ /* 0-based index of the selected PSK.
+ * This only applies if the HSK_PSK_SELECTED flag is set in internals.hsk_flags,
+ * which signals a PSK has indeed been selected. */
+ uint8_t idx;
+ uint8_t resumption; /* whether it is a resumption binder */
+};
+
struct gnutls_key_st {
struct { /* These are kept outside the TLS1.3 union as they are
* negotiated via extension, even before protocol is negotiated */
@@ -476,14 +500,6 @@ struct gnutls_key_st {
*/
union {
struct {
- /*
- * 0-based index of the selected PSK.
- * This only applies if the HSK_PSK_SELECTED flag is set in internals.hsk_flags,
- * which signals a PSK has indeed been selected.
- */
- unsigned psk_index;
- const struct mac_entry_st *binder_prf;
-
/* the current (depending on state) secret, can be
* early_secret, client_early_traffic_secret, ... */
uint8_t temp_secret[MAX_HASH_SIZE];
@@ -491,6 +507,7 @@ struct gnutls_key_st {
uint8_t hs_ckey[MAX_HASH_SIZE]; /* client_handshake_traffic_secret */
uint8_t hs_skey[MAX_HASH_SIZE]; /* server_handshake_traffic_secret */
uint8_t ap_expkey[MAX_HASH_SIZE]; /* exporter_master_secret */
+ uint8_t ap_rms[MAX_HASH_SIZE]; /* resumption_master_secret */
} tls13; /* tls1.3 */
/* Folow the SSL3.0 and TLS1.2 key exchanges */
@@ -525,13 +542,21 @@ struct gnutls_key_st {
} tls12; /* from ssl3.0 to tls12 */
} proto;
- /* Pre-shared key in use (if any); temporary storage */
- gnutls_datum_t psk;
- unsigned psk_needs_free;
+ /* binders / pre-shared keys in use; temporary storage.
+ * On client side it will hold data for the resumption and external
+ * PSKs After server hello is received the selected binder is set on 0 position
+ * and HSK_PSK_SELECTED is set.
+ *
+ * On server side the first value is populated with
+ * the selected PSK data if HSK_PSK_SELECTED flag is set. */
+ struct binder_data_st binders[2];
/* TLS pre-master key; applies to 1.2 and 1.3 */
gnutls_datum_t key;
+ /* The key to encrypt and decrypt session tickets */
+ uint8_t session_ticket_key[TICKET_MASTER_KEY_SIZE];
+
/* this is used to hold the peers authentication data
*/
/* auth_info_t structures SHOULD NOT contain malloced
@@ -738,6 +763,9 @@ typedef struct {
/* encrypt-then-mac -> rfc7366 */
uint8_t etm;
+ uint8_t client_auth_type; /* gnutls_credentials_type_t */
+ uint8_t server_auth_type;
+
/* Note: if you add anything in Security_Parameters struct, then
* also modify CPY_COMMON in constate.c, and session_pack.c,
* in order to save it in the session storage.
@@ -927,6 +955,19 @@ typedef struct gnutls_dh_params_int {
*/
} dh_params_st;
+/* TLS 1.3 session ticket
+ */
+typedef struct tls13_ticket {
+ time_t timestamp;
+ uint32_t lifetime;
+ uint32_t age_add;
+ uint8_t nonce[255];
+ size_t nonce_size;
+ const mac_entry_st *prf;
+ uint8_t resumption_master_secret[MAX_HASH_SIZE];
+ gnutls_datum_t ticket;
+} tls13_ticket_t;
+
/* DTLS session state
*/
typedef struct {
@@ -981,10 +1022,13 @@ typedef struct {
* the client key exchange message */
unsigned handshake_hash_buffer_server_finished_len;/* if non-zero it is the length of data until the
* the server finished message */
+ unsigned handshake_hash_buffer_client_finished_len;/* if non-zero it is the length of data until the
+ * the client finished message */
gnutls_buffer_st handshake_hash_buffer; /* used to keep the last received handshake
* message */
bool resumable; /* TRUE or FALSE - if we can resume that session */
- bool ticket_sent; /* whether a session ticket was sent */
+
+ send_ticket_state_t ticket_state; /* used by gnutls_session_ticket_send() */
bye_state_t bye_state; /* used by gnutls_bye() */
reauth_state_t reauth_state; /* used by gnutls_reauth() */
@@ -1227,6 +1271,13 @@ typedef struct {
#define HSK_PSK_SELECTED (1<<15)
#define HSK_KEY_SHARE_SENT (1<<16) /* server: key share was sent to client */
#define HSK_KEY_SHARE_RECEIVED (1<<17) /* client: key share was received */
+#define HSK_TLS13_TICKET_SENT (1<<18) /* client: sent a ticket under TLS1.3;
+ * server: a ticket was sent to client.
+ */
+#define HSK_TLS12_TICKET_SENT (1<<19) /* client: sent a ticket under TLS1.2;
+ * server: a ticket was sent to client.
+ */
+#define HSK_TICKET_RECEIVED (1<<20) /* client: a session ticket was received */
/* The hsk_flags are for use within the ongoing handshake;
* they are reset to zero prior to handshake start by gnutls_handshake. */
@@ -1322,6 +1373,11 @@ typedef struct {
/* the ciphersuite received in HRR */
uint8_t hrr_cs[2];
+ /* this is only used under TLS1.2 or earlier */
+ int session_ticket_renew;
+
+ tls13_ticket_t tls13_ticket;
+
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
*/
} internals_st;
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index de14cf106e..2ebf20af5f 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -54,7 +54,7 @@
#include "tls13/certificate.h"
#include "tls13/finished.h"
#include "tls13/key_update.h"
-#include "tls13/session_ticket.h"
+#include "ext/pre_shared_key.h"
static int generate_hs_traffic_keys(gnutls_session_t session);
static int generate_ap_traffic_keys(gnutls_session_t session);
@@ -158,6 +158,9 @@ int _gnutls13_handshake_client(gnutls_session_t session)
SAVE_TRANSCRIPT;
+ if (session->internals.resumed != RESUME_FALSE)
+ _gnutls_set_resumed_parameters(session);
+
return 0;
}
@@ -189,6 +192,14 @@ static int generate_ap_traffic_keys(gnutls_session_t session)
session->key.proto.tls13.ap_expkey,
session->security_parameters.prf->output_size);
+ ret = _tls13_derive_secret(session, RMS_MASTER_LABEL, sizeof(RMS_MASTER_LABEL)-1,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer_client_finished_len,
+ session->key.proto.tls13.temp_secret,
+ session->key.proto.tls13.ap_rms);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
_gnutls_epoch_bump(session);
ret = _gnutls_epoch_dup(session);
if (ret < 0)
@@ -210,9 +221,11 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
if ((session->security_parameters.entity == GNUTLS_CLIENT &&
- !(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED)) ||
+ (!(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED) ||
+ (!(session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK) &&
+ session->internals.resumed != RESUME_FALSE))) ||
(session->security_parameters.entity == GNUTLS_SERVER &&
- !(session->internals.hsk_flags & HSK_KEY_SHARE_SENT))) {
+ !(session->internals.hsk_flags & HSK_KEY_SHARE_SENT))) {
if ((session->internals.hsk_flags & HSK_PSK_SELECTED) &&
(session->internals.hsk_flags & HSK_PSK_KE_MODE_PSK)) {
@@ -225,7 +238,7 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
unsigned digest_size;
if (unlikely(session->security_parameters.prf == NULL))
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
digest_size = session->security_parameters.prf->output_size;
memset(digest, 0, digest_size);
@@ -237,7 +250,7 @@ static int generate_hs_traffic_keys(gnutls_session_t session)
}
} else {
if (unlikely(session->key.key.size == 0))
- return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
ret = _tls13_update_secret(session, session->key.key.data, session->key.key.size);
if (ret < 0) {
@@ -363,6 +376,15 @@ int _gnutls13_handshake_server(gnutls_session_t session)
STATE = STATE111;
IMED_RET("generate app keys", ret, 0);
+ if (session->internals.resumed != RESUME_FALSE)
+ _gnutls_set_resumed_parameters(session);
+ /* fall through */
+ case STATE112:
+
+ ret = _gnutls13_send_session_ticket(session, AGAIN(STATE112));
+ STATE = STATE112;
+ IMED_RET("send session ticket", ret, 0);
+
STATE = STATE0;
break;
default:
@@ -375,6 +397,7 @@ int _gnutls13_handshake_server(gnutls_session_t session)
SAVE_TRANSCRIPT;
+
return 0;
}
@@ -409,6 +432,10 @@ _gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
if (ret < 0)
return gnutls_assert_val(ret);
+ ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_PRE, 1, buf->data, buf->length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
switch(type) {
case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
if (!(session->security_parameters.entity == GNUTLS_CLIENT) ||
@@ -440,12 +467,69 @@ _gnutls13_recv_async_handshake(gnutls_session_t session, gnutls_buffer_st *buf)
ret = _gnutls13_recv_session_ticket(session, buf);
if (ret < 0)
return gnutls_assert_val(ret);
+
+ memcpy(session->internals.tls13_ticket.resumption_master_secret,
+ session->key.proto.tls13.ap_rms,
+ session->key.proto.tls13.temp_secret_size);
+
+ session->internals.tls13_ticket.prf = session->security_parameters.prf;
+ session->internals.hsk_flags |= HSK_TICKET_RECEIVED;
break;
default:
gnutls_assert();
return GNUTLS_E_UNEXPECTED_PACKET;
}
+ ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_POST, 1, buf->data, buf->length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
return 0;
}
+/**
+ * gnutls_session_ticket_send:
+ * @session: is a #gnutls_session_t type.
+ * @flags: must be zero
+ *
+ * Sends a fresh session ticket to the peer. This is relevant only
+ * in server side under TLS1.3. This function may also return %GNUTLS_E_AGAIN
+ * or %GNUTLS_E_INTERRUPTED.
+ *
+ * Returns: %GNUTLS_E_SUCCESS on success, or a negative error code.
+ **/
+int gnutls_session_ticket_send(gnutls_session_t session, unsigned flags)
+{
+ int ret = 0;
+ const version_entry_st *vers = get_version(session);
+
+ if (!vers->tls13_sem || session->security_parameters.entity == GNUTLS_CLIENT)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ switch (TICKET_STATE) {
+ case TICKET_STATE0:
+ ret = _gnutls_io_write_flush(session);
+ TICKET_STATE = TICKET_STATE0;
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ /* fall through */
+ case TICKET_STATE1:
+ ret =
+ _gnutls13_send_session_ticket(session, TICKET_STATE==TICKET_STATE1?1:0);
+ TICKET_STATE = TICKET_STATE1;
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+ break;
+ default:
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ TICKET_STATE = TICKET_STATE0;
+
+ return 0;
+}
diff --git a/lib/handshake.c b/lib/handshake.c
index e756574277..30e229f3d0 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -54,6 +54,7 @@
#include <random.h>
#include <dtls.h>
#include "secrets.h"
+#include "tls13/session_ticket.h"
#define TRUE 1
#define FALSE 0
@@ -81,6 +82,7 @@ handshake_hash_buffer_reset(gnutls_session_t session)
session->internals.handshake_hash_buffer_client_kx_len = 0;
session->internals.handshake_hash_buffer_server_finished_len = 0;
+ session->internals.handshake_hash_buffer_client_finished_len = 0;
session->internals.handshake_hash_buffer_prev_len = 0;
session->internals.handshake_hash_buffer.length = 0;
session->internals.full_client_hello.length = 0;
@@ -510,6 +512,28 @@ _gnutls_user_hello_func(gnutls_session_t session,
return sret;
}
+static int set_auth_types(gnutls_session_t session)
+{
+ const version_entry_st *ver = get_version(session);
+ gnutls_kx_algorithm_t kx;
+
+ kx = session->security_parameters.cs->kx_algorithm;
+ if (kx == 0 && ver->tls13_sem) {
+ /* if we are resuming then the KX seen doesn't match the original */
+ if (session->internals.resumed == RESUME_FALSE)
+ kx = gnutls_kx_get(session);
+ }
+
+ if (kx) {
+ session->security_parameters.server_auth_type = _gnutls_map_kx_get_cred(kx, 1);
+ session->security_parameters.client_auth_type = _gnutls_map_kx_get_cred(kx, 0);
+ } else if (session->internals.resumed == RESUME_FALSE) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ return 0;
+}
+
/* Read a client hello packet.
* A client hello must be a known version client hello
* or version 2.0 client hello (only for compatibility
@@ -699,7 +723,7 @@ read_client_hello(gnutls_session_t session, uint8_t * data,
}
/* resumed by session_ticket extension */
- if (session->internals.resumed != RESUME_FALSE) {
+ if (!vers->tls13_sem && session->internals.resumed != RESUME_FALSE) {
/* to indicate the client that the current session is resumed */
memcpy(session->internals.resumed_security_parameters.
session_id, session_id, session_id_len);
@@ -753,6 +777,12 @@ read_client_hello(gnutls_session_t session, uint8_t * data,
return ret;
}
+ ret = set_auth_types(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
return sret;
}
@@ -770,7 +800,7 @@ int _gnutls_send_finished(gnutls_session_t session, int again)
if (again == 0) {
bufel =
- _gnutls_handshake_alloc(session,
+ _gnutls_handshake_alloc(session,
MAX_VERIFY_DATA_SIZE);
if (bufel == NULL) {
gnutls_assert();
@@ -952,6 +982,7 @@ _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
unsigned int i;
ciphersuite_list_st peer_clist;
const gnutls_cipher_suite_entry_st *selected;
+ gnutls_kx_algorithm_t kx;
int retval;
const version_entry_st *vers = get_version(session);
@@ -1015,7 +1046,8 @@ _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
if (!vers->tls13_sem) {
/* check if the credentials (username, public key etc.) are ok
*/
- if (_gnutls_get_kx_cred(session, selected->kx_algorithm) == NULL) {
+ kx = selected->kx_algorithm;
+ if (_gnutls_get_kx_cred(session, kx) == NULL) {
gnutls_assert();
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
@@ -1024,7 +1056,7 @@ _gnutls_server_select_suite(gnutls_session_t session, uint8_t * data,
* according to the KX algorithm. This is needed since all the
* handshake functions are read from there;
*/
- session->internals.auth_struct = _gnutls_kx_auth_struct(selected->kx_algorithm);
+ session->internals.auth_struct = _gnutls_kx_auth_struct(kx);
if (session->internals.auth_struct == NULL) {
_gnutls_handshake_log
("HSK[%p]: Cannot find the appropriate handler for the KX algorithm\n",
@@ -1084,11 +1116,10 @@ _gnutls_send_empty_handshake(gnutls_session_t session,
return _gnutls_send_handshake(session, bufel, type);
}
-inline
- static int call_hook_func(gnutls_session_t session,
- gnutls_handshake_description_t type,
- int post, unsigned incoming,
- const uint8_t *data, unsigned data_size)
+int _gnutls_call_hook_func(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ int post, unsigned incoming,
+ const uint8_t *data, unsigned data_size)
{
gnutls_datum_t msg = {(void*)data, data_size};
@@ -1116,6 +1147,13 @@ inline
return 0;
}
+/* Note that the "New session ticket" handshake packet behaves differently under
+ * TLS 1.2 or 1.3. In 1.2 it is included in the handshake process, while in 1.3
+ * it is sent asynchronously */
+#define IS_ASYNC(t, v) \
+ (t == GNUTLS_HANDSHAKE_HELLO_REQUEST || t == GNUTLS_HANDSHAKE_KEY_UPDATE || \
+ (t == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET && v->tls13_sem))
+
/* This function sends a handshake message of type 'type' containing the
* data specified here. If the previous _gnutls_send_handshake() returned
* GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED, then it must be called again
@@ -1173,7 +1211,7 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
/* Here we keep the handshake messages in order to hash them...
*/
- if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST)
+ if (!IS_ASYNC(type, vers))
if ((ret =
handshake_hash_add_sent(session, type, data,
datasize)) < 0) {
@@ -1182,8 +1220,8 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
return ret;
}
- ret = call_hook_func(session, type, GNUTLS_HOOK_PRE, 0,
- _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel));
+ ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_PRE, 0,
+ _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel));
if (ret < 0) {
gnutls_assert();
_mbuffer_xfree(&bufel);
@@ -1199,42 +1237,60 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
return ret;
}
- ret = call_hook_func(session, type, GNUTLS_HOOK_POST, 0,
- _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel));
+ ret = _gnutls_call_hook_func(session, type, GNUTLS_HOOK_POST, 0,
+ _mbuffer_get_udata_ptr(bufel), _mbuffer_get_udata_size(bufel));
if (ret < 0) {
gnutls_assert();
return ret;
}
- if (vers && vers->tls13_sem &&
- session->internals.initial_negotiation_completed) {
- /* we are under TLS1.3 in a re-authentication phase.
- * we don't attempt to cache any messages */
- goto force_send;
- }
+ /* Decide when to cache and when to send */
+ if (vers && vers->tls13_sem) {
- /* The messages which are followed by another are not sent by default
- * but are cached instead */
- switch (type) {
- case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
- * or ClientKeyExchange always.
- */
- case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
- case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: /* as above */
- case GNUTLS_HANDSHAKE_SERVER_HELLO: /* as above */
- case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* as above */
- case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: /* followed by ChangeCipherSpec */
+ if (session->internals.initial_negotiation_completed) {
+ /* we are under TLS1.3 in a re-authentication phase.
+ * we don't attempt to cache any messages */
+ goto force_send;
+ }
- /* now for client Certificate, ClientKeyExchange and
- * CertificateVerify are always followed by ChangeCipherSpec
- */
- case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
- case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
- ret = 0;
- break;
- default:
- /* send cached messages */
- goto force_send;
+ /* The messages which are followed by another are not sent by default
+ * but are cached instead */
+ switch (type) {
+ case GNUTLS_HANDSHAKE_SERVER_HELLO: /* always followed by something */
+ case GNUTLS_HANDSHAKE_ENCRYPTED_EXTENSIONS: /* followed by finished or cert */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* followed by certificate */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by cert verify */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY: /* followed by finished */
+ ret = 0; /* cache */
+ break;
+ default:
+ /* send this and any cached messages */
+ goto force_send;
+ }
+ } else {
+ /* The messages which are followed by another are not sent by default
+ * but are cached instead */
+ switch (type) {
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT: /* this one is followed by ServerHelloDone
+ * or ClientKeyExchange always.
+ */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS:
+ case GNUTLS_HANDSHAKE_SERVER_KEY_EXCHANGE: /* as above */
+ case GNUTLS_HANDSHAKE_SERVER_HELLO: /* as above */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST: /* as above */
+ case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: /* followed by ChangeCipherSpec */
+
+ /* now for client Certificate, ClientKeyExchange and
+ * CertificateVerify are always followed by ChangeCipherSpec
+ */
+ case GNUTLS_HANDSHAKE_CERTIFICATE_VERIFY:
+ case GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE:
+ ret = 0;
+ break;
+ default:
+ /* send this and any cached messages */
+ goto force_send;
+ }
}
return ret;
@@ -1251,6 +1307,7 @@ _gnutls_send_handshake(gnutls_session_t session, mbuffer_st * bufel,
return gnutls_assert_val(GNUTLS_E_HANDSHAKE_TOO_LARGE); \
}
+
/* This function add the handshake headers and the
* handshake data to the handshake hash buffers. Needed
* for the finished messages calculations.
@@ -1269,7 +1326,7 @@ handshake_hash_add_recvd(gnutls_session_t session,
if ((vers->id != GNUTLS_DTLS0_9 &&
recv_type == GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST) ||
- recv_type == GNUTLS_HANDSHAKE_HELLO_REQUEST)
+ IS_ASYNC(recv_type, vers))
return 0;
CHECK_SIZE(header_size + datalen);
@@ -1303,6 +1360,9 @@ handshake_hash_add_recvd(gnutls_session_t session,
if (recv_type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_CLIENT)
session->internals.handshake_hash_buffer_server_finished_len =
session->internals.handshake_hash_buffer.length;
+ if (recv_type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER)
+ session->internals.handshake_hash_buffer_client_finished_len =
+ session->internals.handshake_hash_buffer.length;
return 0;
}
@@ -1320,41 +1380,40 @@ handshake_hash_add_sent(gnutls_session_t session,
if (unlikely(vers == NULL))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
- /* We don't check for GNUTLS_HANDSHAKE_HELLO_VERIFY_REQUEST because it
- * is not sent via that channel.
- */
- if (type != GNUTLS_HANDSHAKE_HELLO_REQUEST) {
- CHECK_SIZE(datalen);
+ if (IS_ASYNC(type, vers))
+ return 0;
- if (vers->id == GNUTLS_DTLS0_9) {
- /* Old DTLS doesn't include the header in the MAC */
- if (datalen < 12) {
- gnutls_assert();
- return GNUTLS_E_INTERNAL_ERROR;
- }
- dataptr += 12;
- datalen -= 12;
+ CHECK_SIZE(datalen);
- if (datalen == 0)
- return 0;
+ if (vers->id == GNUTLS_DTLS0_9) {
+ /* Old DTLS doesn't include the header in the MAC */
+ if (datalen < 12) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
}
+ dataptr += 12;
+ datalen -= 12;
- ret =
- _gnutls_buffer_append_data(&session->internals.
- handshake_hash_buffer,
- dataptr, datalen);
- if (ret < 0)
- return gnutls_assert_val(ret);
+ if (datalen == 0)
+ return 0;
+ }
- if (type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
- session->internals.handshake_hash_buffer_client_kx_len =
- session->internals.handshake_hash_buffer.length;
- if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER)
- session->internals.handshake_hash_buffer_server_finished_len =
- session->internals.handshake_hash_buffer.length;
+ ret =
+ _gnutls_buffer_append_data(&session->internals.
+ handshake_hash_buffer,
+ dataptr, datalen);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- return 0;
- }
+ if (type == GNUTLS_HANDSHAKE_CLIENT_KEY_EXCHANGE)
+ session->internals.handshake_hash_buffer_client_kx_len =
+ session->internals.handshake_hash_buffer.length;
+ if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_SERVER)
+ session->internals.handshake_hash_buffer_server_finished_len =
+ session->internals.handshake_hash_buffer.length;
+ if (type == GNUTLS_HANDSHAKE_FINISHED && session->security_parameters.entity == GNUTLS_CLIENT)
+ session->internals.handshake_hash_buffer_client_finished_len =
+ session->internals.handshake_hash_buffer.length;
return 0;
}
@@ -1385,7 +1444,7 @@ _gnutls_recv_handshake(gnutls_session_t session,
}
session->internals.last_handshake_in = hsk.htype;
- ret = call_hook_func(session, hsk.htype, GNUTLS_HOOK_PRE, 1, hsk.data.data, hsk.data.length);
+ ret = _gnutls_call_hook_func(session, hsk.htype, GNUTLS_HOOK_PRE, 1, hsk.data.data, hsk.data.length);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -1511,7 +1570,7 @@ _gnutls_recv_handshake(gnutls_session_t session,
goto cleanup;
}
- ret2 = call_hook_func(session, hsk.htype, GNUTLS_HOOK_POST, 1, hsk.data.data, hsk.data.length);
+ ret2 = _gnutls_call_hook_func(session, hsk.htype, GNUTLS_HOOK_POST, 1, hsk.data.data, hsk.data.length);
if (ret2 < 0) {
ret = ret2;
gnutls_assert();
@@ -1538,6 +1597,7 @@ set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
int ret;
const gnutls_cipher_suite_entry_st *selected = NULL;
const version_entry_st *vers = get_version(session);
+ gnutls_kx_algorithm_t kx;
for (j = 0; j < session->internals.priorities->cs.size; j++) {
if (suite[0] == session->internals.priorities->cs.entry[j]->id[0] &&
@@ -1568,20 +1628,21 @@ set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
* Actually checks if they exist.
*/
if (!vers->tls13_sem) {
+ kx = selected->kx_algorithm;
+
if (!session->internals.premaster_set &&
_gnutls_get_kx_cred
- (session, selected->kx_algorithm) == NULL) {
+ (session, kx) == NULL) {
gnutls_assert();
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
-
/* set the mod_auth_st to the appropriate struct
* according to the KX algorithm. This is needed since all the
* handshake functions are read from there;
*/
session->internals.auth_struct =
- _gnutls_kx_auth_struct(selected->kx_algorithm);
+ _gnutls_kx_auth_struct(kx);
if (session->internals.auth_struct == NULL) {
_gnutls_handshake_log
@@ -1592,11 +1653,12 @@ set_client_ciphersuite(gnutls_session_t session, uint8_t suite[2])
}
} else {
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- if (session->key.proto.tls13.binder_prf->id != selected->prf) {
+ if (session->key.binders[0].prf->id != selected->prf) {
_gnutls_handshake_log
("HSK[%p]: PRF of ciphersuite differs with the PSK identity (cs: %s, id: %s)\n",
- session, selected->name, session->key.proto.tls13.binder_prf->name);
+ session, selected->name, session->key.binders[0].prf->name);
gnutls_assert();
+ return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
}
}
}
@@ -1843,8 +1905,11 @@ read_server_hello(gnutls_session_t session,
/* Calculate TLS 1.3 Early Secret */
if (vers->tls13_sem) {
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- psk = session->key.psk.data;
- psk_size = session->key.psk.size;
+ psk = session->key.binders[0].psk.data;
+ psk_size = session->key.binders[0].psk.size;
+
+ if (psk_size == 0)
+ return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
}
ret = _tls13_init_secret(session, psk, psk_size);
@@ -1856,8 +1921,16 @@ read_server_hello(gnutls_session_t session,
ret = _tls13_derive_secret(session, DERIVED_LABEL, sizeof(DERIVED_LABEL)-1,
NULL, 0, session->key.proto.tls13.temp_secret,
session->key.proto.tls13.temp_secret);
- if (ret < 0)
+ if (ret < 0) {
gnutls_assert();
+ goto cleanup;
+ }
+ }
+
+ ret = set_auth_types(session);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
}
cleanup:
@@ -1925,6 +1998,9 @@ static int send_client_hello(gnutls_session_t session, int again)
hver =
session->internals.resumed_security_parameters.
pversion;
+
+ if (hver && hver->tls13_sem)
+ hver = _gnutls_legacy_version_max(session);
}
if (hver == NULL) {
@@ -2113,6 +2189,7 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
unsigned extflag = 0;
const uint8_t *psk = NULL;
size_t psk_size = 0;
+ gnutls_ext_parse_type_t etype;
_gnutls_buffer_init(&buf);
@@ -2124,8 +2201,8 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
if (vers->tls13_sem) {
/* TLS 1.3 Early Secret */
if (session->internals.hsk_flags & HSK_PSK_SELECTED) {
- psk = session->key.psk.data;
- psk_size = session->key.psk.size;
+ psk = session->key.binders[0].psk.data;
+ psk_size = session->key.binders[0].psk.size;
}
ret = _tls13_init_secret(session, psk, psk_size);
@@ -2192,13 +2269,12 @@ int _gnutls_send_server_hello(gnutls_session_t session, int again)
goto fail;
}
+ if (!vers->tls13_sem && session->internals.resumed != RESUME_FALSE)
+ etype = GNUTLS_EXT_MANDATORY;
+ else
+ etype = GNUTLS_EXT_ANY;
ret =
- _gnutls_gen_hello_extensions(session, &buf,
- extflag,
- (session->internals.resumed ==
- RESUME_TRUE) ?
- GNUTLS_EXT_MANDATORY :
- GNUTLS_EXT_ANY);
+ _gnutls_gen_hello_extensions(session, &buf, extflag, etype);
if (ret < 0) {
gnutls_assert();
goto fail;
@@ -2494,11 +2570,10 @@ static int _gnutls_recv_supplemental(gnutls_session_t session)
**/
int gnutls_handshake(gnutls_session_t session)
{
+ const version_entry_st *vers = get_version(session);
int ret;
if (unlikely(session->internals.initial_negotiation_completed)) {
- const version_entry_st *vers = get_version(session);
-
if (vers->tls13_sem) {
if (session->security_parameters.entity == GNUTLS_CLIENT) {
return gnutls_session_key_update(session, GNUTLS_KU_PEER);
@@ -2546,6 +2621,7 @@ int gnutls_handshake(gnutls_session_t session)
} else {
ret = handshake_server(session);
}
+
if (ret < 0) {
/* In the case of a rehandshake abort
* we should reset the handshake's internal state.
@@ -2848,12 +2924,10 @@ static int handshake_client(gnutls_session_t session)
if (session->internals.resumed == RESUME_FALSE) {
ret = send_handshake_final(session, TRUE);
IMED_RET("send handshake final 2", ret, 1);
-#ifdef ENABLE_SESSION_TICKETS
} else {
ret = _gnutls_recv_new_session_ticket(session);
IMED_RET("recv handshake new session ticket", ret,
1);
-#endif
}
/* fall through */
case STATE17:
@@ -2874,11 +2948,9 @@ static int handshake_client(gnutls_session_t session)
STATE = STATE18;
if (session->internals.resumed == RESUME_FALSE) {
-#ifdef ENABLE_SESSION_TICKETS
ret = _gnutls_recv_new_session_ticket(session);
IMED_RET("recv handshake new session ticket", ret,
1);
-#endif
} else {
ret = recv_handshake_final(session, TRUE);
IMED_RET("recv handshake final", ret, 1);
@@ -2942,8 +3014,8 @@ ssize_t _gnutls_send_change_cipher_spec(gnutls_session_t session, int again)
session->internals.dtls.hsk_write_seq++;
}
- ret = call_hook_func(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, GNUTLS_HOOK_PRE, 0,
- data, 1);
+ ret = _gnutls_call_hook_func(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, GNUTLS_HOOK_PRE, 0,
+ data, 1);
if (ret < 0) {
_mbuffer_xfree(&bufel);
return gnutls_assert_val(ret);
@@ -2958,8 +3030,8 @@ ssize_t _gnutls_send_change_cipher_spec(gnutls_session_t session, int again)
return gnutls_assert_val(ret);
}
- ret = call_hook_func(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, GNUTLS_HOOK_POST, 0,
- data, 1);
+ ret = _gnutls_call_hook_func(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC, GNUTLS_HOOK_POST, 0,
+ data, 1);
if (ret < 0) {
return gnutls_assert_val(ret);
}
@@ -3279,13 +3351,11 @@ static int handshake_server(gnutls_session_t session)
}
/* fall through */
case STATE16:
-#ifdef ENABLE_SESSION_TICKETS
ret =
_gnutls_send_new_session_ticket(session,
AGAIN(STATE16));
STATE = STATE16;
IMED_RET("send handshake new session ticket", ret, 0);
-#endif
/* fall through */
case STATE17:
STATE = STATE17;
@@ -3295,7 +3365,7 @@ static int handshake_server(gnutls_session_t session)
if (session->security_parameters.entity ==
GNUTLS_SERVER
- && session->internals.ticket_sent == 0) {
+ && !(session->internals.hsk_flags & HSK_TLS12_TICKET_SENT)) {
/* if no ticket, save session data */
_gnutls_server_register_current_session
(session);
diff --git a/lib/handshake.h b/lib/handshake.h
index 2175d6f2db..465ee03b80 100644
--- a/lib/handshake.h
+++ b/lib/handshake.h
@@ -138,9 +138,15 @@ int _gnutls_check_if_cert_hash_is_same(gnutls_session_t session, gnutls_certific
#define APPLICATION_SERVER_TRAFFIC_LABEL "s ap traffic"
#define APPLICATION_TRAFFIC_UPDATE "traffic upd"
#define EXPORTER_MASTER_LABEL "exp master"
+#define RMS_MASTER_LABEL "res master"
#define EXPORTER_LABEL "exp master"
#define RES_LABEL "res master"
+int _gnutls_call_hook_func(gnutls_session_t session,
+ gnutls_handshake_description_t type,
+ int post, unsigned incoming,
+ const uint8_t *data, unsigned data_size);
+
int _gnutls_run_verify_callback(gnutls_session_t session, unsigned int side);
int _gnutls_recv_finished(gnutls_session_t session);
int _gnutls_send_finished(gnutls_session_t session, int again);
diff --git a/lib/hello_ext.c b/lib/hello_ext.c
index d61f846f51..ad3cf54d3f 100644
--- a/lib/hello_ext.c
+++ b/lib/hello_ext.c
@@ -75,9 +75,7 @@ static hello_ext_entry_st const *extfunc[MAX_EXT_TYPES+1] = {
#ifdef ENABLE_HEARTBEAT
[GNUTLS_EXTENSION_HEARTBEAT] = &ext_mod_heartbeat,
#endif
-#ifdef ENABLE_SESSION_TICKETS
[GNUTLS_EXTENSION_SESSION_TICKET] = &ext_mod_session_ticket,
-#endif
[GNUTLS_EXTENSION_SUPPORTED_ECC] = &ext_mod_supported_ecc,
[GNUTLS_EXTENSION_SUPPORTED_ECC_PF] = &ext_mod_supported_ecc_pf,
[GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS] = &ext_mod_sig,
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index dac1c505dd..be350ecb15 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -1360,6 +1360,7 @@ unsigned gnutls_session_etm_status(gnutls_session_t session);
* @GNUTLS_SFLAGS_HB_LOCAL_SEND: The heartbeat negotiation allows the local side to send heartbeat messages
* @GNUTLS_SFLAGS_HB_PEER_SEND: The heartbeat negotiation allows the peer to send heartbeat messages
* @GNUTLS_SFLAGS_FALSE_START: The appdata set with gnutls_handshake_set_appdata() were sent during handshake (false start)
+ * @GNUTLS_SFLAGS_SESSION_TICKET: A session ticket has been received by the server.
*
* Enumeration of different session parameters.
*/
@@ -1370,7 +1371,8 @@ typedef enum {
GNUTLS_SFLAGS_HB_LOCAL_SEND = 1<<3,
GNUTLS_SFLAGS_HB_PEER_SEND = 1<<4,
GNUTLS_SFLAGS_FALSE_START = 1<<5,
- GNUTLS_SFLAGS_RFC7919 = 1<<6
+ GNUTLS_SFLAGS_RFC7919 = 1<<6,
+ GNUTLS_SFLAGS_SESSION_TICKET = 1<<7
} gnutls_session_flags_t;
unsigned gnutls_session_get_flags(gnutls_session_t session);
@@ -1394,6 +1396,8 @@ int gnutls_session_ticket_enable_client(gnutls_session_t session);
int gnutls_session_ticket_enable_server(gnutls_session_t session,
const gnutls_datum_t * key);
+int gnutls_session_ticket_send(gnutls_session_t session, unsigned flags);
+
/* SRTP, RFC 5764 */
/**
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index bcde6c177b..cfbd58c40e 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1219,6 +1219,7 @@ GNUTLS_3_6_3
gnutls_pcert_list_import_x509_file;
gnutls_pkcs11_token_get_ptr;
gnutls_pkcs11_obj_get_ptr;
+ gnutls_session_ticket_send;
} GNUTLS_3_6_2;
GNUTLS_FIPS140_3_4 {
diff --git a/lib/priority.c b/lib/priority.c
index 0d2498d998..0dc39f362c 100644
--- a/lib/priority.c
+++ b/lib/priority.c
@@ -595,7 +595,7 @@ gnutls_priority_set(gnutls_session_t session, gnutls_priority_t priority)
if (priority->no_tickets != 0) {
/* when PFS is explicitly requested, disable session tickets */
- _gnutls_hello_ext_unset_priv(session, GNUTLS_EXTENSION_SESSION_TICKET);
+ session->internals.flags |= GNUTLS_NO_TICKETS;
}
if (session->internals.priorities->protocol.algorithms == 0 ||
diff --git a/lib/session.c b/lib/session.c
index 3e29c15292..84a529c7a1 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -25,6 +25,7 @@
#include "debug.h"
#include <session_pack.h>
#include <datum.h>
+#include "buffers.h"
#include "state.h"
/**
@@ -33,13 +34,10 @@
* @session_data: is a pointer to space to hold the session.
* @session_data_size: is the session_data's size, or it will be set by the function.
*
- * Returns all session parameters needed to be stored to support resumption.
- * The client should call this, and store the returned session data. A session
- * may be resumed later by calling gnutls_session_set_data().
+ * Returns all session parameters needed to be stored to support resumption,
+ * in a pre-allocated buffer.
*
- * This function will fail if called prior to handshake completion. In
- * case of false start TLS, the handshake completes only after data have
- * been successfully received from the peer.
+ * See gnutls_session_get_data2() for more information.
*
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
* an error code is returned.
@@ -75,14 +73,20 @@ gnutls_session_get_data(gnutls_session_t session,
return ret;
}
+#define EMPTY_DATA "\x00\x00\x00\x00"
+#define EMPTY_DATA_SIZE 4
+
/**
* gnutls_session_get_data2:
* @session: is a #gnutls_session_t type.
* @data: is a pointer to a datum that will hold the session.
*
- * Returns all session parameters needed to be stored to support resumption.
- * The client should call this, and store the returned session data. A session
- * may be resumed later by calling gnutls_session_set_data().
+ * Returns necessary parameters to support resumption. The client
+ * should call this function and store the returned session data. A session
+ * can be resumed later by calling gnutls_session_set_data() with the returned
+ * data. Note that under TLS 1.3, it is recommended for clients to use
+ * session parameters only once, to prevent passive-observers from correlating
+ * the different connections.
*
* The returned @data are allocated and must be released using gnutls_free().
*
@@ -90,25 +94,54 @@ gnutls_session_get_data(gnutls_session_t session,
* case of false start TLS, the handshake completes only after data have
* been successfully received from the peer.
*
+ * Under TLS1.3 session resumption is possible only after a session ticket
+ * is received by the client. To ensure that such a ticket has been received use
+ * gnutls_session_get_flags() and check for flag %GNUTLS_SFLAGS_SESSION_TICKET;
+ * if this flag is not set, this function will wait for a new ticket within
+ * 50ms, and if not received will return dummy data which cannot lead to
+ * resumption. To get notified when new tickets are received by the server
+ * use gnutls_handshake_set_hook_function() to wait for %GNUTLS_HANDSHAKE_NEW_SESSION_TICKET
+ * messages. Each call of gnutls_session_get_data2() after a ticket is
+ * received, will return session resumption data corresponding to the last
+ * received ticket.
+ *
* Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
* an error code is returned.
**/
int
gnutls_session_get_data2(gnutls_session_t session, gnutls_datum_t *data)
{
-
+ const version_entry_st *vers = get_version(session);
int ret;
- if (data == NULL) {
+ if (data == NULL || vers == NULL) {
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
}
- if (gnutls_session_is_resumed(session) && session->internals.resumption_data.data) {
- ret = _gnutls_set_datum(data, session->internals.resumption_data.data, session->internals.resumption_data.size);
- if (ret < 0)
+ if (vers->tls13_sem && !(session->internals.hsk_flags & HSK_TICKET_RECEIVED)) {
+ /* wait for a message with timeout of 1ms */
+ ret = _gnutls_recv_in_buffers(session, GNUTLS_APPLICATION_DATA, -1, 100);
+ if (ret < 0 && (gnutls_error_is_fatal(ret) && ret != GNUTLS_E_TIMEDOUT)) {
return gnutls_assert_val(ret);
+ }
- return 0;
+ if (!(session->internals.hsk_flags & HSK_TICKET_RECEIVED)) {
+ ret = _gnutls_set_datum(data, EMPTY_DATA, EMPTY_DATA_SIZE);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+ }
+ } else if (!vers->tls13_sem) {
+ /* under TLS1.3 we want to pack the latest ticket, while that's
+ * not the case in TLS1.2 or earlier. */
+ if (gnutls_session_is_resumed(session) && session->internals.resumption_data.data) {
+ ret = _gnutls_set_datum(data, session->internals.resumption_data.data, session->internals.resumption_data.size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return 0;
+ }
}
if (session->internals.resumable == RESUME_FALSE)
@@ -221,6 +254,14 @@ gnutls_session_set_data(gnutls_session_t session,
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
+
+ /* under TLS1.3 we always return some data on resumption when there
+ * is no ticket in order to keep compatibility with existing apps */
+ if (session_data_size == EMPTY_DATA_SIZE &&
+ memcmp(session_data, EMPTY_DATA, EMPTY_DATA_SIZE) == 0) {
+ return 0;
+ }
+
ret = _gnutls_session_unpack(session, &psession);
if (ret < 0) {
gnutls_assert();
diff --git a/lib/session_pack.c b/lib/session_pack.c
index 977110595b..615eb6c2a5 100644
--- a/lib/session_pack.c
+++ b/lib/session_pack.c
@@ -44,6 +44,7 @@
#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);
@@ -69,6 +70,10 @@ 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
@@ -150,10 +155,22 @@ _gnutls_session_pack(gnutls_session_t session,
goto fail;
}
- ret = _gnutls_hello_ext_pack(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);
@@ -256,10 +273,21 @@ _gnutls_session_unpack(gnutls_session_t session,
goto error;
}
- ret = _gnutls_hello_ext_unpack(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;
@@ -270,7 +298,107 @@ _gnutls_session_unpack(gnutls_session_t session,
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
+ *
+ * WE DON'T STORE NewSessionTicket EXTENSIONS, as we don't support them yet.
+ *
+ * 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_t *ticket = &session->internals.tls13_ticket;
+
+ length_pos = ps->length;
+ BUFFER_APPEND_NUM(ps, 0);
+ if (ticket->ticket.data != NULL) {
+ BUFFER_APPEND_NUM(ps, ticket->timestamp);
+ length += 4;
+ 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);
+
+ /* 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_t *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->timestamp);
+ 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;
+ }
+
+error:
+ return ret;
+}
/* Format:
* 1 byte the credentials type
@@ -723,37 +851,6 @@ unpack_psk_auth_info(gnutls_session_t session, gnutls_buffer_st * ps)
/* Packs the security parameters.
*/
-
-/* Format:
- * 4 bytes the total security data size
- * 1 byte the entity type (client/server)
- * 1 byte the key exchange algorithm used
- * 1 byte the read cipher algorithm
- * 1 byte the read mac algorithm
- *
- * 1 byte the write cipher algorithm
- * 1 byte the write mac algorithm
- *
- * 1 byte the certificate type
- * 1 byte the protocol version
- *
- * 2 bytes the cipher suite
- * 4 bytes the PRF ID
- *
- * 48 bytes the master secret
- *
- * 32 bytes the client random
- * 32 bytes the server random
- *
- * 1 byte the session ID size
- * x bytes the session ID (32 bytes max)
- *
- * 4 bytes the new record padding flag
- * 4 bytes the ECC curve
- * -------------------
- * MAX: 169 bytes
- *
- */
static int
pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
{
@@ -780,49 +877,58 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
BUFFER_APPEND_NUM(ps, 0);
cur_size = ps->length;
-
BUFFER_APPEND_NUM(ps, session->security_parameters.entity);
- BUFFER_APPEND(ps, session->security_parameters.cs->id, 2);
BUFFER_APPEND_NUM(ps, session->security_parameters.prf->id);
- BUFFER_APPEND_NUM(ps, session->security_parameters.cert_type);
- BUFFER_APPEND_NUM(ps, session->security_parameters.pversion->id);
-
- BUFFER_APPEND(ps, session->security_parameters.master_secret,
- GNUTLS_MASTER_SIZE);
- BUFFER_APPEND(ps, session->security_parameters.client_random,
- GNUTLS_RANDOM_SIZE);
- BUFFER_APPEND(ps, session->security_parameters.server_random,
- GNUTLS_RANDOM_SIZE);
-
- 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.
- max_record_send_size);
+ session->security_parameters.client_auth_type);
BUFFER_APPEND_NUM(ps,
- session->security_parameters.
- max_record_recv_size);
+ session->security_parameters.server_auth_type);
- 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.pversion->id);
- BUFFER_APPEND_NUM(ps, session->security_parameters.post_handshake_auth);
+ /* 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(ps, session->security_parameters.cs->id, 2);
+
+ BUFFER_APPEND_NUM(ps, session->security_parameters.cert_type);
+
+ 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);
+
+ 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.
+ max_record_send_size);
+ BUFFER_APPEND_NUM(ps,
+ session->security_parameters.
+ max_record_recv_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);
+ }
- 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);
@@ -836,6 +942,7 @@ 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];
@@ -856,10 +963,6 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
BUFFER_POP_NUM(ps,
session->internals.resumed_security_parameters.
entity);
- 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);
BUFFER_POP_NUM(ps, version);
session->internals.resumed_security_parameters.prf = mac_to_entry(version);
@@ -868,7 +971,11 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
BUFFER_POP_NUM(ps,
session->internals.resumed_security_parameters.
- cert_type);
+ client_auth_type);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ server_auth_type);
+
BUFFER_POP_NUM(ps, version);
session->internals.resumed_security_parameters.pversion =
version_to_entry(version);
@@ -876,57 +983,86 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
NULL)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
- BUFFER_POP(ps,
- session->internals.resumed_security_parameters.
- master_secret, GNUTLS_MASTER_SIZE);
-
- BUFFER_POP(ps,
- session->internals.resumed_security_parameters.
- client_random, GNUTLS_RANDOM_SIZE);
- BUFFER_POP(ps,
- session->internals.resumed_security_parameters.
- server_random, GNUTLS_RANDOM_SIZE);
- 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);
+ if (!session->internals.resumed_security_parameters.pversion->tls13_sem) {
+ 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);
- 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,
+ session->internals.resumed_security_parameters.
+ cert_type);
- 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->security_parameters.post_handshake_auth);
+ /* 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);
- 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);
+ /* 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);
- 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);
+ /* 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(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,
+ 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);
+ }
}
if (timestamp -
diff --git a/lib/state.c b/lib/state.c
index a669cf3d6c..284214b87f 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2002-2016 Free Software Foundation, Inc.
* Copyright (C) 2014-2016 Nikos Mavrogiannopoulos
- * Copyright (C) 2015-2016 Red Hat, Inc.
+ * Copyright (C) 2015-2018 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
@@ -51,6 +51,7 @@
#include <intprops.h>
#include <gnutls/dtls.h>
#include "dtls.h"
+#include "tls13/session_ticket.h"
/* These should really be static, but src/tests.c calls them. Make
them public functions? */
@@ -178,6 +179,13 @@ gnutls_compression_get(gnutls_session_t session)
return GNUTLS_COMP_NULL;
}
+void reset_binders(gnutls_session_t session)
+{
+ _gnutls_free_temp_key_datum(&session->key.binders[0].psk);
+ _gnutls_free_temp_key_datum(&session->key.binders[1].psk);
+ memset(session->key.binders, 0, sizeof(session->key.binders));
+}
+
static void deinit_keys(gnutls_session_t session)
{
const version_entry_st *vers = get_version(session);
@@ -218,8 +226,7 @@ static void deinit_keys(gnutls_session_t session)
sizeof(session->key.proto.tls13.hs_skey));
}
- if (session->key.psk_needs_free)
- _gnutls_free_temp_key_datum(&session->key.psk);
+ reset_binders(session);
_gnutls_free_temp_key_datum(&session->key.key);
}
@@ -328,7 +335,7 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
_mbuffer_head_init(&(*session)->internals.handshake_send_buffer);
_gnutls_handshake_recv_buffer_init(*session);
- (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME; /* one hour default */
+ (*session)->internals.expire_time = DEFAULT_EXPIRE_TIME;
gnutls_handshake_set_max_packet_length((*session),
MAX_HANDSHAKE_PACKET_SIZE);
@@ -382,16 +389,16 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
/* Enable useful extensions */
if ((flags & GNUTLS_CLIENT) && !(flags & GNUTLS_NO_EXTENSIONS)) {
-#ifdef ENABLE_SESSION_TICKETS
- if (!(flags & GNUTLS_NO_TICKETS))
- gnutls_session_ticket_enable_client(*session);
-#endif
#ifdef ENABLE_OCSP
gnutls_ocsp_status_request_enable_client(*session, NULL, 0,
NULL);
#endif
}
+ /* session tickets in server side are enabled by setting a key */
+ if (flags & GNUTLS_SERVER)
+ flags |= GNUTLS_NO_TICKETS;
+
(*session)->internals.flags = flags;
return 0;
@@ -457,6 +464,9 @@ void gnutls_deinit(gnutls_session_t session)
gnutls_credentials_clear(session);
_gnutls_selected_certs_deinit(session);
+ /* destroy any session ticket we may have received */
+ _gnutls13_session_ticket_unset(session);
+
/* we rely on priorities' internal reference counting */
gnutls_priority_deinit(session->internals.priorities);
@@ -716,7 +726,8 @@ gnutls_handshake_set_private_extensions(gnutls_session_t session,
* gnutls_session_is_resumed:
* @session: is a #gnutls_session_t type.
*
- * Check whether session is resumed or not.
+ * Checks whether session is resumed or not. This is functional
+ * for both server and client side.
*
* Returns: non zero if this session is resumed, or a zero if this is
* a new session.
@@ -724,6 +735,11 @@ gnutls_handshake_set_private_extensions(gnutls_session_t session,
int gnutls_session_is_resumed(gnutls_session_t session)
{
if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ const version_entry_st *ver = get_version(session);
+ if (ver && ver->tls13_sem &&
+ session->internals.resumed != RESUME_FALSE)
+ return 1;
+
if (session->security_parameters.session_id_size > 0 &&
session->security_parameters.session_id_size ==
session->internals.resumed_security_parameters.
@@ -1334,6 +1350,8 @@ unsigned gnutls_session_get_flags(gnutls_session_t session)
flags |= GNUTLS_SFLAGS_FALSE_START;
if (session->internals.hsk_flags & HSK_USED_FFDHE)
flags |= GNUTLS_SFLAGS_RFC7919;
+ if (session->internals.hsk_flags & HSK_TICKET_RECEIVED)
+ flags |= GNUTLS_SFLAGS_SESSION_TICKET;
return flags;
}
diff --git a/lib/state.h b/lib/state.h
index 266af94e5c..75d0f35fc1 100644
--- a/lib/state.h
+++ b/lib/state.h
@@ -97,6 +97,8 @@ int _gnutls_session_is_psk(gnutls_session_t session);
int _gnutls_openpgp_send_fingerprint(gnutls_session_t session);
+void reset_binders(gnutls_session_t session);
+
inline static int
_gnutls_PRF(gnutls_session_t session,
const uint8_t * secret, unsigned int secret_size,
diff --git a/lib/tls13/certificate.c b/lib/tls13/certificate.c
index 52c485aaa4..90bd366854 100644
--- a/lib/tls13/certificate.c
+++ b/lib/tls13/certificate.c
@@ -203,6 +203,9 @@ int _gnutls13_send_certificate(gnutls_session_t session, unsigned again)
if (again == 0) {
if (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);
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
index 0a3fe7e9de..f1dbabab05 100644
--- a/lib/tls13/certificate_verify.c
+++ b/lib/tls13/certificate_verify.c
@@ -156,6 +156,9 @@ int _gnutls13_send_certificate_verify(gnutls_session_t session, unsigned again)
if (again == 0) {
if (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;
diff --git a/lib/tls13/finished.c b/lib/tls13/finished.c
index bb535fff87..cb768b9739 100644
--- a/lib/tls13/finished.c
+++ b/lib/tls13/finished.c
@@ -30,7 +30,6 @@
int _gnutls13_compute_finished(const mac_entry_st *prf,
const uint8_t *base_key,
- unsigned hash_size,
gnutls_buffer_st *handshake_hash_buffer,
void *out)
{
@@ -42,7 +41,7 @@ int _gnutls13_compute_finished(const mac_entry_st *prf,
"finished", 8,
NULL, 0,
base_key,
- hash_size, fkey);
+ prf->output_size, fkey);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -54,8 +53,8 @@ int _gnutls13_compute_finished(const mac_entry_st *prf,
return gnutls_assert_val(ret);
ret = gnutls_hmac_fast(prf->id,
- fkey, hash_size,
- ts_hash, hash_size,
+ fkey, prf->output_size,
+ ts_hash, prf->output_size,
out);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -82,7 +81,7 @@ int _gnutls13_recv_finished(gnutls_session_t session)
base_key = session->key.proto.tls13.hs_ckey;
ret = _gnutls13_compute_finished(session->security_parameters.prf,
- base_key, hash_size,
+ base_key,
&session->internals.handshake_hash_buffer,
verifier);
if (ret < 0) {
@@ -140,7 +139,7 @@ int _gnutls13_send_finished(gnutls_session_t session, unsigned again)
base_key = session->key.proto.tls13.hs_skey;
ret = _gnutls13_compute_finished(session->security_parameters.prf,
- base_key, hash_size,
+ base_key,
&session->internals.handshake_hash_buffer,
verifier);
if (ret < 0) {
diff --git a/lib/tls13/finished.h b/lib/tls13/finished.h
index 2e732e7493..7b676f1253 100644
--- a/lib/tls13/finished.h
+++ b/lib/tls13/finished.h
@@ -22,7 +22,6 @@
int _gnutls13_compute_finished(const mac_entry_st *prf,
const uint8_t *base_key,
- unsigned hash_size,
gnutls_buffer_st *handshake_hash_buffer,
void *out);
int _gnutls13_recv_finished(gnutls_session_t session);
diff --git a/lib/tls13/hello_retry.c b/lib/tls13/hello_retry.c
index 51f545ec00..7f2bd1e529 100644
--- a/lib/tls13/hello_retry.c
+++ b/lib/tls13/hello_retry.c
@@ -27,6 +27,7 @@
#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)
{
@@ -89,6 +90,8 @@ int _gnutls13_send_hello_retry_request(gnutls_session_t session, unsigned again)
/* 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);
}
diff --git a/lib/tls13/session_ticket.c b/lib/tls13/session_ticket.c
index d5d62f433f..25e067fc00 100644
--- a/lib/tls13/session_ticket.c
+++ b/lib/tls13/session_ticket.c
@@ -1,7 +1,7 @@
/*
- * Copyright (C) 2017 Red Hat, Inc.
+ * Copyright (C) 2017-2018 Red Hat, Inc.
*
- * Author: Nikos Mavrogiannopoulos
+ * Author: Nikos Mavrogiannopoulos, Ander Juaristi
*
* This file is part of GnuTLS.
*
@@ -24,60 +24,363 @@
#include "errors.h"
#include "extv.h"
#include "handshake.h"
-#include "tls13/session_ticket.h"
+#include "mbuffers.h"
+#include "ext/pre_shared_key.h"
+#include "ext/session_ticket.h"
#include "auth/cert.h"
+#include "tls13/session_ticket.h"
+#include "session_pack.h"
-static int parse_nst_extension(void *ctx, unsigned tls_id, const uint8_t *data, unsigned data_size);
-
-int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf)
+static int
+pack_ticket(gnutls_session_t session, tls13_ticket_t *ticket, gnutls_datum_t *packed)
{
+ uint8_t *p;
+ gnutls_datum_t state;
int ret;
- size_t val;
- gnutls_datum_t nonce;
- gnutls_datum_t ticket;
- _gnutls_handshake_log("HSK[%p]: parsing session ticket message\n", session);
+ ret = _gnutls_session_pack(session, &state);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- /* ticket_lifetime */
- ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ packed->size = 2 + 4 + 4 +
+ 1 + ticket->prf->output_size +
+ 1 + ticket->nonce_size + 2 + state.size;
- /* ticket_age_add */
- ret = _gnutls_buffer_pop_prefix32(buf, &val, 0);
- if (ret < 0) {
+ packed->data = gnutls_malloc(packed->size);
+ if (!packed->data) {
gnutls_assert();
+ ret = GNUTLS_E_MEMORY_ERROR;
goto cleanup;
}
- ret = _gnutls_buffer_pop_datum_prefix8(buf, &nonce);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ p = packed->data;
- ret = _gnutls_buffer_pop_datum_prefix16(buf, &ticket);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ _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;
- ret = _gnutls_extv_parse(NULL, parse_nst_extension, buf->data, buf->length);
- if (ret < 0) {
- gnutls_assert();
- goto cleanup;
- }
+ 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);
ret = 0;
+
+ cleanup:
+ gnutls_free(state.data);
+ return ret;
+}
+
+static int
+unpack_ticket(gnutls_session_t session, gnutls_datum_t *packed, tls13_ticket_t *data)
+{
+ uint32_t age_add, lifetime;
+ 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;
+ ssize_t len;
+ 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_INTERNAL_ERROR);
+
+ /* 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_INTERNAL_ERROR);
+
+ 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;
+
+ 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;
+
+ return 0;
+}
+
+static int
+generate_session_ticket(gnutls_session_t session, tls13_ticket_t *ticket)
+{
+ int ret;
+ gnutls_datum_t packed = { NULL, 0 };
+ tls13_ticket_t ticket_data;
+
+ /* Generate a random 128-bit ticket nonce */
+ ticket->nonce_size = 16;
+
+ 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);
+
+ /* Set ticket lifetime to 1 day (86400 seconds) */
+ ticket->lifetime = session->internals.expire_time;
+
+ 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.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;
+}
+
+int _gnutls13_send_session_ticket(gnutls_session_t session, unsigned again)
+{
+ int ret = 0;
+ mbuffer_st *bufel = NULL;
+ gnutls_buffer_st buf;
+ tls13_ticket_t ticket;
+
+ /* 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 (again == 0) {
+ memset(&ticket, 0, sizeof(tls13_ticket_t));
+
+ ret = _gnutls_buffer_init_handshake_mbuffer(&buf);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = generate_session_ticket(session, &ticket);
+ if (ret < 0) {
+ 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;
+ }
+
+ ret = _gnutls_buffer_append_prefix(&buf, 16, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ _gnutls_free_datum(&ticket.ticket);
+
+ bufel = _gnutls_buffer_to_mbuffer(&buf);
+ }
+
+ ret = _gnutls_send_handshake(session, bufel,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+ if (ret > 0)
+ session->internals.hsk_flags |= HSK_TLS13_TICKET_SENT;
+
+ return ret;
+
cleanup:
+ _gnutls_free_datum(&ticket.ticket);
+ _mbuffer_xfree(&bufel);
return ret;
}
-static int parse_nst_extension(void *ctx, unsigned tls_id, const uint8_t *data, unsigned data_size)
+static int parse_nst_extension(void *ctx, unsigned tls_id, const unsigned char *data, unsigned data_size)
{
/* ignore all extensions */
return 0;
}
+
+int _gnutls13_recv_session_ticket(gnutls_session_t session, gnutls_buffer_st *buf)
+{
+ int ret;
+ uint8_t value;
+ tls13_ticket_t *ticket = &session->internals.tls13_ticket;
+ gnutls_datum_t t;
+
+ if (unlikely(buf == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ _gnutls_free_datum(&ticket->ticket);
+ memset(ticket, 0, sizeof(tls13_ticket_t));
+
+ _gnutls_handshake_log("HSK[%p]: parsing session ticket message\n", session);
+
+ /* ticket_lifetime */
+ ret = _gnutls_buffer_pop_prefix32(buf, (size_t *) &ticket->lifetime, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* ticket_age_add */
+ ret = _gnutls_buffer_pop_prefix32(buf, (size_t *) &ticket->age_add, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* 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(NULL, parse_nst_extension, buf->data, buf->length);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* Set the ticket timestamp */
+ ticket->timestamp = gnutls_time(0);
+
+ 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_t *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);
+
+ /* 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;
+ }
+
+ return 0;
+}
diff --git a/lib/tls13/session_ticket.h b/lib/tls13/session_ticket.h
index 1c31589a26..073c28f1f2 100644
--- a/lib/tls13/session_ticket.h
+++ b/lib/tls13/session_ticket.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2017 Red Hat, Inc.
*
- * Author: Nikos Mavrogiannopoulos
+ * Author: Nikos Mavrogiannopoulos, Ander Juaristi
*
* This file is part of GnuTLS.
*
@@ -19,5 +19,33 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
+#ifndef SESSION_TICKET_H
+#define 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 again);
+
+int _gnutls13_unpack_session_ticket(gnutls_session_t session,
+ gnutls_datum_t *data,
+ tls13_ticket_t *ticket_data);
+
+inline static
+void tls13_ticket_deinit(tls13_ticket_t *ticket)
+{
+ if (ticket) {
+ zeroize_temp_key(&ticket->resumption_master_secret,
+ sizeof(ticket->resumption_master_secret));
+
+ _gnutls_free_datum(&ticket->ticket);
+ memset(ticket, 0, sizeof(tls13_ticket_t));
+ }
+}
+
+inline static
+void _gnutls13_session_ticket_unset(gnutls_session_t session)
+{
+ if (session->internals.tls13_ticket.ticket.data != NULL)
+ tls13_ticket_deinit(&session->internals.tls13_ticket);
+}
+
+#endif
diff --git a/m4/hooks.m4 b/m4/hooks.m4
index aef186d932..f407753b74 100644
--- a/m4/hooks.m4
+++ b/m4/hooks.m4
@@ -329,22 +329,6 @@ LIBTASN1_MINIMUM=4.9
fi
AM_CONDITIONAL(ENABLE_OCSP, test "$ac_enable_ocsp" != "no")
-
- AC_MSG_CHECKING([whether to disable session tickets support])
- AC_ARG_ENABLE(session-tickets,
- AS_HELP_STRING([--disable-session-tickets],
- [disable session tickets support]),
- ac_enable_session_tickets=$enableval,ac_enable_session_tickets=yes)
- if test x$ac_enable_session_tickets != xno; then
- ac_enable_session_tickets=yes
- AC_MSG_RESULT(no)
- AC_DEFINE([ENABLE_SESSION_TICKETS], 1, [enable session tickets support])
- else
- ac_full=0
- AC_MSG_RESULT(yes)
- fi
- AM_CONDITIONAL(ENABLE_SESSION_TICKETS, test "$ac_enable_session_tickets" != "no")
-
# For storing integers in pointers without warnings
# http://developer.gnome.org/doc/API/2.0/glib/glib-Type-Conversion-Macros.html#desc
AC_CHECK_SIZEOF(void *)
diff --git a/src/serv.c b/src/serv.c
index ab1a6e6c65..34996d1792 100644
--- a/src/serv.c
+++ b/src/serv.c
@@ -398,11 +398,9 @@ gnutls_session_t initialize_session(int dtls)
gnutls_db_set_ptr(session, NULL);
}
-#ifdef ENABLE_SESSION_TICKETS
if (noticket == 0)
gnutls_session_ticket_enable_server(session,
&session_ticket_key);
-#endif
if (sni_hostname != NULL)
gnutls_handshake_set_post_client_hello_function(session,
@@ -1220,10 +1218,8 @@ int main(int argc, char **argv)
}
#endif
-#ifdef ENABLE_SESSION_TICKETS
if (noticket == 0)
gnutls_session_ticket_key_generate(&session_ticket_key);
-#endif
if (HAVE_OPT(MTU))
mtu = OPT_VALUE_MTU;
diff --git a/src/socket.c b/src/socket.c
index 363bd92c86..253607e5a8 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -61,7 +61,7 @@ socket_recv(const socket_st * socket, void *buffer, int buffer_size)
if (ret == GNUTLS_E_HEARTBEAT_PING_RECEIVED)
gnutls_heartbeat_pong(socket->session, 0);
}
- while (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN
+ while (ret == GNUTLS_E_INTERRUPTED
|| ret == GNUTLS_E_HEARTBEAT_PING_RECEIVED);
} else
diff --git a/symbols.last b/symbols.last
index 6f8c6e494b..3d3824262b 100644
--- a/symbols.last
+++ b/symbols.last
@@ -714,6 +714,7 @@ gnutls_session_supplemental_register@GNUTLS_3_4
gnutls_session_ticket_enable_client@GNUTLS_3_4
gnutls_session_ticket_enable_server@GNUTLS_3_4
gnutls_session_ticket_key_generate@GNUTLS_3_4
+gnutls_session_ticket_send@GNUTLS_3_6_3
gnutls_set_default_priority@GNUTLS_3_4
gnutls_sign_algorithm_get@GNUTLS_3_4
gnutls_sign_algorithm_get_client@GNUTLS_3_4
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a7e57ccea1..e095e36cbc 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -104,7 +104,7 @@ libutils_la_LIBADD = ../lib/libgnutls.la
indirect_tests =
ctests = tls13/supported_versions tls13/tls12-no-tls13-exts \
tls13/post-handshake-with-cert tls13/post-handshake-without-cert \
- tls13/cookie tls13/key_share tls13/prf \
+ tls13/cookie tls13/key_share tls13/prf tls13/post-handshake-with-cert-ticket \
tls12-rollback-detection tls11-rollback-detection \
tls12-check-rollback-val tls11-check-rollback-val tls13/hello_random_value
@@ -256,6 +256,14 @@ tls12_resume_x509_CFLAGS = -DUSE_X509 -DTLS12
tls12_resume_x509_SOURCES = resume.c
tls12_resume_x509_LDADD = $(LDADD) ../gl/libgnu.la
+tls13_resume_psk_CFLAGS = -DUSE_PSK -DTLS13
+tls13_resume_psk_SOURCES = resume.c
+tls13_resume_psk_LDADD = $(LDADD) ../gl/libgnu.la
+
+tls13_resume_x509_CFLAGS = -DUSE_X509 -DTLS13
+tls13_resume_x509_SOURCES = resume.c
+tls13_resume_x509_LDADD = $(LDADD) ../gl/libgnu.la
+
dtls_repro_20170915_SOURCES = dtls-repro-20170915.c common-cert-key-exchange.c cert-repro-20170915.h
dtls12_cert_key_exchange_SOURCES = common-cert-key-exchange.c dtls12-cert-key-exchange.c common-cert-key-exchange.h
dtls10_cert_key_exchange_SOURCES = common-cert-key-exchange.c dtls10-cert-key-exchange.c common-cert-key-exchange.h
@@ -357,7 +365,8 @@ endif
if HAVE_FORK
ctests += x509self x509dn anonself pskself dhepskself \
- setcredcrash tls12-resume-x509 tls12-resume-psk tls12-resume-anon
+ setcredcrash tls12-resume-x509 tls12-resume-psk tls12-resume-anon \
+ tls13-resume-x509 tls13-resume-psk
endif
gc_CPPFLAGS = $(AM_CPPFLAGS) \
diff --git a/tests/psk-file.c b/tests/psk-file.c
index a73031193f..e1e058ffe9 100644
--- a/tests/psk-file.c
+++ b/tests/psk-file.c
@@ -87,7 +87,7 @@ static void client(int sd, const char *prio, const char *user, const gnutls_datu
/* Initialize TLS session
*/
- gnutls_init(&session, GNUTLS_CLIENT);
+ gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_KEY_SHARE_TOP);
/* Use default priorities */
assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
@@ -392,6 +392,9 @@ void doit(void)
run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+DHE-PSK", NULL, "non-hex", &key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_KEYFILE_ERROR);
run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+DHE-PSK", NULL, "unknown", &key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+DHE-PSK", NULL, "jas", &wrong_key, 0, 0, GNUTLS_E_FATAL_ALERT_RECEIVED, GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+
+ /* try with HelloRetryRequest and PSK */
+ run_test2("NORMAL:-VERS-ALL:+VERS-TLS1.3:+DHE-PSK:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-FFDHE4096", "NORMAL:-VERS-ALL:+VERS-TLS1.3:+DHE-PSK:-GROUP-ALL:+GROUP-FFDHE4096", "jas", &key, 0, GNUTLS_KX_DHE_PSK, 0, 0);
}
#endif /* _WIN32 */
diff --git a/tests/resume.c b/tests/resume.c
index cc46e294ef..482af8e271 100644
--- a/tests/resume.c
+++ b/tests/resume.c
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2004-2016 Free Software Foundation, Inc.
* Copyright (C) 2013 Adam Sampson <ats@offog.org>
- * Copyright (C) 2016 Red Hat, Inc.
+ * Copyright (C) 2016-2018 Red Hat, Inc.
*
* Author: Simon Josefsson, Nikos Mavrogiannopoulos
*
@@ -17,9 +17,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with GnuTLS; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
*/
/* Parts copied from GnuTLS example programs. */
@@ -54,6 +53,7 @@ int main(int argc, char **argv)
#include <gnutls/gnutls.h>
#include <sys/wait.h>
#include <signal.h>
+#include <assert.h>
#include "utils.h"
#include "cert-common.h"
@@ -72,17 +72,21 @@ struct params_res {
int enable_session_ticket_server;
int enable_session_ticket_client;
int expect_resume;
+ int client_cert;
int first_no_ext_master;
int second_no_ext_master;
int try_alpn;
int try_resumed_data;
int try_diff_sni;
int try_sni;
+ int expire_ticket;
+ int change_ciphersuite;
};
pid_t child;
struct params_res resume_tests[] = {
+#ifndef TLS13
{.desc = "try to resume from db",
.enable_db = 1,
.enable_session_ticket_server = 0,
@@ -114,26 +118,50 @@ struct params_res resume_tests[] = {
.expect_resume = 0,
.first_no_ext_master = 1,
.second_no_ext_master = 0},
- {.desc = "try to resume from session ticket",
- .enable_db = 0,
+#endif
+#ifdef TLS13
+ /* only makes sense under TLS1.3 as negotiation involves a new
+ * handshake with different parameters */
+ {.desc = "try to resume from session ticket (different cipher order)",
+ .enable_db = 0,
.enable_session_ticket_server = 1,
.enable_session_ticket_client = 1,
+ .change_ciphersuite = 1,
.expect_resume = 1},
- {.desc = "try to resume from session ticket using resumed session's data",
- .enable_db = 0,
+#endif
+ {.desc = "try to resume from session ticket",
+ .enable_db = 0,
+ .enable_session_ticket_server = 1,
+ .enable_session_ticket_client = 1,
+ .expect_resume = 1},
+ {.desc = "try to resume from session ticket (client cert)",
+ .enable_db = 0,
+ .client_cert = 1,
+ .enable_session_ticket_server = 1,
+ .enable_session_ticket_client = 1,
+ .expect_resume = 1},
+ {.desc = "try to resume from session ticket (expired)",
+ .enable_db = 0,
+ .enable_session_ticket_server = 1,
+ .enable_session_ticket_client = 1,
+ .expire_ticket = 1,
+ .expect_resume = 0},
+ {.desc = "try to resume from session ticket using resumed session's data",
+ .enable_db = 0,
.enable_session_ticket_server = 1,
.enable_session_ticket_client = 1,
.try_resumed_data = 1,
.expect_resume = 1},
- {.desc = "try to resume from session ticket (ext master secret -> none)",
- .enable_db = 0,
+#ifndef TLS13
+ {.desc = "try to resume from session ticket (ext master secret -> none)",
+ .enable_db = 0,
.enable_session_ticket_server = 1,
.enable_session_ticket_client = 1,
.expect_resume = 0,
.first_no_ext_master = 0,
.second_no_ext_master = 1},
- {.desc = "try to resume from session ticket (none -> ext master secret)",
- .enable_db = 0,
+ {.desc = "try to resume from session ticket (none -> ext master secret)",
+ .enable_db = 0,
.enable_session_ticket_server = 1,
.enable_session_ticket_client = 1,
.expect_resume = 0,
@@ -169,6 +197,7 @@ struct params_res resume_tests[] = {
.enable_db = 1,
.try_sni = 1,
.expect_resume = 1},
+#endif
{.desc = "try to resume with ticket and same SNI",
.enable_session_ticket_server = 1,
.enable_session_ticket_client = 1,
@@ -285,9 +314,54 @@ static void verify_group(gnutls_session_t session, gnutls_group_t *group, unsign
}
}
+static void verify_server_params(gnutls_session_t session, unsigned counter, struct params_res *params)
+{
+#if defined(USE_PSK)
+ const char *username;
+ username = gnutls_psk_server_get_username(session);
+ if (counter != 0) {
+ if (username == NULL)
+ fail("no username was returned on server side resumption\n");
+
+ if (strcmp(username, "test") != 0)
+ fail("wrong username was returned on server side resumption\n");
+ }
+#endif
+
+#if defined(USE_X509)
+ unsigned int l;
+
+ if (counter == 0 && gnutls_certificate_get_ours(session) == NULL)
+ fail("no certificate returned on server side (%s)\n", counter?"resumed session":"first session");
+ else if (counter != 0 && gnutls_certificate_get_ours(session) != NULL)
+ fail("certificate was returned on server side (%s)\n", counter?"resumed session":"first session");
+
+ if (params->client_cert) {
+ if (gnutls_certificate_get_peers(session, &l) == NULL || l < 1)
+ fail("no client certificate returned on server side (%s)\n", counter?"resumed session":"first session");
+ }
+#endif
+
+ return;
+}
+
+static void verify_client_params(gnutls_session_t session, unsigned counter)
+{
+#if defined(USE_X509)
+ unsigned int l;
+ if (gnutls_certificate_get_peers(session, &l) == NULL || l < 1)
+ fail("no server certificate returned on client side (%s)\n", counter?"resumed session":"first session");
+#else
+ return;
+#endif
+}
+
#ifdef TLS12
# define VERS_STR "+VERS-TLS1.2"
#endif
+#ifdef TLS13
+# define VERS_STR "-VERS-ALL:+VERS-TLS1.3"
+#endif
static void client(int sds[], struct params_res *params)
{
@@ -331,8 +405,13 @@ static void client(int sds[], struct params_res *params)
gnutls_anon_allocate_client_credentials(&anoncred);
#elif defined(USE_X509)
gnutls_certificate_allocate_credentials(&clientx509cred);
-#endif
+ if (params->client_cert) {
+ assert(gnutls_certificate_set_x509_key_mem(clientx509cred,
+ &cli_cert, &cli_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+ }
+#endif
for (t = 0; t < SESSIONS; t++) {
int sd = sds[t];
@@ -358,6 +437,13 @@ static void client(int sds[], struct params_res *params)
ext_master_secret_check = 0;
}
+ if (params->change_ciphersuite) {
+ if (t > 0)
+ strcat(prio_str, ":-CIPHER-ALL:+AES-256-GCM:+AES-128-GCM");
+ else
+ strcat(prio_str, ":-CIPHER-ALL:+AES-128-GCM");
+ }
+
append_alpn(session, params, t);
ret = gnutls_priority_set_direct(session, prio_str, NULL);
@@ -383,6 +469,9 @@ static void client(int sds[], struct params_res *params)
gnutls_server_name_set(session, GNUTLS_NAME_DNS, dns_name1, strlen(dns_name1));
else if (params->try_sni)
gnutls_server_name_set(session, GNUTLS_NAME_DNS, dns_name2, strlen(dns_name2));
+
+ if (params->expire_ticket)
+ sleep(2);
} else {
if (params->try_sni)
gnutls_server_name_set(session, GNUTLS_NAME_DNS, dns_name2, strlen(dns_name2));
@@ -447,14 +536,27 @@ static void client(int sds[], struct params_res *params)
("*** Previous session was NOT resumed (expected)\n");
}
}
+
+ if (params->change_ciphersuite) {
+ /* check if the expected cipher was negotiated */
+ if (gnutls_cipher_get(session) != GNUTLS_CIPHER_AES_128_GCM) {
+ fail("negotiated different cipher: %s\n",
+ gnutls_cipher_get_name(gnutls_cipher_get(session)));
+ }
+ }
}
verify_alpn(session, params, t);
verify_group(session, &pgroup, t);
+ if (params->expect_resume)
+ verify_client_params(session, t);
+
gnutls_record_send(session, MSG, strlen(MSG));
- ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN);
if (ret == 0) {
if (debug)
success
@@ -491,43 +593,11 @@ static void client(int sds[], struct params_res *params)
#endif
}
-/* This is a sample TLS 1.0 echo server, for anonymous authentication only.
- */
-
#define DH_BITS 1024
/* These are global */
static gnutls_datum_t session_ticket_key = { NULL, 0 };
-static gnutls_session_t initialize_tls_session(struct params_res *params)
-{
- gnutls_session_t session;
-
- gnutls_init(&session, GNUTLS_SERVER);
-
- /* avoid calling all the priority functions, since the defaults
- * are adequate.
- */
- gnutls_priority_set_direct(session,
- PRIO_STR,
- NULL);
-
-
- gnutls_dh_set_prime_bits(session, DH_BITS);
-
- if (params->enable_db) {
- gnutls_db_set_retrieve_function(session, wrap_db_fetch);
- gnutls_db_set_remove_function(session, wrap_db_delete);
- gnutls_db_set_store_function(session, wrap_db_store);
- gnutls_db_set_ptr(session, NULL);
- }
-
- if (params->enable_session_ticket_server)
- gnutls_session_ticket_enable_server(session,
- &session_ticket_key);
-
- return session;
-}
static gnutls_dh_params_t dh_params;
@@ -611,8 +681,8 @@ static void server(int sds[], struct params_res *params)
gnutls_anon_allocate_server_credentials(&anoncred);
#elif defined(USE_X509)
gnutls_certificate_allocate_credentials(&serverx509cred);
- gnutls_certificate_set_x509_key_mem(serverx509cred,
- &server_cert, &server_key, GNUTLS_X509_FMT_PEM);
+ assert(gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key, GNUTLS_X509_FMT_PEM) >= 0);
#endif
if (debug)
@@ -634,10 +704,41 @@ static void server(int sds[], struct params_res *params)
for (t = 0; t < SESSIONS; t++) {
int sd = sds[t];
- session = initialize_tls_session(params);
+ assert(gnutls_init(&session, GNUTLS_SERVER) >= 0);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ assert(gnutls_priority_set_direct(session,
+ PRIO_STR,
+ NULL) >= 0);
+
+
+#if defined(USE_X509)
+ if (params->client_cert) {
+ gnutls_certificate_server_set_request(session,
+ GNUTLS_CERT_REQUIRE);
+ }
+#endif
+
+ gnutls_dh_set_prime_bits(session, DH_BITS);
+
+ if (params->enable_db) {
+ gnutls_db_set_retrieve_function(session, wrap_db_fetch);
+ gnutls_db_set_remove_function(session, wrap_db_delete);
+ gnutls_db_set_store_function(session, wrap_db_store);
+ gnutls_db_set_ptr(session, NULL);
+ }
+
+ if (params->enable_session_ticket_server)
+ gnutls_session_ticket_enable_server(session,
+ &session_ticket_key);
append_alpn(session, params, t);
+ if (params->expire_ticket)
+ gnutls_db_set_cache_expiration(session, 1);
+
#ifdef USE_PSK
gnutls_credentials_set(session, GNUTLS_CRD_PSK, pskcred);
#elif defined(USE_ANON)
@@ -662,9 +763,19 @@ static void server(int sds[], struct params_res *params)
if (debug)
success("server: Handshake was completed\n");
+ if (t > 0 && params->expect_resume) {
+ ret = gnutls_session_is_resumed(session);
+ if (ret == 0) {
+ fail("server: session_is_resumed error (%d)\n", t);
+ }
+ }
+
verify_alpn(session, params, t);
verify_group(session, &pgroup, t);
+ if (params->expect_resume)
+ verify_server_params(session, t, params);
+
/* see the Getting peer's information example */
/* print_info(session); */
diff --git a/tests/scripts/common.sh b/tests/scripts/common.sh
index 4615770f6b..8662d93cf1 100644
--- a/tests/scripts/common.sh
+++ b/tests/scripts/common.sh
@@ -195,3 +195,14 @@ else
LOCKFILE="lockfile global.lock"
UNLOCKFILE="rm -f global.lock"
fi
+
+create_testdir() {
+ local PREFIX=$1
+ d=`mktemp -d -t ${PREFIX}.XXXXXX`
+ if test $? -ne 0; then
+ d=${TMPDIR}/${PREFIX}.$$
+ mkdir "$d" || exit 1
+ fi
+ trap "test -e \"$d\" && rm -rf \"$d\"" 1 15 2
+ echo "$d"
+}
diff --git a/tests/session-tickets-missing.c b/tests/session-tickets-missing.c
index 0a546491dd..a767cbfd37 100644
--- a/tests/session-tickets-missing.c
+++ b/tests/session-tickets-missing.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Red Hat, Inc
+ * Copyright (C) 2016-2018 Red Hat, Inc
*
* Author: Nikos Mavrogiannopoulos
*
@@ -15,9 +15,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with GnuTLS; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
*/
#ifdef HAVE_CONFIG_H
@@ -55,7 +55,10 @@ int main()
static void terminate(void);
/* This program tests that handshakes do not include a session ticket
- * if the flag GNUTLS_NO_TICKETS is specified.
+ * if the flag GNUTLS_NO_TICKETS is specified under TLS 1.2.
+ *
+ * Under TLS 1.3 it verifies that not enabling session tickets doesn't
+ * result in a ticket being sent.
*/
static time_t mytime(time_t * t)
@@ -168,13 +171,17 @@ static void terminate(void)
exit(1);
}
-static void server(int fd, const char *prio)
+static void server(int fd, const char *prio, unsigned server_no_tickets)
{
int ret;
char buffer[MAX_BUF + 1];
gnutls_session_t session;
gnutls_certificate_credentials_t x509_cred;
- gnutls_datum_t skey;
+ gnutls_datum_t skey = {NULL, 0};
+ unsigned int flags = GNUTLS_SERVER;
+
+ if (server_no_tickets)
+ flags |= GNUTLS_NO_TICKETS;
/* this must be called once in the program
*/
@@ -191,10 +198,12 @@ static void server(int fd, const char *prio)
&server_key,
GNUTLS_X509_FMT_PEM)>=0);
- assert(gnutls_init(&session, GNUTLS_SERVER)>=0);
+ assert(gnutls_init(&session, flags)>=0);
- assert(gnutls_session_ticket_key_generate(&skey)>=0);
- assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ if (!server_no_tickets) {
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ }
gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
GNUTLS_HOOK_POST,
@@ -254,7 +263,7 @@ static void ch_handler(int sig)
}
static
-void start(const char *prio)
+void start(const char *prio, unsigned server_no_tickets)
{
int fd[2];
int ret, status = 0;
@@ -281,7 +290,7 @@ void start(const char *prio)
if (child) {
/* parent */
close(fd[1]);
- server(fd[0], prio);
+ server(fd[0], prio, server_no_tickets);
waitpid(child, &status, 0);
check_wait_status(status);
} else {
@@ -295,9 +304,11 @@ void start(const char *prio)
void doit(void)
{
- start("NORMAL:-VERS-ALL:+VERS-TLS1.2");
- start("NORMAL:-VERS-ALL:+VERS-TLS1.3");
- start("NORMAL");
+ start("NORMAL:-VERS-ALL:+VERS-TLS1.2", 0);
+ /* Under TLS 1.3 session tickets are not negotiated; they are
+ * "always sent unless server sets GNUTLS_NO_TICKETS */
+ start("NORMAL:-VERS-ALL:+VERS-TLS1.3", 1);
+ start("NORMAL", 0);
}
#endif /* _WIN32 */
diff --git a/tests/suite/testcompat-tls13-openssl.sh b/tests/suite/testcompat-tls13-openssl.sh
index b03e6a2111..4058da8f6a 100755
--- a/tests/suite/testcompat-tls13-openssl.sh
+++ b/tests/suite/testcompat-tls13-openssl.sh
@@ -210,6 +210,23 @@ run_client_suite() {
kill ${PID}
wait
+ # Try resumption
+ echo_cmd "${PREFIX}Checking TLS 1.3 with resumption..."
+ testdir=`create_testdir tls13-openssl-resumption`
+ eval "${GETPORT}"
+ launch_bare_server $$ s_server -quiet -www -accept "${PORT}" -keyform pem -certform pem ${OPENSSL_DH_PARAMS_OPT} -key "${RSA_KEY}" -cert "${RSA_CERT}" -CAfile "${CA_CERT}"
+ PID=$!
+ wait_server ${PID}
+
+ # ${VALGRIND} "${CLI}" ${DEBUG} -p "${PORT}" 127.0.0.1 --priority "NORMAL:-VERS-ALL:+VERS-TLS1.3:+GROUP-ALL${ADD}" --x509cafile "${CA_CERT}" --inline-commands | tee "${testdir}/client.out" >> ${OUTPUT}
+ { echo a; sleep 1; echo '^resume^'; } | \
+ ${VALGRIND} "${CLI}" ${DEBUG} -p "${PORT}" 127.0.0.1 --priority "NORMAL:-VERS-ALL:+VERS-TLS1.3:+GROUP-ALL${ADD}" --insecure --inline-commands | tee "${testdir}/client.out" >> ${OUTPUT}
+ grep '^\*\*\* This is a resumed session' "${testdir}/client.out" || \
+ fail ${PID} "Failed"
+
+ kill ${PID}
+ wait
+ rm -rf "$testdir"
}
@@ -375,6 +392,27 @@ run_server_suite() {
wait
done
+ # Try resumption
+ echo_cmd "${PREFIX}Checking TLS 1.3 with resumption..."
+ testdir=`create_testdir tls13-openssl-resumption`
+ eval "${GETPORT}"
+ launch_server $$ --priority "NORMAL:-VERS-ALL:+VERS-TLS1.3${ADD}" --x509certfile "${RSA_CERT}" --x509keyfile "${RSA_KEY}" --x509cafile "${CA_CERT}" >>${OUTPUT} 2>&1
+ PID=$!
+ wait_server ${PID}
+
+ { echo a; sleep 1; } | \
+ ${OPENSSL_CLI} s_client -host localhost -port "${PORT}" -CAfile "${CA_CERT}" -sess_out "${testdir}/sess.pem" 2>&1 | grep "\:error\:" && \
+ fail ${PID} "Failed"
+ ${OPENSSL_CLI} s_client -host localhost -port "${PORT}" -CAfile "${CA_CERT}" -sess_in "${testdir}/sess.pem" </dev/null 2>&1 > "${testdir}/server.out"
+ grep "\:error\:" "${testdir}/server.out" && \
+ fail ${PID} "Failed"
+ grep "^Reused, TLSv1.3" "${testdir}/server.out" || \
+ fail ${PID} "Failed"
+
+ kill ${PID}
+ wait
+ rm -rf "$testdir"
+
}
run_server_suite
diff --git a/tests/tls13/key_update.c b/tests/tls13/key_update.c
index ac5f5cc1a5..bed8a682b4 100644
--- a/tests/tls13/key_update.c
+++ b/tests/tls13/key_update.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 Red Hat, Inc.
+ * Copyright (C) 2017-2018 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
@@ -46,6 +46,26 @@ static void tls_log_func(int level, const char *str)
#define MAX_BUF 1024
#define MSG "Hello TLS, and hi and how are you and more data here... and more... and even more and even more more data..."
+static unsigned key_update_msg_inc = 0;
+static unsigned key_update_msg_out = 0;
+
+static int hsk_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ assert(post == GNUTLS_HOOK_PRE);
+
+ assert(msg->size == 1);
+
+ if (htype == GNUTLS_HANDSHAKE_KEY_UPDATE) {
+ if (incoming)
+ key_update_msg_inc++;
+ else
+ key_update_msg_out++;
+ }
+
+ return 0;
+}
+
static void run(const char *name, unsigned test)
{
/* Server stuff. */
@@ -105,6 +125,7 @@ static void run(const char *name, unsigned test)
gnutls_transport_set_pull_function(client, client_pull);
gnutls_transport_set_ptr(client, client);
+
HANDSHAKE(client, server);
if (debug)
success("Handshake established\n");
@@ -169,6 +190,8 @@ static void run(const char *name, unsigned test)
TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ sec_sleep(2);
break;
case 5:
success("%s: client cork\n", name);
@@ -199,11 +222,34 @@ static void run(const char *name, unsigned test)
fail("cannot send: %s\n", gnutls_strerror(ret));
EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ sec_sleep(2);
+ break;
+ case 6:
+ key_update_msg_inc = 0;
+ key_update_msg_out = 0;
+
+ success("%s: callbacks are called\n", name);
+
+ gnutls_handshake_set_hook_function(client, -1, GNUTLS_HOOK_PRE, hsk_callback);
+ gnutls_handshake_set_hook_function(server, -1, GNUTLS_HOOK_PRE, hsk_callback);
+
+ do {
+ ret = gnutls_session_key_update(client, GNUTLS_KU_PEER);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+
+ /* server receives the client key update and sends data */
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ assert(key_update_msg_inc == 2);
+ assert(key_update_msg_out == 2);
break;
}
- if (debug)
- fputs("\n", stdout);
gnutls_bye(client, GNUTLS_SHUT_WR);
gnutls_bye(server, GNUTLS_SHUT_WR);
@@ -225,5 +271,6 @@ void doit(void)
run("single", 3);
run("single", 4);
run("single", 5);
+ run("single", 6);
run("all", 0); /* all one after each other */
}
diff --git a/tests/tls13/post-handshake-with-cert-ticket.c b/tests/tls13/post-handshake-with-cert-ticket.c
new file mode 100644
index 0000000000..edac74b30f
--- /dev/null
+++ b/tests/tls13/post-handshake-with-cert-ticket.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the certificate seen in Post Handshake Auth
+ * is found in a resumed session under TLS 1.3.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static int ticket_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ gnutls_datum *d;
+ static int counter = 0;
+ int ret;
+
+ assert(htype == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+
+ counter++;
+ if (counter == 1) /* ignore the first ticket */
+ return 0;
+
+ d = gnutls_session_get_ptr(session);
+
+ if (post == GNUTLS_HOOK_POST) {
+ if (d->data)
+ gnutls_free(d->data);
+ ret = gnutls_session_get_data2(session, d);
+ assert(ret >= 0);
+ assert(d->size > 4);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ char buf[64];
+ unsigned try = 0;
+ gnutls_datum_t session_data = {NULL, 0};
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ retry:
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (try == 0) {
+ gnutls_session_set_ptr(session, &session_data);
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
+ GNUTLS_HOOK_BOTH,
+ ticket_callback);
+ } else {
+ assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (try == 0) {
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != GNUTLS_E_REAUTH_REQUEST) {
+ fail("recv: unexpected error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("received reauth request\n");
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0)
+ fail("client: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+ } else {
+ assert(gnutls_session_is_resumed(session) != 0);
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0) {
+ fail("error in recv: %s\n", gnutls_strerror(ret));
+ }
+
+ gnutls_deinit(session);
+
+ if (try == 0) {
+ try++;
+ goto retry;
+ }
+
+ close(fd);
+ gnutls_free(session_data.data);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static void compare(const gnutls_datum_t *der, const void *ipem)
+{
+ gnutls_datum_t pem = {(void*)ipem, strlen((char*)ipem)};
+ gnutls_datum_t new_der;
+ int ret;
+
+ ret = gnutls_pem_base64_decode2("CERTIFICATE", &pem, &new_der);
+ if (ret < 0) {
+ fail("error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (der->size != new_der.size || memcmp(der->data, new_der.data, der->size) != 0) {
+ fail("error in %d: %s\n", __LINE__, "cert don't match");
+ exit(1);
+ }
+ gnutls_free(new_der.data);
+ return;
+}
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ unsigned clist_size;
+ gnutls_datum_t skey;
+ const gnutls_datum_t *clist;
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ assert(gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
+
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+ /* send a fresh ticket after re-auth */
+ do {
+ ret = gnutls_session_ticket_send(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_deinit(session);
+
+ /* resume session
+ */
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+ assert(gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ assert(gnutls_session_is_resumed(session) != 0);
+
+ /* check if cert is visible */
+ clist = gnutls_certificate_get_peers(session, &clist_size);
+ assert(clist != NULL);
+ assert(clist_size > 0);
+
+ compare(&clist[0], cli_ca3_cert.data);
+
+ gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(session);
+
+ gnutls_free(skey.data);
+ close(fd);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+
+}
+#endif /* _WIN32 */