]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
record: added TLS 1.3 record parsing and key derivation
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Fri, 11 Aug 2017 13:16:51 +0000 (15:16 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Feb 2018 14:29:34 +0000 (15:29 +0100)
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
lib/algorithms/ciphers.c
lib/cipher.c
lib/cipher.h
lib/constate.c
lib/constate.h
lib/gnutls_int.h
lib/handshake-tls13.c
lib/handshake.c
lib/range.c
lib/record.c

index acfb3450a70b486f24b795f8e7e132028cf44520..8c73c4cac3a3df4ac659b28ff9f14ade8f375d6f 100644 (file)
@@ -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).
  */
index 795255bbf42b2176bdc3caaedb5d5316a515c272..a380a71d751e21c358676faff8b337a0f70a8e7c 100644 (file)
@@ -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;
+}
index a1dc80c4fd88616ea27d32bc4f8ebe29826a0409..0d7c74adaf8f12e9ac28d3c6cf87c4780f4f0289 100644 (file)
@@ -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);
index 85bc0ffcc9fab64919c68c1cc396c26e497ccdfd..161c9b3fb4e62430433a9ba320b7feaa65f9b5ab 100644 (file)
 #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.
@@ -193,6 +198,107 @@ _gnutls_set_keys(gnutls_session_t session, record_parameters_st * params,
        return 0;
 }
 
+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,
@@ -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, 1);
        session->internals.max_recv_size += session->security_parameters.max_record_recv_size + RECORD_HEADER_SIZE(session);
@@ -619,13 +736,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
@@ -634,8 +754,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;
+}
index 6145d77f3d5f699780522c372422143de430655b..e7cf0a0a23f197a9e9c2e754614c5ed43f38d109 100644 (file)
@@ -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)
 {
index b1daa83084af3e000e73e2348afac1f7709ac0d0..21aa56bafe5967047a64cab7aa9459555abd38b3 100644 (file)
@@ -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;
 };
 
index a61d1bfc45f16f03c75a604cfe64794db4a2a106..4307b5dc2962aacea90fcca18c748f231c67f437 100644 (file)
@@ -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;
index fec06283e16af6aebb21af218d2e0a0fc2964691..86771ab8e7893affaf71ce8562f79ec9a3ee1859 100644 (file)
@@ -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 =
index 0b5655974f42a427c24f26868ce3b870fd561f8e..9a1af277dbfd58a3d4d107cce0935414022be9ee 100644 (file)
@@ -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:
index 03038ef49ec601c7589b1e3ff2cf3a166492305c..5be4ba3094e3dc240320005dc5b394fcc99319fe 100644 (file)
@@ -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);