Don't use the buffer BIO in DTLS. Instead, "writing" a message merely adds it to the outgoing_messages structure. The code to write the flight then loops over it all and now shares code with retransmission. The verbs here are all a little odd, but they'll be fixed in later commits. In doing so, this fixes a slight miscalculation of the record-layer overhead when retransmitting a flight that spans two epochs. (We'd use the encrypted epoch's overhead for the unencrypted epoch.) BUG=72 Change-Id: I8ac897c955cc74799f8b5ca6923906e97d6dad17 Reviewed-on: https://boringssl-review.googlesource.com/13223 Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 1491a53..df227fb 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -4126,10 +4126,6 @@ /* init_num is the length of the current handshake message body. */ uint32_t init_num; - /* init_off, in DTLS, is the number of bytes of the current message that have - * been written. */ - uint32_t init_off; - struct ssl3_state_st *s3; /* SSLv3 variables */ struct dtls1_state_st *d1; /* DTLSv1 variables */
diff --git a/ssl/d1_both.c b/ssl/d1_both.c index d3e4a92..1d89636 100644 --- a/ssl/d1_both.c +++ b/ssl/d1_both.c
@@ -122,6 +122,7 @@ #include <openssl/evp.h> #include <openssl/mem.h> #include <openssl/rand.h> +#include <openssl/type_check.h> #include <openssl/x509.h> #include "../crypto/internal.h" @@ -311,9 +312,9 @@ } /* Cross-epoch records are discarded, but we may receive out-of-order - * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec - * before the appropriate point in the handshake. Those must be silently - * discarded. + * application data between ChangeCipherSpec and Finished or a + * ChangeCipherSpec before the appropriate point in the handshake. Those must + * be silently discarded. * * However, only allow the out-of-order records in the correct epoch. * Application data must come in the encrypted epoch, and ChangeCipherSpec in @@ -384,8 +385,8 @@ assert(msg_len > 0); /* Copy the body into the fragment. */ - OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off, CBS_data(&body), - CBS_len(&body)); + OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off, + CBS_data(&body), CBS_len(&body)); dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len); } @@ -507,219 +508,14 @@ /* Sending handshake messages. */ -static void dtls1_update_mtu(SSL *ssl) { - /* TODO(davidben): What is this code doing and do we need it? */ - if (ssl->d1->mtu < dtls1_min_mtu() && - !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { - long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); - if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) { - ssl->d1->mtu = (unsigned)mtu; - } else { - ssl->d1->mtu = kDefaultMTU; - BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL); - } - } - - /* The MTU should be above the minimum now. */ - assert(ssl->d1->mtu >= dtls1_min_mtu()); -} - -/* dtls1_max_record_size returns the maximum record body length that may be - * written without exceeding the MTU. It accounts for any buffering installed on - * the write BIO. If no record may be written, it returns zero. */ -static size_t dtls1_max_record_size(const SSL *ssl) { - size_t ret = ssl->d1->mtu; - - size_t overhead = SSL_max_seal_overhead(ssl); - if (ret <= overhead) { - return 0; - } - ret -= overhead; - - size_t pending = BIO_wpending(ssl->wbio); - if (ret <= pending) { - return 0; - } - ret -= pending; - - return ret; -} - -static int dtls1_write_change_cipher_spec(SSL *ssl, - enum dtls1_use_epoch_t use_epoch) { - dtls1_update_mtu(ssl); - - /* During the handshake, wbio is buffered to pack messages together. Flush the - * buffer if the ChangeCipherSpec would not fit in a packet. */ - if (dtls1_max_record_size(ssl) == 0) { - int ret = BIO_flush(ssl->wbio); - if (ret <= 0) { - ssl->rwstate = SSL_WRITING; - return ret; - } - } - - static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS}; - int ret = - dtls1_write_record(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, - sizeof(kChangeCipherSpec), use_epoch); - if (ret <= 0) { - return ret; - } - - ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC, - kChangeCipherSpec, sizeof(kChangeCipherSpec)); - return 1; -} - -/* dtls1_do_handshake_write writes handshake message |in| using the given epoch, - * starting |offset| bytes into the message body. It returns one on success. On - * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body - * that were successfully written. This may be used to retry the write - * later. |in| must be a reassembled handshake message with the full DTLS - * handshake header. */ -static int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset, - const uint8_t *in, size_t offset, - size_t len, - enum dtls1_use_epoch_t use_epoch) { - dtls1_update_mtu(ssl); - - int ret = -1; - CBB cbb; - CBB_zero(&cbb); - /* Allocate a temporary buffer to hold the message fragments to avoid - * clobbering the message. */ - uint8_t *buf = OPENSSL_malloc(ssl->d1->mtu); - if (buf == NULL) { - goto err; - } - - /* Although it may be sent as multiple fragments, a DTLS message must be sent - * serialized as a single fragment for purposes of |ssl_do_msg_callback| and - * the handshake hash. */ - CBS cbs, body; - struct hm_header_st hdr; - CBS_init(&cbs, in, len); - if (!dtls1_parse_fragment(&cbs, &hdr, &body) || - hdr.frag_off != 0 || - hdr.frag_len != CBS_len(&body) || - hdr.msg_len != CBS_len(&body) || - !CBS_skip(&body, offset) || - CBS_len(&cbs) != 0) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - goto err; - } - - do { - /* During the handshake, wbio is buffered to pack messages together. Flush - * the buffer if there isn't enough room to make progress. */ - if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) { - int flush_ret = BIO_flush(ssl->wbio); - if (flush_ret <= 0) { - ssl->rwstate = SSL_WRITING; - ret = flush_ret; - goto err; - } - assert(BIO_wpending(ssl->wbio) == 0); - } - - size_t todo = dtls1_max_record_size(ssl); - if (todo < DTLS1_HM_HEADER_LENGTH + 1) { - /* To make forward progress, the MTU must, at minimum, fit the handshake - * header and one byte of handshake body. */ - OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL); - goto err; - } - todo -= DTLS1_HM_HEADER_LENGTH; - - if (todo > CBS_len(&body)) { - todo = CBS_len(&body); - } - if (todo >= (1u << 24)) { - todo = (1u << 24) - 1; - } - - size_t buf_len; - if (!CBB_init_fixed(&cbb, buf, ssl->d1->mtu) || - !CBB_add_u8(&cbb, hdr.type) || - !CBB_add_u24(&cbb, hdr.msg_len) || - !CBB_add_u16(&cbb, hdr.seq) || - !CBB_add_u24(&cbb, offset) || - !CBB_add_u24(&cbb, todo) || - !CBB_add_bytes(&cbb, CBS_data(&body), todo) || - !CBB_finish(&cbb, NULL, &buf_len)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - goto err; - } - - int write_ret = - dtls1_write_record(ssl, SSL3_RT_HANDSHAKE, buf, buf_len, use_epoch); - if (write_ret <= 0) { - ret = write_ret; - goto err; - } - - if (!CBS_skip(&body, todo)) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - goto err; - } - offset += todo; - } while (CBS_len(&body) != 0); - - ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, in, len); - - ret = 1; - -err: - *out_offset = offset; - CBB_cleanup(&cbb); - OPENSSL_free(buf); - return ret; -} - void dtls_clear_outgoing_messages(SSL *ssl) { for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) { OPENSSL_free(ssl->d1->outgoing_messages[i].data); ssl->d1->outgoing_messages[i].data = NULL; } ssl->d1->outgoing_messages_len = 0; -} - -/* dtls1_add_change_cipher_spec adds a ChangeCipherSpec to the current - * handshake flight. */ -static int dtls1_add_change_cipher_spec(SSL *ssl) { - if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return 0; - } - - DTLS_OUTGOING_MESSAGE *msg = - &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; - msg->data = NULL; - msg->len = 0; - msg->epoch = ssl->d1->w_epoch; - msg->is_ccs = 1; - - ssl->d1->outgoing_messages_len++; - return 1; -} - -static int dtls1_add_message(SSL *ssl, uint8_t *data, size_t len) { - if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - OPENSSL_free(data); - return 0; - } - - DTLS_OUTGOING_MESSAGE *msg = - &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; - msg->data = data; - msg->len = len; - msg->epoch = ssl->d1->w_epoch; - msg->is_ccs = 0; - - ssl->d1->outgoing_messages_len++; - return 1; + ssl->d1->outgoing_written = 0; + ssl->d1->outgoing_offset = 0; } int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { @@ -752,36 +548,87 @@ return 1; } -int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len) { - ssl3_update_handshake_hash(ssl, msg, len); +/* add_outgoing adds a new handshake message or ChangeCipherSpec to the current + * outgoing flight. It returns one on success and zero on error. In both cases, + * it takes ownership of |data| and releases it with |OPENSSL_free| when + * done. */ +static int add_outgoing(SSL *ssl, int is_ccs, uint8_t *data, size_t len) { + OPENSSL_COMPILE_ASSERT(SSL_MAX_HANDSHAKE_FLIGHT < + (1 << 8 * sizeof(ssl->d1->outgoing_messages_len)), + outgoing_messages_len_is_too_small); + if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + OPENSSL_free(data); + return 0; + } - ssl->d1->handshake_write_seq++; - ssl->init_off = 0; - return dtls1_add_message(ssl, msg, len); + if (!is_ccs) { + ssl3_update_handshake_hash(ssl, data, len); + ssl->d1->handshake_write_seq++; + } + + DTLS_OUTGOING_MESSAGE *msg = + &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; + msg->data = data; + msg->len = len; + msg->epoch = ssl->d1->w_epoch; + msg->is_ccs = is_ccs; + + ssl->d1->outgoing_messages_len++; + return 1; +} + +int dtls1_queue_message(SSL *ssl, uint8_t *data, size_t len) { + return add_outgoing(ssl, 0 /* handshake */, data, len); } int dtls1_write_message(SSL *ssl) { - if (ssl->d1->outgoing_messages_len == 0) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; - } - - const DTLS_OUTGOING_MESSAGE *msg = - &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len - 1]; - if (msg->is_ccs) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); - return -1; - } - - size_t offset = ssl->init_off; - int ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len, - dtls1_use_current_epoch); - ssl->init_off = offset; - return ret; + /* The message is written in |dtls1_flush_flight|. */ + return 1; } -static int dtls1_retransmit_message(SSL *ssl, - const DTLS_OUTGOING_MESSAGE *msg) { +int dtls1_send_change_cipher_spec(SSL *ssl) { + return add_outgoing(ssl, 1 /* ChangeCipherSpec */, NULL, 0); +} + +/* dtls1_update_mtu updates the current MTU from the BIO, ensuring it is above + * the minimum. */ +static void dtls1_update_mtu(SSL *ssl) { + /* TODO(davidben): No consumer implements |BIO_CTRL_DGRAM_SET_MTU| and the + * only |BIO_CTRL_DGRAM_QUERY_MTU| implementation could use + * |SSL_set_mtu|. Does this need to be so complex? */ + if (ssl->d1->mtu < dtls1_min_mtu() && + !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { + long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); + if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) { + ssl->d1->mtu = (unsigned)mtu; + } else { + ssl->d1->mtu = kDefaultMTU; + BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL); + } + } + + /* The MTU should be above the minimum now. */ + assert(ssl->d1->mtu >= dtls1_min_mtu()); +} + +enum seal_result_t { + seal_error, + seal_no_progress, + seal_partial, + seal_success, +}; + +/* seal_next_message seals |msg|, which must be the next message, to |out|. If + * progress was made, it returns |seal_partial| or |seal_success| and sets + * |*out_len| to the number of bytes written. */ +static enum seal_result_t seal_next_message(SSL *ssl, uint8_t *out, + size_t *out_len, size_t max_out, + const DTLS_OUTGOING_MESSAGE *msg) { + assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len); + assert(msg == &ssl->d1->outgoing_messages[ssl->d1->outgoing_written]); + /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 * (negotiated cipher) exist. */ assert(ssl->d1->w_epoch == 0 || ssl->d1->w_epoch == 1); @@ -790,32 +637,156 @@ if (ssl->d1->w_epoch == 1 && msg->epoch == 0) { use_epoch = dtls1_use_previous_epoch; } + size_t overhead = dtls_max_seal_overhead(ssl, use_epoch); + size_t prefix = dtls_seal_prefix_len(ssl, use_epoch); - /* TODO(davidben): This cannot handle non-blocking writes. */ - int ret; if (msg->is_ccs) { - ret = dtls1_write_change_cipher_spec(ssl, use_epoch); - } else { - size_t offset = 0; - ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len, - use_epoch); + /* Check there is room for the ChangeCipherSpec. */ + static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS}; + if (max_out < sizeof(kChangeCipherSpec) + overhead) { + return seal_no_progress; + } + + if (!dtls_seal_record(ssl, out, out_len, max_out, + SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, + sizeof(kChangeCipherSpec), use_epoch)) { + return seal_error; + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC, + kChangeCipherSpec, sizeof(kChangeCipherSpec)); + return seal_success; } - return ret; + /* DTLS messages are serialized as a single fragment in |msg|. */ + CBS cbs, body; + struct hm_header_st hdr; + CBS_init(&cbs, msg->data, msg->len); + if (!dtls1_parse_fragment(&cbs, &hdr, &body) || + hdr.frag_off != 0 || + hdr.frag_len != CBS_len(&body) || + hdr.msg_len != CBS_len(&body) || + !CBS_skip(&body, ssl->d1->outgoing_offset) || + CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return seal_error; + } + + /* Determine how much progress can be made. */ + if (max_out < DTLS1_HM_HEADER_LENGTH + 1 + overhead || max_out < prefix) { + return seal_no_progress; + } + size_t todo = CBS_len(&body); + if (todo > max_out - DTLS1_HM_HEADER_LENGTH - overhead) { + todo = max_out - DTLS1_HM_HEADER_LENGTH - overhead; + } + + /* Assemble a fragment, to be sealed in-place. */ + CBB cbb; + uint8_t *frag = out + prefix; + size_t max_frag = max_out - prefix, frag_len; + if (!CBB_init_fixed(&cbb, frag, max_frag) || + !CBB_add_u8(&cbb, hdr.type) || + !CBB_add_u24(&cbb, hdr.msg_len) || + !CBB_add_u16(&cbb, hdr.seq) || + !CBB_add_u24(&cbb, ssl->d1->outgoing_offset) || + !CBB_add_u24(&cbb, todo) || + !CBB_add_bytes(&cbb, CBS_data(&body), todo) || + !CBB_finish(&cbb, NULL, &frag_len)) { + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return seal_error; + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, frag, frag_len); + + if (!dtls_seal_record(ssl, out, out_len, max_out, SSL3_RT_HANDSHAKE, + out + prefix, frag_len, use_epoch)) { + return seal_error; + } + + if (todo == CBS_len(&body)) { + /* The next message is complete. */ + ssl->d1->outgoing_offset = 0; + return seal_success; + } + + ssl->d1->outgoing_offset += todo; + return seal_partial; } -int dtls1_retransmit_outgoing_messages(SSL *ssl) { - /* Ensure we are packing handshake messages. */ - const int was_buffered = ssl_is_wbio_buffered(ssl); - assert(was_buffered == SSL_in_init(ssl)); - if (!was_buffered && !ssl_init_wbio_buffer(ssl)) { - return -1; +/* seal_next_packet writes as much of the next flight as possible to |out| and + * advances |ssl->d1->outgoing_written| and |ssl->d1->outgoing_offset| as + * appropriate. */ +static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out) { + int made_progress = 0; + size_t total = 0; + assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len); + for (; ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len; + ssl->d1->outgoing_written++) { + const DTLS_OUTGOING_MESSAGE *msg = + &ssl->d1->outgoing_messages[ssl->d1->outgoing_written]; + size_t len; + enum seal_result_t ret = seal_next_message(ssl, out, &len, max_out, msg); + switch (ret) { + case seal_error: + return 0; + + case seal_no_progress: + goto packet_full; + + case seal_partial: + case seal_success: + out += len; + max_out -= len; + total += len; + made_progress = 1; + + if (ret == seal_partial) { + goto packet_full; + } + break; + } } - assert(ssl_is_wbio_buffered(ssl)); + +packet_full: + /* The MTU was too small to make any progress. */ + if (!made_progress) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL); + return 0; + } + + *out_len = total; + return 1; +} + +int dtls1_flush_flight(SSL *ssl) { + dtls1_update_mtu(ssl); int ret = -1; - for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) { - if (dtls1_retransmit_message(ssl, &ssl->d1->outgoing_messages[i]) <= 0) { + uint8_t *packet = OPENSSL_malloc(ssl->d1->mtu); + if (packet == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + while (ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len) { + uint8_t old_written = ssl->d1->outgoing_written; + uint32_t old_offset = ssl->d1->outgoing_offset; + + size_t packet_len; + if (!seal_next_packet(ssl, packet, &packet_len, ssl->d1->mtu)) { + goto err; + } + + int bio_ret = BIO_write(ssl->wbio, packet, packet_len); + if (bio_ret <= 0) { + /* Retry this packet the next time around. */ + ssl->d1->outgoing_written = old_written; + ssl->d1->outgoing_offset = old_offset; + ssl->rwstate = SSL_WRITING; + ret = bio_ret; goto err; } } @@ -823,23 +794,22 @@ ret = BIO_flush(ssl->wbio); if (ret <= 0) { ssl->rwstate = SSL_WRITING; - goto err; } err: - if (!was_buffered) { - ssl_free_wbio_buffer(ssl); - } + OPENSSL_free(packet); return ret; } -int dtls1_send_change_cipher_spec(SSL *ssl) { - int ret = dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch); - if (ret <= 0) { - return ret; - } - dtls1_add_change_cipher_spec(ssl); - return 1; +int dtls1_retransmit_outgoing_messages(SSL *ssl) { + /* Rewind to the start of the flight and write it again. + * + * TODO(davidben): This does not allow retransmits to be resumed on + * non-blocking write. */ + ssl->d1->outgoing_written = 0; + ssl->d1->outgoing_offset = 0; + + return dtls1_flush_flight(ssl); } unsigned int dtls1_min_mtu(void) {
diff --git a/ssl/dtls_method.c b/ssl/dtls_method.c index 702b3c0..a774c82 100644 --- a/ssl/dtls_method.c +++ b/ssl/dtls_method.c
@@ -99,14 +99,6 @@ return cipher->algorithm_enc != SSL_eNULL; } -static int dtls1_flush_flight(SSL *ssl) { - int ret = BIO_flush(ssl->wbio); - if (ret <= 0) { - ssl->rwstate = SSL_WRITING; - } - return ret; -} - static void dtls1_expect_flight(SSL *ssl) { dtls1_start_timer(ssl); } static void dtls1_received_flight(SSL *ssl) { dtls1_stop_timer(ssl); }
diff --git a/ssl/dtls_record.c b/ssl/dtls_record.c index 35c08b0..879706d 100644 --- a/ssl/dtls_record.c +++ b/ssl/dtls_record.c
@@ -249,16 +249,27 @@ return ssl_open_record_success; } -size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) { - const SSL_AEAD_CTX *aead = ssl->s3->aead_write_ctx; +static const SSL_AEAD_CTX *get_write_aead(const SSL *ssl, + enum dtls1_use_epoch_t use_epoch) { if (use_epoch == dtls1_use_previous_epoch) { /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 * (negotiated cipher) exist. */ assert(ssl->d1->w_epoch == 1); - aead = NULL; + return NULL; } - return DTLS1_RT_HEADER_LENGTH + SSL_AEAD_CTX_explicit_nonce_len(aead); + return ssl->s3->aead_write_ctx; +} + +size_t dtls_max_seal_overhead(const SSL *ssl, + enum dtls1_use_epoch_t use_epoch) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_max_overhead(get_write_aead(ssl, use_epoch)); +} + +size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(get_write_aead(ssl, use_epoch)); } int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
diff --git a/ssl/internal.h b/ssl/internal.h index a320e72..ffe4d62 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -433,6 +433,10 @@ dtls1_use_current_epoch, }; +/* dtls_max_seal_overhead returns the maximum overhead, in bytes, of sealing a + * record. */ +size_t dtls_max_seal_overhead(const SSL *ssl, enum dtls1_use_epoch_t use_epoch); + /* dtls_seal_prefix_len returns the number of bytes of prefix to reserve in * front of the plaintext when sealing a record in-place. */ size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch); @@ -1645,6 +1649,13 @@ DTLS_OUTGOING_MESSAGE outgoing_messages[SSL_MAX_HANDSHAKE_FLIGHT]; uint8_t outgoing_messages_len; + /* outgoing_written is the number of outgoing messages that have been + * written. */ + uint8_t outgoing_written; + /* outgoing_offset is the number of bytes of the next outgoing message have + * been written. */ + uint32_t outgoing_offset; + unsigned int mtu; /* max DTLS packet size */ /* num_timeouts is the number of times the retransmit timer has fired since @@ -1779,6 +1790,8 @@ size_t *out_len); int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len); int dtls1_write_message(SSL *ssl); +int dtls1_send_change_cipher_spec(SSL *ssl); +int dtls1_flush_flight(SSL *ssl); /* ssl_complete_message calls |finish_message| and |queue_message| on |cbb| to * queue the message for writing. */ @@ -1805,7 +1818,6 @@ int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len, enum dtls1_use_epoch_t use_epoch); -int dtls1_send_change_cipher_spec(SSL *ssl); int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen); int dtls1_retransmit_outgoing_messages(SSL *ssl); void dtls1_clear_record_buffer(SSL *ssl);
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index ba1ee8f..d653378 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c
@@ -2028,6 +2028,13 @@ } int ssl_init_wbio_buffer(SSL *ssl) { + if (SSL_is_dtls(ssl)) { + /* DTLS does not use the BIO buffer. + * TODO(davidben): Remove this altogether when TLS no longer uses it. + * https://crbug.com/boringssl/72. */ + return 1; + } + if (ssl->bbio != NULL) { /* Already buffered. */ assert(ssl->bbio == ssl->wbio);
diff --git a/ssl/tls_record.c b/ssl/tls_record.c index 362b0c2..6ff79c4 100644 --- a/ssl/tls_record.c +++ b/ssl/tls_record.c
@@ -205,20 +205,19 @@ } size_t SSL_max_seal_overhead(const SSL *ssl) { - size_t ret = SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx); if (SSL_is_dtls(ssl)) { - ret += DTLS1_RT_HEADER_LENGTH; - } else if (ssl_uses_short_header(ssl, evp_aead_seal)) { - ret += 2; - } else { - ret += SSL3_RT_HEADER_LENGTH; + return dtls_max_seal_overhead(ssl, dtls1_use_current_epoch); } + + size_t ret = + ssl_uses_short_header(ssl, evp_aead_seal) ? 2 : SSL3_RT_HEADER_LENGTH; + ret += SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx); /* TLS 1.3 needs an extra byte for the encrypted record type. */ if (ssl->s3->have_version && ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { ret += 1; } - if (!SSL_is_dtls(ssl) && ssl_needs_record_splitting(ssl)) { + if (ssl_needs_record_splitting(ssl)) { ret *= 2; } return ret;