Skip to content

Commit

Permalink
QUIC: Allow application to trigger TXKU
Browse files Browse the repository at this point in the history
Reviewed-by: Tomas Mraz <[email protected]>
Reviewed-by: Matt Caswell <[email protected]>
Reviewed-by: Paul Dale <[email protected]>
(Merged from openssl#21029)
  • Loading branch information
hlandau authored and paulidale committed Jun 15, 2023
1 parent 692a3ca commit 2525109
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 7 deletions.
27 changes: 22 additions & 5 deletions doc/man3/SSL_key_update.pod
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ peer to additionally update its sending keys. It is an error if B<updatetype> is
set to B<SSL_KEY_UPDATE_NONE>.

SSL_key_update() must only be called after the initial handshake has been
completed and TLSv1.3 has been negotiated, at the same time, the application
needs to ensure that the writing of data has been completed. The key update
will not take place until the next time an IO operation such as SSL_read_ex()
or SSL_write_ex() takes place on the connection. Alternatively SSL_do_handshake()
can be called to force the update to take place immediately.
completed and TLSv1.3 or QUIC has been negotiated, at the same time, the
application needs to ensure that the writing of data has been completed. The key
update will not take place until the next time an IO operation such as
SSL_read_ex() or SSL_write_ex() takes place on the connection. Alternatively
SSL_do_handshake() can be called to force the update to take place immediately.

SSL_get_key_update_type() can be used to determine whether a key update
operation has been scheduled but not yet performed. The type of the pending key
Expand Down Expand Up @@ -77,6 +77,23 @@ the session in the new handshake.
The SSL_renegotiate_pending() function returns 1 if a renegotiation or
renegotiation request has been scheduled but not yet acted on, or 0 otherwise.

=head1 USAGE WITH QUIC

SSL_key_update() can also be used to perform a key update when using QUIC. The
function must be called on a QUIC connection SSL object. This is normally done
automatically when needed. Since a locally initiated QUIC key update always
causes a peer to also trigger a key update, passing
B<SSL_KEY_UPDATE_NOT_REQUESTED> as B<updatetype> has the same effect as passing
B<SSL_KEY_UPDATE_REQUESTED>.

The QUIC connection must have been fully established before a key update can be
performed, and other QUIC protocol rules govern how frequently QUIC key update
can be performed. SSL_key_update() will fail if these requirements are not met.

Because QUIC key updates are always handled immediately,
SSL_get_key_update_type() always returns SSL_KEY_UPDATE_NONE when called on a
QUIC connection SSL object.

=head1 RETURN VALUES

SSL_key_update(), SSL_renegotiate() and SSL_renegotiate_abbreviated() return 1
Expand Down
3 changes: 3 additions & 0 deletions include/internal/quic_channel.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ void ossl_quic_channel_set_txku_threshold_override(QUIC_CHANNEL *ch,
uint64_t ossl_quic_channel_get_tx_key_epoch(QUIC_CHANNEL *ch);
uint64_t ossl_quic_channel_get_rx_key_epoch(QUIC_CHANNEL *ch);

/* Artificially trigger a spontaneous TXKU if possible. */
int ossl_quic_channel_trigger_txku(QUIC_CHANNEL *ch);

# endif

#endif
2 changes: 2 additions & 0 deletions include/internal/quic_ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ __owur long ossl_quic_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg);
__owur long ossl_quic_callback_ctrl(SSL *s, int cmd, void (*fp) (void));
__owur long ossl_quic_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp) (void));
__owur size_t ossl_quic_pending(const SSL *s);
__owur int ossl_quic_key_update(SSL *s, int update_type);
__owur int ossl_quic_get_key_update_type(const SSL *s);
__owur int ossl_quic_num_ciphers(void);
__owur const SSL_CIPHER *ossl_quic_get_cipher(unsigned int u);
int ossl_quic_renegotiate_check(SSL *ssl, int initok);
Expand Down
51 changes: 51 additions & 0 deletions ssl/quic/quic_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,7 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags)
* (BIO/)SSL_write => ossl_quic_write
* SSL_pending => ossl_quic_pending
* SSL_stream_conclude => ossl_quic_conn_stream_conclude
* SSL_key_update => ossl_quic_key_update
*/

/* SSL_get_error */
Expand Down Expand Up @@ -2688,6 +2689,56 @@ int ossl_quic_get_conn_close_info(SSL *ssl,
return 1;
}

/*
* SSL_key_update
* --------------
*/
int ossl_quic_key_update(SSL *ssl, int update_type)
{
QCTX ctx;

if (!expect_quic_conn_only(ssl, &ctx))
return 0;

switch (update_type) {
case SSL_KEY_UPDATE_NOT_REQUESTED:
/*
* QUIC signals peer key update implicily by triggering a local
* spontaneous TXKU. Silently upgrade this to SSL_KEY_UPDATE_REQUESTED.
*/
case SSL_KEY_UPDATE_REQUESTED:
break;

default:
/* Unknown type - error. */
return 0;
}

quic_lock(ctx.qc);

/* Attempt to perform a TXKU. */
if (!ossl_quic_channel_trigger_txku(ctx.qc->ch)) {
quic_unlock(ctx.qc);
return 0;
}

quic_unlock(ctx.qc);
return 1;
}

/*
* SSL_get_key_update_type
* -----------------------
*/
int ossl_quic_get_key_update_type(const SSL *s)
{
/*
* We always handle key updates immediately so a key update is never
* pending.
*/
return SSL_KEY_UPDATE_NONE;
}

/*
* QUIC Front-End I/O API: SSL_CTX Management
* ==========================================
Expand Down
10 changes: 10 additions & 0 deletions ssl/ssl_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -2727,6 +2727,11 @@ int SSL_key_update(SSL *s, int updatetype)
{
SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(s);

#ifndef OPENSSL_NO_QUIC
if (IS_QUIC(s))
return ossl_quic_key_update(s, updatetype);
#endif

if (sc == NULL)
return 0;

Expand Down Expand Up @@ -2760,6 +2765,11 @@ int SSL_get_key_update_type(const SSL *s)
{
const SSL_CONNECTION *sc = SSL_CONNECTION_FROM_CONST_SSL(s);

#ifndef OPENSSL_NO_QUIC
if (IS_QUIC(s))
return ossl_quic_get_key_update_type(s);
#endif

if (sc == NULL)
return 0;

Expand Down
34 changes: 32 additions & 2 deletions test/quic_multistream_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,14 @@ static int override_key_update(struct helper *h, const struct script_op *op)
return 1;
}

static int trigger_key_update(struct helper *h, const struct script_op *op)
{
if (!TEST_true(SSL_key_update(h->c_conn, SSL_KEY_UPDATE_REQUESTED)))
return 0;

return 1;
}

static int check_key_update_ge(struct helper *h, const struct script_op *op)
{
QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn);
Expand Down Expand Up @@ -683,7 +691,7 @@ static int run_script_worker(struct helper *h, const struct script_op *script,
if (!TEST_true(helper_local_init(&hl, h, thread_idx)))
goto out;

#define SPIN_AGAIN() { no_advance = 1; continue; }
#define SPIN_AGAIN() { OSSL_sleep(1); no_advance = 1; continue; }

for (;;) {
SSL *c_tgt = h->c_conn;
Expand Down Expand Up @@ -1933,7 +1941,7 @@ static const struct script_op script_18[] = {
* 1 packet above, which is absurd; thus this ensures we only actually
* generate TXKUs when we are allowed to.
*/
OP_CHECK (check_key_update_ge, 5)
OP_CHECK (check_key_update_ge, 4)
OP_CHECK (check_key_update_lt, 120)

/*
Expand All @@ -1949,6 +1957,27 @@ static const struct script_op script_18[] = {
OP_END
};

/* 19. Key update test - artificially triggered */
static const struct script_op script_19[] = {
OP_C_SET_ALPN ("ossltest")
OP_C_CONNECT_WAIT ()

OP_C_WRITE (DEFAULT, "apple", 5)

OP_S_BIND_STREAM_ID (a, C_BIDI_ID(0))
OP_S_READ_EXPECT (a, "apple", 5)

OP_CHECK (check_key_update_lt, 1)
OP_CHECK (trigger_key_update, 0)

OP_C_WRITE (DEFAULT, "orange", 6)
OP_S_READ_EXPECT (a, "orange", 6)

OP_CHECK (check_key_update_ge, 1)

OP_END
};

static const struct script_op *const scripts[] = {
script_1,
script_2,
Expand All @@ -1968,6 +1997,7 @@ static const struct script_op *const scripts[] = {
script_16,
script_17,
script_18,
script_19,
};

static int test_script(int idx)
Expand Down

0 comments on commit 2525109

Please sign in to comment.