diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-08-11 15:16:51 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-11-14 15:00:32 +0100 |
commit | 37412c853490917c58a8ad03e68701249d82d59a (patch) | |
tree | d5f936b464620026576e03010b4051d0c6af7872 | |
parent | 8b421ba60de6a3ddf0cc1258b083fa062da12f11 (diff) | |
download | gnutls-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.c | 5 | ||||
-rw-r--r-- | lib/cipher.c | 275 | ||||
-rw-r--r-- | lib/cipher.h | 2 | ||||
-rw-r--r-- | lib/constate.c | 202 | ||||
-rw-r--r-- | lib/constate.h | 2 | ||||
-rw-r--r-- | lib/gnutls_int.h | 13 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 35 | ||||
-rw-r--r-- | lib/handshake.c | 38 | ||||
-rw-r--r-- | lib/range.c | 5 | ||||
-rw-r--r-- | lib/record.c | 27 |
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(¶ms->write.cipher_state); + _gnutls_auth_cipher_tag_len(¶ms->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(¶ms->write.cipher_state); - uint8_t nonce[MAX_CIPHER_BLOCK_SIZE]; + _gnutls_auth_cipher_is_aead(¶ms->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(¶ms->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(¶ms->write.cipher_state, + _gnutls_auth_cipher_add_auth(¶ms->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(¶ms->write.cipher_state, + _gnutls_auth_cipher_add_auth(¶ms->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(¶ms->write.cipher_state, + _gnutls_auth_cipher_encrypt2_tag(¶ms->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(¶ms->write.cipher_state.cipher, + ret = _gnutls_aead_cipher_encrypt(¶ms->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 (¶ms->read. - cipher_state, + ctx.tls12, plaintext->data, len); else _gnutls_auth_cipher_add_auth (¶ms->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(¶ms->read.cipher_state); + _gnutls_auth_cipher_tag_len(¶ms->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(¶ms->read. - cipher_state, preamble, + ctx.tls12, preamble, preamble_size); if (unlikely(ret < 0)) return gnutls_assert_val(ret); ret = _gnutls_auth_cipher_add_auth(¶ms->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(¶ms->read.cipher_state, tag, tag_size); + ret = _gnutls_auth_cipher_tag(¶ms->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(¶ms->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(¶ms->read.cipher_state.cipher, + ret = _gnutls_aead_cipher_decrypt(¶ms->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(¶ms->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(¶ms->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(¶ms->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(¶ms->read.cipher_state. + _gnutls_cipher_decrypt2(¶ms->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(¶ms->read. - cipher_state, preamble, + ctx.tls12, preamble, preamble_size); if (unlikely(ret < 0)) return gnutls_assert_val(ret); ret = _gnutls_auth_cipher_add_auth(¶ms->read. - cipher_state, + ctx.tls12, plain->data, length); if (unlikely(ret < 0)) return gnutls_assert_val(ret); } else { /* EtM */ ret = - _gnutls_cipher_decrypt2(¶ms->read.cipher_state. + _gnutls_cipher_decrypt2(¶ms->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(¶ms->read.cipher_state, tag, + _gnutls_auth_cipher_tag(¶ms->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 = ¶ms->write; + server_write = ¶ms->read; + } else { + client_write = ¶ms->read; + server_write = ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->read); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _gnutls_init_record_state(params, ver, 0, ¶ms->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(¶ms->read, 1); - free_record_state(¶ms->write, 0); + free_record_state(¶ms->read); + free_record_state(¶ms->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(¶ms->read.ctx.aead, + params->cipher->id, ¶ms->read.key); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_aead_cipher_init(¶ms->write.ctx.aead, + params->cipher->id, ¶ms->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); |