Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve QUIC defragmentation of TLS packets. #173

Open
wants to merge 1 commit into
base: openssl-3.1.7+quic
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ssl/ssl_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,6 @@ int SSL_clear_quic(SSL *s)
s->quic_input_data_tail = NULL;
BUF_MEM_free(s->quic_buf);
s->quic_buf = NULL;
s->quic_next_record_start = 0;
memset(s->client_hand_traffic_secret, 0, EVP_MAX_MD_SIZE);
memset(s->server_hand_traffic_secret, 0, EVP_MAX_MD_SIZE);
memset(s->client_early_traffic_secret, 0, EVP_MAX_MD_SIZE);
Expand Down
3 changes: 1 addition & 2 deletions ssl/ssl_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -1226,8 +1226,8 @@ typedef struct cert_pkey_st CERT_PKEY;
struct quic_data_st {
struct quic_data_st *next;
OSSL_ENCRYPTION_LEVEL level;
size_t start; /* offset into quic_buf->data */
size_t length;
/* char data[]; should be here but C90 VLAs not allowed here */
};
typedef struct quic_data_st QUIC_DATA;
int quic_set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL level);
Expand Down Expand Up @@ -1735,7 +1735,6 @@ struct ssl_st {
int quic_transport_version;
QUIC_DATA *quic_input_data_head;
QUIC_DATA *quic_input_data_tail;
size_t quic_next_record_start;
const SSL_QUIC_METHOD *quic_method;
#endif
/*
Expand Down
91 changes: 60 additions & 31 deletions ssl/ssl_quic.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ OSSL_ENCRYPTION_LEVEL SSL_quic_write_level(const SSL *ssl)
int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
const uint8_t *data, size_t len)
{
size_t l, offset;
size_t l;
size_t fragment_length;

if (!SSL_IS_QUIC(ssl)) {
ERR_raise(ERR_LIB_SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
Expand All @@ -146,70 +147,98 @@ int SSL_provide_quic_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL level,
if (len == 0)
return 1;

if (ssl->quic_buf == NULL) {
BUF_MEM *buf;
if ((buf = BUF_MEM_new()) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
if (!BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
BUF_MEM_free(buf);
return 0;
}
ssl->quic_buf = buf;
/* We preallocated storage, but there's still no *data*. */
ssl->quic_buf->length = 0;
buf = NULL;
fragment_length = 0;
if (ssl->quic_buf != NULL) {
fragment_length = ssl->quic_buf->length;
}

/* A TLS message must not cross an encryption level boundary */
if (ssl->quic_buf->length != ssl->quic_next_record_start
&& level != ssl->quic_latest_level_received) {
if (fragment_length != 0 && level != ssl->quic_latest_level_received) {
ERR_raise(ERR_LIB_SSL, SSL_R_WRONG_ENCRYPTION_LEVEL_RECEIVED);
return 0;
}
ssl->quic_latest_level_received = level;

offset = ssl->quic_buf->length;
if (!BUF_MEM_grow(ssl->quic_buf, offset + len)) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
if (fragment_length != 0) {
/*
* If we have a partial record, copy the data into the buffer, and
* parse records from it.
*/
if (!BUF_MEM_grow(ssl->quic_buf, fragment_length + len)) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
memcpy(ssl->quic_buf->data + fragment_length, data, len);
data = (const uint8_t *)ssl->quic_buf->data;
len = ssl->quic_buf->length;
}
memcpy(ssl->quic_buf->data + offset, data, len);

/* Split on handshake message boundaries */
while (ssl->quic_buf->length > ssl->quic_next_record_start
+ SSL3_HM_HEADER_LENGTH) {
while (len >= SSL3_HM_HEADER_LENGTH) {
QUIC_DATA *qd;
const uint8_t *p;

/* TLS Handshake message header has 1-byte type and 3-byte length */
p = (const uint8_t *)ssl->quic_buf->data
+ ssl->quic_next_record_start + 1;
p = data + 1;
n2l3(p, l);
l += SSL3_HM_HEADER_LENGTH;
/* Don't allocate a QUIC_DATA if we don't have a full record */
if (l > ssl->quic_buf->length - ssl->quic_next_record_start)
if (l > len)
break;

qd = OPENSSL_zalloc(sizeof(*qd));
qd = OPENSSL_zalloc(sizeof(*qd) + l);
if (qd == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}

qd->next = NULL;
qd->length = l;
qd->start = ssl->quic_next_record_start;
qd->level = level;
memcpy((void *)(qd + 1), data, l);

if (ssl->quic_input_data_tail != NULL)
ssl->quic_input_data_tail->next = qd;
else
ssl->quic_input_data_head = qd;
ssl->quic_input_data_tail = qd;
ssl->quic_next_record_start += l;

/* Remove the now-consumed data. */
data += l;
len -= l;
}

/*
* If we have any unread data, we need to save it.
*/
if (len > 0) {
if (ssl->quic_buf != NULL) {
if (data == (const uint8_t *)ssl->quic_buf->data) {
/* It's already in the buffer; no need to do anything.. */
return 1;
}
} else {
BUF_MEM *buf;
if ((buf = BUF_MEM_new()) == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
ssl->quic_buf = buf;
}
/*
* Note: 'data' might be in this buffer, but if that's the case,
* we're going to be shrinking the buffer, so the pointer will not be
* invalidated. We do need to use memmove() and not memcpy(), though.
*/
if (!BUF_MEM_grow(ssl->quic_buf, len)) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return 0;
}
memmove(ssl->quic_buf->data, data, len);
} else {
if (ssl->quic_buf != NULL) {
ssl->quic_buf->length = 0;
}
}

return 1;
Expand Down
2 changes: 1 addition & 1 deletion ssl/statem/statem_quic.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ int quic_get_message(SSL *s, int *mt)
}

/* Copy buffered data */
memcpy(s->init_buf->data, s->quic_buf->data + qd->start, qd->length);
memcpy(s->init_buf->data, (void *)(qd + 1), qd->length);
s->init_buf->length = qd->length;
s->quic_input_data_head = qd->next;
if (s->quic_input_data_head == NULL)
Expand Down
Loading