Skip to content

Commit

Permalink
QUIC: SSL_new_session_ticket() support (#26)
Browse files Browse the repository at this point in the history
* Let SSL_new_session_ticket() work immediately

The initial implementation always deferred the generation of the
requested ticket(s) until the next application write, but this
means that the ticket cannot be written at all until there is
application data ready to write.  In some scenarios this application
data may never arrive or may take a long time to arrive, so (when
already at a record boundary) allow the application to explicitly call
SSL_do_handshake() after SSL_new_session_ticket() to force an immediate
write, even when there is no application data available.  The default
behavior remains to defer the generation of the ticket and coalesce the
network traffic for the ticket and application data.

* Test new SSL_new_session_ticket() functionality

Now that we can become "in init" directly after the call, test the
various scenarios where explicit SSL_do_handshake() calls can come
into play.

* Update SSL_new_session_ticket() manual for triggered send

Document the recently added functionality.

(cherry picked from commit 4fb1ff7)
  • Loading branch information
kaduk authored and tmshort committed Aug 2, 2023
1 parent 9cf2c00 commit 8530d68
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 15 deletions.
27 changes: 16 additions & 11 deletions doc/man3/SSL_CTX_set_num_tickets.pod
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,22 @@ sent.
To issue tickets after other events (such as application-layer changes),
SSL_new_session_ticket() is used by a server application to request that a new
ticket be sent when it is safe to do so. New tickets are only allowed to be
sent in this manner after the initial handshake has completed, and only for TLS
1.3 connections. The ticket generation and transmission are delayed until the
server is starting a new write operation, so that it is bundled with other
application data being written and properly aligned to a record boundary.
SSL_new_session_ticket() can be called more than once to request additional
tickets be sent; all such requests are queued and written together when it is
safe to do so. Note that a successful return from SSL_new_session_ticket()
indicates only that the request to send a ticket was processed, not that the
ticket itself was sent. To be notified when the ticket itself is sent, a
new-session callback can be registered with L<SSL_CTX_sess_set_new_cb(3)> that
will be invoked as the ticket or tickets are generated.
sent in this manner after the initial handshake has completed, and only for
TLS 1.3 connections. By default, the ticket generation and transmission are
delayed until the server is starting a new write operation, so that it is
bundled with other application data being written and properly aligned to a
record boundary. If the connection was at a record boundary when
SSL_new_session_ticket() was called, the ticket can be sent immediately
(without waiting for the next application write) by calling
SSL_do_handshake(). SSL_new_session_ticket() can be called more than once to
request additional tickets be sent; all such requests are queued and written
together when it is safe to do so and triggered by SSL_write() or
SSL_do_handshake(). Note that a successful return from
SSL_new_session_ticket() indicates only that the request to send a ticket was
processed, not that the ticket itself was sent. To be notified when the
ticket itself is sent, a new-session callback can be registered with
L<SSL_CTX_sess_set_new_cb(3)> that will be invoked as the ticket or tickets
are generated.

SSL_CTX_get_num_tickets() and SSL_get_num_tickets() return the number of
tickets set by a previous call to SSL_CTX_set_num_tickets() or
Expand Down
6 changes: 5 additions & 1 deletion ssl/ssl_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -2234,10 +2234,14 @@ int SSL_renegotiate_pending(const SSL *s)

int SSL_new_session_ticket(SSL *s)
{
if (SSL_in_init(s) || SSL_IS_FIRST_HANDSHAKE(s) || !s->server
/* If we are in init because we're sending tickets, okay to send more. */
if ((SSL_in_init(s) && s->ext.extra_tickets_expected == 0)
|| SSL_IS_FIRST_HANDSHAKE(s) || !s->server
|| !SSL_IS_TLS13(s))
return 0;
s->ext.extra_tickets_expected++;
if (s->rlayer.wbuf[0].left == 0 && !SSL_in_init(s))
ossl_statem_set_in_init(s, 1);
return 1;
}

Expand Down
26 changes: 23 additions & 3 deletions test/sslapitest.c
Original file line number Diff line number Diff line change
Expand Up @@ -1844,11 +1844,22 @@ static int test_extra_tickets(int idx)
|| !TEST_int_eq(4, new_called))
goto end;

/* Once more, but with SSL_do_handshake() to drive the ticket generation */
c = '4';
new_called = 0;
if (!TEST_true(SSL_new_session_ticket(serverssl))
|| !TEST_true(SSL_new_session_ticket(serverssl))
|| !TEST_true(SSL_do_handshake(serverssl))
|| !TEST_int_eq(2, new_called)
|| !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes))
|| !TEST_int_eq(4, new_called))
goto end;

/*
* Use the always-retry BIO to exercise the logic that forces ticket
* generation to wait until a record boundary.
*/
c = '4';
c = '5';
new_called = 0;
tmp = SSL_get_wbio(serverssl);
if (!TEST_ptr(tmp) || !TEST_true(BIO_up_ref(tmp))) {
Expand All @@ -1864,9 +1875,14 @@ static int test_extra_tickets(int idx)
/* Restore a BIO that will let the write succeed */
SSL_set0_wbio(serverssl, tmp);
tmp = NULL;
/* These calls should just queue the request and not send anything. */
/*
* These calls should just queue the request and not send anything
* even if we explicitly try to hit the state machine.
*/
if (!TEST_true(SSL_new_session_ticket(serverssl))
|| !TEST_true(SSL_new_session_ticket(serverssl))
|| !TEST_int_eq(0, new_called)
|| !TEST_true(SSL_do_handshake(serverssl))
|| !TEST_int_eq(0, new_called))
goto end;
/* Re-do the write; still no tickets sent */
Expand All @@ -1879,8 +1895,12 @@ static int test_extra_tickets(int idx)
|| !TEST_int_eq(c, buf[0])
|| !TEST_false(SSL_read_ex(clientssl, buf, sizeof(buf), &nbytes)))
goto end;
/* Even trying to hit the state machine now will still not send tickets */
if (!TEST_true(SSL_do_handshake(serverssl))
|| !TEST_int_eq(0, new_called))
goto end;
/* Now the *next* write should send the tickets */
c = '5';
c = '6';
if (!TEST_true(SSL_write_ex(serverssl, &c, 1, &nbytes))
|| !TEST_size_t_eq(1, nbytes)
|| !TEST_int_eq(2, new_called)
Expand Down

0 comments on commit 8530d68

Please sign in to comment.