Also set correct AAD for DTLS 1.3 message de-/encryption.
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25668)
### 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
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
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.
extern "C" {
#endif
-#include <openssl/opensslconf.h>
+# include <openssl/opensslconf.h>
/* DTLS*_VERSION constants are defined in prov_ssl.h */
# ifndef OPENSSL_NO_DEPRECATED_3_0
# 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
# 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
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);
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. */
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;
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;
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
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;
}
}
/* 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);
|| 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));
*/
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;
/* 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 */
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
}
}
- 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;
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;
/*
* 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;
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) {
unsigned char **recdata)
{
size_t maxcomplen;
+ int unifiedheader = rl->version == DTLS1_3_VERSION && rl->epoch > 0;
*recdata = NULL;
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)
|| (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;
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))
* 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 = {
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);
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);
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);
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);
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;
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);
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);
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);
* 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;
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;
* 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;
}
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),
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)
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;
|| !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;
}
}
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) {
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,
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),
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)
}
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;
return 0;
}
- TLS_RL_RECORD_add_length(thiswr, headerlen);
+ TLS_RL_RECORD_add_length(thiswr, rechdrlen);
return 1;
}
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)
{
#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)
{
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;
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;
#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 = (
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 {
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;
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)) {
orig_decrypt_data => $decrypt_data,
sent => 0,
encrypted => 0,
- outer_content_type => RT_APPLICATION_DATA
+ outer_content_type => $content_type,
};
return bless $self, $class;
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);