diff options
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/cipher.c | 28 | ||||
-rw-r--r-- | lib/constate.c | 25 | ||||
-rw-r--r-- | lib/dtls13.c | 125 | ||||
-rw-r--r-- | lib/dtls13.h | 17 | ||||
-rw-r--r-- | lib/gnutls_int.h | 2 | ||||
-rw-r--r-- | lib/handshake-tls13.c | 7 | ||||
-rw-r--r-- | lib/record.c | 78 |
8 files changed, 262 insertions, 22 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 6d4e8d225a..ea039b037d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -83,7 +83,7 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \ crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \ hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c \ - iov.c iov.h system/ktls.c system/ktls.h pathbuf.c pathbuf.h + iov.c iov.h system/ktls.c system/ktls.h pathbuf.c pathbuf.hd dtls13.c dtls13.h if ENABLE_GOST COBJECTS += vko.c diff --git a/lib/cipher.c b/lib/cipher.c index 3cbf63841b..ab697db96b 100644 --- a/lib/cipher.c +++ b/lib/cipher.c @@ -39,6 +39,7 @@ #include "record.h" #include "constate.h" #include "mbuffers.h" +#include "dtls13.h" #include <state.h> #include <random.h> @@ -118,14 +119,25 @@ _gnutls_encrypt(gnutls_session_t session, } - if (IS_DTLS(session)) - _gnutls_write_uint16(ret, - ((uint8_t *) - _mbuffer_get_uhead_ptr(bufel)) + 11); - else - _gnutls_write_uint16(ret, - ((uint8_t *) - _mbuffer_get_uhead_ptr(bufel)) + 3); + uint8_t *header = _mbuffer_get_uhead_ptr(bufel); + if (IS_DTLS(session)) { + if ((vers->id == GNUTLS_DTLS1_3 && params->cipher->id != GNUTLS_CIPHER_NULL)) { + /* DLTS1.3 encrypt sequence number, seq_num sizse 16 bit*/ + uint16_t seq_num = (uint16_t)params->write.sequence_number; + + int rn = _dtls13_encrypt_seq_num(&seq_num, 1, + _mbuffer_get_udata_ptr(bufel), ret, + params, 0); + if (rn < 0) + return gnutls_assert_val(rn); + + _gnutls_write_uint16(seq_num, header+1); + } else { + _gnutls_write_uint16(ret, header+11); + } + } else { + _gnutls_write_uint16(ret, header+3); + } _mbuffer_set_udata_size(bufel, ret); _mbuffer_set_uhead_size(bufel, 0); diff --git a/lib/constate.c b/lib/constate.c index fbb047509f..0950b5176e 100644 --- a/lib/constate.c +++ b/lib/constate.c @@ -534,6 +534,31 @@ _tls13_set_keys(gnutls_session_t session, hs_stage_t stage, buf, sizeof(buf), NULL)); } + /* Derive DTLS 1.3 sequence number encryption keys */ + server_write->sn_key_size = key_size; + ret = _tls13_expand_secret(session, "sn", 2, NULL, 0, skey, + server_write->sn_key_size, + server_write->sn_key); + if (ret < 0) { + return gnutls_assert_val(ret); + } + _gnutls_hard_log("INT: dtls13: SN server key: %s\n", + _gnutls_bin2hex(server_write->sn_key, + server_write->sn_key_size, + buf, sizeof(buf), NULL)); + + client_write->sn_key_size = key_size; + ret = _tls13_expand_secret(session, "sn", 2, NULL, 0, ckey, + client_write->sn_key_size, + client_write->sn_key); + if (ret < 0) { + return gnutls_assert_val(ret); + } + _gnutls_hard_log("INT: dtls13: SN client key: %s\n", + _gnutls_bin2hex(client_write->sn_key, + client_write->sn_key_size, + buf, sizeof(buf), NULL)); + client_write->level = server_write->level = stage == STAGE_HS ? GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE : GNUTLS_ENCRYPTION_LEVEL_APPLICATION; diff --git a/lib/dtls13.c b/lib/dtls13.c new file mode 100644 index 0000000000..6cf0e7f7c8 --- /dev/null +++ b/lib/dtls13.c @@ -0,0 +1,125 @@ +#include "gnutls_int.h" +#include "secrets.h" +#include "dtls13.h" +#include "secrets.h" + +#define DTLS_SUPP_EPOCHS 4 //TODO adjust, see 4.2.1. Processing Guidelines +#define DTLS_SEQ_NUM_LIM 64 + +/* This function returns epoch number based on the last two bits of DTLS13 + * unified header flags (epoch_lob) + */ +// TODO: can we receive new epoch? +uint16_t _dtls13_resolve_epoch(gnutls_session_t session, uint8_t epoch_bits) +{ + uint16_t mask = 0x0003; + uint16_t current = session->security_parameters.epoch_read; + + for (uint16_t e = current; e > (current - DTLS_SUPP_EPOCHS); e--) { + if ( e == 0) break; + if ((uint8_t)(e & mask) == epoch_bits) + return e; + } + + return 0; // This will fail on epoch check +} + + +int _dtls13_resolve_seq_num(gnutls_session_t session, uint64_t *seq_num, + uint8_t seq_bits_size, const void *ciphertext, + ssize_t ciphertext_size) +{ + int ret; + record_parameters_st *params; + uint16_t sn_bits = *(uint16_t*)seq_num; + uint64_t exp_sn, bit_mask = 0; + + ret = _gnutls_epoch_get(session, EPOCH_READ_CURRENT, ¶ms); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = _dtls13_encrypt_seq_num(&sn_bits, seq_bits_size, + ciphertext, ciphertext_size, params, 1); + if (ret < 0) + return ret; + + bit_mask |= (seq_bits_size) ? 0xffff : 0xff; + + exp_sn = params->read.sequence_number; + for (uint64_t sn = exp_sn; sn < exp_sn + DTLS_SEQ_NUM_LIM; sn++) { + if ((uint16_t)(sn & bit_mask) == sn_bits) { + *seq_num = sn; + return 0; + } + } + + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET); + +} + +//TODO: chachapoly +int _dtls13_encrypt_seq_num(uint16_t *sn_bits, uint8_t sn_bits_size, + const void *ciphertext, ssize_t ciphertext_size, + record_parameters_st *params, uint8_t read) +{ + int ret; + gnutls_cipher_hd_t cipher; + uint8_t dec_text[16]; + uint8_t empty_iv[MAX_CIPHER_IV_SIZE]; + + memset(empty_iv, 0, MAX_CIPHER_IV_SIZE); + + gnutls_datum_t key; + gnutls_datum_t iv = {empty_iv, sizeof(empty_iv)}; + + if (read) { + key.data = params->read.sn_key; + key.size = params->read.sn_key_size; + } else { + key.data = params->write.sn_key; + key.size = params->write.sn_key_size; + } + + /* ECB is not available so we use CCM with empty IV */ + ret = gnutls_cipher_init(&cipher, GNUTLS_CIPHER_AES_128_CBC, &key, &iv); + if (ret < 0) { + _gnutls_record_log("DTLS1.3: failed to decrypt sequence number %s: cipher failed\n", gnutls_strerror(ret)); + return GNUTLS_E_INTERNAL_ERROR; + } + + ret = gnutls_cipher_encrypt2(cipher, ciphertext, 16, (void*)dec_text, 16); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_INTERNAL_ERROR; + } + + uint8_t tmp[32]; + _gnutls_hard_log("INT: dtls13: SN ciphertext: %s\n", + _gnutls_bin2hex(ciphertext, 16, (char *)tmp, sizeof(tmp), NULL)); + _gnutls_hard_log("INT: dtls13: SN mask: %s\n", + _gnutls_bin2hex(dec_text, sn_bits_size ? 2 : 1, (char *)tmp, sizeof(tmp), NULL)); + + if (sn_bits_size) { // 16bit + uint16_t mask = (dec_text[0] << 8) | dec_text[1]; + *sn_bits ^= mask; + } else { + *dec_text = *dec_text >> 8; + *sn_bits ^= *dec_text; + } + + return 0; +} + +int gnutls_dtls13_recv_ack(gnutls_session_t session) +{ + int ret; + uint16_t record_number; + uint8_t data[1024]; + uint64_t seq_num; + + ret = _gnutls_recv_int(session, GNUTLS_ACK, + data, 1024, &seq_num, + session->internals.record_timeout_ms); + + return ret; +} diff --git a/lib/dtls13.h b/lib/dtls13.h new file mode 100644 index 0000000000..b81d1c708b --- /dev/null +++ b/lib/dtls13.h @@ -0,0 +1,17 @@ +#ifndef GNUTLS_LIB_DTLS13_H +#define GNUTLS_LIB_DLTS13_H + +#include "gnutls_int.h" +#include "record.h" + + +uint16_t _dtls13_resolve_epoch(gnutls_session_t session, uint8_t epoch_bits); +int _dtls13_resolve_seq_num(gnutls_session_t session, uint64_t *seq_num, + uint8_t seq_bits_size, const void *ciphertext, + ssize_t ciphertext_size); +int _dtls13_encrypt_seq_num(uint16_t *sn_num, uint8_t sn_bits_size, + const void *ciphertext, ssize_t ciphertext_size, + record_parameters_st *params, uint8_t read); + +int gnutls_dtls13_recv_ack(gnutls_session_t session); +#endif diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 9b2afcf80c..0a2d21256a 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -860,6 +860,8 @@ struct record_state_st { unsigned is_aead; uint64_t sequence_number; gnutls_record_encryption_level_t level; + uint8_t sn_key[MAX_CIPHER_KEY_SIZE]; + unsigned sn_key_size; }; diff --git a/lib/handshake-tls13.c b/lib/handshake-tls13.c index 8dee8d1186..9f542047ee 100644 --- a/lib/handshake-tls13.c +++ b/lib/handshake-tls13.c @@ -308,6 +308,7 @@ static int generate_hs_traffic_keys(gnutls_session_t session) return ret; } + if ((session->security_parameters.entity == GNUTLS_CLIENT && (!(session->internals.hsk_flags & HSK_KEY_SHARE_RECEIVED) || (!(session->internals.hsk_flags & HSK_PSK_KE_MODE_DHE_PSK) && @@ -321,6 +322,9 @@ static int generate_hs_traffic_keys(gnutls_session_t session) } } + if (ret < 0) + return gnutls_assert_val(ret); + if (null_key) { uint8_t digest[MAX_HASH_SIZE]; unsigned digest_size; @@ -347,6 +351,9 @@ static int generate_hs_traffic_keys(gnutls_session_t session) } } + _gnutls_epoch_bump(session); + ret = _gnutls_epoch_dup(session, 1); + return 0; } diff --git a/lib/record.c b/lib/record.c index 225cb5236e..6b9ed8c9e3 100644 --- a/lib/record.c +++ b/lib/record.c @@ -50,6 +50,7 @@ #include <ext/heartbeat.h> #include <state.h> #include <dtls.h> +#include <dtls13.h> #include <dh.h> #include <random.h> #include <xsize.h> @@ -62,6 +63,7 @@ struct tls_record_st { uint16_t header_size; uint8_t version[2]; uint64_t sequence; /* DTLS */ + uint8_t dtls13_header_tags; /* first byte ofi DTLS1.3 unified header */ uint16_t length; uint16_t packet_size; /* header_size + length */ content_type_t type; @@ -719,7 +721,8 @@ record_check_version(gnutls_session_t session, if (vers->tls13_sem) { /* TLS 1.3 requires version to be 0x0303 */ - if (version[0] != 0x03 || version[1] != 0x03) + if ((version[0] != 0x03 || version[1] != 0x03) && + (version[0] != 0xfe || version[1] != 0xfc)) diff = 1; } else { if (vers->major != version[0] || vers->minor != version[1]) @@ -1166,14 +1169,48 @@ record_read_headers(gnutls_session_t session, #endif record->type = headers[0]; - record->version[0] = headers[1]; - record->version[1] = headers[2]; if (IS_DTLS(session)) { - record->sequence = _gnutls_read_uint64(&headers[3]); - record->length = _gnutls_read_uint16(&headers[11]); - record->epoch = record->sequence >> 48; + if (record->type & 0x20) { // DTLS1.3 Ciphertext RFC9147 4.1 + record->dtls13_header_tags = headers[0]; + uint8_t *p = &headers[1]; + + record->version[0] = 0xfe; + record->version[1] = 0xfc; + + record->epoch = //TODO: handle invalid epoch + _dtls13_resolve_epoch(session, record->type & 0x03); + + //TODO: if (record->type & 0x10) // Conection ID present + + if (record->type & 0x08) { // sequence size 16/8 bit + record->sequence = _gnutls_read_uint16(p); + p += 2; + } else { + record->sequence = *p; + p++; + } + + if (record->type & 0x04) { // length is present + record->length = _gnutls_read_uint16(p); + p += 2; + } + + record->type = 0x17; // application data + record->header_size = record->packet_size = + p - headers; //dynamic header size + + } else { + record->version[0] = headers[1]; + record->version[1] = headers[2]; + record->sequence = _gnutls_read_uint64(&headers[3]); + record->length = _gnutls_read_uint16(&headers[11]); + record->epoch = record->sequence >> 48; + } + } else { + record->version[0] = headers[1]; + record->version[1] = headers[2]; memset(&record->sequence, 0, sizeof(record->sequence)); record->length = _gnutls_read_uint16(&headers[3]); @@ -1347,11 +1384,6 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type, goto recv_error; } - if (IS_DTLS(session)) - packet_sequence = record.sequence; - else - packet_sequence = record_state->sequence_number; - /* Read the packet data and insert it to record_recv_buffer. */ ret = @@ -1365,6 +1397,7 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type, /* ok now we are sure that we have read all the data - so * move on ! */ + ret = _mbuffer_linearize_align16(&session->internals.record_recv_buffer, get_total_headers2(session, record_params)); if (ret < 0) @@ -1408,13 +1441,31 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type, (uint8_t *) _mbuffer_get_udata_ptr(bufel) + record.header_size; ciphertext.size = record.length; + /* resolve sequence number */ + if (IS_DTLS(session)) { + if (vers->tls13_sem && record.epoch > 1) { + ret = _dtls13_resolve_seq_num(session, &record.sequence, + record.dtls13_header_tags & 0x08, + ciphertext.data, + record.length); + if (ret < 0) { + gnutls_assert(); + _gnutls_record_log("REC[%p] unable to resolve seq num\n", + session); + } + } + packet_sequence = record.sequence; + } else + packet_sequence = record_state->sequence_number; + /* decrypt the data we got. */ t.data = _mbuffer_get_udata_ptr(decrypted); 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, record.dtls13_header_tags); if (ret >= 0) _mbuffer_set_udata_size(decrypted, ret); @@ -1519,7 +1570,8 @@ _gnutls_recv_in_buffers(gnutls_session_t session, content_type_t type, * is processed and authenticated to avoid someone * messing with our windows. */ if (likely(!(session->internals.flags & GNUTLS_NO_REPLAY_PROTECTION))) { - ret = _dtls_record_check(record_params, packet_sequence); + ret = _dtls_record_check(record_params, packet_sequence, + record.epoch, vers); if (ret < 0) { _gnutls_record_log ("REC[%p]: Discarded duplicate message[%u.%lu]: %s\n", |