summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugo Landau <hlandau@openssl.org>2023-05-23 12:23:06 +0100
committerPauli <pauli@openssl.org>2023-06-16 09:26:48 +1000
commit693b23e3d07813985de510a00b1db58070439a51 (patch)
tree3a9a2a1475a2af54f9e81b2059da8d5e8dacb9f6
parent37ba2bc72281c196534e265c34be94beb760393e (diff)
QUIC: Test key update works correctly
Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/openssl/pull/21029)
-rw-r--r--test/quic_multistream_test.c179
-rw-r--r--test/testutil.h7
2 files changed, 184 insertions, 2 deletions
diff --git a/test/quic_multistream_test.c b/test/quic_multistream_test.c
index 3f56e0baa7..44da562da8 100644
--- a/test/quic_multistream_test.c
+++ b/test/quic_multistream_test.c
@@ -59,6 +59,15 @@ struct helper {
#endif
OSSL_TIME start_time;
+
+ /*
+ * This is a duration recording the amount of time we have skipped forwards
+ * for testing purposes relative to the real ossl_time_now() clock. We add
+ * a quantity of time to this every time we skip some time.
+ */
+ CRYPTO_RWLOCK *time_lock;
+ OSSL_TIME time_slip; /* protected by time_lock */
+
int init, blocking, check_spin_again;
int free_order;
};
@@ -209,6 +218,31 @@ struct script_op {
#define OP_S_UNBIND_STREAM_ID(stream_name) \
{OPK_S_UNBIND_STREAM_ID, NULL, 0, NULL, #stream_name},
+static OSSL_TIME get_time(void *arg)
+{
+ struct helper *h = arg;
+ OSSL_TIME t;
+
+ if (!TEST_true(CRYPTO_THREAD_read_lock(h->time_lock)))
+ return ossl_time_zero();
+
+ t = ossl_time_add(ossl_time_now(), h->time_slip);
+
+ CRYPTO_THREAD_unlock(h->time_lock);
+ return t;
+}
+
+static int skip_time_ms(struct helper *h, const struct script_op *op)
+{
+ if (!TEST_true(CRYPTO_THREAD_write_lock(h->time_lock)))
+ return 0;
+
+ h->time_slip = ossl_time_add(h->time_slip, ossl_ms2time(op->arg2));
+
+ CRYPTO_THREAD_unlock(h->time_lock);
+ return 1;
+}
+
static int check_rejected(struct helper *h, const struct script_op *op)
{
uint64_t stream_id = op->arg2;
@@ -246,6 +280,47 @@ static int check_stream_stopped(struct helper *h, const struct script_op *op)
return 1;
}
+static int override_key_update(struct helper *h, const struct script_op *op)
+{
+ QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn);
+
+ ossl_quic_channel_set_txku_threshold_override(ch, op->arg2);
+ 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);
+ int64_t txke = (int64_t)ossl_quic_channel_get_tx_key_epoch(ch);
+ int64_t rxke = (int64_t)ossl_quic_channel_get_rx_key_epoch(ch);
+ int64_t diff = txke - rxke;
+
+ /*
+ * TXKE must always be equal to or ahead of RXKE.
+ * It can be ahead of RXKE by at most 1.
+ */
+ if (!TEST_int64_t_ge(diff, 0) || !TEST_int64_t_le(diff, 1))
+ return 0;
+
+ /* Caller specifies a minimum number of RXKEs which must have happened. */
+ if (!TEST_uint64_t_ge((uint64_t)rxke, op->arg2))
+ return 0;
+
+ return 1;
+}
+
+static int check_key_update_lt(struct helper *h, const struct script_op *op)
+{
+ QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn);
+ uint64_t txke = ossl_quic_channel_get_tx_key_epoch(ch);
+
+ /* Caller specifies a maximum number of TXKEs which must have happened. */
+ if (!TEST_uint64_t_lt(txke, op->arg2))
+ return 0;
+
+ return 1;
+}
+
static unsigned long stream_info_hash(const STREAM_INFO *info)
{
return OPENSSL_LH_strhash(info->name);
@@ -348,6 +423,9 @@ static void helper_cleanup(struct helper *h)
SSL_CTX_free(h->c_ctx);
h->c_ctx = NULL;
+
+ CRYPTO_THREAD_lock_free(h->time_lock);
+ h->time_lock = NULL;
}
static int helper_init(struct helper *h, int free_order)
@@ -360,6 +438,10 @@ static int helper_init(struct helper *h, int free_order)
h->c_fd = -1;
h->s_fd = -1;
h->free_order = free_order;
+ h->time_slip = ossl_time_zero();
+
+ if (!TEST_ptr(h->time_lock = CRYPTO_THREAD_lock_new()))
+ goto err;
if (!TEST_ptr(h->s_streams = lh_STREAM_INFO_new(stream_info_hash,
stream_info_cmp)))
@@ -397,8 +479,10 @@ static int helper_init(struct helper *h, int free_order)
if (!BIO_up_ref(h->s_net_bio))
goto err;
- s_args.net_rbio = h->s_net_bio;
- s_args.net_wbio = h->s_net_bio;
+ s_args.net_rbio = h->s_net_bio;
+ s_args.net_wbio = h->s_net_bio;
+ s_args.now_cb = get_time;
+ s_args.now_cb_arg = h;
if (!TEST_ptr(h->s = ossl_quic_tserver_new(&s_args, certfile, keyfile)))
goto err;
@@ -425,6 +509,10 @@ static int helper_init(struct helper *h, int free_order)
if (!TEST_ptr(h->c_conn = SSL_new(h->c_ctx)))
goto err;
+ /* Use custom time function for virtual time skip. */
+ if (!TEST_true(ossl_quic_conn_set_override_now_cb(h->c_conn, get_time, h)))
+ goto err;
+
/* Takes ownership of our reference to the BIO. */
SSL_set0_rbio(h->c_conn, h->c_net_bio);
h->c_net_bio_own = NULL;
@@ -1776,6 +1864,91 @@ static const struct script_op script_16[] = {
OP_END
};
+/* 17. Key update test - unlimited */
+static const struct script_op script_17[] = {
+ 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 (override_key_update, 1)
+
+ OP_BEGIN_REPEAT (200)
+
+ OP_C_WRITE (DEFAULT, "apple", 5)
+ OP_S_READ_EXPECT (a, "apple", 5)
+
+ /*
+ * TXKU frequency is bounded by RTT because a previous TXKU needs to be
+ * acknowledged by the peer first before another one can be begin. By
+ * waiting this long, we eliminate any such concern and ensure as many key
+ * updates as possible can occur for the purposes of this test.
+ */
+ OP_CHECK (skip_time_ms, 100)
+
+ OP_END_REPEAT ()
+
+ /* At least 5 RXKUs detected */
+ OP_CHECK (check_key_update_ge, 5)
+
+ /*
+ * Prove the connection is still healthy by sending something in both
+ * directions.
+ */
+ OP_C_WRITE (DEFAULT, "xyzzy", 5)
+ OP_S_READ_EXPECT (a, "xyzzy", 5)
+
+ OP_S_WRITE (a, "plugh", 5)
+ OP_C_READ_EXPECT (DEFAULT, "plugh", 5)
+
+ OP_END
+};
+
+/* 18. Key update test - RTT-bounded */
+static const struct script_op script_18[] = {
+ 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 (override_key_update, 1)
+
+ OP_BEGIN_REPEAT (200)
+
+ OP_C_WRITE (DEFAULT, "apple", 5)
+ OP_S_READ_EXPECT (a, "apple", 5)
+ OP_CHECK (skip_time_ms, 2)
+
+ OP_END_REPEAT ()
+
+ /*
+ * This time we simulate far less time passing between writes, so there are
+ * fewer opportunities to initiate TXKUs. Note that we ask for a TXKU every
+ * 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_lt, 120)
+
+ /*
+ * Prove the connection is still healthy by sending something in both
+ * directions.
+ */
+ OP_C_WRITE (DEFAULT, "xyzzy", 5)
+ OP_S_READ_EXPECT (a, "xyzzy", 5)
+
+ OP_S_WRITE (a, "plugh", 5)
+ OP_C_READ_EXPECT (DEFAULT, "plugh", 5)
+
+ OP_END
+};
+
static const struct script_op *const scripts[] = {
script_1,
script_2,
@@ -1793,6 +1966,8 @@ static const struct script_op *const scripts[] = {
script_14,
script_15,
script_16,
+ script_17,
+ script_18,
};
static int test_script(int idx)
diff --git a/test/testutil.h b/test/testutil.h
index 3a3aafc655..033c6f587d 100644
--- a/test/testutil.h
+++ b/test/testutil.h
@@ -471,6 +471,13 @@ void test_perror(const char *s);
# define TEST_ulong_gt(a, b) test_ulong_gt(__FILE__, __LINE__, #a, #b, a, b)
# define TEST_ulong_ge(a, b) test_ulong_ge(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_int64_t_eq(a, b) test_int64_t_eq(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_int64_t_ne(a, b) test_int64_t_ne(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_int64_t_lt(a, b) test_int64_t_lt(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_int64_t_le(a, b) test_int64_t_le(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_int64_t_gt(a, b) test_int64_t_gt(__FILE__, __LINE__, #a, #b, a, b)
+# define TEST_int64_t_ge(a, b) test_int64_t_ge(__FILE__, __LINE__, #a, #b, a, b)
+
# define TEST_uint64_t_eq(a, b) test_uint64_t_eq(__FILE__, __LINE__, #a, #b, a, b)
# define TEST_uint64_t_ne(a, b) test_uint64_t_ne(__FILE__, __LINE__, #a, #b, a, b)
# define TEST_uint64_t_lt(a, b) test_uint64_t_lt(__FILE__, __LINE__, #a, #b, a, b)