summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2022-10-20 16:36:37 +0100
committerTomas Mraz <tomas@openssl.org>2022-11-02 11:11:40 +0100
commitb718f6fcc4bbf48cfc6ab3fa64e6cb95453299b3 (patch)
treefa9a1e26889f737a5516e671fd8cf0e85a38b5f9
parenta88e97fcace01ecf557b207f04328a72df5110df (diff)
Add a test for TLS pipelining
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. Reviewed-by: Hugo Landau <hlandau@openssl.org> Reviewed-by: Todd Short <todd.short@me.com> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/19456)
-rw-r--r--ssl/record/methods/tls_common.c4
-rw-r--r--test/sslapitest.c196
2 files changed, 196 insertions, 4 deletions
diff --git a/ssl/record/methods/tls_common.c b/ssl/record/methods/tls_common.c
index 08dfca82e1..a7b31163c5 100644
--- a/ssl/record/methods/tls_common.c
+++ b/ssl/record/methods/tls_common.c
@@ -1449,10 +1449,6 @@ size_t tls_get_max_records_default(OSSL_RECORD_LAYER *rl, int type, size_t len,
size_t maxfrag, size_t *preffrag)
{
/*
- * TODO(RECLAYER): There is no test for the pipelining code. We should add
- * one.
- */
- /*
* If we have a pipeline capable cipher, and we have been configured to use
* it, then return the preferred number of pipelines.
*/
diff --git a/test/sslapitest.c b/test/sslapitest.c
index 7d3432de28..57ff080dd5 100644
--- a/test/sslapitest.c
+++ b/test/sslapitest.c
@@ -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"
@@ -10150,6 +10151,198 @@ end:
}
#endif /* OSSL_NO_USABLE_TLS1_3 */
+#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
+ */
+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;
+
+ if (!TEST_ptr(e = ENGINE_by_id("dasync")))
+ return 0;
+
+ if (!TEST_true(ENGINE_init(e))) {
+ ENGINE_free(e);
+ return 0;
+ }
+
+ if (!TEST_true(ENGINE_register_ciphers(e)))
+ goto end;
+
+ 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 (!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);
+ ENGINE_unregister_ciphers(e);
+ ENGINE_finish(e);
+ ENGINE_free(e);
+ OPENSSL_free(buf);
+ if (idx == 5)
+ OPENSSL_free(msg);
+ return testresult;
+}
+#endif /* !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE) */
+
OPT_TEST_DECLARE_USAGE("certfile privkeyfile srpvfile tmpfile provider config dhfile\n")
int setup_tests(void)
@@ -10426,6 +10619,9 @@ int setup_tests(void)
#if !defined(OPENSSL_NO_TLS1_2) && !defined(OSSL_NO_USABLE_TLS1_3)
ADD_ALL_TESTS(test_serverinfo_custom, 4);
#endif
+#if !defined(OPENSSL_NO_TLS1_2) && !defined(OPENSSL_NO_DYNAMIC_ENGINE)
+ ADD_ALL_TESTS(test_pipelining, 6);
+#endif
return 1;
err: