diff options
author | Matt Caswell <matt@openssl.org> | 2022-06-17 16:37:24 +0100 |
---|---|---|
committer | Tomas Mraz <tomas@openssl.org> | 2022-06-28 17:13:49 +0200 |
commit | e1c153d31d4f913ebe2202a4bc20305919274d1f (patch) | |
tree | ea9a6637b01df328b5ab0b8b9847534d4bdeeec9 | |
parent | 5f7d4e9111dcd2a91429ecab807c4f282164ea46 (diff) | |
download | openssl-e1c153d31d4f913ebe2202a4bc20305919274d1f.tar.gz |
Add a DTLS next epoch test
Test that if we receive a packet from the next epoch, we can buffer it
and still use it.
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18601)
-rw-r--r-- | test/dtlstest.c | 75 | ||||
-rw-r--r-- | test/helpers/ssltestlib.c | 83 | ||||
-rw-r--r-- | test/helpers/ssltestlib.h | 1 |
3 files changed, 159 insertions, 0 deletions
diff --git a/test/dtlstest.c b/test/dtlstest.c index 2f3fcae0f6..e2359addbf 100644 --- a/test/dtlstest.c +++ b/test/dtlstest.c @@ -442,6 +442,80 @@ static int test_just_finished(void) return testresult; } +/* + * Test that swapping a record from the next epoch into the current epoch still + * works. Libssl should buffer the record until it needs it. + */ +static int test_swap_epoch(void) +{ + SSL_CTX *sctx = NULL, *cctx = NULL; + SSL *sssl = NULL, *cssl = NULL; + int testresult = 0; + BIO *bio; + + if (!TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(), + DTLS_client_method(), + DTLS1_VERSION, 0, + &sctx, &cctx, cert, privkey))) + return 0; + +#ifndef OPENSSL_NO_DTLS1_2 + if (!TEST_true(SSL_CTX_set_cipher_list(cctx, "AES128-SHA"))) + goto end; +#else + /* Default sigalgs are SHA1 based in <DTLS1.2 which is in security level 0 */ + if (!TEST_true(SSL_CTX_set_cipher_list(sctx, "AES128-SHA:@SECLEVEL=0")) + || !TEST_true(SSL_CTX_set_cipher_list(cctx, + "AES128-SHA:@SECLEVEL=0"))) + goto end; +#endif + + + /* BIO is freed by create_ssl_connection on error */ + if (!TEST_true(create_ssl_objects(sctx, cctx, &sssl, &cssl, + NULL, NULL))) + goto end; + + /* Send flight 1: ClientHello */ + if (!TEST_int_le(SSL_connect(cssl), 0)) + goto end; + + /* Recv flight 1, send flight 2: ServerHello, Certificate, ServerHelloDone */ + if (!TEST_int_le(SSL_accept(sssl), 0)) + goto end; + + /* Recv flight 2, send flight 3: ClientKeyExchange, CCS, Finished */ + if (!TEST_int_le(SSL_connect(cssl), 0)) + goto end; + + bio = SSL_get_wbio(cssl); + if (!TEST_ptr(bio) + || !TEST_true(mempacket_swap_epoch(bio))) + goto end; + + /* + * Recv flight 3, send flight 4: CCS, Finished. + * This should work in a single call because we have all the messages we + * need. Even though we had out of order packets, the record for the next + * epoch that we read early should be buffered and used later. + */ + if (!TEST_int_gt(SSL_accept(sssl), 0)) + goto end; + + + /* Recv flight 4 */ + if (!TEST_int_gt(SSL_connect(cssl), 0)) + goto end; + + testresult = 1; + end: + SSL_free(cssl); + SSL_free(sssl); + SSL_CTX_free(cctx); + SSL_CTX_free(sctx); + return testresult; +} + OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n") int setup_tests(void) @@ -462,6 +536,7 @@ int setup_tests(void) ADD_TEST(test_cookie); ADD_TEST(test_dtls_duplicate_records); ADD_TEST(test_just_finished); + ADD_TEST(test_swap_epoch); return 1; } diff --git a/test/helpers/ssltestlib.c b/test/helpers/ssltestlib.c index 86be8ed00f..f4f5b5049d 100644 --- a/test/helpers/ssltestlib.c +++ b/test/helpers/ssltestlib.c @@ -406,10 +406,93 @@ static int mempacket_test_read(BIO *bio, char *out, int outl) } memcpy(out, thispkt->data, outl); + mempacket_free(thispkt); return outl; } +/* + * Look for records from different epochs and swap them around + */ +int mempacket_swap_epoch(BIO *bio) +{ + MEMPACKET_TEST_CTX *ctx = BIO_get_data(bio); + MEMPACKET *thispkt; + int rem, len, prevlen = 0, pktnum; + unsigned char *rec, *prevrec = NULL, *tmp; + unsigned int epoch; + int numpkts = sk_MEMPACKET_num(ctx->pkts); + + if (numpkts <= 0) + return 0; + + /* + * If there are multiple packets we only look in the last one. This should + * always be the one where any epoch change occurs. + */ + thispkt = sk_MEMPACKET_value(ctx->pkts, numpkts - 1); + if (thispkt == NULL) + return 0; + + for (rem = thispkt->len, rec = thispkt->data; rem > 0; rem -= len, rec += len) { + if (rem < DTLS1_RT_HEADER_LENGTH) + return 0; + epoch = (rec[EPOCH_HI] << 8) | rec[EPOCH_LO]; + len = ((rec[RECORD_LEN_HI] << 8) | rec[RECORD_LEN_LO]) + + DTLS1_RT_HEADER_LENGTH; + if (rem < len) + return 0; + + /* Assumes the epoch change does not happen on the first record */ + if (epoch != ctx->epoch) { + if (prevrec == NULL) + return 0; + + /* + * We found 2 records with different epochs. Take a copy of the + * earlier record + */ + tmp = OPENSSL_malloc(prevlen); + if (tmp == NULL) + return 0; + + memcpy(tmp, prevrec, prevlen); + /* + * Move everything from this record onwards, including any trailing + * records, and overwrite the earlier record + */ + memmove(prevrec, rec, rem); + thispkt->len -= prevlen; + pktnum = thispkt->num; + + /* + * Create a new packet for the earlier record that we took out and + * add it to the end of the packet list. + */ + thispkt = OPENSSL_malloc(sizeof(*thispkt)); + if (thispkt == NULL) { + OPENSSL_free(tmp); + return 0; + } + thispkt->type = INJECT_PACKET; + thispkt->data = tmp; + thispkt->len = prevlen; + thispkt->num = pktnum + 1; + if (sk_MEMPACKET_insert(ctx->pkts, thispkt, numpkts) <= 0) { + OPENSSL_free(tmp); + OPENSSL_free(thispkt); + return 0; + } + + return 1; + } + prevrec = rec; + prevlen = len; + } + + return 0; +} + int mempacket_test_inject(BIO *bio, const char *in, int inl, int pktnum, int type) { diff --git a/test/helpers/ssltestlib.h b/test/helpers/ssltestlib.h index 0466286365..ee144e2f25 100644 --- a/test/helpers/ssltestlib.h +++ b/test/helpers/ssltestlib.h @@ -49,6 +49,7 @@ void bio_s_always_retry_free(void); #define MEMPACKET_CTRL_GET_DROP_REC (3 << 15) #define MEMPACKET_CTRL_SET_DUPLICATE_REC (4 << 15) +int mempacket_swap_epoch(BIO *bio); int mempacket_test_inject(BIO *bio, const char *in, int inl, int pktnum, int type); |