From: Frederik Wedel-Heinen Date: Mon, 21 Oct 2024 09:05:25 +0000 (+0200) Subject: Support DTLS 1.3 Unified Headers X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=42d467380c7241d5b2afd9a784e1b1d20bdbc07f;p=thirdparty%2Fopenssl.git Support DTLS 1.3 Unified Headers Also set correct AAD for DTLS 1.3 message de-/encryption. Reviewed-by: Matt Caswell Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/25668) --- diff --git a/doc/designs/dtlsv1_3/dtlsv1_3-main.md b/doc/designs/dtlsv1_3/dtlsv1_3-main.md index 88d985d5e8f..a97b6e273ab 100644 --- a/doc/designs/dtlsv1_3/dtlsv1_3-main.md +++ b/doc/designs/dtlsv1_3/dtlsv1_3-main.md @@ -40,7 +40,7 @@ as a feature request in issue ###########. ### DTLSv1.3 connection id -OpenSSL does not support Connection ID's (RFC 9146). Notably Openssl DTLSv1.3 clients +OpenSSL does not support Connection IDs (RFC 9146). Notably Openssl DTLSv1.3 clients will not offer the "connection_id" extension even though RFC 9147 states: > DTLS clients which do not want to receive a Connection ID SHOULD still offer @@ -88,14 +88,12 @@ A summary of larger work items that needs to be addressed. Notice that some of the requirements mentioned in [List of DTLSv1.3 requirements](#list-of-dtls-13-requirements) is not covered by these workitems and must be implemented separately. -| Summary | #PR | -|-----------------------------------------------------|----------------| -| ACK messages | - | -| Use HelloRetryRequest instead of HelloVerifyRequest | #22985, #22400 | -| ClientHello | #23320 | -| EndOfEarlyData message | - | -| Variable length header | - | -| DTLSv1.3 Fuzzer | - | +| Summary | #PR | +|-----------------------------------------------------|--------| +| ACK messages | #25119 | +| Use HelloRetryRequest instead of HelloVerifyRequest | #22985 | +| EndOfEarlyData message | - | +| DTLSv1.3 Fuzzer | - | ### Changes from DTLS 1.2 and/or TLS 1.3 diff --git a/doc/man3/SSL_get_version.pod b/doc/man3/SSL_get_version.pod index a6f5e7fb84b..0b041d7dddf 100644 --- a/doc/man3/SSL_get_version.pod +++ b/doc/man3/SSL_get_version.pod @@ -77,6 +77,10 @@ The connection uses the DTLSv1 protocol The connection uses the DTLSv1.2 protocol +=item DTLSv1.3 + +The connection uses the DTLSv1.3 protocol + =item QUICv1 The connection uses the QUICv1 protocol. diff --git a/include/openssl/dtls1.h b/include/openssl/dtls1.h index e5096d1ef53..6ad1c061d33 100644 --- a/include/openssl/dtls1.h +++ b/include/openssl/dtls1.h @@ -22,7 +22,7 @@ extern "C" { #endif -#include +# include /* DTLS*_VERSION constants are defined in prov_ssl.h */ # ifndef OPENSSL_NO_DEPRECATED_3_0 @@ -39,6 +39,7 @@ extern "C" { # define DTLS1_COOKIE_LENGTH 255 # define DTLS1_RT_HEADER_SEQ_OFFS 5 +# define DTLS1_RT_HEADER_SEQ_LEN 6 # define DTLS1_RT_HEADER_LENGTH 13 # define DTLS1_HM_HEADER_LENGTH 12 @@ -52,7 +53,19 @@ extern "C" { # define DTLS1_TMO_ALERT_COUNT 12 -#ifdef __cplusplus +/* DTLS 1.3 Unified header */ +# define DTLS13_UNI_HDR_FIXED_LENGTH 5 +# define DTLS13_UNI_HDR_FIX_BITS 0x20 +# define DTLS13_UNI_HDR_CID_BIT 0x10 +# define DTLS13_UNI_HDR_SEQ_BIT 0x08 +# define DTLS13_UNI_HDR_SEQ_OFF 1 +# define DTLS13_UNI_HDR_LEN_BIT 0x04 +# define DTLS13_UNI_HDR_FIX_BITS_MASK 0xe0 +# define DTLS13_UNI_HDR_EPOCH_BITS_MASK 0x03 + +# define DTLS13_CIPHERTEXT_MINSIZE 16 + +# ifdef __cplusplus } -#endif +# endif #endif diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c index 91419ae8908..5a9c32c261c 100644 --- a/ssl/d1_lib.c +++ b/ssl/d1_lib.c @@ -923,7 +923,7 @@ size_t dtls1_min_mtu(SSL_CONNECTION *s) size_t DTLS_get_data_mtu(const SSL *ssl) { - size_t mac_overhead, int_overhead, blocksize, ext_overhead; + size_t mac_overhead, int_overhead, blocksize, ext_overhead, rechdrlen = 0; const SSL_CIPHER *ciph = SSL_get_current_cipher(ssl); size_t mtu; const SSL_CONNECTION *s = SSL_CONNECTION_FROM_CONST_SSL_ONLY(ssl); @@ -945,10 +945,33 @@ size_t DTLS_get_data_mtu(const SSL *ssl) else int_overhead += mac_overhead; + if (SSL_version(ssl) == DTLS1_3_VERSION) { + switch (SSL_get_state(ssl)) { + case TLS_ST_BEFORE: + case DTLS_ST_CR_HELLO_VERIFY_REQUEST: + case TLS_ST_CR_SRVR_HELLO: + case TLS_ST_CW_CLNT_HELLO: + case TLS_ST_CW_COMP_CERT: + case TLS_ST_CW_KEY_EXCH: + case TLS_ST_SW_HELLO_REQ: + case TLS_ST_SR_CLNT_HELLO: + case DTLS_ST_SW_HELLO_VERIFY_REQUEST: + case TLS_ST_SW_SRVR_HELLO: + case TLS_ST_CR_HELLO_REQ: + rechdrlen = DTLS1_RT_HEADER_LENGTH; + break; + default: + rechdrlen = DTLS13_UNI_HDR_FIXED_LENGTH; + break; + } + } else { + rechdrlen = DTLS1_RT_HEADER_LENGTH; + } + /* Subtract external overhead (e.g. IV/nonce, separate MAC) */ - if (ext_overhead + DTLS1_RT_HEADER_LENGTH >= mtu) + if (ext_overhead + rechdrlen >= mtu) return 0; - mtu -= ext_overhead + DTLS1_RT_HEADER_LENGTH; + mtu -= ext_overhead + rechdrlen; /* Round encrypted payload down to cipher block size (for CBC etc.) * No check for overflow since 'mtu % blocksize' cannot exceed mtu. */ diff --git a/ssl/record/methods/dtls_meth.c b/ssl/record/methods/dtls_meth.c index de497c57858..bfa411520d8 100644 --- a/ssl/record/methods/dtls_meth.c +++ b/ssl/record/methods/dtls_meth.c @@ -105,6 +105,24 @@ static void dtls_set_in_init(OSSL_RECORD_LAYER *rl, int in_init) rl->in_init = in_init; } +size_t dtls_get_rec_header_size(uint8_t hdr_first_byte) +{ + size_t size = 0; + + if (DTLS13_UNI_HDR_FIX_BITS_IS_SET(hdr_first_byte) + && ossl_assert(!DTLS13_UNI_HDR_CID_BIT_IS_SET(hdr_first_byte))) { + /* DTLSv1.3 unified record header */ + size = 1; + size += DTLS13_UNI_HDR_SEQ_BIT_IS_SET(hdr_first_byte) ? 2 : 1; + size += DTLS13_UNI_HDR_LEN_BIT_IS_SET(hdr_first_byte) ? 2 : 0; + } else { + /* DTLSv1.0, DTLSv1.2 or unencrypted DTLSv1.3 record header */ + size = DTLS1_RT_HEADER_LENGTH; + } + + return size; +} + static int dtls_process_record(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap) { int i; @@ -112,6 +130,7 @@ static int dtls_process_record(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap) TLS_RL_RECORD *rr; int imac_size; size_t mac_size = 0; + size_t rechdrsize = dtls_get_rec_header_size(rl->packet[0]); unsigned char md[EVP_MAX_MD_SIZE]; SSL_MAC_BUF macbuf = { NULL, 0 }; int ret = 0; @@ -119,10 +138,10 @@ static int dtls_process_record(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap) rr = &rl->rrec[0]; /* - * At this point, rl->packet_length == DTLS1_RT_HEADER_LENGTH + rr->length, + * At this point, rl->packet_length == rechdrsize + rr->length, * and we have that many bytes in rl->packet */ - rr->input = &(rl->packet[DTLS1_RT_HEADER_LENGTH]); + rr->input = rl->packet + rechdrsize; /* * ok, we can now read from 'rl->packet' data into 'rr'. rr->input @@ -342,7 +361,8 @@ static int dtls_copy_rlayer_record(OSSL_RECORD_LAYER *rl, pitem *item) memcpy(&rl->rrec[0], &(rdata->rrec), sizeof(TLS_RL_RECORD)); /* Set proper sequence number for mac calculation */ - memcpy(&(rl->sequence[2]), &(rdata->packet[5]), 6); + assert(sizeof(rl->sequence) == sizeof(rdata->rrec.seq_num)); + memcpy(rl->sequence, rdata->rrec.seq_num, sizeof(rl->sequence)); return 1; } @@ -366,14 +386,13 @@ static int dtls_retrieve_rlayer_buffered_record(OSSL_RECORD_LAYER *rl, } /* rfc9147 section 4.2.3 */ -int dtls_crypt_sequence_number(EVP_CIPHER_CTX *ctx, unsigned char *seq, +int dtls_crypt_sequence_number(EVP_CIPHER_CTX *ctx, unsigned char *seq, size_t seqlen, unsigned char *rec_data, size_t rec_data_offs) { unsigned char mask[16]; int outlen, inlen; unsigned char *iv, *in; size_t i; - size_t seq_len = 6; if (ossl_assert(sizeof(mask) > rec_data_offs)) inlen = (int)(sizeof(mask) - rec_data_offs); @@ -393,7 +412,10 @@ int dtls_crypt_sequence_number(EVP_CIPHER_CTX *ctx, unsigned char *seq, || outlen != 0) return 0; - for (i = 0; i < seq_len; i++) + if (!ossl_assert(seqlen <= sizeof(mask))) + return 0; + + for (i = 0; i < seqlen; i++) seq[i] ^= mask[i]; OPENSSL_cleanse(mask, sizeof(mask)); @@ -412,13 +434,17 @@ int dtls_crypt_sequence_number(EVP_CIPHER_CTX *ctx, unsigned char *seq, */ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) { - int ssl_major, ssl_minor; int rret; - size_t more, n; + size_t more, nread = 0; TLS_RL_RECORD *rr; - unsigned char *p = NULL; DTLS_BITMAP *bitmap; unsigned int is_next_epoch; + unsigned char recseqnum[6]; + size_t recseqnumlen = 0; + size_t rechdrlen = 0; + size_t recseqnumoffs = 0; + + memset(recseqnum, 0, sizeof(recseqnum)); rl->num_recs = 0; rl->curr_rec = 0; @@ -443,10 +469,13 @@ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) /* get something from the wire */ /* check if we have the header */ - if ((rl->rstate != SSL_ST_READ_BODY) || - (rl->packet_length < DTLS1_RT_HEADER_LENGTH)) { + if (rl->rstate != SSL_ST_READ_BODY + || rl->packet_length < DTLS1_RT_HEADER_LENGTH) { + PACKET dtlsrecord; + unsigned int record_type, record_version, epoch, length; + rret = rl->funcs->read_n(rl, DTLS1_RT_HEADER_LENGTH, - TLS_BUFFER_get_len(&rl->rbuf), 0, 1, &n); + TLS_BUFFER_get_len(&rl->rbuf), 0, 1, &nread); /* read timeout is handled by dtls1_read_bytes */ if (rret < OSSL_RECORD_RETURN_SUCCESS) { /* RLAYERfatal() already called if appropriate */ @@ -461,25 +490,112 @@ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) rl->rstate = SSL_ST_READ_BODY; - p = rl->packet; + if (!PACKET_buf_init(&dtlsrecord, rl->packet, rl->packet_length) + || !PACKET_get_1(&dtlsrecord, &record_type)) { + rl->packet_length = 0; + goto again; + } /* Pull apart the header into the DTLS1_RECORD */ - rr->type = *(p++); - ssl_major = *(p++); - ssl_minor = *(p++); - rr->rec_version = (ssl_major << 8) | ssl_minor; + rr->type = (int)record_type; + + /*- + * rfc9147: + * Implementations can demultiplex DTLS 1.3 records by examining the first + * byte as follows: + * * If the first byte is alert(21), handshake(22), or ack(proposed, 26), + * the record MUST be interpreted as a DTLSPlaintext record. + * * If the first byte is any other value, then receivers MUST check to + * see if the leading bits of the first byte are 001. If so, the implementation + * MUST process the record as DTLSCiphertext; the true content type + * will be inside the protected portion. + * * Otherwise, the record MUST be rejected as if it had failed deprotection, + * as described in Section 4.5.2. + */ + if (rl->version == DTLS1_3_VERSION + && rr->type != SSL3_RT_ALERT + && rr->type != SSL3_RT_HANDSHAKE + /* TODO(DTLSv1.3): && rr->type != SSL3_RT_ACK depends on acknowledge implementation */ + && !DTLS13_UNI_HDR_FIX_BITS_IS_SET(rr->type)) { + /* Silently discard */ + rr->length = 0; + rl->packet_length = 0; + goto again; + } - /* sequence number is 64 bits, with top 2 bytes = epoch */ - n2s(p, rr->epoch); + if (DTLS13_UNI_HDR_FIX_BITS_IS_SET(rr->type)) { + /* + * rfc9147: + * receivers MUST check to if the leading bits of the first byte are 001. + * If so, the implementation MUST process the record as DTLSCiphertext; + */ + int cbitisset = DTLS13_UNI_HDR_CID_BIT_IS_SET(rr->type); + int sbitisset = DTLS13_UNI_HDR_SEQ_BIT_IS_SET(rr->type); + int lbitisset = DTLS13_UNI_HDR_LEN_BIT_IS_SET(rr->type); + uint16_t eebits = rr->type & DTLS13_UNI_HDR_EPOCH_BITS_MASK; + + record_version = DTLS1_2_VERSION; + epoch = rl->epoch; + recseqnumlen = sbitisset ? 2 : 1; + recseqnumoffs = sizeof(recseqnum) - recseqnumlen; + + if (/* OpenSSL does not support connection IDs: silently discard */ + cbitisset + /* + * Naive approach? We expect sequence number to be filled already + * and then override the last bytes of the sequence number. + */ + || !PACKET_copy_bytes(&dtlsrecord, recseqnum + recseqnumoffs, recseqnumlen)) { + rr->length = 0; + rl->packet_length = 0; + goto again; + } - memcpy(&(rl->sequence[2]), p, 6); - p += 6; + /* + * rfc9147: + * The length field MAY be omitted by clearing the L bit, which means + * that the record consumes the entire rest of the datagram in the + * lower level transport + */ + length = TLS_BUFFER_get_len(&rl->rbuf) - dtls_get_rec_header_size(rr->type); - n2s(p, rr->length); + if ((lbitisset && !PACKET_get_net_2(&dtlsrecord, &length)) + || length == 0) { + rr->length = 0; + rl->packet_length = 0; + goto again; + } + + /* + * We should not be getting records from a previous epoch so + * choose the current epoch if the bits match or else choose the + * next epoch with matching bits + */ + while (eebits != (epoch & DTLS13_UNI_HDR_EPOCH_BITS_MASK)) + epoch++; + + } else { + if (!PACKET_get_net_2(&dtlsrecord, &record_version) + || !PACKET_get_net_2(&dtlsrecord, &epoch) + || !PACKET_copy_bytes(&dtlsrecord, recseqnum, 6) + || !PACKET_get_net_2(&dtlsrecord, &length)) { + rr->length = 0; + rl->packet_length = 0; + goto again; + } + + recseqnumoffs = 0; + recseqnumlen = 6; + } + + rechdrlen = PACKET_data(&dtlsrecord) - rl->packet; + rr->rec_version = (int)record_version; + rr->epoch = epoch; + rr->length = length; if (rl->msg_callback != NULL) - rl->msg_callback(0, rr->rec_version, SSL3_RT_HEADER, rl->packet, DTLS1_RT_HEADER_LENGTH, - rl->cbarg); + rl->msg_callback(0, rr->rec_version, SSL3_RT_HEADER, rl->packet, + rechdrlen, rl->cbarg); /* * Lets check the version. We tolerate alerts that don't have the exact @@ -497,9 +613,9 @@ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) } } - if (ssl_major != - (rl->version == DTLS_ANY_VERSION ? DTLS1_VERSION_MAJOR - : rl->version >> 8)) { + if (rr->rec_version >> 8 != + (rl->version == DTLS_ANY_VERSION ? DTLS1_VERSION_MAJOR + : rl->version >> 8)) { /* wrong version, silently discard record */ rr->length = 0; rl->packet_length = 0; @@ -531,10 +647,10 @@ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) if (rr->length > rl->packet_length - DTLS1_RT_HEADER_LENGTH) { /* now rl->packet_length == DTLS1_RT_HEADER_LENGTH */ - more = rr->length; - rret = rl->funcs->read_n(rl, more, more, 1, 1, &n); + more = rr->length - (nread - rechdrlen); + rret = rl->funcs->read_n(rl, more, more, 1, 1, &nread); /* this packet contained a partial record, dump it */ - if (rret < OSSL_RECORD_RETURN_SUCCESS || n != more) { + if (rret < OSSL_RECORD_RETURN_SUCCESS || nread != more) { if (rl->alert != SSL_AD_NO_ALERT) { /* read_n() called RLAYERfatal() */ return OSSL_RECORD_RETURN_FATAL; @@ -554,15 +670,18 @@ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) /* * rfc9147: - * This procedure requires the ciphertext length to be at least 16 bytes. + * This procedure requires the ciphertext length to be at least + * DTLS13_CIPHERTEXT_MINSIZE (16) bytes. * Receivers MUST reject shorter records as if they had failed deprotection - * TODO(DTLSv1.3): This check will need to be modified when support for variable - * length headers is added. */ - if (rl->sn_enc_ctx != NULL - && (rl->packet_length < DTLS1_RT_HEADER_LENGTH + 16 - || !dtls_crypt_sequence_number(rl->sn_enc_ctx, &(rl->sequence[2]), - rl->packet + DTLS1_RT_HEADER_LENGTH, + if (DTLS13_UNI_HDR_FIX_BITS_IS_SET(rr->type) + && rl->version == DTLS1_3_VERSION + && (!ossl_assert(rl->sn_enc_ctx != NULL) + || !ossl_assert(rl->packet_length >= rechdrlen + DTLS13_CIPHERTEXT_MINSIZE) + || !dtls_crypt_sequence_number(rl->sn_enc_ctx, + recseqnum + recseqnumoffs, + recseqnumlen, + rl->packet + rechdrlen, rl->sn_enc_offs))) { /* sequence number encryption failed dump record */ rr->length = 0; @@ -570,6 +689,9 @@ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) goto again; } + memset(rl->sequence, 0, sizeof(rl->sequence)); + memcpy(rl->sequence + 2, recseqnum, sizeof(recseqnum)); + /* match epochs. NULL means the packet is dropped on the floor */ bitmap = dtls_get_bitmap(rl, rr, &is_next_epoch); if (bitmap == NULL) { @@ -742,6 +864,7 @@ int dtls_prepare_record_header(OSSL_RECORD_LAYER *rl, unsigned char **recdata) { size_t maxcomplen; + int unifiedheader = rl->version == DTLS1_3_VERSION && rl->epoch > 0; *recdata = NULL; @@ -749,7 +872,26 @@ int dtls_prepare_record_header(OSSL_RECORD_LAYER *rl, if (rl->compctx != NULL) maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD; - if (!WPACKET_put_bytes_u8(thispkt, rectype) + if (unifiedheader) { + uint8_t fixedbits = 0x20; + uint8_t cbit = 0; + uint8_t sbit = DTLS13_UNI_HDR_SEQ_BIT; + uint8_t lbit = DTLS13_UNI_HDR_LEN_BIT; + uint8_t ebits = rl->epoch & DTLS13_UNI_HDR_EPOCH_BITS_MASK; + uint8_t unifiedhdrbits = fixedbits | cbit | sbit | lbit | ebits; + + if (!WPACKET_put_bytes_u8(thispkt, unifiedhdrbits) + || !WPACKET_memcpy(thispkt, rl->sequence + 6, 2) + || !WPACKET_start_sub_packet_u16(thispkt) + || (rl->eivlen > 0 + && !WPACKET_allocate_bytes(thispkt, rl->eivlen, NULL)) + || (maxcomplen > 0 + && !WPACKET_reserve_bytes(thispkt, maxcomplen, recdata))) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + } else { + if (!WPACKET_put_bytes_u8(thispkt, rectype) || !WPACKET_put_bytes_u16(thispkt, templ->version) || !WPACKET_put_bytes_u16(thispkt, rl->epoch) || !WPACKET_memcpy(thispkt, &(rl->sequence[2]), 6) @@ -757,10 +899,10 @@ int dtls_prepare_record_header(OSSL_RECORD_LAYER *rl, || (rl->eivlen > 0 && !WPACKET_allocate_bytes(thispkt, rl->eivlen, NULL)) || (maxcomplen > 0 - && !WPACKET_reserve_bytes(thispkt, maxcomplen, - recdata))) { - RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); - return 0; + && !WPACKET_reserve_bytes(thispkt, maxcomplen, recdata))) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } } return 1; @@ -784,6 +926,7 @@ int dtls_post_encryption_processing(OSSL_RECORD_LAYER *rl, static size_t dtls_get_max_record_overhead(OSSL_RECORD_LAYER *rl) { size_t blocksize = 0, contenttypelen = 0; + size_t rchdrlen = tls_get_record_header_len(rl); if (rl->enc_ctx != NULL && (EVP_CIPHER_CTX_get_mode(rl->enc_ctx) == EVP_CIPH_CBC_MODE)) @@ -815,8 +958,7 @@ static size_t dtls_get_max_record_overhead(OSSL_RECORD_LAYER *rl) * MTU size - so isn't very helpful. We just ignore potential expansion * due to compression. */ - return DTLS1_RT_HEADER_LENGTH + rl->eivlen + blocksize + rl->taglen - + contenttypelen; + return rchdrlen + rl->eivlen + blocksize + rl->taglen + contenttypelen; } const OSSL_RECORD_METHOD ossl_dtls_record_method = { diff --git a/ssl/record/methods/recmethod_local.h b/ssl/record/methods/recmethod_local.h index 8cf1141ee01..8da1c8a2824 100644 --- a/ssl/record/methods/recmethod_local.h +++ b/ssl/record/methods/recmethod_local.h @@ -423,7 +423,24 @@ int tls_default_read_n(OSSL_RECORD_LAYER *rl, size_t n, size_t max, int extend, int clearold, size_t *readbytes); int tls_get_more_records(OSSL_RECORD_LAYER *rl); -int dtls_crypt_sequence_number(EVP_CIPHER_CTX *ctx, unsigned char *seq, +/* Returns true if the unified header fixed bits are set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_FIX_BITS_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_FIX_BITS_MASK) == DTLS13_UNI_HDR_FIX_BITS) + +/* Returns true if the unified header connection id bit is set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_CID_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_CID_BIT) == DTLS13_UNI_HDR_CID_BIT) + +/* Returns true if the unified header sequence number bit is set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_SEQ_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_SEQ_BIT) == DTLS13_UNI_HDR_SEQ_BIT) + +/* Returns true if the unified header length bit is set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_LEN_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_LEN_BIT) == DTLS13_UNI_HDR_LEN_BIT) + +size_t dtls_get_rec_header_size(uint8_t hdr_first_byte); +int dtls_crypt_sequence_number(EVP_CIPHER_CTX *ctx, unsigned char *seq, size_t seqlen, unsigned char *rec_data, size_t rec_data_offs); int dtls_get_more_records(OSSL_RECORD_LAYER *rl); @@ -440,6 +457,7 @@ int dtls_post_encryption_processing(OSSL_RECORD_LAYER *rl, int tls_default_set_protocol_version(OSSL_RECORD_LAYER *rl, int version); int tls_default_validate_record_header(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *re); +size_t tls_get_record_header_len(OSSL_RECORD_LAYER *rl); int tls_do_compress(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *wr); int tls_do_uncompress(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec); int tls_default_post_process_record(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec); @@ -469,7 +487,6 @@ int tls_read_record(OSSL_RECORD_LAYER *rl, void **rechandle, int *rversion, uint8_t *type, const unsigned char **data, size_t *datalen, uint16_t *epoch, unsigned char *seq_num); int tls_release_record(OSSL_RECORD_LAYER *rl, void *rechandle, size_t length); -int tls_default_set_protocol_version(OSSL_RECORD_LAYER *rl, int version); int tls_set_protocol_version(OSSL_RECORD_LAYER *rl, int version); void tls_set_plain_alerts(OSSL_RECORD_LAYER *rl, int allow); void tls_set_first_handshake(OSSL_RECORD_LAYER *rl, int first); @@ -493,6 +510,8 @@ size_t tls_get_max_records_default(OSSL_RECORD_LAYER *rl, uint8_t type, size_t tls_get_max_records_multiblock(OSSL_RECORD_LAYER *rl, uint8_t type, size_t len, size_t maxfrag, size_t *preffrag); +size_t tls_get_record_body_alignment_offset(OSSL_RECORD_LAYER *rl, + const unsigned char *rec); int tls_allocate_write_buffers_default(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, size_t numtempl, size_t *prefix); diff --git a/ssl/record/methods/tls13_meth.c b/ssl/record/methods/tls13_meth.c index dd44764059c..e66f5865e84 100644 --- a/ssl/record/methods/tls13_meth.c +++ b/ssl/record/methods/tls13_meth.c @@ -112,7 +112,8 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, EVP_CIPHER_CTX *enc_ctx; unsigned char recheader[SSL3_RT_HEADER_LENGTH]; unsigned char tag[EVP_MAX_MD_SIZE]; - size_t nonce_len, offset, loop, hdrlen, taglen; + size_t nonce_len, offset, loop, hdrlen, taglen, exphdrlen; + int isdtls, sbit = 0, addlen; unsigned char *staticiv; unsigned char *nonce; unsigned char *seq = rl->sequence; @@ -132,6 +133,7 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, enc_ctx = rl->enc_ctx; /* enc_ctx is ignored when rl->mac_ctx != NULL */ staticiv = rl->iv; nonce = rl->nonce; + isdtls = rl->isdtls; if (enc_ctx == NULL && rl->mac_ctx == NULL) { RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); @@ -185,18 +187,46 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, for (loop = 0; loop < SEQ_NUM_SIZE; loop++) nonce[offset + loop] = staticiv[offset + loop] ^ seq[loop]; - if (!tls_increment_sequence_ctr(rl)) { + if (!isdtls && !tls_increment_sequence_ctr(rl)) { /* RLAYERfatal already called */ return 0; } - /* Set up the AAD */ - if (!WPACKET_init_static_len(&wpkt, recheader, sizeof(recheader), 0) + /*- + * Set up the additional data as described in rfc8446 section 5.2: + * "and the additional data input is the record header. + * I.e., + * additional_data = TLSCiphertext.opaque_type || + * TLSCiphertext.legacy_record_version || + * TLSCiphertext.length" + * and in rfc1947 section 4: + * "The entire header value shown in Figure 4 (but prior to record number + * encryption; see Section 4.2.3) is used as the additional data value for + * the AEAD function. For instance, if the minimal variant is used, the + * Associated Data (AD) is 2 octets long." + * + * For DTLS: at this point rec->type is just the first byte of the variable + * header. So it is not an actual record type. The record type is set in + * tls13_post_process_record() for incoming records. + */ + if (isdtls) { + exphdrlen = dtls_get_rec_header_size(rec->type); + sbit = DTLS13_UNI_HDR_SEQ_BIT_IS_SET(rec->type); + addlen = DTLS13_UNI_HDR_LEN_BIT_IS_SET(rec->type); + } else { + exphdrlen = SSL3_RT_HEADER_LENGTH; + addlen = 1; + } + + if ((isdtls && !ossl_assert(!DTLS13_UNI_HDR_CID_BIT_IS_SET(rec->type))) + || !WPACKET_init_static_len(&wpkt, recheader, sizeof(recheader), 0) || !WPACKET_put_bytes_u8(&wpkt, rec->type) - || !WPACKET_put_bytes_u16(&wpkt, rec->rec_version) - || !WPACKET_put_bytes_u16(&wpkt, rec->length + rl->taglen) + || (isdtls && (sbit ? !WPACKET_memcpy(&wpkt, rl->sequence + 6, 2) + : !WPACKET_memcpy(&wpkt, rl->sequence + 7, 1))) + || (!isdtls && !WPACKET_put_bytes_u16(&wpkt, rec->rec_version)) + || (addlen && !WPACKET_put_bytes_u16(&wpkt, rec->length + rl->taglen)) || !WPACKET_get_total_written(&wpkt, &hdrlen) - || hdrlen != SSL3_RT_HEADER_LENGTH + || hdrlen != exphdrlen || !WPACKET_finish(&wpkt)) { RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); WPACKET_cleanup(&wpkt); @@ -208,7 +238,7 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, if ((mac_ctx = EVP_MAC_CTX_dup(rl->mac_ctx)) == NULL || !EVP_MAC_update(mac_ctx, nonce, nonce_len) - || !EVP_MAC_update(mac_ctx, recheader, sizeof(recheader)) + || !EVP_MAC_update(mac_ctx, recheader, hdrlen) || !EVP_MAC_update(mac_ctx, rec->input, rec->length) || !EVP_MAC_final(mac_ctx, tag, &taglen, rl->taglen)) { RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); @@ -248,12 +278,9 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, * any AAD. */ if ((mode == EVP_CIPH_CCM_MODE - && EVP_CipherUpdate(enc_ctx, NULL, &lenu, NULL, - (unsigned int)rec->length) <= 0) - || EVP_CipherUpdate(enc_ctx, NULL, &lenu, recheader, - sizeof(recheader)) <= 0 - || EVP_CipherUpdate(enc_ctx, rec->data, &lenu, rec->input, - (unsigned int)rec->length) <= 0 + && EVP_CipherUpdate(enc_ctx, NULL, &lenu, NULL, (int)rec->length) <= 0) + || EVP_CipherUpdate(enc_ctx, NULL, &lenu, recheader, (int)hdrlen) <= 0 + || EVP_CipherUpdate(enc_ctx, rec->data, &lenu, rec->input, (int)rec->length) <= 0 || EVP_CipherFinal_ex(enc_ctx, rec->data + lenu, &lenf) <= 0 || (size_t)(lenu + lenf) != rec->length) { return 0; @@ -302,7 +329,8 @@ static int tls13_post_process_record(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec) size_t end; if (rec->length == 0 - || rec->type != SSL3_RT_APPLICATION_DATA) { + || rl->isdtls ? !DTLS13_UNI_HDR_FIX_BITS_IS_SET(rec->type) + : rec->type != SSL3_RT_APPLICATION_DATA) { RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_RECORD_TYPE); return 0; @@ -340,6 +368,15 @@ static uint8_t tls13_get_record_type(OSSL_RECORD_LAYER *rl, * when encrypting in TLSv1.3. The "inner" record type encodes the "real" * record type from the template. */ + if (rl->isdtls) { + const unsigned char fixed = DTLS13_UNI_HDR_FIX_BITS; + const unsigned char sbit = DTLS13_UNI_HDR_SEQ_BIT; + const unsigned char lbit = DTLS13_UNI_HDR_LEN_BIT; + const unsigned char epochbits = DTLS13_UNI_HDR_EPOCH_BITS_MASK & rl->epoch; + + return fixed | sbit | lbit | epochbits; + } + return SSL3_RT_APPLICATION_DATA; } diff --git a/ssl/record/methods/tls1_meth.c b/ssl/record/methods/tls1_meth.c index aaf13051c47..2bc0cbc72e3 100644 --- a/ssl/record/methods/tls1_meth.c +++ b/ssl/record/methods/tls1_meth.c @@ -626,12 +626,7 @@ int tls1_initialise_write_packets(OSSL_RECORD_LAYER *rl, prefixtempl->type = SSL3_RT_APPLICATION_DATA; wb = &bufs[0]; - -#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 - align = (size_t)TLS_BUFFER_get_buf(wb) + SSL3_RT_HEADER_LENGTH; - align = SSL3_ALIGN_PAYLOAD - 1 - - ((align - 1) % SSL3_ALIGN_PAYLOAD); -#endif + align = tls_get_record_body_alignment_offset(rl, TLS_BUFFER_get_buf(wb)); TLS_BUFFER_set_offset(wb, align); if (!WPACKET_init_static_len(&pkt[0], TLS_BUFFER_get_buf(wb), diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c index 77b09720136..dfbb7decc1e 100644 --- a/ssl/record/methods/tls_common.c +++ b/ssl/record/methods/tls_common.c @@ -151,10 +151,7 @@ int tls_setup_write_buffer(OSSL_RECORD_LAYER *rl, size_t numwpipes, const int version1_3 = rl->isdtls ? DTLS1_3_VERSION : TLS1_3_VERSION; if (firstlen == 0 || (numwpipes > 1 && nextlen == 0)) { - if (rl->isdtls) - headerlen = DTLS1_RT_HEADER_LENGTH + 1; - else - headerlen = SSL3_RT_HEADER_LENGTH; + headerlen = tls_get_record_header_len(rl); /* (D)TLSv1.3 adds an extra content type byte after payload data */ if (rl->version == version1_3) @@ -235,10 +232,7 @@ int tls_setup_read_buffer(OSSL_RECORD_LAYER *rl) b = &rl->rbuf; - if (rl->isdtls) - headerlen = DTLS1_RT_HEADER_LENGTH; - else - headerlen = SSL3_RT_HEADER_LENGTH; + headerlen = tls_get_record_header_len(rl); #if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 maxalign = SSL3_ALIGN_PAYLOAD - 1; @@ -636,7 +630,7 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl) || !PACKET_get_net_2(&pkt, &version) || !PACKET_get_net_2_len(&pkt, &thisrr->length)) { if (rl->msg_callback != NULL) - rl->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, rl->cbarg); + rl->msg_callback(0, 0, SSL3_RT_HEADER, p, SSL3_RT_HEADER_LENGTH, rl->cbarg); RLAYERfatal(rl, SSL_AD_DECODE_ERROR, ERR_R_INTERNAL_ERROR); return OSSL_RECORD_RETURN_FATAL; } @@ -656,7 +650,8 @@ int tls_get_more_records(OSSL_RECORD_LAYER *rl) } if (rl->msg_callback != NULL) - rl->msg_callback(0, version, SSL3_RT_HEADER, p, 5, rl->cbarg); + rl->msg_callback(0, version, SSL3_RT_HEADER, p, + SSL3_RT_HEADER_LENGTH, rl->cbarg); if (thisrr->length > TLS_BUFFER_get_len(rbuf) - SSL3_RT_HEADER_LENGTH) { @@ -1562,6 +1557,21 @@ int tls_allocate_write_buffers_default(OSSL_RECORD_LAYER *rl, return 1; } +size_t tls_get_record_body_alignment_offset(OSSL_RECORD_LAYER *rl, + const unsigned char *rec) +{ + size_t alignoffset = 0; + size_t headersize = tls_get_record_header_len(rl); + +#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 + alignoffset = (size_t)rec; + alignoffset += headersize; + alignoffset = SSL3_ALIGN_PAYLOAD - 1 - ((alignoffset - 1) % SSL3_ALIGN_PAYLOAD); +#endif + + return alignoffset; +} + int tls_initialise_write_packets_default(OSSL_RECORD_LAYER *rl, OSSL_RECORD_TEMPLATE *templates, size_t numtempl, @@ -1579,13 +1589,7 @@ int tls_initialise_write_packets_default(OSSL_RECORD_LAYER *rl, wb = &bufs[j]; wb->type = templates[j].type; - -#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 - align = (size_t)TLS_BUFFER_get_buf(wb); - align += rl->isdtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH; - align = SSL3_ALIGN_PAYLOAD - 1 - - ((align - 1) % SSL3_ALIGN_PAYLOAD); -#endif + align = tls_get_record_body_alignment_offset(rl, TLS_BUFFER_get_buf(wb)); TLS_BUFFER_set_offset(wb, align); if (!WPACKET_init_static_len(thispkt, TLS_BUFFER_get_buf(wb), @@ -1689,8 +1693,9 @@ int tls_post_encryption_processing_default(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *thiswr) { size_t origlen, len; - size_t headerlen = rl->isdtls ? DTLS1_RT_HEADER_LENGTH - : SSL3_RT_HEADER_LENGTH; + unsigned char *recordstart; + size_t rechdrlen; + size_t written; /* Allocate bytes for the encryption overhead */ if (!WPACKET_get_length(thispkt, &origlen) @@ -1719,31 +1724,37 @@ int tls_post_encryption_processing_default(OSSL_RECORD_LAYER *rl, } if (!WPACKET_get_length(thispkt, &len) - || !WPACKET_close(thispkt)) { + || !WPACKET_close(thispkt) + || !WPACKET_get_total_written(thispkt, &written)) { RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } - if (rl->sn_enc_ctx != NULL) { - unsigned char *recordstart; + recordstart = WPACKET_get_curr(thispkt) - written; + recordstart += tls_get_record_body_alignment_offset(rl, recordstart); + + if (rl->isdtls) + rechdrlen = dtls_get_rec_header_size(*recordstart); + else + rechdrlen = SSL3_RT_HEADER_LENGTH; - recordstart = WPACKET_get_curr(thispkt) - len - headerlen; + if (rl->isdtls && DTLS13_UNI_HDR_FIX_BITS_IS_SET(*recordstart)) { + size_t seqnumlen = DTLS13_UNI_HDR_SEQ_BIT_IS_SET(*recordstart) ? 2 : 1; - if (!dtls_crypt_sequence_number(rl->sn_enc_ctx, recordstart + DTLS1_RT_HEADER_SEQ_OFFS, - recordstart + DTLS1_RT_HEADER_LENGTH, - rl->sn_enc_offs)) { + if (!ossl_assert(DTLS13_UNI_HDR_SEQ_OFF + seqnumlen <= rechdrlen) + || !dtls_crypt_sequence_number(rl->sn_enc_ctx, recordstart + DTLS13_UNI_HDR_SEQ_OFF, + seqnumlen, recordstart + rechdrlen, + rl->sn_enc_offs)) { RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } } if (rl->msg_callback != NULL) { - unsigned char *recordstart; const int version1_3 = rl->isdtls ? DTLS1_3_VERSION : TLS1_3_VERSION; - recordstart = WPACKET_get_curr(thispkt) - len - headerlen; rl->msg_callback(1, thiswr->rec_version, SSL3_RT_HEADER, recordstart, - headerlen, rl->cbarg); + rechdrlen, rl->cbarg); if (rl->version == version1_3 && rl->enc_ctx != NULL) { unsigned char ctype = thistempl->type; @@ -1758,7 +1769,7 @@ int tls_post_encryption_processing_default(OSSL_RECORD_LAYER *rl, return 0; } - TLS_RL_RECORD_add_length(thiswr, headerlen); + TLS_RL_RECORD_add_length(thiswr, rechdrlen); return 1; } @@ -2017,6 +2028,22 @@ int tls_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio) return 1; } +size_t tls_get_record_header_len(OSSL_RECORD_LAYER *rl) +{ + size_t headerlen; + + if (rl->isdtls) { + if (rl->version == DTLS1_3_VERSION && rl->epoch > 0) + headerlen = DTLS13_UNI_HDR_FIXED_LENGTH; + else + headerlen = DTLS1_RT_HEADER_LENGTH; + } else { + headerlen = SSL3_RT_HEADER_LENGTH; + } + + return headerlen; +} + /* Shared by most methods except tlsany_meth */ int tls_default_set_protocol_version(OSSL_RECORD_LAYER *rl, int version) { diff --git a/test/helpers/ssltestlib.c b/test/helpers/ssltestlib.c index 07e0756803c..dcb562e34a1 100644 --- a/test/helpers/ssltestlib.c +++ b/test/helpers/ssltestlib.c @@ -120,6 +120,21 @@ static void copy_flags(BIO *bio) #define MSG_FRAG_LEN_MID 10 #define MSG_FRAG_LEN_LO 11 +/* Returns true if the unified header fixed bits are set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_FIX_BITS_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_FIX_BITS_MASK) == DTLS13_UNI_HDR_FIX_BITS) + +/* Returns true if the unified header connection id bit is set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_CID_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_CID_BIT) == DTLS13_UNI_HDR_CID_BIT) + +/* Returns true if the unified header sequence number bit is set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_SEQ_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_SEQ_BIT) == DTLS13_UNI_HDR_SEQ_BIT) + +/* Returns true if the unified header length bit is set (rfc9147 section 4) */ +#define DTLS13_UNI_HDR_LEN_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_LEN_BIT) == DTLS13_UNI_HDR_LEN_BIT) static void dump_data(const char *data, int len) { @@ -135,6 +150,9 @@ static void dump_data(const char *data, int len) if (rem != len) printf("*\n"); printf("*---- START OF RECORD ----\n"); + /* + * TODO(DTLSv1.3): support variable length headers + */ if (rem < DTLS1_RT_HEADER_LENGTH) { printf("*---- RECORD TRUNCATED ----\n"); break; @@ -543,8 +561,22 @@ int mempacket_test_inject(BIO *bio, const char *in, int inl, int pktnum, MEMPACKET *thispkt = NULL, *looppkt, *nextpkt, *allpkts[3]; int i, duprec; const unsigned char *inu = (const unsigned char *)in; - size_t len = ((inu[RECORD_LEN_HI] << 8) | inu[RECORD_LEN_LO]) - + DTLS1_RT_HEADER_LENGTH; + size_t len; + + if (DTLS13_UNI_HDR_FIX_BITS_IS_SET(*in) + /* The following code does not handle connection ids */ + && ossl_assert(!DTLS13_UNI_HDR_CID_BIT_IS_SET(*in))) { + if (DTLS13_UNI_HDR_LEN_BIT_IS_SET(*in)) { + len = 3; /* 2 bytes for len field and 1 byte for record type */ + len += DTLS13_UNI_HDR_SEQ_BIT_IS_SET(*in) ? 2 : 1; + } else { + /* We assert that inl is the correct record length */ + len = inl; + } + } else { + len = ((inu[RECORD_LEN_HI] << 8) | inu[RECORD_LEN_LO]); + len += DTLS1_RT_HEADER_LENGTH; + } if (ctx == NULL) return -1; diff --git a/util/perl/TLSProxy/Record.pm b/util/perl/TLSProxy/Record.pm index 4d4a48498f2..8f90dea4be5 100644 --- a/util/perl/TLSProxy/Record.pm +++ b/util/perl/TLSProxy/Record.pm @@ -20,11 +20,15 @@ use constant TLS_RECORD_HEADER_LENGTH => 5; #Record types use constant { - RT_APPLICATION_DATA => 23, - RT_HANDSHAKE => 22, - RT_ALERT => 21, - RT_CCS => 20, - RT_UNKNOWN => 100 + RT_APPLICATION_DATA => 23, + RT_HANDSHAKE => 22, + RT_ALERT => 21, + RT_CCS => 20, + RT_UNKNOWN => 100, + RT_DTLS_UNIHDR_EPOCH4 => 0x2c, + RT_DTLS_UNIHDR_EPOCH1 => 0x2d, + RT_DTLS_UNIHDR_EPOCH2 => 0x2e, + RT_DTLS_UNIHDR_EPOCH3 => 0x2f, }; my %record_type = ( @@ -32,7 +36,11 @@ my %record_type = ( RT_HANDSHAKE, "HANDSHAKE", RT_ALERT, "ALERT", RT_CCS, "CCS", - RT_UNKNOWN, "UNKNOWN" + RT_UNKNOWN, "UNKNOWN", + RT_DTLS_UNIHDR_EPOCH4, "DTLS UNIFIED HEADER (EPOCH 4)", + RT_DTLS_UNIHDR_EPOCH1, "DTLS UNIFIED HEADER (EPOCH 1)", + RT_DTLS_UNIHDR_EPOCH2, "DTLS UNIFIED HEADER (EPOCH 2)", + RT_DTLS_UNIHDR_EPOCH3, "DTLS UNIFIED HEADER (EPOCH 3)", ); use constant { @@ -71,14 +79,12 @@ sub get_records my $partial = ""; my @record_list = (); my @message_list = (); - my $record_hdr_len = $isdtls ? DTLS_RECORD_HEADER_LENGTH - : TLS_RECORD_HEADER_LENGTH; my $recnum = 1; while (length ($packet) > 0) { print " Record $recnum ", $server ? "(server -> client)\n" : "(client -> server)\n"; - + my $record_hdr_len; my $content_type; my $version; my $len; @@ -86,16 +92,56 @@ sub get_records my $seq; if ($isdtls) { - my $seqhi; - my $seqmi; - my $seqlo; - #Get the record header (unpack can't fail if $packet is too short) - ($content_type, $version, $epoch, - $seqhi, $seqmi, $seqlo, $len) = unpack('Cnnnnnn', $packet); - $seq = ($seqhi << 32) | ($seqmi << 16) | $seqlo + my $isunifiedhdr; + + $content_type = unpack('B[8]', $packet); + $isunifiedhdr = substr($content_type, 0, 3) == "001"; + + if ($isunifiedhdr == 1) { + my $cbit = substr($content_type, 3, 1); + my $sbit = substr($content_type, 4, 1); + my $lbit = substr($content_type, 5, 1); + my $eebits = substr($content_type, 6, 2); + + if ($cbit == "1" || $lbit == "0") { + die("TLSProxy does not support variable DTLSv1.3 unified header bits"); + } + + # This is a unified header + if ($sbit == "1") { + ($content_type, $seq, $len) = unpack('Cnn', $packet); + $record_hdr_len = 5; + } else { + ($content_type, $seq, $len) = unpack('CCn', $packet); + $record_hdr_len = 4; + } + $version = VERS_DTLS_1_2; # DTLSv1.3 headers has DTLSv1.2 in its legacy_version field + + if ($eebits == "00") { + $epoch = 4; # must be at least 4 since 0 epoch are not sent with unified hdr + } elsif ($eebits == "01") { + $epoch = 1; + } elsif ($eebits == "10") { + $epoch = 2; + } elsif ($eebits == "11") { + $epoch = 3; + } else { + die("Epoch bits is not 0's or 1's: should not happen") + } + } else { + my $seqhi; + my $seqmi; + my $seqlo; + #Get the record header (unpack can't fail if $packet is too short) + ($content_type, $version, $epoch, + $seqhi, $seqmi, $seqlo, $len) = unpack('Cnnnnnn', $packet); + $seq = ($seqhi << 32) | ($seqmi << 16) | $seqlo; + $record_hdr_len = DTLS_RECORD_HEADER_LENGTH; + } } else { #Get the record header (unpack can't fail if $packet is too short) ($content_type, $version, $len) = unpack('Cnn', $packet); + $record_hdr_len = TLS_RECORD_HEADER_LENGTH; } if (length($packet) < $record_hdr_len + ($len // 0)) { @@ -295,7 +341,7 @@ sub init orig_decrypt_data => $decrypt_data, sent => 0, encrypted => 0, - outer_content_type => RT_APPLICATION_DATA + outer_content_type => $content_type, }; return bless $self, $class; @@ -397,11 +443,16 @@ sub reconstruct_record my $content_type = (TLSProxy::Proxy->is_tls13() && $self->encrypted) ? $self->outer_content_type : $self->content_type; if($self->{isdtls}) { - my $seqhi = ($self->seq >> 32) & 0xffff; - my $seqmi = ($self->seq >> 16) & 0xffff; - my $seqlo = ($self->seq >> 0) & 0xffff; - $data = pack('Cnnnnnn', $content_type, $self->version, - $self->epoch, $seqhi, $seqmi, $seqlo, $self->len); + if (TLSProxy::Proxy->is_tls13() && $self->encrypted) { + # Prepare a unified header + $data = pack('Cnn', $content_type, $self->seq, $self->len); + } else { + my $seqhi = ($self->seq >> 32) & 0xffff; + my $seqmi = ($self->seq >> 16) & 0xffff; + my $seqlo = ($self->seq >> 0) & 0xffff; + $data = pack('Cnnnnnn', $content_type, $self->version, + $self->epoch, $seqhi, $seqmi, $seqlo, $self->len); + } } else { $data = pack('Cnn', $content_type, $self->version, $self->len);