summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@redhat.com>2017-08-11 15:16:51 +0200
committerNikos Mavrogiannopoulos <nmav@redhat.com>2017-11-14 15:00:32 +0100
commit37412c853490917c58a8ad03e68701249d82d59a (patch)
treed5f936b464620026576e03010b4051d0c6af7872
parent8b421ba60de6a3ddf0cc1258b083fa062da12f11 (diff)
downloadgnutls-37412c853490917c58a8ad03e68701249d82d59a.tar.gz
record: added TLS 1.3 record parsing and key derivation
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
-rw-r--r--lib/algorithms/ciphers.c5
-rw-r--r--lib/cipher.c275
-rw-r--r--lib/cipher.h2
-rw-r--r--lib/constate.c202
-rw-r--r--lib/constate.h2
-rw-r--r--lib/gnutls_int.h13
-rw-r--r--lib/handshake-tls13.c35
-rw-r--r--lib/handshake.c38
-rw-r--r--lib/range.c5
-rw-r--r--lib/record.c27
10 files changed, 525 insertions, 79 deletions
diff --git a/lib/algorithms/ciphers.c b/lib/algorithms/ciphers.c
index 04d675acda..5f04eba72e 100644
--- a/lib/algorithms/ciphers.c
+++ b/lib/algorithms/ciphers.c
@@ -31,8 +31,9 @@
* View first: "The order of encryption and authentication for
* protecting communications" by Hugo Krawczyk - CRYPTO 2001
*
- * On update, make sure to update MAX_CIPHER_BLOCK_SIZE and MAX_CIPHER_KEY_SIZE
- * as well. If any ciphers are removed, remove them from the back-end but
+ * On update, make sure to update MAX_CIPHER_BLOCK_SIZE, MAX_CIPHER_IV_SIZE,
+ * and MAX_CIPHER_KEY_SIZE as well.
+ * If any ciphers are removed, remove them from the back-end but
* keep them in that list to allow backwards compatibility with applications
* that specify them (they will be a no-op).
*/
diff --git a/lib/cipher.c b/lib/cipher.c
index 795255bbf4..a380a71d75 100644
--- a/lib/cipher.c
+++ b/lib/cipher.c
@@ -48,13 +48,31 @@ static int encrypt_packet(gnutls_session_t session,
size_t min_pad,
content_type_t _type,
record_parameters_st * params);
+
static int decrypt_packet(gnutls_session_t session,
gnutls_datum_t * ciphertext,
gnutls_datum_t * plain,
- uint8_t type,
+ content_type_t type,
record_parameters_st * params,
gnutls_uint64 * sequence);
+static int
+decrypt_packet_tls13(gnutls_session_t session,
+ gnutls_datum_t * ciphertext,
+ gnutls_datum_t * plain,
+ content_type_t *type, record_parameters_st * params,
+ gnutls_uint64 * sequence);
+
+static int
+encrypt_packet_tls13(gnutls_session_t session,
+ uint8_t * cipher_data, size_t cipher_size,
+ gnutls_datum_t * plain,
+ size_t min_pad,
+ content_type_t type,
+ record_parameters_st * params);
+
+
+
/* returns ciphertext which contains the headers too. This also
* calculates the size in the header field.
*
@@ -67,17 +85,26 @@ _gnutls_encrypt(gnutls_session_t session,
content_type_t type, record_parameters_st * params)
{
gnutls_datum_t plaintext;
+ const version_entry_st *vers = get_version(session);
int ret;
plaintext.data = (uint8_t *) data;
plaintext.size = data_size;
- ret =
- encrypt_packet(session,
- _mbuffer_get_udata_ptr(bufel),
- _mbuffer_get_udata_size
- (bufel), &plaintext, min_pad, type,
- params);
+ if (vers && vers->tls13_sem)
+ ret =
+ encrypt_packet_tls13(session,
+ _mbuffer_get_udata_ptr(bufel),
+ _mbuffer_get_udata_size
+ (bufel), &plaintext, min_pad, type,
+ params);
+ else
+ ret =
+ encrypt_packet(session,
+ _mbuffer_get_udata_ptr(bufel),
+ _mbuffer_get_udata_size
+ (bufel), &plaintext, min_pad, type,
+ params);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -105,18 +132,25 @@ int
_gnutls_decrypt(gnutls_session_t session,
gnutls_datum_t * ciphertext,
gnutls_datum_t * output,
- content_type_t type,
+ content_type_t *type,
record_parameters_st * params, gnutls_uint64 * sequence)
{
int ret;
+ const version_entry_st *vers = get_version(session);
if (ciphertext->size == 0)
return 0;
- ret =
- decrypt_packet(session, ciphertext,
- output, type, params,
- sequence);
+ if (vers && vers->tls13_sem)
+ ret =
+ decrypt_packet_tls13(session, ciphertext,
+ output, type, params,
+ sequence);
+ else
+ ret =
+ decrypt_packet(session, ciphertext,
+ output, *type, params,
+ sequence);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -220,15 +254,15 @@ encrypt_packet(gnutls_session_t session,
uint8_t preamble[MAX_PREAMBLE_SIZE];
int preamble_size;
int tag_size =
- _gnutls_auth_cipher_tag_len(&params->write.cipher_state);
+ _gnutls_auth_cipher_tag_len(&params->write.ctx.tls12);
int blocksize = _gnutls_cipher_get_block_size(params->cipher);
unsigned algo_type = _gnutls_cipher_type(params->cipher);
uint8_t *data_ptr, *full_cipher_ptr;
const version_entry_st *ver = get_version(session);
int explicit_iv = _gnutls_version_has_explicit_iv(ver);
int auth_cipher =
- _gnutls_auth_cipher_is_aead(&params->write.cipher_state);
- uint8_t nonce[MAX_CIPHER_BLOCK_SIZE];
+ _gnutls_auth_cipher_is_aead(&params->write.ctx.tls12);
+ uint8_t nonce[MAX_CIPHER_IV_SIZE];
unsigned imp_iv_size = 0, exp_iv_size = 0;
bool etm = 0;
@@ -286,7 +320,7 @@ encrypt_packet(gnutls_session_t session,
*/
memcpy(data_ptr, nonce, blocksize);
_gnutls_auth_cipher_setiv(&params->write.
- cipher_state, data_ptr,
+ ctx.tls12, data_ptr,
blocksize);
/*data_ptr += blocksize;*/
@@ -341,7 +375,7 @@ encrypt_packet(gnutls_session_t session,
if (algo_type == CIPHER_BLOCK || algo_type == CIPHER_STREAM) {
/* add the authenticated data */
ret =
- _gnutls_auth_cipher_add_auth(&params->write.cipher_state,
+ _gnutls_auth_cipher_add_auth(&params->write.ctx.tls12,
preamble, preamble_size);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -349,7 +383,7 @@ encrypt_packet(gnutls_session_t session,
if (etm && explicit_iv) {
/* In EtM we need to hash the IV as well */
ret =
- _gnutls_auth_cipher_add_auth(&params->write.cipher_state,
+ _gnutls_auth_cipher_add_auth(&params->write.ctx.tls12,
full_cipher_ptr, blocksize);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -358,14 +392,14 @@ encrypt_packet(gnutls_session_t session,
/* Actual encryption.
*/
ret =
- _gnutls_auth_cipher_encrypt2_tag(&params->write.cipher_state,
+ _gnutls_auth_cipher_encrypt2_tag(&params->write.ctx.tls12,
plain->data,
plain->size, cipher_data,
cipher_size, pad);
if (ret < 0)
return gnutls_assert_val(ret);
} else { /* AEAD */
- ret = _gnutls_aead_cipher_encrypt(&params->write.cipher_state.cipher,
+ ret = _gnutls_aead_cipher_encrypt(&params->write.ctx.tls12.cipher,
nonce, imp_iv_size + exp_iv_size,
preamble, preamble_size,
tag_size,
@@ -378,6 +412,79 @@ encrypt_packet(gnutls_session_t session,
return length;
}
+static int
+encrypt_packet_tls13(gnutls_session_t session,
+ uint8_t * cipher_data, size_t cipher_size,
+ gnutls_datum_t * plain,
+ size_t min_pad,
+ content_type_t type,
+ record_parameters_st * params)
+{
+ int ret;
+ unsigned int tag_size = params->write.aead_tag_size;
+ const version_entry_st *ver = get_version(session);
+ uint8_t nonce[MAX_CIPHER_IV_SIZE];
+ unsigned iv_size = 0;
+ uint8_t *fdata;
+ ssize_t fdata_size, max;
+
+ if (unlikely(ver == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ _gnutls_hard_log("ENC[%p]: cipher: %s, MAC: %s, Epoch: %u\n",
+ session, _gnutls_cipher_get_name(params->cipher),
+ _gnutls_mac_get_name(params->mac),
+ (unsigned int) params->epoch);
+
+ iv_size = params->write.IV.size;
+
+ if (params->cipher->id == GNUTLS_CIPHER_NULL) {
+ if (cipher_size < plain->size+1)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ memcpy(cipher_data, plain->data, plain->size);
+ return plain->size;
+ }
+
+ memcpy(nonce, params->write.IV.data, iv_size);
+ memxor(&nonce[iv_size-8], UINT64DATA(params->write.sequence_number), 8);
+
+ max = MAX_RECORD_SEND_SIZE(session);
+
+ /* make TLS 1.3 form of data */
+ fdata_size = plain->size + 1 + min_pad;
+
+ /* check whether padding would exceed max */
+ if (fdata_size > max) {
+ if (unlikely(max-plain->size-1 < 0))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ min_pad = max - plain->size - 1;
+ fdata_size = max;
+ }
+
+ fdata = gnutls_malloc(fdata_size);
+ if (fdata == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ memcpy(fdata, plain->data, plain->size);
+ fdata[plain->size] = type;
+ if (min_pad)
+ memset(&fdata[plain->size+1], 0, min_pad);
+
+ ret = gnutls_aead_cipher_encrypt(params->write.ctx.aead,
+ nonce, iv_size,
+ NULL, 0,
+ tag_size,
+ fdata, fdata_size,
+ cipher_data, &cipher_size);
+ gnutls_free(fdata);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ return cipher_size;
+}
+
static void dummy_wait(record_parameters_st * params,
gnutls_datum_t * plaintext, unsigned pad_failed,
unsigned int pad, unsigned total)
@@ -400,12 +507,12 @@ static void dummy_wait(record_parameters_st * params,
if (len < plaintext->size)
_gnutls_auth_cipher_add_auth
(&params->read.
- cipher_state,
+ ctx.tls12,
plaintext->data, len);
else
_gnutls_auth_cipher_add_auth
(&params->read.
- cipher_state,
+ ctx.tls12,
plaintext->data,
plaintext->size);
}
@@ -419,13 +526,13 @@ static void dummy_wait(record_parameters_st * params,
*/
static int
decrypt_packet(gnutls_session_t session,
- gnutls_datum_t * ciphertext,
- gnutls_datum_t * plain,
- uint8_t type, record_parameters_st * params,
- gnutls_uint64 * sequence)
+ gnutls_datum_t * ciphertext,
+ gnutls_datum_t * plain,
+ content_type_t type, record_parameters_st * params,
+ gnutls_uint64 * sequence)
{
uint8_t tag[MAX_HASH_SIZE];
- uint8_t nonce[MAX_CIPHER_BLOCK_SIZE];
+ uint8_t nonce[MAX_CIPHER_IV_SIZE];
const uint8_t *tag_ptr = NULL;
unsigned int pad = 0, i;
int length, length_to_decrypt;
@@ -437,7 +544,7 @@ decrypt_packet(gnutls_session_t session,
unsigned int preamble_size = 0;
const version_entry_st *ver = get_version(session);
unsigned int tag_size =
- _gnutls_auth_cipher_tag_len(&params->read.cipher_state);
+ _gnutls_auth_cipher_tag_len(&params->read.ctx.tls12);
unsigned int explicit_iv = _gnutls_version_has_explicit_iv(ver);
unsigned imp_iv_size, exp_iv_size;
unsigned cipher_type = _gnutls_cipher_type(params->cipher);
@@ -463,19 +570,19 @@ decrypt_packet(gnutls_session_t session,
ver, preamble);
ret = _gnutls_auth_cipher_add_auth(&params->read.
- cipher_state, preamble,
+ ctx.tls12, preamble,
preamble_size);
if (unlikely(ret < 0))
return gnutls_assert_val(ret);
ret = _gnutls_auth_cipher_add_auth(&params->read.
- cipher_state,
+ ctx.tls12,
ciphertext->data,
ciphertext->size-tag_size);
if (unlikely(ret < 0))
return gnutls_assert_val(ret);
- ret = _gnutls_auth_cipher_tag(&params->read.cipher_state, tag, tag_size);
+ ret = _gnutls_auth_cipher_tag(&params->read.ctx.tls12, tag, tag_size);
if (unlikely(ret < 0))
return gnutls_assert_val(ret);
@@ -493,7 +600,7 @@ decrypt_packet(gnutls_session_t session,
* only stream ciphers.
*/
if (unlikely(_gnutls_auth_cipher_is_aead(&params->read.
- cipher_state) == 0))
+ ctx.tls12) == 0))
return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
@@ -550,7 +657,7 @@ decrypt_packet(gnutls_session_t session,
gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
}
- ret = _gnutls_aead_cipher_decrypt(&params->read.cipher_state.cipher,
+ ret = _gnutls_aead_cipher_decrypt(&params->read.ctx.tls12.cipher,
nonce, exp_iv_size + imp_iv_size,
preamble, preamble_size,
tag_size,
@@ -581,7 +688,7 @@ decrypt_packet(gnutls_session_t session,
ret =
_gnutls_auth_cipher_add_auth(&params->read.
- cipher_state, preamble,
+ ctx.tls12, preamble,
preamble_size);
if (unlikely(ret < 0))
return gnutls_assert_val(ret);
@@ -598,7 +705,7 @@ decrypt_packet(gnutls_session_t session,
ret =
_gnutls_auth_cipher_decrypt2(&params->read.
- cipher_state,
+ ctx.tls12,
ciphertext->data,
length_to_decrypt,
plain->data,
@@ -626,7 +733,7 @@ decrypt_packet(gnutls_session_t session,
*/
if (explicit_iv) {
_gnutls_auth_cipher_setiv(&params->read.
- cipher_state,
+ ctx.tls12,
ciphertext->data,
blocksize);
@@ -650,7 +757,7 @@ decrypt_packet(gnutls_session_t session,
if (etm == 0) {
ret =
- _gnutls_cipher_decrypt2(&params->read.cipher_state.
+ _gnutls_cipher_decrypt2(&params->read.ctx.tls12.
cipher, ciphertext->data,
ciphertext->size,
plain->data,
@@ -697,20 +804,20 @@ decrypt_packet(gnutls_session_t session,
ret =
_gnutls_auth_cipher_add_auth(&params->read.
- cipher_state, preamble,
+ ctx.tls12, preamble,
preamble_size);
if (unlikely(ret < 0))
return gnutls_assert_val(ret);
ret =
_gnutls_auth_cipher_add_auth(&params->read.
- cipher_state,
+ ctx.tls12,
plain->data, length);
if (unlikely(ret < 0))
return gnutls_assert_val(ret);
} else { /* EtM */
ret =
- _gnutls_cipher_decrypt2(&params->read.cipher_state.
+ _gnutls_cipher_decrypt2(&params->read.ctx.tls12.
cipher, ciphertext->data,
ciphertext->size - tag_size,
plain->data,
@@ -732,7 +839,7 @@ decrypt_packet(gnutls_session_t session,
/* STREAM or BLOCK arrive here */
if (etm == 0) {
ret =
- _gnutls_auth_cipher_tag(&params->read.cipher_state, tag,
+ _gnutls_auth_cipher_tag(&params->read.ctx.tls12, tag,
tag_size);
if (unlikely(ret < 0))
return gnutls_assert_val(ret);
@@ -755,3 +862,89 @@ decrypt_packet(gnutls_session_t session,
return length;
}
+
+static int
+decrypt_packet_tls13(gnutls_session_t session,
+ gnutls_datum_t * ciphertext,
+ gnutls_datum_t * plain,
+ content_type_t *type, record_parameters_st * params,
+ gnutls_uint64 * sequence)
+{
+ uint8_t nonce[MAX_CIPHER_IV_SIZE];
+ size_t length, length_to_decrypt;
+ int ret;
+ const version_entry_st *ver = get_version(session);
+ unsigned int tag_size = params->read.aead_tag_size;
+ unsigned iv_size;
+ unsigned j;
+ volatile unsigned length_set;
+
+ if (unlikely(ver == NULL))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ if (params->cipher->id == GNUTLS_CIPHER_NULL) {
+ if (plain->size < ciphertext->size)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ length = ciphertext->size;
+ memcpy(plain->data, ciphertext->data, length);
+
+ return length;
+ }
+
+ iv_size = _gnutls_cipher_get_iv_size(params->cipher);
+
+ /* The way AEAD ciphers are defined in RFC5246, it allows
+ * only stream ciphers.
+ */
+ if (unlikely(ciphertext->size < tag_size))
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+ if (unlikely(params->read.IV.size != iv_size || iv_size < 8))
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+ memcpy(nonce, params->read.IV.data, params->read.IV.size);
+ memxor(&nonce[iv_size-8], UINT64DATA(*sequence), 8);
+
+ length =
+ ciphertext->size - tag_size;
+
+ length_to_decrypt = ciphertext->size;
+
+ if (unlikely
+ ((unsigned) length_to_decrypt > plain->size)) {
+ _gnutls_audit_log(session,
+ "Received %u bytes, while expecting less than %u\n",
+ (unsigned int) length_to_decrypt,
+ (unsigned int) plain->size);
+ return
+ gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+
+ ret = gnutls_aead_cipher_decrypt(params->read.ctx.aead,
+ nonce, iv_size,
+ NULL, 0,
+ tag_size,
+ ciphertext->data, length_to_decrypt,
+ plain->data, &length);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ length_set = 0;
+
+ /* now figure the actual data size. We intentionally iterate through all data,
+ * to avoid leaking the padding length due to timing differences in processing.
+ */
+ for (j=length-1;j>0;j--) {
+ if (plain->data[j]!=0 && length_set == 0) {
+ *type = plain->data[j];
+ length = j;
+ length_set = 1;
+ }
+ }
+
+ if (!length_set)
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+ return length;
+}
diff --git a/lib/cipher.h b/lib/cipher.h
index a1dc80c4fd..0d7c74adaf 100644
--- a/lib/cipher.h
+++ b/lib/cipher.h
@@ -28,5 +28,5 @@ int _gnutls_encrypt(gnutls_session_t session,
int _gnutls_decrypt(gnutls_session_t session,
gnutls_datum_t * ciphertext, gnutls_datum_t * output,
- content_type_t type, record_parameters_st * params,
+ content_type_t *type, record_parameters_st * params,
gnutls_uint64 * sequence);
diff --git a/lib/constate.c b/lib/constate.c
index 25bed3e573..4f46eea872 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -36,10 +36,15 @@
#include <hello_ext.h>
#include <buffers.h>
#include "dtls.h"
+#include "secrets.h"
+#include "handshake.h"
static const char keyexp[] = "key expansion";
static const int keyexp_length = sizeof(keyexp) - 1;
+static int
+_tls13_init_record_state(record_parameters_st * params);
+
/* This function is to be called after handshake, when master_secret,
* client_random and server_random have been initialized.
* This function creates the keys and stores them into pending session.
@@ -194,6 +199,107 @@ _gnutls_set_keys(gnutls_session_t session, record_parameters_st * params,
}
static int
+_tls13_set_keys(gnutls_session_t session, record_parameters_st * params,
+ unsigned iv_size, unsigned key_size)
+{
+ uint8_t hs_ckey[MAX_HASH_SIZE];
+ uint8_t hs_skey[MAX_HASH_SIZE];
+ uint8_t ckey_block[MAX_CIPHER_KEY_SIZE];
+ uint8_t civ_block[MAX_CIPHER_IV_SIZE];
+ uint8_t skey_block[MAX_CIPHER_KEY_SIZE];
+ uint8_t siv_block[MAX_CIPHER_IV_SIZE];
+ char buf[65];
+ record_state_st *client_write, *server_write;
+ int ret;
+
+ ret = _tls13_derive_secret(session, HANDSHAKE_CLIENT_TRAFFIC_LABEL, sizeof(HANDSHAKE_CLIENT_TRAFFIC_LABEL)-1,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer.length, hs_ckey);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* client keys */
+ ret = _tls13_expand_secret(session, "key", 3, NULL, 0, hs_ckey, key_size, ckey_block);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, hs_ckey, iv_size, civ_block);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* server keys */
+ ret = _tls13_derive_secret(session, HANDSHAKE_SERVER_TRAFFIC_LABEL, sizeof(HANDSHAKE_SERVER_TRAFFIC_LABEL)-1,
+ session->internals.handshake_hash_buffer.data,
+ session->internals.handshake_hash_buffer.length, hs_skey);
+
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _tls13_expand_secret(session, "key", 3, NULL, 0, hs_skey, key_size, skey_block);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _tls13_expand_secret(session, "iv", 2, NULL, 0, hs_skey, iv_size, siv_block);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (session->security_parameters.entity == GNUTLS_CLIENT) {
+ client_write = &params->write;
+ server_write = &params->read;
+ } else {
+ client_write = &params->read;
+ server_write = &params->write;
+ }
+
+ client_write->mac_secret.data = NULL;
+ client_write->mac_secret.size = 0;
+
+ server_write->mac_secret.data = NULL;
+ server_write->mac_secret.size = 0;
+
+ ret = _gnutls_set_datum(&client_write->key, ckey_block, key_size);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ _gnutls_hard_log("INT: CLIENT WRITE KEY [%d]: %s\n",
+ key_size,
+ _gnutls_bin2hex(ckey_block, key_size,
+ buf, sizeof(buf), NULL));
+
+ ret = _gnutls_set_datum(&server_write->key, skey_block, key_size);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ _gnutls_hard_log("INT: SERVER WRITE KEY [%d]: %s\n",
+ key_size,
+ _gnutls_bin2hex(skey_block, key_size,
+ buf, sizeof(buf), NULL));
+
+ if (iv_size > 0) {
+ ret = _gnutls_set_datum(&client_write->IV, civ_block, iv_size);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ _gnutls_hard_log("INT: CLIENT WRITE IV [%d]: %s\n",
+ iv_size,
+ _gnutls_bin2hex(civ_block, iv_size,
+ buf, sizeof(buf), NULL));
+
+ ret = _gnutls_set_datum(&server_write->IV, siv_block, iv_size);
+ if (ret < 0)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ _gnutls_hard_log("INT: SERVER WRITE IV [%d]: %s\n",
+ iv_size,
+ _gnutls_bin2hex(siv_block, iv_size,
+ buf, sizeof(buf), NULL));
+ }
+
+ return 0;
+}
+
+static int
_gnutls_init_record_state(record_parameters_st * params,
const version_entry_st * ver, int read,
record_state_st * state)
@@ -206,7 +312,7 @@ _gnutls_init_record_state(record_parameters_st * params,
iv = &state->IV;
}
- ret = _gnutls_auth_cipher_init(&state->cipher_state,
+ ret = _gnutls_auth_cipher_init(&state->ctx.tls12,
params->cipher, &state->key, iv,
params->mac, &state->mac_secret,
params->etm,
@@ -286,29 +392,40 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch)
|| _gnutls_mac_is_ok(params->mac) == 0)
return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
- if (!_gnutls_version_has_explicit_iv(ver) &&
- _gnutls_cipher_type(params->cipher) == CIPHER_BLOCK) {
- IV_size = _gnutls_cipher_get_iv_size(params->cipher);
- } else {
+ if (_gnutls_version_has_explicit_iv(ver) &&
+ (_gnutls_cipher_type(params->cipher) != CIPHER_BLOCK)) {
IV_size = _gnutls_cipher_get_implicit_iv_size(params->cipher);
+ } else {
+ IV_size = _gnutls_cipher_get_iv_size(params->cipher);
}
key_size = _gnutls_cipher_get_key_size(params->cipher);
hash_size = _gnutls_mac_get_key_size(params->mac);
params->etm = session->security_parameters.etm;
- ret = _gnutls_set_keys
- (session, params, hash_size, IV_size, key_size);
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- ret = _gnutls_init_record_state(params, ver, 1, &params->read);
- if (ret < 0)
- return gnutls_assert_val(ret);
+ if (ver->tls13_sem) {
+ ret = _tls13_set_keys
+ (session, params, IV_size, key_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
- ret = _gnutls_init_record_state(params, ver, 0, &params->write);
- if (ret < 0)
- return gnutls_assert_val(ret);
+ ret = _tls13_init_record_state(params);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ } else {
+ ret = _gnutls_set_keys
+ (session, params, hash_size, IV_size, key_size);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_init_record_state(params, ver, 1, &params->read);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_init_record_state(params, ver, 0, &params->write);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+ }
session->internals.max_recv_size = _gnutls_record_overhead(params->cipher, params->mac, params->etm, 0);
session->internals.max_recv_size += session->security_parameters.max_record_recv_size + RECORD_HEADER_SIZE(session);
@@ -618,13 +735,16 @@ void _gnutls_epoch_gc(gnutls_session_t session)
_gnutls_record_log("REC[%p]: End of epoch cleanup\n", session);
}
-static inline void free_record_state(record_state_st * state, int d)
+static inline void free_record_state(record_state_st * state)
{
_gnutls_free_datum(&state->mac_secret);
_gnutls_free_datum(&state->IV);
_gnutls_free_datum(&state->key);
- _gnutls_auth_cipher_deinit(&state->cipher_state);
+ if (state->is_aead)
+ gnutls_aead_cipher_deinit(state->ctx.aead);
+ else
+ _gnutls_auth_cipher_deinit(&state->ctx.tls12);
}
void
@@ -633,8 +753,50 @@ _gnutls_epoch_free(gnutls_session_t session, record_parameters_st * params)
_gnutls_record_log("REC[%p]: Epoch #%u freed\n", session,
params->epoch);
- free_record_state(&params->read, 1);
- free_record_state(&params->write, 0);
+ free_record_state(&params->read);
+ free_record_state(&params->write);
gnutls_free(params);
}
+
+int _tls13_connection_state_init(gnutls_session_t session)
+{
+ const uint16_t epoch_next =
+ session->security_parameters.epoch_next;
+ int ret;
+
+ ret = _gnutls_epoch_set_keys(session, epoch_next);
+ if (ret < 0)
+ return ret;
+
+ _gnutls_handshake_log("HSK[%p]: TLS 1.3 cipher suite: %s\n",
+ session,
+ session->security_parameters.cs->name);
+
+ session->security_parameters.epoch_read = epoch_next;
+ session->security_parameters.epoch_write = epoch_next;
+
+ return 0;
+}
+
+static int
+_tls13_init_record_state(record_parameters_st * params)
+{
+ int ret;
+
+ ret = gnutls_aead_cipher_init(&params->read.ctx.aead,
+ params->cipher->id, &params->read.key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = gnutls_aead_cipher_init(&params->write.ctx.aead,
+ params->cipher->id, &params->write.key);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ params->read.aead_tag_size = params->write.aead_tag_size = gnutls_cipher_get_tag_size(params->cipher->id);
+ params->read.is_aead = 1;
+ params->write.is_aead = 1;
+
+ return 0;
+}
diff --git a/lib/constate.h b/lib/constate.h
index 6145d77f3d..e7cf0a0a23 100644
--- a/lib/constate.h
+++ b/lib/constate.h
@@ -42,6 +42,8 @@ void _gnutls_epoch_gc(gnutls_session_t session);
void _gnutls_epoch_free(gnutls_session_t session,
record_parameters_st * state);
+int _tls13_connection_state_init(gnutls_session_t session);
+
static inline int _gnutls_epoch_is_valid(gnutls_session_t session,
int epoch)
{
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index dcbcfe4f65..48fbcc828b 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -122,6 +122,8 @@ typedef struct {
#define MAX_CIPHER_BLOCK_SIZE 16
#define MAX_CIPHER_KEY_SIZE 32
+#define MAX_CIPHER_IV_SIZE 16
+
#define MAX_USERNAME_SIZE 128
#define MAX_SERVER_NAME_SIZE 256
@@ -423,8 +425,8 @@ struct gnutls_key_st {
/* the current (depending on state) secret, can be
* early_secret, client_early_traffic_secret, ... */
- uint8_t temp_secret[MAX_CIPHER_KEY_SIZE];
- unsigned temp_secret_size;
+ uint8_t temp_secret[MAX_HASH_SIZE];
+ unsigned temp_secret_size; /* depends on negotiated PRF size */
/* For ECDH KX */
gnutls_pk_params_st ecdh_params; /* private part */
@@ -671,7 +673,12 @@ struct record_state_st {
gnutls_datum_t mac_secret;
gnutls_datum_t IV;
gnutls_datum_t key;
- auth_cipher_hd_st cipher_state;
+ union {
+ auth_cipher_hd_st tls12;
+ gnutls_aead_cipher_hd_t aead;
+ } ctx;
+ unsigned aead_tag_size;
+ unsigned is_aead;
gnutls_uint64 sequence_number;
};
diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c
index a61d1bfc45..4307b5dc29 100644
--- a/lib/handshake-tls13.c
+++ b/lib/handshake-tls13.c
@@ -46,6 +46,7 @@
#include <state.h>
#include <random.h>
#include <dtls.h>
+#include "secrets.h"
/*
* _gnutls13_handshake_client
@@ -114,6 +115,35 @@ int _gnutls13_handshake_client(gnutls_session_t session)
return 0;
}
+static int generate_hs_traffic_keys(gnutls_session_t session)
+{
+ int ret;
+
+ if (unlikely(session->key.key.size == 0))
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ ret = _tls13_update_secret(session, session->key.key.data, session->key.key.size);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _tls13_connection_state_init(session);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ ret = _tls13_derive_secret(session, DERIVED_LABEL, sizeof(DERIVED_LABEL)-1,
+ NULL, 0, session->key.temp_secret);
+ if (ret < 0) {
+ gnutls_assert();
+ return ret;
+ }
+
+ return 0;
+}
+
/*
* _gnutls13_handshake_server
* This function does the server stuff of the handshake protocol.
@@ -124,6 +154,11 @@ int _gnutls13_handshake_server(gnutls_session_t session)
switch (STATE) {
case STATE100:
+ ret =
+ generate_hs_traffic_keys(session);
+ STATE = STATE100;
+ IMED_RET("generate session keys", ret, 0);
+ /* fall through */
case STATE101:
abort();
STATE = STATE101;
diff --git a/lib/handshake.c b/lib/handshake.c
index fec06283e1..86771ab8e7 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -53,6 +53,7 @@
#include <auth/psk.h> /* for gnutls_psk_server_credentials_t */
#include <random.h>
#include <dtls.h>
+#include "secrets.h"
#define TRUE 1
#define FALSE 0
@@ -1598,14 +1599,24 @@ read_server_hello(gnutls_session_t session,
}
pos += 2;
- if (!vers->tls13_sem) {
+ if (vers->tls13_sem) {
+ /* TLS 1.3 Early Secret */
+ ret = _tls13_init_secret(session, NULL, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = _tls13_derive_secret(session, DERIVED_LABEL, sizeof(DERIVED_LABEL)-1,
+ NULL, 0, session->key.temp_secret);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ext_parse_flag = GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO;
+ } else {
/* move to compression
*/
DECR_LEN(len, 1);
pos++;
ext_parse_flag = GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO;
- } else {
- ext_parse_flag = GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO;
}
/* Parse extensions in order.
@@ -1895,10 +1906,18 @@ static int send_server_hello(gnutls_session_t session, int again)
if (unlikely(vers == NULL))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
- if (vers->tls13_sem)
+ if (vers->tls13_sem) {
+ /* TLS 1.3 Early Secret */
+ ret = _tls13_init_secret(session, NULL, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+
ext_parse_flag = GNUTLS_EXT_FLAG_TLS13_SERVER_HELLO;
- else
+ } else {
ext_parse_flag = GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO;
+ }
ret =
_gnutls_gen_hello_extensions(session, &extdata,
@@ -1962,6 +1981,15 @@ static int send_server_hello(gnutls_session_t session, int again)
if (extdata.length > 0) {
memcpy(&data[pos], extdata.data, extdata.length);
}
+
+ if (vers->tls13_sem) {
+ ret = _tls13_derive_secret(session, DERIVED_LABEL, sizeof(DERIVED_LABEL)-1,
+ NULL, 0, session->key.temp_secret);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fail;
+ }
+ }
}
ret =
diff --git a/lib/range.c b/lib/range.c
index 0b5655974f..9a1af277db 100644
--- a/lib/range.c
+++ b/lib/range.c
@@ -58,6 +58,9 @@ _gnutls_range_max_lh_pad(gnutls_session_t session, ssize_t data_length,
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
}
+ if (record_params->write.is_aead) /* not yet ready */
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
max_pad = MAX_PAD_SIZE;
fixed_pad = 1;
@@ -66,7 +69,7 @@ _gnutls_range_max_lh_pad(gnutls_session_t session, ssize_t data_length,
block_size = _gnutls_cipher_get_block_size(record_params->cipher);
tag_size =
_gnutls_auth_cipher_tag_len(&record_params->write.
- cipher_state);
+ ctx.tls12);
switch (_gnutls_cipher_type(record_params->cipher)) {
case CIPHER_AEAD:
case CIPHER_STREAM:
diff --git a/lib/record.c b/lib/record.c
index 03038ef49e..5be4ba3094 100644
--- a/lib/record.c
+++ b/lib/record.c
@@ -369,8 +369,13 @@ copy_record_version(gnutls_session_t session,
if (unlikely(lver == NULL))
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
- version[0] = lver->major;
- version[1] = lver->minor;
+ if (lver->tls13_sem) {
+ version[0] = 0x03;
+ version[1] = 0x01;
+ } else {
+ version[0] = lver->major;
+ version[1] = lver->minor;
+ }
} else {
version[0] = session->internals.default_record_version[0];
version[1] = session->internals.default_record_version[1];
@@ -430,6 +435,7 @@ _gnutls_send_tlen_int(gnutls_session_t session, content_type_t type,
record_parameters_st *record_params;
size_t max_send_size;
record_state_st *record_state;
+ const version_entry_st *vers = get_version(session);
ret = _gnutls_epoch_get(session, epoch_rel, &record_params);
if (ret < 0)
@@ -494,7 +500,10 @@ _gnutls_send_tlen_int(gnutls_session_t session, content_type_t type,
return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
headers = _mbuffer_get_uhead_ptr(bufel);
- headers[0] = type;
+ if (vers->tls13_sem && record_params->cipher->id != GNUTLS_CIPHER_NULL)
+ headers[0] = GNUTLS_APPLICATION_DATA;
+ else
+ headers[0] = type;
/* Use the default record version, if it is
* set. */
@@ -665,8 +674,14 @@ record_check_version(gnutls_session_t session,
const version_entry_st *vers = get_version(session);
int diff = 0;
- if (vers->major != version[0] || vers->minor != version[1])
- diff = 1;
+ if (vers->tls13_sem) {
+ /* TLS 1.3 requires version to be 0x0301 */
+ if (version[0] != 0x03 || version[1] != 0x01)
+ diff = 1;
+ } else {
+ if (vers->major != version[0] || vers->minor != version[1])
+ diff = 1;
+ }
if (!IS_DTLS(session)) {
if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO ||
@@ -1238,7 +1253,7 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type,
t.size = _mbuffer_get_udata_size(decrypted);
ret =
_gnutls_decrypt(session, &ciphertext, &t,
- record.type, record_params, packet_sequence);
+ &record.type, record_params, packet_sequence);
if (ret >= 0)
_mbuffer_set_udata_size(decrypted, ret);