shim: move handshake helper functions into their own file. To wit, |RetryAsync| and |CheckIdempotentError|. This helps with creating a separate binary to perform split handshakes. Separate handshake utilities Change-Id: I81d0bc38f58e7e1a92b58bf09407452b345213b4 Reviewed-on: https://boringssl-review.googlesource.com/29346 Commit-Queue: Matt Braithwaite <mab@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/test/CMakeLists.txt b/ssl/test/CMakeLists.txt index 2fdee73..ca2cd38 100644 --- a/ssl/test/CMakeLists.txt +++ b/ssl/test/CMakeLists.txt
@@ -5,6 +5,7 @@ async_bio.cc bssl_shim.cc + handshake_util.cc packeted_bio.cc settings_writer.cc test_config.cc
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index f73fc69..275b4b4 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc
@@ -64,6 +64,7 @@ #include "../../crypto/internal.h" #include "../internal.h" #include "async_bio.h" +#include "handshake_util.h" #include "packeted_bio.h" #include "settings_writer.h" #include "test_config.h" @@ -187,97 +188,6 @@ const int sock_; }; -// RetryAsync is called after a failed operation on |ssl| with return code -// |ret|. If the operation should be retried, it simulates one asynchronous -// event and returns true. Otherwise it returns false. -static bool RetryAsync(SSL *ssl, int ret) { - // No error; don't retry. - if (ret >= 0) { - return false; - } - - TestState *test_state = GetTestState(ssl); - assert(GetTestConfig(ssl)->async); - - if (test_state->packeted_bio != nullptr && - PacketedBioAdvanceClock(test_state->packeted_bio)) { - // The DTLS retransmit logic silently ignores write failures. So the test - // may progress, allow writes through synchronously. - AsyncBioEnforceWriteQuota(test_state->async_bio, false); - int timeout_ret = DTLSv1_handle_timeout(ssl); - AsyncBioEnforceWriteQuota(test_state->async_bio, true); - - if (timeout_ret < 0) { - fprintf(stderr, "Error retransmitting.\n"); - return false; - } - return true; - } - - // See if we needed to read or write more. If so, allow one byte through on - // the appropriate end to maximally stress the state machine. - switch (SSL_get_error(ssl, ret)) { - case SSL_ERROR_WANT_READ: - AsyncBioAllowRead(test_state->async_bio, 1); - return true; - case SSL_ERROR_WANT_WRITE: - AsyncBioAllowWrite(test_state->async_bio, 1); - return true; - case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: { - bssl::UniquePtr<EVP_PKEY> pkey = - LoadPrivateKey(GetTestConfig(ssl)->send_channel_id); - if (!pkey) { - return false; - } - test_state->channel_id = std::move(pkey); - return true; - } - case SSL_ERROR_WANT_X509_LOOKUP: - test_state->cert_ready = true; - return true; - case SSL_ERROR_PENDING_SESSION: - test_state->session = std::move(test_state->pending_session); - return true; - case SSL_ERROR_PENDING_CERTIFICATE: - test_state->early_callback_ready = true; - return true; - case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: - test_state->private_key_retries++; - return true; - case SSL_ERROR_WANT_CERTIFICATE_VERIFY: - test_state->custom_verify_ready = true; - return true; - default: - return false; - } -} - -// CheckIdempotentError runs |func|, an operation on |ssl|, ensuring that -// errors are idempotent. -static int CheckIdempotentError(const char *name, SSL *ssl, - std::function<int()> func) { - int ret = func(); - int ssl_err = SSL_get_error(ssl, ret); - uint32_t err = ERR_peek_error(); - if (ssl_err == SSL_ERROR_SSL || ssl_err == SSL_ERROR_ZERO_RETURN) { - int ret2 = func(); - int ssl_err2 = SSL_get_error(ssl, ret2); - uint32_t err2 = ERR_peek_error(); - if (ret != ret2 || ssl_err != ssl_err2 || err != err2) { - fprintf(stderr, "Repeating %s did not replay the error.\n", name); - char buf[256]; - ERR_error_string_n(err, buf, sizeof(buf)); - fprintf(stderr, "Wanted: %d %d %s\n", ret, ssl_err, buf); - ERR_error_string_n(err2, buf, sizeof(buf)); - fprintf(stderr, "Got: %d %d %s\n", ret2, ssl_err2, buf); - // runner treats exit code 90 as always failing. Otherwise, it may - // accidentally consider the result an expected protocol failure. - exit(90); - } - } - return ret; -} - // DoRead reads from |ssl|, resolving any asynchronous operations. It returns // the result value of the final |SSL_read| call. static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) {
diff --git a/ssl/test/handshake_util.cc b/ssl/test/handshake_util.cc new file mode 100644 index 0000000..b3d39f4 --- /dev/null +++ b/ssl/test/handshake_util.cc
@@ -0,0 +1,112 @@ +/* Copyright (c) 2018, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "handshake_util.h" + +#include <assert.h> + +#include <functional> + +#include "async_bio.h" +#include "packeted_bio.h" +#include "test_config.h" +#include "test_state.h" + +#include <openssl/ssl.h> + +bool RetryAsync(SSL *ssl, int ret) { + // No error; don't retry. + if (ret >= 0) { + return false; + } + + TestState *test_state = GetTestState(ssl); + assert(GetTestConfig(ssl)->async); + + if (test_state->packeted_bio != nullptr && + PacketedBioAdvanceClock(test_state->packeted_bio)) { + // The DTLS retransmit logic silently ignores write failures. So the test + // may progress, allow writes through synchronously. + AsyncBioEnforceWriteQuota(test_state->async_bio, false); + int timeout_ret = DTLSv1_handle_timeout(ssl); + AsyncBioEnforceWriteQuota(test_state->async_bio, true); + + if (timeout_ret < 0) { + fprintf(stderr, "Error retransmitting.\n"); + return false; + } + return true; + } + + // See if we needed to read or write more. If so, allow one byte through on + // the appropriate end to maximally stress the state machine. + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_READ: + AsyncBioAllowRead(test_state->async_bio, 1); + return true; + case SSL_ERROR_WANT_WRITE: + AsyncBioAllowWrite(test_state->async_bio, 1); + return true; + case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: { + bssl::UniquePtr<EVP_PKEY> pkey = + LoadPrivateKey(GetTestConfig(ssl)->send_channel_id); + if (!pkey) { + return false; + } + test_state->channel_id = std::move(pkey); + return true; + } + case SSL_ERROR_WANT_X509_LOOKUP: + test_state->cert_ready = true; + return true; + case SSL_ERROR_PENDING_SESSION: + test_state->session = std::move(test_state->pending_session); + return true; + case SSL_ERROR_PENDING_CERTIFICATE: + test_state->early_callback_ready = true; + return true; + case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: + test_state->private_key_retries++; + return true; + case SSL_ERROR_WANT_CERTIFICATE_VERIFY: + test_state->custom_verify_ready = true; + return true; + default: + return false; + } +} + +int CheckIdempotentError(const char *name, SSL *ssl, + std::function<int()> func) { + int ret = func(); + int ssl_err = SSL_get_error(ssl, ret); + uint32_t err = ERR_peek_error(); + if (ssl_err == SSL_ERROR_SSL || ssl_err == SSL_ERROR_ZERO_RETURN) { + int ret2 = func(); + int ssl_err2 = SSL_get_error(ssl, ret2); + uint32_t err2 = ERR_peek_error(); + if (ret != ret2 || ssl_err != ssl_err2 || err != err2) { + fprintf(stderr, "Repeating %s did not replay the error.\n", name); + char buf[256]; + ERR_error_string_n(err, buf, sizeof(buf)); + fprintf(stderr, "Wanted: %d %d %s\n", ret, ssl_err, buf); + ERR_error_string_n(err2, buf, sizeof(buf)); + fprintf(stderr, "Got: %d %d %s\n", ret2, ssl_err2, buf); + // runner treats exit code 90 as always failing. Otherwise, it may + // accidentally consider the result an expected protocol failure. + exit(90); + } + } + return ret; +}
diff --git a/ssl/test/handshake_util.h b/ssl/test/handshake_util.h new file mode 100644 index 0000000..2798695 --- /dev/null +++ b/ssl/test/handshake_util.h
@@ -0,0 +1,31 @@ +/* Copyright (c) 2018, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef HEADER_TEST_HANDSHAKE +#define HEADER_TEST_HANDSHAKE + +#include <functional> + +#include <openssl/base.h> + +// RetryAsync is called after a failed operation on |ssl| with return code +// |ret|. If the operation should be retried, it simulates one asynchronous +// event and returns true. Otherwise it returns false. +bool RetryAsync(SSL *ssl, int ret); + +// CheckIdempotentError runs |func|, an operation on |ssl|, ensuring that +// errors are idempotent. +int CheckIdempotentError(const char *name, SSL *ssl, std::function<int()> func); + +#endif // HEADER_TEST_HANDSHAKE