summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS8
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/cert-cred-x509.c22
-rw-r--r--lib/db.c30
-rw-r--r--lib/errors.c2
-rw-r--r--lib/ext/session_ticket.c141
-rw-r--r--lib/gnutls_int.h36
-rw-r--r--lib/includes/gnutls/gnutls.h.in6
-rw-r--r--lib/includes/gnutls/x509.h6
-rw-r--r--lib/libgnutls.map2
-rw-r--r--lib/session_pack.c11
-rw-r--r--lib/state.c3
-rw-r--r--lib/stek.c357
-rw-r--r--lib/stek.h38
-rw-r--r--lib/tls13/session_ticket.c8
-rw-r--r--lib/x509/verify-high.c2
-rw-r--r--src/cli.c14
-rw-r--r--tests/Makefile.am5
-rwxr-xr-xtests/gnutls-cli-invalid-crl.sh188
-rwxr-xr-xtests/gnutls-cli-self-signed.sh2
-rw-r--r--tests/resume-with-previous-stek.c255
-rw-r--r--tests/resume-with-stek-expiration.c326
-rw-r--r--tests/resume.c12
23 files changed, 1362 insertions, 116 deletions
diff --git a/NEWS b/NEWS
index d1b52f7db8..b6539fd9f7 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,11 @@ See the end for copying conditions.
certificate is presented by client and the gnutls_init() flag GNUTLS_ENABLE_EARLY_START
is specified.
+** libgnutls: Added session ticket key rotation on server side with TOTP.
+ The key set with gnutls_session_ticket_enable_server() is used as a
+ master key to generate time-based keys for tickets. The rotation
+ relates to the gnutls_db_set_cache_expiration() period.
+
** libgnutls: The 'record size limit' extension is added and preferred to the
'max record size' extension when possible.
@@ -28,6 +33,7 @@ See the end for copying conditions.
defined in RFC7250. This mechanism must be explicitly enabled via the
GNUTLS_ENABLE_CERT_TYPE_NEG flag in gnutls_init().
+** gnutls-cli: enable CRL validation on startup (#564)
** API and ABI modifications:
GNUTLS_ENABLE_EARLY_START: Added
@@ -35,6 +41,8 @@ gnutls_record_set_max_early_data_size: Added
gnutls_certificate_type_get2: Added
gnutls_priority_certificate_type_list2: Added
GNUTLS_ENABLE_CERT_TYPE_NEG: Added
+GNUTLS_TL_FAIL_ON_INVALID_CRL: Added
+GNUTLS_CERTIFICATE_VERIFY_CRLS: Added
gnutls_ctype_target_t: New enumeration
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 11de0a05bf..e101ec68f2 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -80,7 +80,7 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls
system-keys.h urls.c urls.h prf.c auto-verify.c dh-session.c \
cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \
crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \
- hello_ext_lib.c hello_ext_lib.h ocsp-api.c
+ hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c
if WINDOWS
COBJECTS += system/keys-win.c
@@ -122,7 +122,7 @@ HFILES = abstract_int.h debug.h cipher.h \
srp.h auth/srp_kx.h auth/srp_passwd.h \
file.h supplemental.h crypto.h random.h system.h\
locks.h mbuffers.h ecc.h pin.h fips.h \
- priority_options.h secrets.h
+ priority_options.h secrets.h stek.h
if ENABLE_PKCS11
HFILES += pkcs11_int.h pkcs11x.h
diff --git a/lib/cert-cred-x509.c b/lib/cert-cred-x509.c
index 99a0b366e7..f342a420b5 100644
--- a/lib/cert-cred-x509.c
+++ b/lib/cert-cred-x509.c
@@ -1493,10 +1493,14 @@ gnutls_certificate_set_x509_crl_mem(gnutls_certificate_credentials_t res,
const gnutls_datum_t * CRL,
gnutls_x509_crt_fmt_t type)
{
-int ret;
+ unsigned flags = GNUTLS_TL_USE_IN_TLS;
+ int ret;
+
+ if (res->flags & GNUTLS_CERTIFICATE_VERIFY_CRLS)
+ flags |= GNUTLS_TL_VERIFY_CRL|GNUTLS_TL_FAIL_ON_INVALID_CRL;
ret = gnutls_x509_trust_list_add_trust_mem(res->tlist, NULL, CRL,
- type, GNUTLS_TL_USE_IN_TLS, 0);
+ type, flags, 0);
if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
return 0;
@@ -1526,6 +1530,10 @@ gnutls_certificate_set_x509_crl(gnutls_certificate_credentials_t res,
{
int ret, i, j;
gnutls_x509_crl_t *new_crl = gnutls_malloc(crl_list_size * sizeof(gnutls_x509_crl_t));
+ unsigned flags = GNUTLS_TL_USE_IN_TLS;
+
+ if (res->flags & GNUTLS_CERTIFICATE_VERIFY_CRLS)
+ flags |= GNUTLS_TL_VERIFY_CRL|GNUTLS_TL_FAIL_ON_INVALID_CRL;
if (!new_crl)
return GNUTLS_E_MEMORY_ERROR;
@@ -1546,7 +1554,7 @@ gnutls_certificate_set_x509_crl(gnutls_certificate_credentials_t res,
ret =
gnutls_x509_trust_list_add_crls(res->tlist, new_crl,
- crl_list_size, GNUTLS_TL_USE_IN_TLS, 0);
+ crl_list_size, flags, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -1582,10 +1590,14 @@ gnutls_certificate_set_x509_crl_file(gnutls_certificate_credentials_t res,
const char *crlfile,
gnutls_x509_crt_fmt_t type)
{
-int ret;
+ int ret;
+ unsigned flags = GNUTLS_TL_USE_IN_TLS;
+
+ if (res->flags & GNUTLS_CERTIFICATE_VERIFY_CRLS)
+ flags |= GNUTLS_TL_VERIFY_CRL|GNUTLS_TL_FAIL_ON_INVALID_CRL;
ret = gnutls_x509_trust_list_add_trust_file(res->tlist, NULL, crlfile,
- type, GNUTLS_TL_USE_IN_TLS, 0);
+ type, flags, 0);
if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
return 0;
diff --git a/lib/db.c b/lib/db.c
index 38225d31f5..a029f351cd 100644
--- a/lib/db.c
+++ b/lib/db.c
@@ -260,12 +260,28 @@ int _gnutls_server_register_current_session(gnutls_session_t session)
int _gnutls_check_resumed_params(gnutls_session_t session)
{
- if (session->internals.resumed_security_parameters.ext_master_secret !=
- session->security_parameters.ext_master_secret)
- return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
-
- if (!_gnutls_server_name_matches_resumed(session))
- return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
+ time_t timestamp = gnutls_time(0);
+ const version_entry_st *vers;
+
+ /* check whether the session is expired */
+ 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);
+
+ /* check various parameters applicable to resumption in TLS1.2 or earlier
+ */
+ vers = get_version(session);
+ if (!vers || !vers->tls13_sem) {
+ if (session->internals.resumed_security_parameters.ext_master_secret !=
+ session->security_parameters.ext_master_secret)
+ return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
+
+ if (!_gnutls_server_name_matches_resumed(session))
+ return gnutls_assert_val(GNUTLS_E_INVALID_SESSION);
+ }
return 0;
}
@@ -311,7 +327,6 @@ _gnutls_server_restore_session(gnutls_session_t session,
return GNUTLS_E_INVALID_SESSION;
}
- /* expiration check is performed inside */
ret = gnutls_session_set_data(session, data.data, data.size);
gnutls_free(data.data);
@@ -320,6 +335,7 @@ _gnutls_server_restore_session(gnutls_session_t session,
return ret;
}
+ /* expiration check is performed inside */
ret = _gnutls_check_resumed_params(session);
if (ret < 0)
return gnutls_assert_val(ret);
diff --git a/lib/errors.c b/lib/errors.c
index c4bc8c72b9..fb6b54b4b0 100644
--- a/lib/errors.c
+++ b/lib/errors.c
@@ -165,6 +165,8 @@ static const gnutls_error_entry error_entries[] = {
GNUTLS_E_CERTIFICATE_TIME_ERROR),
ERROR_ENTRY(N_("Error in the certificate verification."),
GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR),
+ ERROR_ENTRY(N_("Error in the CRL verification."),
+ GNUTLS_E_CRL_VERIFICATION_ERROR),
ERROR_ENTRY(N_("Error in the private key verification; seed doesn't match."),
GNUTLS_E_PRIVKEY_VERIFICATION_ERROR),
ERROR_ENTRY(N_("Could not authenticate peer."),
diff --git a/lib/ext/session_ticket.c b/lib/ext/session_ticket.c
index d2a15e5eb3..3eb63818b9 100644
--- a/lib/ext/session_ticket.c
+++ b/lib/ext/session_ticket.c
@@ -34,16 +34,9 @@
#include <hello_ext.h>
#include <constate.h>
#include <dtls.h>
+#include "stek.h"
#include "db.h"
-/* 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_ALGO GNUTLS_MAC_SHA1
-#define MAC_SIZE 20 /* HMAC-SHA1 */
-
static int session_ticket_recv_params(gnutls_session_t session,
const uint8_t * data,
size_t data_size);
@@ -70,23 +63,11 @@ const hello_ext_entry_st ext_mod_session_ticket = {
.cannot_be_overriden = 1
};
-#define NAME_POS (0)
-#define KEY_POS (TICKET_KEY_NAME_SIZE)
-#define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE)
-
typedef struct {
uint8_t *session_ticket;
int session_ticket_len;
} session_ticket_ext_st;
-struct ticket_st {
- 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)
{
@@ -111,9 +92,9 @@ unpack_ticket(const gnutls_datum_t *ticket_data, struct ticket_st *ticket)
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, TICKET_IV_SIZE);
+ memcpy(ticket->IV, data, TICKET_IV_SIZE);
+ data += TICKET_IV_SIZE;
DECR_LEN(data_size, 2);
ticket->encrypted_state_len = _gnutls_read_uint16(data);
@@ -124,8 +105,8 @@ unpack_ticket(const gnutls_datum_t *ticket_data, struct ticket_st *ticket)
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);
+ DECR_LEN(data_size, TICKET_MAC_SIZE);
+ memcpy(ticket->mac, data, TICKET_MAC_SIZE);
ticket->encrypted_state =
gnutls_malloc(ticket->encrypted_state_len);
@@ -149,8 +130,8 @@ pack_ticket(const struct ticket_st *ticket, gnutls_datum_t *ticket_data)
memcpy(p, ticket->key_name, TICKET_KEY_NAME_SIZE);
p += TICKET_KEY_NAME_SIZE;
- memcpy(p, ticket->IV, IV_SIZE);
- p += IV_SIZE;
+ memcpy(p, ticket->IV, TICKET_IV_SIZE);
+ p += TICKET_IV_SIZE;
_gnutls_write_uint16(ticket->encrypted_state_len, p);
p += 2;
@@ -158,7 +139,7 @@ pack_ticket(const struct ticket_st *ticket, gnutls_datum_t *ticket_data)
memcpy(p, ticket->encrypted_state, ticket->encrypted_state_len);
p += ticket->encrypted_state_len;
- memcpy(p, ticket->mac, MAC_SIZE);
+ memcpy(p, ticket->mac, TICKET_MAC_SIZE);
}
static
@@ -169,7 +150,7 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket,
uint16_t length16;
int ret;
- ret = _gnutls_mac_init(&digest_hd, mac_to_entry(MAC_ALGO),
+ ret = _gnutls_mac_init(&digest_hd, mac_to_entry(TICKET_MAC_ALGO),
key->data, key->size);
if (ret < 0) {
gnutls_assert();
@@ -177,7 +158,7 @@ int digest_ticket(const gnutls_datum_t * key, struct ticket_st *ticket,
}
_gnutls_mac(&digest_hd, ticket->key_name, TICKET_KEY_NAME_SIZE);
- _gnutls_mac(&digest_hd, ticket->IV, IV_SIZE);
+ _gnutls_mac(&digest_hd, ticket->IV, TICKET_IV_SIZE);
length16 = _gnutls_conv_uint16(ticket->encrypted_state_len);
_gnutls_mac(&digest_hd, &length16, 2);
_gnutls_mac(&digest_hd, ticket->encrypted_state,
@@ -193,50 +174,60 @@ _gnutls_decrypt_session_ticket(gnutls_session_t session,
gnutls_datum_t *state)
{
cipher_hd_st cipher_hd;
- gnutls_datum_t key, IV, mac_secret;
- uint8_t cmac[MAC_SIZE];
+ gnutls_datum_t IV;
+ gnutls_datum_t stek_key_name, stek_cipher_key, stek_mac_key;
+ uint8_t cmac[TICKET_MAC_SIZE];
struct ticket_st ticket;
int ret;
- /* If the key name of the ticket does not match the one that we
- hold, issue a new ticket. */
- if (ticket_data->size < TICKET_KEY_NAME_SIZE ||
- memcmp(ticket_data->data, &session->key.session_ticket_key[NAME_POS],
- TICKET_KEY_NAME_SIZE))
+ /* callers must have that checked */
+ assert(!(session->internals.flags & GNUTLS_NO_TICKETS));
+
+ /* Retrieve ticket decryption keys */
+ if (_gnutls_get_session_ticket_decryption_key(session,
+ ticket_data,
+ &stek_key_name,
+ &stek_mac_key,
+ &stek_cipher_key) < 0)
return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
ret = unpack_ticket(ticket_data, &ticket);
if (ret < 0)
return ret;
+ /* If the key name of the ticket does not match the one that is currently active,
+ issue a new ticket. */
+ if (memcmp
+ (ticket.key_name, stek_key_name.data,
+ stek_key_name.size)) {
+ ret = GNUTLS_E_DECRYPTION_FAILED;
+ goto cleanup;
+ }
+
/* 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);
+ ret = digest_ticket(&stek_mac_key, &ticket, cmac);
if (ret < 0) {
gnutls_assert();
goto cleanup;
}
- if (memcmp(ticket.mac, cmac, MAC_SIZE)) {
+ if (memcmp(ticket.mac, cmac, TICKET_MAC_SIZE)) {
ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
goto cleanup;
}
- if (ticket.encrypted_state_len % BLOCK_SIZE != 0) {
+ if (ticket.encrypted_state_len % TICKET_BLOCK_SIZE != 0) {
ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
goto cleanup;
}
/* 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;
+ IV.size = TICKET_IV_SIZE;
ret =
_gnutls_cipher_init(&cipher_hd,
- cipher_to_entry(CIPHER),
- &key, &IV, 0);
+ cipher_to_entry(TICKET_CIPHER),
+ &stek_cipher_key, &IV, 0);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -272,32 +263,39 @@ _gnutls_encrypt_session_ticket(gnutls_session_t session,
gnutls_datum_t *ticket_data)
{
cipher_hd_st cipher_hd;
- gnutls_datum_t key, IV;
+ gnutls_datum_t IV;
gnutls_datum_t encrypted_state = {NULL,0};
- uint8_t iv[IV_SIZE];
- gnutls_datum_t mac_secret;
+ uint8_t iv[TICKET_IV_SIZE];
+ gnutls_datum_t stek_cipher_key, stek_mac_key, stek_key_name;
struct ticket_st ticket;
int ret;
- 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;
+ encrypted_state.size = ((state->size + TICKET_BLOCK_SIZE - 1) / TICKET_BLOCK_SIZE) * TICKET_BLOCK_SIZE;
+ ticket_data->size = TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2 +
+ encrypted_state.size + TICKET_MAC_SIZE;
ticket_data->data = gnutls_calloc(1, ticket_data->size);
if (!ticket_data->data) {
gnutls_assert();
ret = GNUTLS_E_MEMORY_ERROR;
goto cleanup;
}
- encrypted_state.data = ticket_data->data + TICKET_KEY_NAME_SIZE + IV_SIZE + 2;
+ encrypted_state.data = ticket_data->data + TICKET_KEY_NAME_SIZE + TICKET_IV_SIZE + 2;
memcpy(encrypted_state.data, state->data, state->size);
+ /* Retrieve ticket encryption keys */
+ if (_gnutls_get_session_ticket_encryption_key(session,
+ &stek_key_name,
+ &stek_mac_key,
+ &stek_cipher_key) < 0) {
+ ret = GNUTLS_E_ENCRYPTION_FAILED;
+ goto cleanup;
+ }
+
/* Encrypt state */
- key.data = (void *) &session->key.session_ticket_key[KEY_POS];
- key.size = TICKET_CIPHER_KEY_SIZE;
IV.data = iv;
- IV.size = IV_SIZE;
+ IV.size = TICKET_IV_SIZE;
- ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, IV_SIZE);
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, iv, TICKET_IV_SIZE);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -305,8 +303,8 @@ _gnutls_encrypt_session_ticket(gnutls_session_t session,
ret =
_gnutls_cipher_init(&cipher_hd,
- cipher_to_entry(CIPHER),
- &key, &IV, 1);
+ cipher_to_entry(TICKET_CIPHER),
+ &stek_cipher_key, &IV, 1);
if (ret < 0) {
gnutls_assert();
goto cleanup;
@@ -321,14 +319,12 @@ _gnutls_encrypt_session_ticket(gnutls_session_t session,
/* Fill the ticket structure to compute MAC. */
- memcpy(ticket.key_name, &session->key.session_ticket_key[NAME_POS], TICKET_KEY_NAME_SIZE);
+ memcpy(ticket.key_name, stek_key_name.data, stek_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);
+ ret = digest_ticket(&stek_mac_key, &ticket, ticket.mac);
if (ret < 0) {
gnutls_assert();
goto cleanup2;
@@ -353,7 +349,6 @@ 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);
@@ -362,13 +357,6 @@ unpack_session(gnutls_session_t session, const gnutls_datum_t *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);
@@ -606,12 +594,17 @@ int
gnutls_session_ticket_enable_server(gnutls_session_t session,
const gnutls_datum_t * key)
{
- if (!session || !key || key->size != TICKET_MASTER_KEY_SIZE) {
+ int ret;
+
+ if (!session || !key || key->size != TICKET_MASTER_KEY_SIZE || !key->data) {
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
}
- memcpy(session->key.session_ticket_key, key->data, key->size);
+ ret = _gnutls_initialize_session_ticket_key_rotation(session, key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
session->internals.flags &= ~GNUTLS_NO_TICKETS;
return 0;
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index f35d6df5f5..2574f4e420 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -157,6 +157,7 @@ typedef struct {
/* expire time for resuming sessions */
#define DEFAULT_EXPIRE_TIME 21600
+#define STEK_ROTATION_PERIOD_PRODUCT 3
#define DEFAULT_HANDSHAKE_TIMEOUT_MS 40*1000
/* The EC group to be used when the extension
@@ -490,6 +491,22 @@ typedef struct auth_cred_st {
#define TICKET_CIPHER_KEY_SIZE 32
#define TICKET_MAC_SECRET_SIZE 16
+/* These are restricted by TICKET_CIPHER_KEY_SIZE and TICKET_MAC_SECRET_SIZE */
+#define TICKET_CIPHER GNUTLS_CIPHER_AES_256_CBC
+#define TICKET_IV_SIZE 16
+#define TICKET_BLOCK_SIZE 16
+
+#define TICKET_MAC_ALGO GNUTLS_MAC_SHA1
+#define TICKET_MAC_SIZE 20 /* HMAC-SHA1 */
+
+struct ticket_st {
+ uint8_t key_name[TICKET_KEY_NAME_SIZE];
+ uint8_t IV[TICKET_IV_SIZE];
+ uint8_t *encrypted_state;
+ uint16_t encrypted_state_len;
+ uint8_t mac[TICKET_MAC_SIZE];
+};
+
struct binder_data_st {
const struct mac_entry_st *prf; /* non-null if this struct is set */
gnutls_datum_t psk;
@@ -501,6 +518,10 @@ struct binder_data_st {
uint8_t resumption; /* whether it is a resumption binder */
};
+typedef void (* gnutls_stek_rotation_callback_t) (const gnutls_datum_t *prev_key,
+ const gnutls_datum_t *new_key,
+ uint64_t t);
+
struct gnutls_key_st {
struct { /* These are kept outside the TLS1.3 union as they are
* negotiated via extension, even before protocol is negotiated */
@@ -572,8 +593,13 @@ struct gnutls_key_st {
/* 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];
+ uint8_t
+ /* The key to encrypt and decrypt session tickets */
+ session_ticket_key[TICKET_MASTER_KEY_SIZE],
+ /* Static buffer for the previous key, whenever we need it */
+ previous_ticket_key[TICKET_MASTER_KEY_SIZE],
+ /* Initial key supplied by the caller */
+ initial_stek[TICKET_MASTER_KEY_SIZE];
/* this is used to hold the peers authentication data
*/
@@ -586,6 +612,12 @@ struct gnutls_key_st {
int auth_info_size; /* needed in order to store to db for restoring
*/
auth_cred_st *cred; /* used to specify keys/certificates etc */
+
+ struct {
+ uint64_t last_result;
+ uint8_t was_rotated;
+ gnutls_stek_rotation_callback_t cb;
+ } totp;
};
typedef struct gnutls_key_st gnutls_key_st;
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 2ee3e4fc83..49990b5f50 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -1928,13 +1928,16 @@ gnutls_certificate_get_verify_flags(gnutls_certificate_credentials_t res);
* @GNUTLS_CERTIFICATE_API_V2: If set the gnutls_certificate_set_*key* functions will return an index of the added key pair instead of zero.
* @GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK: If set, the gnutls_certificate_set_ocsp_status_request_file
* function, will not check whether the response set matches any of the certificates.
+ * @GNUTLS_CERTIFICATE_VERIFY_CRLS: This will enable CRL verification when added in the certificate structure.
+ * When used, it requires CAs to be added before CRLs.
*
* Enumeration of different certificate credentials flags.
*/
typedef enum gnutls_certificate_flags {
GNUTLS_CERTIFICATE_SKIP_KEY_CERT_MATCH = 1,
GNUTLS_CERTIFICATE_API_V2 = (1<<1),
- GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK = (1<<2)
+ GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK = (1<<2),
+ GNUTLS_CERTIFICATE_VERIFY_CRLS = (1<<3)
} gnutls_certificate_flags;
void gnutls_certificate_set_flags(gnutls_certificate_credentials_t,
@@ -3214,6 +3217,7 @@ void gnutls_fips140_set_mode(gnutls_fips_mode_t mode, unsigned flags);
#define GNUTLS_E_NO_COMMON_KEY_SHARE -423
#define GNUTLS_E_REAUTH_REQUEST -424
#define GNUTLS_E_TOO_MANY_MATCHES -425
+#define GNUTLS_E_CRL_VERIFICATION_ERROR -426
#define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250
diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h
index cd54e8c4ca..1573577d96 100644
--- a/lib/includes/gnutls/x509.h
+++ b/lib/includes/gnutls/x509.h
@@ -1562,6 +1562,8 @@ int gnutls_x509_trust_list_get_issuer_by_subject_key_id(gnutls_x509_trust_list_t
* @GNUTLS_TL_GET_COPY: The semantics of this flag are documented to the functions which
* are applicable. In general, on returned value, the function will provide a copy
* if this flag is provided, rather than a pointer to internal data.
+ * @GNUTLS_TL_FAIL_ON_INVALID_CRL: If an CRL is added which cannot be validated return
+ * an error instead of ignoring (must be used with %GNUTLS_TL_VERIFY_CRL).
*
* Enumeration of different certificate trust list flags.
*/
@@ -1574,8 +1576,10 @@ typedef enum gnutls_trust_list_flags_t {
#define GNUTLS_TL_NO_DUPLICATES (1<<2)
GNUTLS_TL_NO_DUPLICATE_KEY = (1<<3),
#define GNUTLS_TL_NO_DUPLICATE_KEY (1<<3)
- GNUTLS_TL_GET_COPY = (1<<4)
+ GNUTLS_TL_GET_COPY = (1<<4),
#define GNUTLS_TL_GET_COPY (1<<4)
+ GNUTLS_TL_FAIL_ON_INVALID_CRL = (1<<5)
+#define GNUTLS_TL_FAIL_ON_INVALID_CRL (1<<5)
} gnutls_trust_list_flags_t;
int
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index dd77025f07..041fda7b80 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1324,4 +1324,6 @@ GNUTLS_PRIVATE_3_4 {
# Internal symbols needed by tests/name-constraints-merge:
_gnutls_x509_name_constraints_merge;
_gnutls_server_name_set_raw;
+ # Internal symbols needed by tests/suite/resume-with-stek-expiration
+ _gnutls_set_session_ticket_key_rotation_callback;
} GNUTLS_3_4;
diff --git a/lib/session_pack.c b/lib/session_pack.c
index f8b1830568..c5801fb32e 100644
--- a/lib/session_pack.c
+++ b/lib/session_pack.c
@@ -960,8 +960,6 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
session->internals.resumed_security_parameters.timestamp =
timestamp;
- timestamp = gnutls_time(0);
-
BUFFER_POP_NUM(ps,
session->internals.resumed_security_parameters.
entity);
@@ -1071,15 +1069,6 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
}
}
- if (timestamp -
- session->internals.resumed_security_parameters.timestamp >
- session->internals.expire_time
- || session->internals.resumed_security_parameters.timestamp >
- timestamp) {
- gnutls_assert();
- return GNUTLS_E_EXPIRED;
- }
-
ret = 0;
error:
diff --git a/lib/state.c b/lib/state.c
index 58db8f9a32..86edd3c4c4 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -491,6 +491,9 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
(*session)->internals.expire_time = DEFAULT_EXPIRE_TIME;
+ /* Ticket key rotation - set the default X to 3 times the ticket expire time */
+ (*session)->key.totp.last_result = 0;
+
gnutls_handshake_set_max_packet_length((*session),
MAX_HANDSHAKE_PACKET_SIZE);
diff --git a/lib/stek.c b/lib/stek.c
new file mode 100644
index 0000000000..2248e51b6f
--- /dev/null
+++ b/lib/stek.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2018 Free Software Foundation, Inc.
+ *
+ * Author: Ander Juaristi
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+#include "gnutls_int.h"
+#include "stek.h"
+
+#define NAME_POS (0)
+#define KEY_POS (TICKET_KEY_NAME_SIZE)
+#define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE)
+
+static int totp_sha3(gnutls_session_t session,
+ uint64_t t,
+ const gnutls_datum_t *secret,
+ uint8_t out[TICKET_MASTER_KEY_SIZE])
+{
+ int retval;
+ uint8_t t_be[8];
+ digest_hd_st hd;
+ /*
+ * We choose SHA3-512 because it outputs 64 bytes,
+ * just the same length as the ticket key.
+ */
+ const gnutls_digest_algorithm_t algo = GNUTLS_DIG_SHA3_512;
+#if TICKET_MASTER_KEY_SIZE != 64
+#error "TICKET_MASTER_KEY_SIZE must be 64 bytes"
+#endif
+
+ if (unlikely(secret == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if ((retval = _gnutls_hash_init(&hd, hash_to_entry(algo))) < 0)
+ return gnutls_assert_val(retval);
+
+ _gnutls_write_uint64(t, t_be);
+
+ if ((retval = _gnutls_hash(&hd, t_be, sizeof(t_be))) < 0)
+ return gnutls_assert_val(retval);
+ if ((retval = _gnutls_hash(&hd, secret->data, secret->size)) < 0)
+ return gnutls_assert_val(retval);
+
+ _gnutls_hash_deinit(&hd, out);
+ return GNUTLS_E_SUCCESS;
+}
+
+static uint64_t T(gnutls_session_t session, time_t t)
+{
+ uint64_t numeral = t;
+ unsigned int x = session->internals.expire_time * STEK_ROTATION_PERIOD_PRODUCT;
+
+ if (numeral <= 0)
+ return 0;
+
+ return (numeral / x);
+}
+
+static int64_t totp_next(gnutls_session_t session)
+{
+ time_t t;
+ uint64_t result;
+
+ t = gnutls_time(NULL);
+ if (unlikely(t == (time_t) -1))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ result = T(session, t);
+ if (result == 0)
+ return 0;
+
+ if (result == session->key.totp.last_result)
+ return 0;
+
+ return result;
+}
+
+static int64_t totp_previous(gnutls_session_t session)
+{
+ uint64_t result;
+
+ if (session->key.totp.last_result == 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ if (!session->key.totp.was_rotated)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ result = session->key.totp.last_result - 1;
+ if (result == 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ return result;
+}
+
+static void call_rotation_callback(gnutls_session_t session,
+ uint8_t key[TICKET_MASTER_KEY_SIZE], uint64_t t)
+{
+ gnutls_datum_t prev_key, new_key;
+
+ if (session->key.totp.cb) {
+ new_key.data = key;
+ new_key.size = TICKET_MASTER_KEY_SIZE;
+ prev_key.data = session->key.session_ticket_key;
+ prev_key.size = TICKET_MASTER_KEY_SIZE;
+
+ session->key.totp.cb(&prev_key, &new_key, t);
+ }
+}
+
+static int rotate(gnutls_session_t session)
+{
+ int64_t t;
+ gnutls_datum_t secret;
+ uint8_t key[TICKET_MASTER_KEY_SIZE];
+
+ /* Do we need to calculate new totp? */
+ t = totp_next(session);
+ if (t > 0) {
+ secret.data = session->key.initial_stek;
+ secret.size = TICKET_MASTER_KEY_SIZE;
+
+ /* Generate next key */
+ if (totp_sha3(session, t, &secret, key) < 0) {
+ gnutls_assert();
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+ }
+
+ /* Replace old key with new one, and call callback if provided */
+ call_rotation_callback(session, key, t);
+ session->key.totp.last_result = t;
+ memcpy(session->key.session_ticket_key, key, sizeof(key));
+
+ session->key.totp.was_rotated = 1;
+ } else if (t < 0) {
+ return gnutls_assert_val(t);
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+static int rotate_back_and_peek(gnutls_session_t session,
+ uint8_t key[TICKET_MASTER_KEY_SIZE])
+{
+ int64_t t;
+ gnutls_datum_t secret;
+
+ /* Get the previous TOTP */
+ t = totp_previous(session);
+ if (t < 0)
+ return gnutls_assert_val(t);
+
+ secret.data = session->key.initial_stek;
+ secret.size = TICKET_MASTER_KEY_SIZE;
+
+ if (totp_sha3(session, t, &secret, key) < 0)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ return 0;
+}
+
+/*
+ * _gnutls_get_session_ticket_encryption_key:
+ * @key_name: an empty datum that will receive the key name part of the STEK
+ * @mac_key: an empty datum that will receive the MAC key part of the STEK
+ * @enc_key: an empty datum that will receive the encryption key part of the STEK
+ *
+ * Get the currently active session ticket encryption key (STEK).
+ *
+ * The STEK is a 64-byte blob which is further divided in three parts,
+ * and this function requires the caller to supply three separate datums for each one.
+ * Though the caller might omit one or more of those if not interested in that part of the STEK.
+ *
+ * These are the three parts the STEK is divided in:
+ *
+ * - Key name: 16 bytes
+ * - Encryption key: 32 bytes
+ * - MAC key: 16 bytes
+ *
+ * This function will transparently rotate the key, if the time has come for that,
+ * before returning it to the caller.
+ */
+int _gnutls_get_session_ticket_encryption_key(gnutls_session_t session,
+ gnutls_datum_t *key_name,
+ gnutls_datum_t *mac_key,
+ gnutls_datum_t *enc_key)
+{
+ int retval;
+
+ if (unlikely(session == NULL)) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ if ((retval = rotate(session)) < 0)
+ return gnutls_assert_val(retval);
+
+ /* Copy key parts to user-supplied datums (if provided) */
+ if (key_name) {
+ key_name->data = &session->key.session_ticket_key[NAME_POS];
+ key_name->size = TICKET_KEY_NAME_SIZE;
+ }
+ if (mac_key) {
+ mac_key->data = &session->key.session_ticket_key[MAC_SECRET_POS];
+ mac_key->size = TICKET_MAC_SECRET_SIZE;
+ }
+ if (enc_key) {
+ enc_key->data = &session->key.session_ticket_key[KEY_POS];
+ enc_key->size = TICKET_CIPHER_KEY_SIZE;
+ }
+
+ return retval;
+}
+
+/*
+ * _gnutls_get_session_ticket_decryption_key:
+ * @ticket_data: the bytes of a session ticket that must be decrypted
+ * @key_name: an empty datum that will receive the key name part of the STEK
+ * @mac_key: an empty datum that will receive the MAC key part of the STEK
+ * @enc_key: an empty datum that will receive the encryption key part of the STEK
+ *
+ * Get the key (STEK) the given session ticket was encrypted with.
+ *
+ * As with its encryption counterpart (%_gnutls_get_session_ticket_encryption_key),
+ * this function will also transparently rotate
+ * the currently active STEK if time has come for that, and it also requires the different
+ * parts of the STEK to be obtained in different datums.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or a negative error code, such as
+ * %GNUTLS_E_REQUSTED_DATA_NOT_AVAILABLE if no key could be found for the supplied ticket.
+ */
+int _gnutls_get_session_ticket_decryption_key(gnutls_session_t session,
+ const gnutls_datum_t *ticket_data,
+ gnutls_datum_t *key_name,
+ gnutls_datum_t *mac_key,
+ gnutls_datum_t *enc_key)
+{
+ int retval;
+ gnutls_datum_t key = {
+ .data = session->key.session_ticket_key,
+ .size = TICKET_MASTER_KEY_SIZE
+ };
+
+ if (unlikely(session == NULL || ticket_data == NULL || ticket_data->data == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (ticket_data->size < TICKET_KEY_NAME_SIZE)
+ return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+ if ((retval = rotate(session)) < 0)
+ return gnutls_assert_val(retval);
+
+ /*
+ * Is current key valid?
+ * We compare the first 16 bytes --> The key_name field.
+ */
+ if (memcmp(ticket_data->data,
+ &key.data[NAME_POS],
+ TICKET_KEY_NAME_SIZE) == 0)
+ goto key_found;
+
+ key.size = TICKET_MASTER_KEY_SIZE;
+ key.data = session->key.previous_ticket_key;
+
+ /*
+ * Current key is not valid.
+ * Compute previous key and see if that matches.
+ */
+ if ((retval = rotate_back_and_peek(session, key.data)) < 0)
+ return gnutls_assert_val(retval);
+
+ if (memcmp(ticket_data->data,
+ &key.data[NAME_POS],
+ TICKET_KEY_NAME_SIZE) == 0)
+ goto key_found;
+
+ return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
+
+key_found:
+ if (key_name) {
+ key_name->data = &key.data[NAME_POS];
+ key_name->size = TICKET_KEY_NAME_SIZE;
+ }
+ if (mac_key) {
+ mac_key->data = &key.data[MAC_SECRET_POS];
+ mac_key->size = TICKET_MAC_SECRET_SIZE;
+ }
+ if (enc_key) {
+ enc_key->data = &key.data[KEY_POS];
+ enc_key->size = TICKET_CIPHER_KEY_SIZE;
+ }
+
+ return GNUTLS_E_SUCCESS;
+}
+
+/*
+ * _gnutls_initialize_session_ticket_key_rotation:
+ * @key: Initial session ticket key
+ *
+ * Initialize the session ticket key rotation.
+ *
+ * This function will not enable session ticket keys on the server side. That is done
+ * with the gnutls_session_ticket_enable_server() function. This function just initializes
+ * the internal state to support periodical rotation of the session ticket encryption key.
+ *
+ * Returns: %GNUTLS_E_SUCCESS (0) on success, or %GNUTLS_E_INVALID_REQUEST on error.
+ */
+int _gnutls_initialize_session_ticket_key_rotation(gnutls_session_t session, const gnutls_datum_t *key)
+{
+ if (unlikely(session == NULL || key == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (session->key.totp.last_result == 0) {
+ int64_t t;
+ memcpy(session->key.initial_stek, key->data, key->size);
+ t = totp_next(session);
+ if (t < 0)
+ return gnutls_assert_val(t);
+
+ session->key.totp.last_result = t;
+ session->key.totp.was_rotated = 0;
+
+ return GNUTLS_E_SUCCESS;
+ }
+
+ return GNUTLS_E_INVALID_REQUEST;
+}
+
+/*
+ * _gnutls_set_session_ticket_key_rotation_callback:
+ * @cb: the callback function
+ *
+ * Set a callback function that will be invoked every time the session ticket key
+ * is rotated.
+ *
+ * The function will take as arguments the previous key, the new key and the time
+ * step value that caused the key to rotate.
+ *
+ */
+void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session, gnutls_stek_rotation_callback_t cb)
+{
+ if (session)
+ session->key.totp.cb = cb;
+}
diff --git a/lib/stek.h b/lib/stek.h
new file mode 100644
index 0000000000..bec781edf0
--- /dev/null
+++ b/lib/stek.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 Free Software Foundation, Inc.
+ *
+ * Author: Ander Juaristi
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+#include "gnutls_int.h"
+
+int _gnutls_get_session_ticket_encryption_key(gnutls_session_t session,
+ gnutls_datum_t *key_name,
+ gnutls_datum_t *mac_key,
+ gnutls_datum_t *enc_key);
+int _gnutls_get_session_ticket_decryption_key(gnutls_session_t session,
+ const gnutls_datum_t *ticket_data,
+ gnutls_datum_t *key_name,
+ gnutls_datum_t *mac_key,
+ gnutls_datum_t *enc_key);
+
+void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session,
+ gnutls_stek_rotation_callback_t cb);
+
+int _gnutls_initialize_session_ticket_key_rotation(gnutls_session_t session,
+ const gnutls_datum_t *key);
diff --git a/lib/tls13/session_ticket.c b/lib/tls13/session_ticket.c
index 8087ba7a8b..36d5dc5260 100644
--- a/lib/tls13/session_ticket.c
+++ b/lib/tls13/session_ticket.c
@@ -31,6 +31,7 @@
#include "auth/cert.h"
#include "tls13/session_ticket.h"
#include "session_pack.h"
+#include "db.h"
static int
pack_ticket(gnutls_session_t session, tls13_ticket_t *ticket, gnutls_datum_t *packed)
@@ -422,9 +423,12 @@ int _gnutls13_unpack_session_ticket(gnutls_session_t session,
/* Return ticket parameters */
ret = unpack_ticket(session, &decrypted, ticket_data);
_gnutls_free_datum(&decrypted);
- if (ret < 0) {
+ if (ret < 0)
return ret;
- }
+
+ ret = _gnutls_check_resumed_params(session);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
return 0;
}
diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c
index 03c63c2687..7f640f1a13 100644
--- a/lib/x509/verify-high.c
+++ b/lib/x509/verify-high.c
@@ -749,6 +749,8 @@ gnutls_x509_trust_list_add_crls(gnutls_x509_trust_list_t list,
_gnutls_debug_log("CRL verification failed, not adding it\n");
if (flags & GNUTLS_TL_NO_DUPLICATES)
gnutls_x509_crl_deinit(crl_list[i]);
+ if (flags & GNUTLS_TL_FAIL_ON_INVALID_CRL)
+ return gnutls_assert_val(GNUTLS_E_CRL_VERIFICATION_ERROR);
continue;
}
}
diff --git a/src/cli.c b/src/cli.c
index 42d0277369..2fb540fd98 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -881,7 +881,8 @@ static
bool parse_for_inline_commands_in_buffer(char *buffer, size_t bytes,
inline_cmds_st * inline_cmds)
{
- ssize_t local_bytes, match_bytes, prev_bytes_copied, ii, jj;
+ ssize_t local_bytes, match_bytes, prev_bytes_copied, ii;
+ unsigned jj;
char *local_buffer_ptr, *ptr;
char inline_command_string[MAX_INLINE_COMMAND_BYTES];
ssize_t l;
@@ -1765,6 +1766,7 @@ static void init_global_tls_stuff(void)
gnutls_certificate_set_pin_function(xcred, pin_callback, NULL);
gnutls_certificate_set_verify_flags(xcred, global_vflags);
+ gnutls_certificate_set_flags(xcred, GNUTLS_CERTIFICATE_VERIFY_CRLS);
if (x509_cafile != NULL) {
ret = gnutls_certificate_set_x509_trust_file(xcred,
@@ -1773,12 +1775,17 @@ static void init_global_tls_stuff(void)
} else {
if (insecure == 0) {
ret = gnutls_certificate_set_x509_system_trust(xcred);
+ if (ret == GNUTLS_E_UNIMPLEMENTED_FEATURE) {
+ fprintf(stderr, "Warning: this system doesn't support a default trust store\n");
+ ret = 0;
+ }
} else {
ret = 0;
}
}
if (ret < 0) {
- fprintf(stderr, "Error setting the x509 trust file\n");
+ fprintf(stderr, "Error setting the x509 trust file: %s\n", gnutls_strerror(ret));
+ exit(1);
} else {
printf("Processed %d CA certificate(s).\n", ret);
}
@@ -1790,7 +1797,8 @@ static void init_global_tls_stuff(void)
x509ctype);
if (ret < 0) {
fprintf(stderr,
- "Error setting the x509 CRL file\n");
+ "Error setting the x509 CRL file: %s\n", gnutls_strerror(ret));
+ exit(1);
} else {
printf("Processed %d CRL(s).\n", ret);
}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 90e0fd71a1..3680437f16 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -204,7 +204,8 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
ip-check mini-x509-ipaddr trust-store base64-raw random-art dhex509self \
dss-sig-val sign-pk-api tls-session-ext-override record-pad \
tls13-server-kx-neg gnutls_ext_raw_parse_dtls key-export-pkcs8 \
- null_retrieve_function tls-record-size-limit tls-crt_type-neg
+ null_retrieve_function tls-record-size-limit tls-crt_type-neg \
+ resume-with-stek-expiration resume-with-previous-stek
if HAVE_SECCOMP_TESTS
ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp
@@ -436,7 +437,7 @@ dist_check_SCRIPTS += fastopen.sh pkgconfig.sh starttls.sh starttls-ftp.sh start
psktool.sh ocsp-tests/ocsp-load-chain gnutls-cli-save-data.sh gnutls-cli-debug.sh \
sni-resume.sh ocsp-tests/ocsptool cert-reencoding.sh pkcs7-cat.sh long-crl.sh
-dist_check_SCRIPTS += gnutls-cli-self-signed.sh
+dist_check_SCRIPTS += gnutls-cli-self-signed.sh gnutls-cli-invalid-crl.sh
if ENABLE_PKCS11
dist_check_SCRIPTS += p11-kit-trust.sh testpkcs11.sh certtool-pkcs11.sh
diff --git a/tests/gnutls-cli-invalid-crl.sh b/tests/gnutls-cli-invalid-crl.sh
new file mode 100755
index 0000000000..7b285c4264
--- /dev/null
+++ b/tests/gnutls-cli-invalid-crl.sh
@@ -0,0 +1,188 @@
+#!/bin/sh
+
+# Copyright (C) 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/>
+
+srcdir="${srcdir:-.}"
+SERV="${SERV:-../src/gnutls-serv${EXEEXT}}"
+CLI="${CLI:-../src/gnutls-cli${EXEEXT}}"
+unset RETCODE
+TMPFILE=crl-inv.$$.pem.tmp
+CAFILE=crl-inv-ca.$$.pem.tmp
+CRLFILE=crl-inv-crl.$$.pem.tmp
+
+if ! test -x "${SERV}"; then
+ exit 77
+fi
+
+if ! test -x "${CLI}"; then
+ exit 77
+fi
+
+if test "${WINDIR}" != ""; then
+ exit 77
+fi
+
+if ! test -z "${VALGRIND}"; then
+ VALGRIND="${LIBTOOL:-libtool} --mode=execute ${VALGRIND} --error-exitcode=15"
+fi
+
+SERV="${SERV} -q"
+
+. "${srcdir}/scripts/common.sh"
+
+check_for_datefudge
+
+echo "Checking whether connecting to a server but with an invalid CRL provided, returns the expected error"
+
+cat <<__EOF__ >${TMPFILE}
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAxfNimQ1uOFXUSVCm0lBems4HpfLkW1Ykf5qLd9kdoHte7YAs
+BHjFPaPSdXitYI36YMwqVcXT6RDJa0mcAV3QmMMxAnpKq7LIDVC9BNgjc7Dq5ou5
+X2wNKrs3ygqg6HR87nJaw9TFqKetoP9mX37igBc2QWg5Fx6/Gem57hwD+mBEs+Hv
+jd7q4wDlLaNS/165DBECr5dDUAIVr0bh0+1s/rDzIpjuq1qtN7b0C1rmWlsyphYu
+aYm07X7x6hZcjvAoM3w5FLzbOnS6QrBeQOc2J6VBHqaHMKEVc+Dwt+Ggn0De0QCK
+ucQRUCO5DQDUZnVLZhUpObvm1cBvQd5Db15IiwIDAQABAoIBACUSqhqkC0p9uJ5q
+fnPRHYa8o24PCXmZrog/d3NgtE3EDUlJwfSscbRTpCzgBwiYTpYmZp9dx4xU2oQ/
+avpOiayykdE2+hkiCJmFz4DCwhD+x1+aN4OJhwXDvnUWfIBMoME/pYQbq1Ek5j3K
+1293IhB/SGgDjv2ngn7l6S6RDKWtYZry61oMEoVRy96FJ+88o5khlvfWE2zF2+M6
+T2qFbzO29oq++cDSIlgm9eSN6gG5uzZcxqTapEvWrRdKZfEqcyGJuysQbShrASvI
+GvJclewdnguBW2+X/bwABSEaG7AdPZJdfQJayk97gKJ8xpFZLY9auub7O/0z1CJi
+lFsj4LECgYEA5TY8Z73ODtR87HEE3uUqiix4wPO4yJXWfZUwxNAyet2Jx5e5HYvL
+iEkbZdadlKtSoPTnVSu6OZxhWZVBS5WoxxijBneDvh7I6gN8eVtch9EJVmJig6Eg
+kHTo5Z2ZwheGe/RxB3ml3IT2IAdr5+QE6CfVBNA0fzVTItCLgO3YI/8CgYEA3RXZ
+yskckcbCr1rceRmQ8CPbKg1bWGujLMpTILW0/Ii51PMredyG3E063G4kbMOFRmVj
+eI5AFgZX7w5N4vjaf8PbOhsqrQvQ/UglB1fD0tLX8LgF9xwh7P1Y4VLHFMEGJUy1
+PEGVCT0FIe2REGxAmyELaP8SSvW8fGjXJSp2K3UCgYBSlq5BOxTKJyo0D60Pm0cu
+rkN8UtUcAVFdwqnl4Javyq9gaXzb9okJvD3Q/fmdnfWR5WyNNcpOA9jX7H2wfGZq
+BqiHJf0kPfdqyoLJP3Ahx+IzbBPPFfmj01wvkA/c7ZkZhMRNSznGMWp1s/bfgTt7
+Yw7QQy0HQPGJs9bwR8L/hQKBgQCXFvvEbjSsG12pYTsTN7mpo5d/4ajvgH//eDXf
+QM7zVq1JLvYjTeaMX+s+Abe67NQEC/4ywWRiqOsnYGsyFkec0UjdKPu9TzoAHnHP
+1tbpGVaiF+Fbw0ocH/fB5URQlqmQjB+/kkI8EguT6DsfMhvk6GxX0Rm7SL0LeMqv
+h5lCkQKBgAR2U6cjbzJRhDyEOmUJH2keYHDwWUMx8ypvfhbPiPJyTC2sDcRrMrnO
+WB3NtiB88aLFPjZ7sFZYE5plCESGkxK4Y21/UJHlw3I7X4JKYslE7dMq8Qzbv58r
+23fZkHop4UJ1bHk7O4FRL3brU6KlIzZTOXzEeP+MRRehhwzkwpxf
+-----END RSA PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIDiTCCAkGgAwIBAgIUEOtG5aJHVFm4ARA8uv4bJ/OqL4YwPQYJKoZIhvcNAQEK
+MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC
+AUAwDzENMAsGA1UEAxMEQ0EtMDAgFw0xODA5MTgwNjQyMzdaGA85OTk5MTIzMTIz
+NTk1OVowEzERMA8GA1UEAxMIc2VydmVyLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDF82KZDW44VdRJUKbSUF6azgel8uRbViR/mot32R2ge17tgCwE
+eMU9o9J1eK1gjfpgzCpVxdPpEMlrSZwBXdCYwzECekqrssgNUL0E2CNzsOrmi7lf
+bA0quzfKCqDodHzuclrD1MWop62g/2ZffuKAFzZBaDkXHr8Z6bnuHAP6YESz4e+N
+3urjAOUto1L/XrkMEQKvl0NQAhWvRuHT7Wz+sPMimO6rWq03tvQLWuZaWzKmFi5p
+ibTtfvHqFlyO8CgzfDkUvNs6dLpCsF5A5zYnpUEepocwoRVz4PC34aCfQN7RAIq5
+xBFQI7kNANRmdUtmFSk5u+bVwG9B3kNvXkiLAgMBAAGjdzB1MAwGA1UdEwEB/wQC
+MAAwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA8GA1UdDwEB/wQFAwMHoAAwHQYDVR0O
+BBYEFJVJTYVERYv5/qI31HwTDqATv4GRMB8GA1UdIwQYMBaAFBnn35UaLvLuW/YH
+E3v2gKntMzNNMD0GCSqGSIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJKoZI
+hvcNAQEIMAsGCWCGSAFlAwQCAaIDAgFAA4IBAQCPVloFdhqdJqGjhxpl2Wv2ftD3
+w+IeHSqURyCeijUCIOkqMlA3085nuoULiJ0p1ryi8rRWOvNjRsRQ30/lnLsxfmMh
+oVR+g9uq0YZcFqkeRL5aDTrfJZWFeVSqXuuJvhyw1el5hs4bDSahMFB+dx2G+3zX
+Ycd4Sq3sXDkdLnfD9GSeVvvbzAb7Z7qD1cdh1HaEnX2fsXT69czsFiaTgknr3Vxc
+P0yFZVNCT360EVsduLkLWnCqZYVWWDFUlut7SOwhsYUx2ZOoM4RuBy+uDF2PM8BP
+BkgYEHeWFA31nnwBNePyvWrAZ1DguOvnETSMB/+8zDX3+teNZNNdTVTQ6ypQ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDgTCCAjmgAwIBAgIUE8klaC2IZj3Tr2/jEVEiJGj8piYwPQYJKoZIhvcNAQEK
+MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC
+AUAwDzENMAsGA1UEAxMEQ0EtMDAgFw0xODA5MTgwNjQyMzdaGA85OTk5MTIzMTIz
+NTk1OVowDzENMAsGA1UEAxMEQ0EtMDCCAVIwPQYJKoZIhvcNAQEKMDCgDTALBglg
+hkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCAUADggEPADCC
+AQoCggEBALUJWYFxTq3vWG+hZq8KsRe0YRf5pqftxR21uZ7MSr25Muo7/s69toZG
+7SaV1ZFp2n+Njm96nRLDqCc7cnaPLpKeMBFI84pQOYMdJs2mxs7wrBvejTBpxw3f
+o1L2cJWznXZwvDQd+iz3qt62kF53tjpUzQ0Cqn6AMU961+H99Tq39iONcAvmTYeT
+Bf+P4jhg3h5cOkdhsB4zrr0ek0OdgSdHiTIWvmYbEvizwhBc8pLOc007FkslqlQ5
+b7Fplx/B+v/etqUoW7/742phxJhTjhRW75BWoCiQyhglwUfpDv0tXnMXousXdwaQ
+Ao1EM1v/OCsYj/U2u10Bo/5y1q6Jjz8CAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB
+/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBQZ59+VGi7y7lv2BxN79oCp7TMz
+TTA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3DQEBCDAL
+BglghkgBZQMEAgGiAwIBQAOCAQEAMAgvcHqmjz1Ox5USoup5pe6HWPKtOR5pVGX2
+1zAk1wq7GoTKvo5QA6HtNR0ex1A2//XhklAKcqsIv1ELEh/3K/L0dEuaN4Zs784e
+zaP0g/Ax6X3ClrHgARA4FA6MtaQblezj+7Zfc6cg1gKtfYleiOoK/Q+kk6JxOYAH
+Lz9MF/6bZ8mYJQv8DURSp2p5NVWSEjbQV5IG2dw/eknZtbFaN5b+db3eVtrK0ZeS
+l1e3hTwopCLNoh4qHUW/qKl0l1Gt7kPPxAsRReOxdcb1Pv73iuK7w5wbPyyWp0kM
+FQj9tqRIMQZIer3gaURWG8OZfntCAvtlSSwc1PjwLBXO9ZvNBw==
+-----END CERTIFICATE-----
+__EOF__
+
+cat <<__EOF__ >${CRLFILE}
+-----BEGIN X509 CRL-----
+MIIB/TCBtgIBATA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqG
+SIb3DQEBCDALBglghkgBZQMEAgGiAwIBQDAPMQ0wCwYDVQQDEwRDQS0wFw0xODA5
+MTgwNjQyMzdaFw0xOTA5MTgwNjQyMzdaMACgQTA/MB8GA1UdIwQYMBaAFBnn35Ua
+LvLuW/YHE3v2gKntMzNNMBwGA1UdFAQVAhNboJ5dKaGvdv1Vo9o1XXTbeiMKMD0G
+CSqGSIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCG
+SAFlAwQCAaIDAgFAA4IBAQBgodBpVGTDHV4HBSgNPUnz7BH/BdRX1OPB8oYclDtv
+l0xTzRR4qm/dMU3N3iH7vMk2y8U/TwD7NueyUnumt0vATTfjR2cle5lu2czksYsR
+e4As9cI5cb4Sk+cf3/HyAVwnmZemTAA+cAJHkL6p7E+mSUoBVB6m8h8d6RH8jXmO
+BXBE3z1xVITqahDdD6sLaR5jpnOtg/1nBAW8Hzr2p8tjEwhI8TCfZXbL9Q6fZtTr
+apDrIx0D/G4hDKmmtQeY2q3RCOSJldg4YzUjjuhWs6BahHj9jDJpz02180ao7bda
+eoNetNEqNvBvFvkO9gtgSzOzS34taiMpkIBwBbCNkm4p
+-----END X509 CRL-----
+__EOF__
+
+cat <<__EOF__ >${CAFILE}
+-----BEGIN CERTIFICATE-----
+MIIDgTCCAjmgAwIBAgIUE8klaC2IZj3Tr2/jEVEiJGj8piYwPQYJKoZIhvcNAQEK
+MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC
+AUAwDzENMAsGA1UEAxMEQ0EtMDAgFw0xODA5MTgwNjQyMzdaGA85OTk5MTIzMTIz
+NTk1OVowDzENMAsGA1UEAxMEQ0EtMDCCAVIwPQYJKoZIhvcNAQEKMDCgDTALBglg
+hkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCAUADggEPADCC
+AQoCggEBALUJWYFxTq3vWG+hZq8KsRe0YRf5pqftxR21uZ7MSr25Muo7/s69toZG
+7SaV1ZFp2n+Njm96nRLDqCc7cnaPLpKeMBFI84pQOYMdJs2mxs7wrBvejTBpxw3f
+o1L2cJWznXZwvDQd+iz3qt62kF53tjpUzQ0Cqn6AMU961+H99Tq39iONcAvmTYeT
+Bf+P4jhg3h5cOkdhsB4zrr0ek0OdgSdHiTIWvmYbEvizwhBc8pLOc007FkslqlQ5
+b7Fplx/B+v/etqUoW7/742phxJhTjhRW75BWoCiQyhglwUfpDv0tXnMXousXdwaQ
+Ao1EM1v/OCsYj/U2u10Bo/5y1q6Jjz8CAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB
+/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBQZ59+VGi7y7lv2BxN79oCp7TMz
+TTA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3DQEBCDAL
+BglghkgBZQMEAgGiAwIBQAOCAQEAMAgvcHqmjz1Ox5USoup5pe6HWPKtOR5pVGX2
+1zAk1wq7GoTKvo5QA6HtNR0ex1A2//XhklAKcqsIv1ELEh/3K/L0dEuaN4Zs784e
+zaP0g/Ax6X3ClrHgARA4FA6MtaQblezj+7Zfc6cg1gKtfYleiOoK/Q+kk6JxOYAH
+Lz9MF/6bZ8mYJQv8DURSp2p5NVWSEjbQV5IG2dw/eknZtbFaN5b+db3eVtrK0ZeS
+l1e3hTwopCLNoh4qHUW/qKl0l1Gt7kPPxAsRReOxdcb1Pv73iuK7w5wbPyyWp0kM
+FQj9tqRIMQZIer3gaURWG8OZfntCAvtlSSwc1PjwLBXO9ZvNBw==
+-----END CERTIFICATE-----
+__EOF__
+
+eval "${GETPORT}"
+launch_server $$ --echo --x509keyfile ${TMPFILE} --x509certfile ${TMPFILE}
+PID=$!
+wait_server ${PID}
+
+datefudge "2018-9-19" \
+${VALGRIND} "${CLI}" -p "${PORT}" localhost --x509crlfile ${CRLFILE} --x509cafile ${CAFILE} >${TMPFILE} 2>&1 </dev/null && \
+ fail ${PID} "1. handshake should have failed!"
+
+
+kill ${PID}
+wait
+
+grep -E "Error setting the x509 CRL file: Error in the CRL verification.[[:space:]]*\$" ${TMPFILE}
+if ! test $? = 0;then
+ echo "Did not find the expected error code"
+ cat ${TMPFILE}
+ exit 1
+fi
+
+rm -f ${TMPFILE} ${CAFILE} ${CRLFILE}
+
+exit 0
diff --git a/tests/gnutls-cli-self-signed.sh b/tests/gnutls-cli-self-signed.sh
index 7ed1c212db..b5058f69ae 100755
--- a/tests/gnutls-cli-self-signed.sh
+++ b/tests/gnutls-cli-self-signed.sh
@@ -124,7 +124,7 @@ PID=$!
wait_server ${PID}
datefudge "2018-1-1" \
-${VALGRIND} "${CLI}" -p "${PORT}" localhost >${TMPFILE} 2>&1 && \
+${VALGRIND} "${CLI}" -p "${PORT}" localhost >${TMPFILE} 2>&1 </dev/null && \
fail ${PID} "1. handshake should have failed!"
diff --git a/tests/resume-with-previous-stek.c b/tests/resume-with-previous-stek.c
new file mode 100644
index 0000000000..2b2a4794a0
--- /dev/null
+++ b/tests/resume-with-previous-stek.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2018 Free Software Foundation, Inc.
+ *
+ * Author: Ander Juaristi
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+#else
+
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <gnutls/gnutls.h>
+#include <assert.h>
+#include "utils.h"
+#include "cert-common.h"
+#include "virt-time.h"
+
+#define TICKET_EXPIRATION 1 /* seconds */
+#define TICKET_ROTATION_PERIOD 3 /* seconds */
+
+unsigned num_stek_rotations;
+
+static void stek_rotation_callback(const gnutls_datum_t *prev_key,
+ const gnutls_datum_t *new_key,
+ uint64_t t)
+{
+ num_stek_rotations++;
+ success("STEK was rotated!\n");
+}
+
+static int client_handshake(gnutls_session_t session, gnutls_datum_t *session_data,
+ int resume)
+{
+ int ret;
+
+ if (resume) {
+ if ((ret = gnutls_session_set_data(session,
+ session_data->data,
+ session_data->size)) < 0) {
+ fail("client: Could not get session data\n");
+ }
+ }
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0) {
+ fail("client: Handshake failed\n");
+ } else {
+ success("client: Handshake was completed\n");
+ }
+
+ if (gnutls_session_is_resumed(session))
+ fail("client: Session was resumed (but should not)\n");
+ else
+ success("client: Success: Session was NOT resumed\n");
+
+ if (!resume) {
+ if ((ret = gnutls_session_get_data2(session, session_data)) < 0) {
+ fail("client: Could not get session data\n");
+ }
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ return 0;
+}
+
+static void client(int fd, int *resume, unsigned rounds, const char *prio)
+{
+ gnutls_session_t session;
+ gnutls_datum_t session_data;
+ gnutls_certificate_credentials_t clientx509cred = NULL;
+
+ for (unsigned i = 0; i < rounds; i++) {
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred)>=0);
+
+ assert(gnutls_init(&session, GNUTLS_CLIENT)>=0);
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ /* Perform TLS handshake and obtain session ticket */
+ if (client_handshake(session, &session_data,
+ resume[i]) < 0)
+ return;
+
+ if (clientx509cred) {
+ gnutls_certificate_free_credentials(clientx509cred);
+ clientx509cred = NULL;
+ }
+
+ gnutls_deinit(session);
+ }
+}
+
+typedef void (* gnutls_stek_rotation_callback_t) (const gnutls_datum_t *prev_key,
+ const gnutls_datum_t *new_key,
+ uint64_t t);
+void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session,
+ gnutls_stek_rotation_callback_t cb);
+
+static void server(int fd, unsigned rounds, const char *prio)
+{
+ int retval;
+ gnutls_session_t session;
+ gnutls_datum_t session_ticket_key = { NULL, 0 };
+ gnutls_certificate_credentials_t serverx509cred = NULL;
+
+ virt_time_init();
+
+ if (gnutls_session_ticket_key_generate(&session_ticket_key) < 0) {
+ fail("server: Could not generate session ticket key\n");
+ }
+
+ for (unsigned i = 0; i < rounds; i++) {
+ assert(gnutls_init(&session, GNUTLS_SERVER)>=0);
+
+ assert(gnutls_certificate_allocate_credentials(&serverx509cred)>=0);
+ retval = gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+ if (retval < 0)
+ fail("error setting key: %s\n", gnutls_strerror(retval));
+
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred);
+
+ gnutls_db_set_cache_expiration(session, TICKET_EXPIRATION);
+ _gnutls_set_session_ticket_key_rotation_callback(session, stek_rotation_callback);
+
+ retval = gnutls_session_ticket_enable_server(session,
+ &session_ticket_key);
+ if (retval != GNUTLS_E_SUCCESS) {
+ fail("server: Could not enable session tickets: %s\n", gnutls_strerror(retval));
+ }
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ virt_sec_sleep(TICKET_ROTATION_PERIOD-1);
+
+ do {
+ retval = gnutls_handshake(session);
+ } while (retval == GNUTLS_E_AGAIN || retval == GNUTLS_E_INTERRUPTED);
+
+ if (retval < 0) {
+ fail("server: Handshake failed: %s\n", gnutls_strerror(retval));
+ } else {
+ success("server: Handshake was completed\n");
+ }
+
+ if (gnutls_session_is_resumed(session))
+ fail("server: Session was resumed (but should not)\n");
+ else
+ success("server: Success: Session was NOT resumed\n");
+
+ gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(serverx509cred);
+ serverx509cred = NULL;
+ }
+
+ if (num_stek_rotations != 2)
+ fail("STEK should be rotated exactly twice (%d)!\n", num_stek_rotations);
+
+ if (serverx509cred)
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_free(session_ticket_key.data);
+}
+
+static void run(const char *name, const char *prio, int resume[], int rounds)
+{
+ pid_t child;
+ int retval, sockets[2], status = 0;
+
+ success("\ntesting %s\n\n", name);
+
+ retval = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (retval == -1) {
+ perror("socketpair");
+ fail("socketpair failed");
+ return;
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork failed");
+ return;
+ }
+
+ if (child) {
+ /* We are the parent */
+ server(sockets[0], rounds, prio);
+ waitpid(child, &status, 0);
+ check_wait_status(status);
+ } else {
+ /* We are the child */
+ client(sockets[1], resume, rounds, prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ int resume[] = { 0, 1, 0 };
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ num_stek_rotations = 0;
+ run("tls1.2 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0", resume, 3);
+
+ num_stek_rotations = 0;
+ run("tls1.3 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.3", resume, 3);
+}
+
+#endif
+
diff --git a/tests/resume-with-stek-expiration.c b/tests/resume-with-stek-expiration.c
new file mode 100644
index 0000000000..7a6b532d63
--- /dev/null
+++ b/tests/resume-with-stek-expiration.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2018 Free Software Foundation, Inc.
+ *
+ * Author: Ander Juaristi
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+#else
+
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <gnutls/gnutls.h>
+#include <assert.h>
+#include "utils.h"
+#include "cert-common.h"
+#include "virt-time.h"
+
+/*
+ * This will set the following values:
+ *
+ * - Ticket key expiration: 1 second.
+ * - Session ticket key rotation period: 3 seconds.
+ */
+#define TICKET_EXPIRATION 1 /* seconds */
+
+unsigned num_stek_rotations;
+
+static void stek_rotation_callback(const gnutls_datum_t *prev_key,
+ const gnutls_datum_t *new_key,
+ uint64_t t)
+{
+ num_stek_rotations++;
+ success("STEK was rotated!\n");
+}
+
+typedef void (* gnutls_stek_rotation_callback_t) (const gnutls_datum_t *prev_key,
+ const gnutls_datum_t *new_key,
+ uint64_t t);
+void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session,
+ gnutls_stek_rotation_callback_t cb);
+
+static int handshake(gnutls_session_t session, gnutls_datum_t *session_data,
+ int resumption_should_succeed)
+{
+ int ret;
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0) {
+ gnutls_perror(ret);
+ fail("client: Handshake failed\n");
+ } else {
+ success("client: Handshake was completed\n");
+ }
+
+ if (gnutls_session_is_resumed(session)) {
+ if (!resumption_should_succeed)
+ fail("client: Session was resumed (but should not)\n");
+ else
+ success("client: Success: Session was resumed\n");
+ } else {
+ if (resumption_should_succeed)
+ fail("client: Session was not resumed (but should)\n");
+ else
+ success("client: Success: Session was NOT resumed\n");
+ }
+
+ ret = gnutls_session_get_data2(session, session_data);
+ if (ret < 0) {
+ gnutls_perror(ret);
+ fail("client: Could not get session data\n");
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ return 0;
+}
+
+static int resume_and_close(gnutls_session_t session, gnutls_datum_t *session_data,
+ int resumption_should_succeed)
+{
+ int ret;
+
+ ret = gnutls_session_set_data(session, session_data->data, session_data->size);
+ if (ret < 0) {
+ gnutls_perror(ret);
+ fail("client: Could not get session data\n");
+ }
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && !gnutls_error_is_fatal(ret));
+
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n", gnutls_strerror(ret));
+ } else {
+ success("client: Handshake was completed\n");
+ }
+
+ if (gnutls_session_is_resumed(session)) {
+ if (!resumption_should_succeed)
+ fail("client: Session was resumed (but should not)\n");
+ else
+ success("client: Success: Session was resumed\n");
+ } else {
+ if (resumption_should_succeed)
+ fail("client: Session was not resumed (but should)\n");
+ else
+ success("client: Success: Session was NOT resumed\n");
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ return 0;
+}
+
+static void client(int fd, int *resumption_should_succeed, unsigned num_sessions, const char *prio)
+{
+ gnutls_session_t session;
+ gnutls_datum_t session_data;
+ gnutls_certificate_credentials_t clientx509cred = NULL;
+
+ gnutls_certificate_allocate_credentials(&clientx509cred);
+
+ /* Initialize TLS layer */
+ gnutls_init(&session, GNUTLS_CLIENT);
+ gnutls_priority_set_direct(session, prio, NULL);
+
+ /* put the anonymous credentials to the current session */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ if (handshake(session, &session_data, resumption_should_succeed[0]) < 0)
+ return;
+
+ if (clientx509cred)
+ gnutls_certificate_free_credentials(clientx509cred);
+ gnutls_deinit(session);
+
+ for (unsigned i = 1; i < num_sessions; i++) {
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred)>=0);
+
+ /* Initialize TLS layer */
+ assert(gnutls_init(&session, GNUTLS_CLIENT)>=0);
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+
+ /* put the anonymous credentials to the current session */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ if (resume_and_close(session, &session_data, resumption_should_succeed[i]) < 0)
+ return;
+
+ if (clientx509cred)
+ gnutls_certificate_free_credentials(clientx509cred);
+ gnutls_deinit(session);
+ }
+}
+
+static void server(int fd, int *resumption_should_succeed, unsigned num_sessions, const char *prio)
+{
+ int retval;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_datum_t session_ticket_key = { NULL, 0 };
+
+ virt_time_init();
+
+ if (gnutls_session_ticket_key_generate(&session_ticket_key) < 0)
+ fail("server: Could not generate session ticket key\n");
+
+ for (unsigned i = 0; i < num_sessions; i++) {
+ if ((retval = gnutls_init(&session, GNUTLS_SERVER)) < 0) {
+ gnutls_perror(retval);
+ fail("gnutls_init() failed\n");
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&serverx509cred)>=0);
+ assert(gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred);
+
+ retval = gnutls_session_ticket_enable_server(session, &session_ticket_key);
+ if (retval != GNUTLS_E_SUCCESS) {
+ gnutls_perror(retval);
+ fail("server: Could not enable session tickets\n");
+ }
+
+
+ gnutls_db_set_cache_expiration(session, TICKET_EXPIRATION);
+
+ _gnutls_set_session_ticket_key_rotation_callback(session, stek_rotation_callback);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_handshake_set_timeout(session, 20 * 1000);
+
+ do {
+ retval = gnutls_handshake(session);
+ } while(retval == GNUTLS_E_AGAIN || retval == GNUTLS_E_INTERRUPTED);
+
+ if (retval < 0) {
+ fail("server: Handshake failed: %s\n", gnutls_strerror(retval));
+ } else {
+ success("server: Handshake was completed\n");
+ }
+
+ if (gnutls_session_is_resumed(session)) {
+ if (!resumption_should_succeed[i])
+ fail("server: Session was resumed (but should not)\n");
+ else
+ success("server: Success: Session was resumed\n");
+ } else {
+ if (resumption_should_succeed[i])
+ fail("server: Session was not resumed (but should)\n");
+ else
+ success("server: Success: Session was NOT resumed\n");
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(serverx509cred);
+ serverx509cred = NULL;
+
+ if (i != 0)
+ virt_sec_sleep(TICKET_EXPIRATION);
+ }
+
+ if (num_stek_rotations != 4)
+ fail("STEK should be rotated exactly 4 times!\n");
+
+ if (serverx509cred)
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_free(session_ticket_key.data);
+}
+
+static void run(const char *name, const char *prio, int resumption_should_succeed[], int rounds)
+{
+ pid_t child;
+ int retval, sockets[2], status = 0;
+
+ success("\ntesting %s\n\n", name);
+
+ retval = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (retval == -1) {
+ perror("socketpair");
+ fail("socketpair failed");
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork failed");
+ }
+
+ if (child) {
+ /* We are the parent */
+ server(sockets[0], resumption_should_succeed, rounds, prio);
+ waitpid(child, &status, 0);
+ check_wait_status(status);
+ } else {
+ /* We are the child */
+ client(sockets[1], resumption_should_succeed, rounds, prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ int resumption_should_succeed[] = { 0, 1, 1, 0 };
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ num_stek_rotations = 0;
+ run("tls1.2 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0",
+ resumption_should_succeed, 4);
+
+ num_stek_rotations = 0;
+ run("tls1.3 resumption", "NORMAL:-VERS-ALL:+VERS-TLS1.3",
+ resumption_should_succeed, 4);
+}
+
+#endif
diff --git a/tests/resume.c b/tests/resume.c
index 953c017388..84314b836c 100644
--- a/tests/resume.c
+++ b/tests/resume.c
@@ -56,6 +56,7 @@ int main(int argc, char **argv)
#include <assert.h>
#include "utils.h"
#include "cert-common.h"
+#include "virt-time.h"
static void wrap_db_init(void);
static void wrap_db_deinit(void);
@@ -528,8 +529,6 @@ static void client(int sds[], struct params_res *params)
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));
@@ -722,6 +721,8 @@ static void server(int sds[], struct params_res *params)
gnutls_group_t pgroup;
unsigned iflags = GNUTLS_SERVER;
+ virt_time_init();
+
if (params->early_start || params->no_early_start)
iflags |= GNUTLS_ENABLE_EARLY_START;
@@ -794,9 +795,10 @@ static void server(int sds[], struct params_res *params)
append_alpn(session, params, t);
- if (params->expire_ticket)
- gnutls_db_set_cache_expiration(session, 1);
-
+ if (params->expire_ticket) {
+ gnutls_db_set_cache_expiration(session, 45);
+ virt_sec_sleep(60);
+ }
#ifdef USE_PSK
gnutls_credentials_set(session, GNUTLS_CRD_PSK, pskcred);
#elif defined(USE_ANON)