diff --git a/include/openssl/dtls1.h b/include/openssl/dtls1.h index 024584f426e9c4..6573ee7b84ccf9 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 @@ -51,6 +51,23 @@ extern "C" { # define DTLS1_TMO_ALERT_COUNT 12 +/* DTLS 1.3 Unified header */ +# 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_LEN_BIT 0x04 +# define DTLS13_UNI_HDR_FIX_BITS_MASK 0xe0 +# define DTLS13_UNI_HDR_EPOCH_BITS_MASK 0x03 + +# define DTLS13_UNI_HDR_FIX_BITS_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_FIX_BITS_MASK) == DTLS13_UNI_HDR_FIX_BITS) +# define DTLS13_UNI_HDR_CID_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_CID_BIT) == DTLS13_UNI_HDR_CID_BIT) +# define DTLS13_UNI_HDR_SEQ_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_SEQ_BIT) == DTLS13_UNI_HDR_SEQ_BIT) +# define DTLS13_UNI_HDR_LEN_BIT_IS_SET(byte) \ + (((byte) & DTLS13_UNI_HDR_LEN_BIT) == DTLS13_UNI_HDR_LEN_BIT) + #ifdef __cplusplus } #endif diff --git a/ssl/record/methods/dtls_meth.c b/ssl/record/methods/dtls_meth.c index 8c009bda4896ef..2dc47002fb3e2b 100644 --- a/ssl/record/methods/dtls_meth.c +++ b/ssl/record/methods/dtls_meth.c @@ -105,6 +105,23 @@ static void dtls_set_in_init(OSSL_RECORD_LAYER *rl, int in_init) rl->in_init = in_init; } +static 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 +129,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; @@ -122,7 +140,7 @@ static int dtls_process_record(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap) * At this point, rl->packet_length == DTLS1_RT_HEADER_LENGTH + 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 +360,7 @@ 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); + memcpy(rl->sequence, rdata->rrec.seq_num, 8); return 1; } @@ -376,11 +394,9 @@ static int dtls_retrieve_rlayer_buffered_record(OSSL_RECORD_LAYER *rl, */ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) { - int ssl_major, ssl_minor; int rret; size_t more, n; TLS_RL_RECORD *rr; - unsigned char *p = NULL; DTLS_BITMAP *bitmap; unsigned int is_next_epoch; @@ -407,8 +423,12 @@ 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; + size_t rechdrlen; + rret = rl->funcs->read_n(rl, DTLS1_RT_HEADER_LENGTH, TLS_BUFFER_get_len(&rl->rbuf), 0, 1, &n); /* read timeout is handled by dtls1_read_bytes */ @@ -425,25 +445,105 @@ 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)) { + // TODO: is this appropriate error handling? + 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 + && rr->type != SSL3_RT_APPLICATION_DATA /* TODO: Disable this check when unified header has been developed */ + //&& rr->type != SSL3_RT_ACK /* TODO: 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; + + if (/* OpenSSL does not support connection ID's and a connection ID + * is only set if one has been negotiated: silently discard */ + cbitisset + /* + * Naive approach? We expect sequence number to be filled already + * and then override the last bytes of the sequence number. + */ + || (sbitisset ? !PACKET_copy_bytes(&dtlsrecord, rl->sequence + 6, 2) + : !PACKET_copy_bytes(&dtlsrecord, rl->sequence + 7, 1)) + /* + * 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 + */ + || (lbitisset ? !PACKET_get_net_2(&dtlsrecord, &length) + /* TODO: We have not read the full record yet */ + : (length = PACKET_remaining(&dtlsrecord)) > 0)) { + rr->length = 0; + rl->packet_length = 0; + goto again; + } - memcpy(&(rl->sequence[2]), p, 6); - p += 6; + /* + * 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, rl->sequence + 2, 6) + || !PACKET_get_net_2(&dtlsrecord, &length)) { + rr->length = 0; + rl->packet_length = 0; + goto again; + } + } - n2s(p, rr->length); + 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 @@ -461,7 +561,7 @@ int dtls_get_more_records(OSSL_RECORD_LAYER *rl) } } - if (ssl_major != + if (rr->rec_version >> 8 != (rl->version == DTLS_ANY_VERSION ? DTLS1_VERSION_MAJOR : rl->version >> 8)) { /* wrong version, silently discard record */ @@ -702,6 +802,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; @@ -709,7 +810,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 = 0x08; + uint8_t lbit = 0x04; + uint8_t ebits = rl->epoch & 0x3; + 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) @@ -717,10 +837,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; diff --git a/ssl/record/methods/tls13_meth.c b/ssl/record/methods/tls13_meth.c index b49faf542094d0..caaf4c2804bccf 100644 --- a/ssl/record/methods/tls13_meth.c +++ b/ssl/record/methods/tls13_meth.c @@ -177,7 +177,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); @@ -212,19 +212,37 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, return 0; } + //if (!sending) { + // printf("receiver: tag(%zu):", rl->taglen); + // for (size_t i = 0; i < rl->taglen; i++) { + // printf("0x%2x, ", *(rec->data + rec->length + i)); + // } + // printf("\n"); + // printf("receiver: hdr(%zu):", hdrlen); + // for (size_t i = 0; i < hdrlen; i++) + // printf("0x%2x, ", *(recheader + i)); + // printf("\n"); + // printf("receiver: data(%zu):", rec->length); + // for (size_t i = 0; i < rec->length; i++) + // printf("0x%2x, ", *(rec->data + i)); + // printf("\n"); + // printf("receiver: nonce(%zu):", nonce_len); + // for (size_t i = 0; i < nonce_len; i++) { + // printf("0x%2x, ", *(nonce + i)); + // } + // printf("\n"); + //} + /* * For CCM we must explicitly set the total plaintext length before we add * 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_CipherFinal_ex(enc_ctx, rec->data + lenu, &lenf) <= 0 - || (size_t)(lenu + lenf) != rec->length) { + && 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; } if (sending) { @@ -234,6 +252,23 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); return 0; } + //printf("sender: tag(%zu):", rl->taglen); + //for (size_t i = 0; i < rl->taglen; i++) + // printf("0x%2x, ", *(rec->data + rec->length + i)); + //printf("\n"); + //printf("sender: hdr(%zu):", hdrlen); + //for (size_t i = 0; i < hdrlen; i++) + // printf("0x%2x, ", *(recheader + i)); + //printf("\n"); + //printf("sender: data(%zu):", rec->length); + //for (size_t i = 0; i < rec->length; i++) + // printf("0x%2x, ", *(rec->data + i)); + //printf("\n"); + //printf("sender: nonce(%zu):", nonce_len); + //for (size_t i = 0; i < nonce_len; i++) { + // printf("0x%2x, ", *(nonce + i)); + //} + //printf("\n"); rec->length += rl->taglen; } @@ -271,7 +306,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 ? (rec->type & 0xe0) != 0x20 + : rec->type != SSL3_RT_APPLICATION_DATA) { RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_RECORD_TYPE); return 0; @@ -309,7 +345,7 @@ 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. */ - return SSL3_RT_APPLICATION_DATA; + return rl->isdtls ? 0x2c | (0x3 & rl->epoch) : SSL3_RT_APPLICATION_DATA; } static int tls13_add_record_padding(OSSL_RECORD_LAYER *rl, diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c index f363e10608b170..856a415b9c117e 100644 --- a/ssl/record/methods/tls_common.c +++ b/ssl/record/methods/tls_common.c @@ -1676,6 +1676,7 @@ int tls_post_encryption_processing_default(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *thiswr) { size_t origlen, len; + /*TODO unified header support*/ size_t headerlen = rl->isdtls ? DTLS1_RT_HEADER_LENGTH : SSL3_RT_HEADER_LENGTH; diff --git a/ssl/record/record.h b/ssl/record/record.h index 9a076a1fb8865e..1dc4320ea96ff2 100644 --- a/ssl/record/record.h +++ b/ssl/record/record.h @@ -18,6 +18,7 @@ *****************************************************************************/ #define SEQ_NUM_SIZE 8 +#define REC_NUM_SIZE 16 typedef struct tls_record_st { void *rechandle; diff --git a/test/helpers/ssltestlib.c b/test/helpers/ssltestlib.c index b2ebed55311d8e..33fe17d1d68819 100644 --- a/test/helpers/ssltestlib.c +++ b/test/helpers/ssltestlib.c @@ -542,8 +542,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]) + + DTLS1_RT_HEADER_LENGTH; + } if (ctx == NULL) return -1;