diff options
Diffstat (limited to 'crypto/openssl/ssl/record/methods/dtls_meth.c')
| -rw-r--r-- | crypto/openssl/ssl/record/methods/dtls_meth.c | 803 |
1 files changed, 803 insertions, 0 deletions
diff --git a/crypto/openssl/ssl/record/methods/dtls_meth.c b/crypto/openssl/ssl/record/methods/dtls_meth.c new file mode 100644 index 000000000000..99cb532d03c3 --- /dev/null +++ b/crypto/openssl/ssl/record/methods/dtls_meth.c @@ -0,0 +1,803 @@ +/* + * Copyright 2018-2025 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <assert.h> +#include "../../ssl_local.h" +#include "../record_local.h" +#include "recmethod_local.h" + +/* mod 128 saturating subtract of two 64-bit values in big-endian order */ +static int satsub64be(const unsigned char *v1, const unsigned char *v2) +{ + int64_t ret; + uint64_t l1, l2; + + n2l8(v1, l1); + n2l8(v2, l2); + + ret = l1 - l2; + + /* We do not permit wrap-around */ + if (l1 > l2 && ret < 0) + return 128; + else if (l2 > l1 && ret > 0) + return -128; + + if (ret > 128) + return 128; + else if (ret < -128) + return -128; + else + return (int)ret; +} + +static int dtls_record_replay_check(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap) +{ + int cmp; + unsigned int shift; + const unsigned char *seq = rl->sequence; + + cmp = satsub64be(seq, bitmap->max_seq_num); + if (cmp > 0) { + ossl_tls_rl_record_set_seq_num(&rl->rrec[0], seq); + return 1; /* this record in new */ + } + shift = -cmp; + if (shift >= sizeof(bitmap->map) * 8) + return 0; /* stale, outside the window */ + else if (bitmap->map & ((uint64_t)1 << shift)) + return 0; /* record previously received */ + + ossl_tls_rl_record_set_seq_num(&rl->rrec[0], seq); + return 1; +} + +static void dtls_record_bitmap_update(OSSL_RECORD_LAYER *rl, + DTLS_BITMAP *bitmap) +{ + int cmp; + unsigned int shift; + const unsigned char *seq = rl->sequence; + + cmp = satsub64be(seq, bitmap->max_seq_num); + if (cmp > 0) { + shift = cmp; + if (shift < sizeof(bitmap->map) * 8) + bitmap->map <<= shift, bitmap->map |= 1UL; + else + bitmap->map = 1UL; + memcpy(bitmap->max_seq_num, seq, SEQ_NUM_SIZE); + } else { + shift = -cmp; + if (shift < sizeof(bitmap->map) * 8) + bitmap->map |= (uint64_t)1 << shift; + } +} + +static DTLS_BITMAP *dtls_get_bitmap(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rr, + unsigned int *is_next_epoch) +{ + *is_next_epoch = 0; + + /* In current epoch, accept HM, CCS, DATA, & ALERT */ + if (rr->epoch == rl->epoch) + return &rl->bitmap; + + /* + * Check if the message is from the next epoch + */ + else if (rr->epoch == rl->epoch + 1) { + *is_next_epoch = 1; + return &rl->next_bitmap; + } + + return NULL; +} + +static void dtls_set_in_init(OSSL_RECORD_LAYER *rl, int in_init) +{ + rl->in_init = in_init; +} + +static int dtls_process_record(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap) +{ + int i; + int enc_err; + TLS_RL_RECORD *rr; + int imac_size; + size_t mac_size = 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, + * and we have that many bytes in rl->packet + */ + rr->input = &(rl->packet[DTLS1_RT_HEADER_LENGTH]); + + /* + * ok, we can now read from 'rl->packet' data into 'rr'. rr->input + * points at rr->length bytes, which need to be copied into rr->data by + * either the decryption or by the decompression. When the data is 'copied' + * into the rr->data buffer, rr->input will be pointed at the new buffer + */ + + /* + * We now have - encrypted [ MAC [ compressed [ plain ] ] ] rr->length + * bytes of encrypted compressed stuff. + */ + + /* check is not needed I believe */ + if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW, SSL_R_ENCRYPTED_LENGTH_TOO_LONG); + return 0; + } + + /* decrypt in place in 'rr->input' */ + rr->data = rr->input; + rr->orig_len = rr->length; + + if (rl->md_ctx != NULL) { + const EVP_MD *tmpmd = EVP_MD_CTX_get0_md(rl->md_ctx); + + if (tmpmd != NULL) { + imac_size = EVP_MD_get_size(tmpmd); + if (!ossl_assert(imac_size > 0 && imac_size <= EVP_MAX_MD_SIZE)) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB); + return 0; + } + mac_size = (size_t)imac_size; + } + } + + if (rl->use_etm && rl->md_ctx != NULL) { + unsigned char *mac; + + if (rr->orig_len < mac_size) { + RLAYERfatal(rl, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_TOO_SHORT); + return 0; + } + rr->length -= mac_size; + mac = rr->data + rr->length; + i = rl->funcs->mac(rl, rr, md, 0 /* not send */); + if (i == 0 || CRYPTO_memcmp(md, mac, (size_t)mac_size) != 0) { + RLAYERfatal(rl, SSL_AD_BAD_RECORD_MAC, + SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); + return 0; + } + /* + * We've handled the mac now - there is no MAC inside the encrypted + * record + */ + mac_size = 0; + } + + /* + * Set a mark around the packet decryption attempt. This is DTLS, so + * bad packets are just ignored, and we don't want to leave stray + * errors in the queue from processing bogus junk that we ignored. + */ + ERR_set_mark(); + enc_err = rl->funcs->cipher(rl, rr, 1, 0, &macbuf, mac_size); + + /*- + * enc_err is: + * 0: if the record is publicly invalid, or an internal error, or AEAD + * decryption failed, or ETM decryption failed. + * 1: Success or MTE decryption failed (MAC will be randomised) + */ + if (enc_err == 0) { + ERR_pop_to_mark(); + if (rl->alert != SSL_AD_NO_ALERT) { + /* RLAYERfatal() already called */ + goto end; + } + /* For DTLS we simply ignore bad packets. */ + rr->length = 0; + rl->packet_length = 0; + goto end; + } + ERR_clear_last_mark(); + OSSL_TRACE_BEGIN(TLS) { + BIO_printf(trc_out, "dec %zd\n", rr->length); + BIO_dump_indent(trc_out, rr->data, rr->length, 4); + } OSSL_TRACE_END(TLS); + + /* r->length is now the compressed data plus mac */ + if (!rl->use_etm + && (rl->enc_ctx != NULL) + && (EVP_MD_CTX_get0_md(rl->md_ctx) != NULL)) { + /* rl->md_ctx != NULL => mac_size != -1 */ + + i = rl->funcs->mac(rl, rr, md, 0 /* not send */); + if (i == 0 || macbuf.mac == NULL + || CRYPTO_memcmp(md, macbuf.mac, mac_size) != 0) + enc_err = 0; + if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH + mac_size) + enc_err = 0; + } + + if (enc_err == 0) { + /* decryption failed, silently discard message */ + rr->length = 0; + rl->packet_length = 0; + goto end; + } + + /* r->length is now just compressed */ + if (rl->compctx != NULL) { + if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH) { + RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW, + SSL_R_COMPRESSED_LENGTH_TOO_LONG); + goto end; + } + if (!tls_do_uncompress(rl, rr)) { + RLAYERfatal(rl, SSL_AD_DECOMPRESSION_FAILURE, SSL_R_BAD_DECOMPRESSION); + goto end; + } + } + + /* + * Check if the received packet overflows the current Max Fragment + * Length setting. + */ + if (rr->length > rl->max_frag_len) { + RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW, SSL_R_DATA_LENGTH_TOO_LONG); + goto end; + } + + rr->off = 0; + /*- + * So at this point the following is true + * ssl->s3.rrec.type is the type of record + * ssl->s3.rrec.length == number of bytes in record + * ssl->s3.rrec.off == offset to first valid byte + * ssl->s3.rrec.data == where to take bytes from, increment + * after use :-). + */ + + /* we have pulled in a full packet so zero things */ + rl->packet_length = 0; + + /* Mark receipt of record. */ + dtls_record_bitmap_update(rl, bitmap); + + ret = 1; + end: + if (macbuf.alloced) + OPENSSL_free(macbuf.mac); + return ret; +} + +static int dtls_rlayer_buffer_record(OSSL_RECORD_LAYER *rl, struct pqueue_st *queue, + unsigned char *priority) +{ + DTLS_RLAYER_RECORD_DATA *rdata; + pitem *item; + + /* Limit the size of the queue to prevent DOS attacks */ + if (pqueue_size(queue) >= 100) + return 0; + + rdata = OPENSSL_malloc(sizeof(*rdata)); + item = pitem_new(priority, rdata); + if (rdata == NULL || item == NULL) { + OPENSSL_free(rdata); + pitem_free(item); + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return -1; + } + + rdata->packet = rl->packet; + rdata->packet_length = rl->packet_length; + memcpy(&(rdata->rbuf), &rl->rbuf, sizeof(TLS_BUFFER)); + memcpy(&(rdata->rrec), &rl->rrec[0], sizeof(TLS_RL_RECORD)); + + item->data = rdata; + + rl->packet = NULL; + rl->packet_length = 0; + memset(&rl->rbuf, 0, sizeof(TLS_BUFFER)); + memset(&rl->rrec[0], 0, sizeof(rl->rrec[0])); + + if (!tls_setup_read_buffer(rl)) { + /* RLAYERfatal() already called */ + OPENSSL_free(rdata->rbuf.buf); + OPENSSL_free(rdata); + pitem_free(item); + return -1; + } + + if (pqueue_insert(queue, item) == NULL) { + /* Must be a duplicate so ignore it */ + OPENSSL_free(rdata->rbuf.buf); + OPENSSL_free(rdata); + pitem_free(item); + } + + return 1; +} + +/* copy buffered record into OSSL_RECORD_LAYER structure */ +static int dtls_copy_rlayer_record(OSSL_RECORD_LAYER *rl, pitem *item) +{ + DTLS_RLAYER_RECORD_DATA *rdata; + + rdata = (DTLS_RLAYER_RECORD_DATA *)item->data; + + ossl_tls_buffer_release(&rl->rbuf); + + rl->packet = rdata->packet; + rl->packet_length = rdata->packet_length; + memcpy(&rl->rbuf, &(rdata->rbuf), sizeof(TLS_BUFFER)); + 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); + + return 1; +} + +static int dtls_retrieve_rlayer_buffered_record(OSSL_RECORD_LAYER *rl, + struct pqueue_st *queue) +{ + pitem *item; + + item = pqueue_pop(queue); + if (item) { + dtls_copy_rlayer_record(rl, item); + + OPENSSL_free(item->data); + pitem_free(item); + + return 1; + } + + return 0; +} + +/*- + * Call this to get a new input record. + * It will return <= 0 if more data is needed, normally due to an error + * or non-blocking IO. + * When it finishes, one packet has been decoded and can be found in + * ssl->s3.rrec.type - is the type of record + * ssl->s3.rrec.data - data + * ssl->s3.rrec.length - number of bytes + */ +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; + + rl->num_recs = 0; + rl->curr_rec = 0; + rl->num_released = 0; + + rr = rl->rrec; + + if (rl->rbuf.buf == NULL) { + if (!tls_setup_read_buffer(rl)) { + /* RLAYERfatal() already called */ + return OSSL_RECORD_RETURN_FATAL; + } + } + + again: + /* if we're renegotiating, then there may be buffered records */ + if (dtls_retrieve_rlayer_buffered_record(rl, rl->processed_rcds)) { + rl->num_recs = 1; + return OSSL_RECORD_RETURN_SUCCESS; + } + + /* 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)) { + 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 */ + if (rret < OSSL_RECORD_RETURN_SUCCESS) { + /* RLAYERfatal() already called if appropriate */ + return rret; /* error or non-blocking */ + } + + /* this packet contained a partial record, dump it */ + if (rl->packet_length != DTLS1_RT_HEADER_LENGTH) { + rl->packet_length = 0; + goto again; + } + + rl->rstate = SSL_ST_READ_BODY; + + p = rl->packet; + + /* 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; + + /* sequence number is 64 bits, with top 2 bytes = epoch */ + n2s(p, rr->epoch); + + memcpy(&(rl->sequence[2]), p, 6); + p += 6; + + n2s(p, rr->length); + + if (rl->msg_callback != NULL) + rl->msg_callback(0, rr->rec_version, SSL3_RT_HEADER, rl->packet, DTLS1_RT_HEADER_LENGTH, + rl->cbarg); + + /* + * Lets check the version. We tolerate alerts that don't have the exact + * version number (e.g. because of protocol version errors) + */ + if (!rl->is_first_record && rr->type != SSL3_RT_ALERT) { + if (rr->rec_version != rl->version) { + /* unexpected version, silently discard */ + rr->length = 0; + rl->packet_length = 0; + goto again; + } + } + + if (ssl_major != + (rl->version == DTLS_ANY_VERSION ? DTLS1_VERSION_MAJOR + : rl->version >> 8)) { + /* wrong version, silently discard record */ + rr->length = 0; + rl->packet_length = 0; + goto again; + } + + if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + /* record too long, silently discard it */ + rr->length = 0; + rl->packet_length = 0; + goto again; + } + + /* + * If received packet overflows maximum possible fragment length then + * silently discard it + */ + if (rr->length > rl->max_frag_len + SSL3_RT_MAX_ENCRYPTED_OVERHEAD) { + /* record too long, silently discard it */ + rr->length = 0; + rl->packet_length = 0; + goto again; + } + + /* now rl->rstate == SSL_ST_READ_BODY */ + } + + /* rl->rstate == SSL_ST_READ_BODY, get and decode the data */ + + 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); + /* this packet contained a partial record, dump it */ + if (rret < OSSL_RECORD_RETURN_SUCCESS || n != more) { + if (rl->alert != SSL_AD_NO_ALERT) { + /* read_n() called RLAYERfatal() */ + return OSSL_RECORD_RETURN_FATAL; + } + rr->length = 0; + rl->packet_length = 0; + goto again; + } + + /* + * now n == rr->length, + * and rl->packet_length == DTLS1_RT_HEADER_LENGTH + rr->length + */ + } + /* set state for later operations */ + rl->rstate = SSL_ST_READ_HEADER; + + /* match epochs. NULL means the packet is dropped on the floor */ + bitmap = dtls_get_bitmap(rl, rr, &is_next_epoch); + if (bitmap == NULL) { + rr->length = 0; + rl->packet_length = 0; /* dump this record */ + goto again; /* get another record */ + } +#ifndef OPENSSL_NO_SCTP + /* Only do replay check if no SCTP bio */ + if (!BIO_dgram_is_sctp(rl->bio)) { +#endif + /* Check whether this is a repeat, or aged record. */ + if (!dtls_record_replay_check(rl, bitmap)) { + rr->length = 0; + rl->packet_length = 0; /* dump this record */ + goto again; /* get another record */ + } +#ifndef OPENSSL_NO_SCTP + } +#endif + + /* just read a 0 length packet */ + if (rr->length == 0) + goto again; + + /* + * If this record is from the next epoch (either HM or ALERT), and a + * handshake is currently in progress, buffer it since it cannot be + * processed at this time. + */ + if (is_next_epoch) { + if (rl->in_init) { + if (dtls_rlayer_buffer_record(rl, rl->unprocessed_rcds, + rr->seq_num) < 0) { + /* RLAYERfatal() already called */ + return OSSL_RECORD_RETURN_FATAL; + } + } + rr->length = 0; + rl->packet_length = 0; + goto again; + } + + if (!dtls_process_record(rl, bitmap)) { + if (rl->alert != SSL_AD_NO_ALERT) { + /* dtls_process_record() called RLAYERfatal */ + return OSSL_RECORD_RETURN_FATAL; + } + rr->length = 0; + rl->packet_length = 0; /* dump this record */ + goto again; /* get another record */ + } + + if (rl->funcs->post_process_record && !rl->funcs->post_process_record(rl, rr)) { + /* RLAYERfatal already called */ + return OSSL_RECORD_RETURN_FATAL; + } + + if (rr->length == 0) { + /* No payload data in this record. Dump it */ + rl->packet_length = 0; + goto again; + } + + rl->num_recs = 1; + return OSSL_RECORD_RETURN_SUCCESS; +} + +static int dtls_free(OSSL_RECORD_LAYER *rl) +{ + TLS_BUFFER *rbuf; + size_t left, written; + pitem *item; + DTLS_RLAYER_RECORD_DATA *rdata; + int ret = 1; + + rbuf = &rl->rbuf; + + left = rbuf->left; + if (left > 0) { + /* + * This record layer is closing but we still have data left in our + * buffer. It must be destined for the next epoch - so push it there. + */ + ret = BIO_write_ex(rl->next, rbuf->buf + rbuf->offset, left, &written); + rbuf->left = 0; + } + + if (rl->unprocessed_rcds != NULL) { + while ((item = pqueue_pop(rl->unprocessed_rcds)) != NULL) { + rdata = (DTLS_RLAYER_RECORD_DATA *)item->data; + /* Push to the next record layer */ + ret &= BIO_write_ex(rl->next, rdata->packet, rdata->packet_length, + &written); + OPENSSL_free(rdata->rbuf.buf); + OPENSSL_free(item->data); + pitem_free(item); + } + pqueue_free(rl->unprocessed_rcds); + } + + if (rl->processed_rcds!= NULL) { + while ((item = pqueue_pop(rl->processed_rcds)) != NULL) { + rdata = (DTLS_RLAYER_RECORD_DATA *)item->data; + OPENSSL_free(rdata->rbuf.buf); + OPENSSL_free(item->data); + pitem_free(item); + } + pqueue_free(rl->processed_rcds); + } + + return tls_free(rl) && ret; +} + +static int +dtls_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers, + int role, int direction, int level, uint16_t epoch, + unsigned char *secret, size_t secretlen, + unsigned char *key, size_t keylen, unsigned char *iv, + size_t ivlen, unsigned char *mackey, size_t mackeylen, + const EVP_CIPHER *ciph, size_t taglen, + int mactype, + const EVP_MD *md, COMP_METHOD *comp, + const EVP_MD *kdfdigest, BIO *prev, BIO *transport, + BIO *next, BIO_ADDR *local, BIO_ADDR *peer, + const OSSL_PARAM *settings, const OSSL_PARAM *options, + const OSSL_DISPATCH *fns, void *cbarg, void *rlarg, + OSSL_RECORD_LAYER **retrl) +{ + int ret; + + ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level, + ciph, taglen, md, comp, prev, + transport, next, settings, + options, fns, cbarg, retrl); + + if (ret != OSSL_RECORD_RETURN_SUCCESS) + return ret; + + (*retrl)->unprocessed_rcds = pqueue_new(); + (*retrl)->processed_rcds = pqueue_new(); + + if ((*retrl)->unprocessed_rcds == NULL + || (*retrl)->processed_rcds == NULL) { + dtls_free(*retrl); + *retrl = NULL; + ERR_raise(ERR_LIB_SSL, ERR_R_SSL_LIB); + return OSSL_RECORD_RETURN_FATAL; + } + + (*retrl)->isdtls = 1; + (*retrl)->epoch = epoch; + (*retrl)->in_init = 1; + + switch (vers) { + case DTLS_ANY_VERSION: + (*retrl)->funcs = &dtls_any_funcs; + break; + case DTLS1_2_VERSION: + case DTLS1_VERSION: + case DTLS1_BAD_VER: + (*retrl)->funcs = &dtls_1_funcs; + break; + default: + /* Should not happen */ + ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); + ret = OSSL_RECORD_RETURN_FATAL; + goto err; + } + + ret = (*retrl)->funcs->set_crypto_state(*retrl, level, key, keylen, iv, + ivlen, mackey, mackeylen, ciph, + taglen, mactype, md, comp); + + err: + if (ret != OSSL_RECORD_RETURN_SUCCESS) { + dtls_free(*retrl); + *retrl = NULL; + } + return ret; +} + +int dtls_prepare_record_header(OSSL_RECORD_LAYER *rl, + WPACKET *thispkt, + OSSL_RECORD_TEMPLATE *templ, + uint8_t rectype, + unsigned char **recdata) +{ + size_t maxcomplen; + + *recdata = NULL; + + maxcomplen = templ->buflen; + if (rl->compctx != NULL) + maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD; + + 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) + || !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; + } + + return 1; +} + +int dtls_post_encryption_processing(OSSL_RECORD_LAYER *rl, + size_t mac_size, + OSSL_RECORD_TEMPLATE *thistempl, + WPACKET *thispkt, + TLS_RL_RECORD *thiswr) +{ + if (!tls_post_encryption_processing_default(rl, mac_size, thistempl, + thispkt, thiswr)) { + /* RLAYERfatal() already called */ + return 0; + } + + return tls_increment_sequence_ctr(rl); +} + +static size_t dtls_get_max_record_overhead(OSSL_RECORD_LAYER *rl) +{ + size_t blocksize = 0; + + if (rl->enc_ctx != NULL && + (EVP_CIPHER_CTX_get_mode(rl->enc_ctx) == EVP_CIPH_CBC_MODE)) + blocksize = EVP_CIPHER_CTX_get_block_size(rl->enc_ctx); + + /* + * If we have a cipher in place then the tag is mandatory. If the cipher is + * CBC mode then an explicit IV is also mandatory. If we know the digest, + * then we check it is consistent with the taglen. In the case of stitched + * ciphers or AEAD ciphers we don't now the digest (or there isn't one) so + * we just trust that the taglen is correct. + */ + assert(rl->enc_ctx == NULL || ((blocksize == 0 || rl->eivlen > 0) + && rl->taglen > 0)); + assert(rl->md == NULL || (int)rl->taglen == EVP_MD_size(rl->md)); + + /* + * Record overhead consists of the record header, the explicit IV, any + * expansion due to cbc padding, and the mac/tag len. There could be + * further expansion due to compression - but we don't know what this will + * be without knowing the length of the data. However when this function is + * called we don't know what the length will be yet - so this is a catch-22. + * We *could* use SSL_3_RT_MAX_COMPRESSED_OVERHEAD which is an upper limit + * for the maximum record size. But this value is larger than our fallback + * 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; +} + +const OSSL_RECORD_METHOD ossl_dtls_record_method = { + dtls_new_record_layer, + dtls_free, + tls_unprocessed_read_pending, + tls_processed_read_pending, + tls_app_data_pending, + tls_get_max_records, + tls_write_records, + tls_retry_write_records, + tls_read_record, + tls_release_record, + tls_get_alert_code, + tls_set1_bio, + tls_set_protocol_version, + NULL, + tls_set_first_handshake, + tls_set_max_pipelines, + dtls_set_in_init, + tls_get_state, + tls_set_options, + tls_get_compression, + tls_set_max_frag_len, + dtls_get_max_record_overhead, + tls_increment_sequence_ctr, + tls_alloc_buffers, + tls_free_buffers +}; |
