summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrantisek Krenzelok <krenzelok.frantisek@gmail.com>2022-12-13 14:48:43 +0100
committerFrantisek Krenzelok <krenzelok.frantisek@gmail.com>2023-02-20 16:30:03 +0100
commit9721fd034ec3447d86568c3bce49d17b81ef5799 (patch)
tree9ff8cde8076db34f9cf6e34ae515bc5d2e8a1b77
parentd8d6952f620e743b9f0be895f51179ffbacd0d11 (diff)
downloadgnutls-9721fd034ec3447d86568c3bce49d17b81ef5799.tar.gz
DTLS1_3: resolve epoch & decrypt record number
[RFC9147] 4.1 Demultiplexing DTLS Records epoch is resolved by internal count and by the last 2 bits of unified header flags (DTLSCiphertextStructure) [RFC9147] 4.2.3 sequence number has to be first deciphered and then resolved just like epoch Signed-off-by: Frantisek Krenzelok <krenzelok.frantisek@gmail.com>
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/cipher.c28
-rw-r--r--lib/constate.c25
-rw-r--r--lib/dtls13.c125
-rw-r--r--lib/dtls13.h17
-rw-r--r--lib/gnutls_int.h2
-rw-r--r--lib/handshake-tls13.c7
-rw-r--r--lib/record.c78
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, &params);
+ 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",