diff --git a/test/sslapitest.c b/test/sslapitest.c index f891e646d..aa65d2cd4 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "helpers/ssltestlib.h" #include "testutil.h" @@ -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 @@ -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;