Skip to content

Commit

Permalink
Add a test for TLS pipelining
Browse files Browse the repository at this point in the history
TLS pipelining provides the ability for libssl to read or write multiple
records in parallel. It requires special ciphers to do this, and there are
currently no built-in ciphers that provide this capability. However, the
dasync engine does have such a cipher, so we add a test for this capability
using that engine.
  • Loading branch information
dongbeiouba committed Jun 25, 2024
1 parent 894a738 commit ff9f60d
Showing 1 changed file with 203 additions and 0 deletions.
203 changes: 203 additions & 0 deletions test/sslapitest.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <openssl/param_build.h>
#include <openssl/x509v3.h>
#include <openssl/dh.h>
#include <openssl/engine.h>

#include "helpers/ssltestlib.h"
#include "testutil.h"
Expand Down Expand Up @@ -9914,6 +9915,205 @@ static int test_load_dhfile(void)
#endif
}

#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE)
/*
* Test TLSv1.2 with a pipeline capable cipher. TLSv1.3 and DTLS do not
* support this yet. The only pipeline capable cipher that we have is in the
* dasync engine (providers don't support this yet), so we have to use
* deprecated APIs for this test.
*
* Test 0: Client has pipelining enabled, server does not
* Test 1: Server has pipelining enabled, client does not
* Test 2: Client has pipelining enabled, server does not: not enough data to
* fill all the pipelines
* Test 3: Client has pipelining enabled, server does not: not enough data to
* fill all the pipelines by more than a full pipeline's worth
* Test 4: Client has pipelining enabled, server does not: more data than all
* the available pipelines can take
* Test 5: Client has pipelining enabled, server does not: Maximum size pipeline
* Test 6: Repeat of test 0, but the engine is loaded late (after the SSL_CTX
* is created)
*/
static int test_pipelining(int idx)
{
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL, *peera, *peerb;
int testresult = 0, numreads;
/* A 55 byte message */
unsigned char *msg = (unsigned char *)
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123";
size_t written, readbytes, offset, msglen, fragsize = 10, numpipes = 5;
size_t expectedreads;
unsigned char *buf = NULL;
ENGINE *e = NULL;

if (idx != 6) {
e = load_dasync();
if (e == NULL)
return 0;
}

if (!TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
TLS_client_method(), 0,
TLS1_2_VERSION, &sctx, &cctx, cert,
privkey)))
goto end;

if (idx == 6) {
e = load_dasync();
if (e == NULL)
goto end;
/* Now act like test 0 */
idx = 0;
}

if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL)))
goto end;

if (!TEST_true(SSL_set_cipher_list(clientssl, "AES128-SHA")))
goto end;

/* peera is always configured for pipelining, while peerb is not. */
if (idx == 1) {
peera = serverssl;
peerb = clientssl;

} else {
peera = clientssl;
peerb = serverssl;
}

if (idx == 5) {
numpipes = 2;
/* Maximum allowed fragment size */
fragsize = SSL3_RT_MAX_PLAIN_LENGTH;
msglen = fragsize * numpipes;
msg = OPENSSL_malloc(msglen);
if (!TEST_ptr(msg))
goto end;
if (!TEST_int_gt(RAND_bytes_ex(libctx, msg, msglen, 0), 0))
goto end;
} else if (idx == 4) {
msglen = 55;
} else {
msglen = 50;
}
if (idx == 2)
msglen -= 2; /* Send 2 less bytes */
else if (idx == 3)
msglen -= 12; /* Send 12 less bytes */

buf = OPENSSL_malloc(msglen);
if (!TEST_ptr(buf))
goto end;

if (idx == 5) {
/*
* Test that setting a split send fragment longer than the maximum
* allowed fails
*/
if (!TEST_false(SSL_set_split_send_fragment(peera, fragsize + 1)))
goto end;
}

/*
* In the normal case. We have 5 pipelines with 10 bytes per pipeline
* (50 bytes in total). This is a ridiculously small number of bytes -
* but sufficient for our purposes
*/
if (!TEST_true(SSL_set_max_pipelines(peera, numpipes))
|| !TEST_true(SSL_set_split_send_fragment(peera, fragsize)))
goto end;

if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
goto end;

/* Write some data from peera to peerb */
if (!TEST_true(SSL_write_ex(peera, msg, msglen, &written))
|| !TEST_size_t_eq(written, msglen))
goto end;

/*
* If the pipelining code worked, then we expect all |numpipes| pipelines to
* have been used - except in test 3 where only |numpipes - 1| pipelines
* will be used. This will result in |numpipes| records (|numpipes - 1| for
* test 3) having been sent to peerb. Since peerb is not using read_ahead we
* expect this to be read in |numpipes| or |numpipes - 1| separate
* SSL_read_ex calls. In the case of test 4, there is then one additional
* read for left over data that couldn't fit in the previous pipelines
*/
for (offset = 0, numreads = 0;
offset < msglen;
offset += readbytes, numreads++) {
if (!TEST_true(SSL_read_ex(peerb, buf + offset,
msglen - offset, &readbytes)))
goto end;
}

expectedreads = idx == 4 ? numpipes + 1
: (idx == 3 ? numpipes - 1 : numpipes);
if (!TEST_mem_eq(msg, msglen, buf, offset)
|| !TEST_int_eq(numreads, expectedreads))
goto end;

/*
* Write some data from peerb to peera. We do this in up to |numpipes + 1|
* chunks to exercise the read pipelining code on peera.
*/
for (offset = 0; offset < msglen; offset += fragsize) {
size_t sendlen = msglen - offset;

if (sendlen > fragsize)
sendlen = fragsize;
if (!TEST_true(SSL_write_ex(peerb, msg + offset, sendlen, &written))
|| !TEST_size_t_eq(written, sendlen))
goto end;
}

/*
* The data was written in |numpipes|, |numpipes - 1| or |numpipes + 1|
* separate chunks (depending on which test we are running). If the
* pipelining is working then we expect peera to read up to numpipes chunks
* and process them in parallel, giving back the complete result in a single
* call to SSL_read_ex
*/
if (!TEST_true(SSL_read_ex(peera, buf, msglen, &readbytes))
|| !TEST_size_t_le(readbytes, msglen))
goto end;

if (idx == 4) {
size_t readbytes2;

if (!TEST_true(SSL_read_ex(peera, buf + readbytes,
msglen - readbytes, &readbytes2)))
goto end;
readbytes += readbytes2;
if (!TEST_size_t_le(readbytes, msglen))
goto end;
}

if (!TEST_mem_eq(msg, msglen, buf, readbytes))
goto end;

testresult = 1;
end:
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
if (e != NULL) {
ENGINE_unregister_ciphers(e);
ENGINE_finish(e);
ENGINE_free(e);
}
OPENSSL_free(buf);
if (fragsize == SSL3_RT_MAX_PLAIN_LENGTH)
OPENSSL_free(msg);
return testresult;
}
#endif /* !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE) */

#ifndef OPENSSL_NO_CERT_COMPRESSION
#define CERT_COMPRESSION_XOR 16384
#define CERT_COMPRESSION_ROL 65535
Expand Down Expand Up @@ -10604,6 +10804,9 @@ int setup_tests(void)
ADD_ALL_TESTS(test_session_cache_overflow, 4);
#endif
ADD_TEST(test_load_dhfile);
#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE)
ADD_ALL_TESTS(test_pipelining, 7);
#endif
ADD_ALL_TESTS(test_multi_resume, 5);
return 1;

Expand Down

0 comments on commit ff9f60d

Please sign in to comment.