From 6acdd3e531e52173a0c76490c0aae3ecebe04a89 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Fri, 20 Jan 2017 16:22:39 +0000 Subject: [PATCH] Add tests for the key logging callbacks. Reviewed-by: Rich Salz Reviewed-by: Richard Levitte (Merged from https://github.com/openssl/openssl/pull/1646) --- test/sslapitest.c | 342 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) diff --git a/test/sslapitest.c b/test/sslapitest.c index 4a84f4db44..ac065b281c 100644 --- a/test/sslapitest.c +++ b/test/sslapitest.c @@ -23,6 +23,13 @@ static char *cert = NULL; static char *privkey = NULL; +#define LOG_BUFFER_SIZE 1024 +static char server_log_buffer[LOG_BUFFER_SIZE + 1] = {0}; +static int server_log_buffer_index = 0; +static char client_log_buffer[LOG_BUFFER_SIZE + 1] = {0}; +static int client_log_buffer_index = 0; +static int error_writing_log = 0; + #ifndef OPENSSL_NO_OCSP static const unsigned char orespder[] = "Dummy OCSP Response"; static int ocsp_server_called = 0; @@ -34,6 +41,337 @@ static X509 *ocspcert = NULL; #define NUM_EXTRA_CERTS 40 +static void client_keylog_callback(const SSL *ssl, const char *line) { + int line_length = strlen(line); + + /* If the log doesn't fit, error out. */ + if ((client_log_buffer_index + line_length) > LOG_BUFFER_SIZE) { + printf("No room in client log\n"); + error_writing_log = 1; + return; + } + + strcat(client_log_buffer, line); + client_log_buffer_index += line_length; + client_log_buffer[client_log_buffer_index] = '\n'; + client_log_buffer_index += 1; + + return; +} + +static void server_keylog_callback(const SSL *ssl, const char *line) { + int line_length = strlen(line); + + /* If the log doesn't fit, error out. */ + if ((server_log_buffer_index + line_length) > LOG_BUFFER_SIZE) { + printf("No room in server log\n"); + error_writing_log = 1; + return; + } + + strcat(server_log_buffer, line); + server_log_buffer_index += line_length; + server_log_buffer[server_log_buffer_index] = '\n'; + server_log_buffer_index += 1; + + return; +} + +static int compare_hex_encoded_buffer(const char *hex_encoded, + size_t hex_length, + const uint8_t *raw, + size_t raw_length) { + size_t i; + size_t j; + + /* One byte too big, just to be safe. */ + char hexed[3] = {0}; + + if ((raw_length * 2) != hex_length) { + printf("Inconsistent hex encoded lengths.\n"); + return 1; + } + + for (i = j = 0; (i < raw_length) && ((j + 1) < hex_length); i++) { + sprintf(hexed, "%02x", raw[i]); + if ((hexed[0] != hex_encoded[j]) || (hexed[1] != hex_encoded[j + 1])) { + printf("Hex output does not match.\n"); + return 1; + } + j += 2; + } + + return 0; +} + +static int test_keylog_output(char *buffer, const SSL *ssl, + const SSL_SESSION *session) { + int saw_client_random = 0; + char *token = NULL; + unsigned char actual_client_random[SSL3_RANDOM_SIZE] = {0}; + size_t client_random_size = SSL3_RANDOM_SIZE; + unsigned char actual_master_key[SSL_MAX_MASTER_KEY_LENGTH] = {0}; + size_t master_key_size = SSL_MAX_MASTER_KEY_LENGTH; + + token = strtok(buffer, " \n"); + while (token) { + if (strcmp(token, "RSA") == 0) { + /* Premaster secret. Tokens should be: 16 ASCII bytes of + * hex-encoded encrypted secret, then the hex-encoded pre-master + * secret. + */ + token = strtok(NULL, " \n"); + if (!token) { + printf("Unexpectedly short premaster secret log.\n"); + return -1; + } + if (strlen(token) != 16) { + printf("Bad value for encrypted secret: %s\n", token); + return -1; + } + token = strtok(NULL, " \n"); + if (!token) { + printf("Unexpectedly short premaster secret log.\n"); + return -1; + } + /* TODO: Can I check this sensibly? */ + } else if (strcmp(token, "CLIENT_RANDOM") == 0) { + /* Master secret. Tokens should be: 64 ASCII bytes of hex-encoded + * client random, then the hex-encoded master secret. + */ + client_random_size = SSL_get_client_random(ssl, + actual_client_random, + SSL3_RANDOM_SIZE); + if (client_random_size != SSL3_RANDOM_SIZE) { + printf("Unexpected short client random.\n"); + return -1; + } + + token = strtok(NULL, " \n"); + if (!token) { + printf("Unexpected short master secret log.\n"); + return -1; + } + if (strlen(token) != 64) { + printf("Bad value for client random: %s\n", token); + return -1; + } + if (compare_hex_encoded_buffer(token, 64, actual_client_random, + client_random_size)) { + printf("Bad value for client random: %s\n", token); + return -1; + } + + token = strtok(NULL, " \n"); + if (!token) { + printf("Unexpectedly short master secret log.\n"); + return -1; + } + + master_key_size = SSL_SESSION_get_master_key(session, + actual_master_key, + master_key_size); + if (!master_key_size) { + printf("Error getting master key to compare.\n"); + return -1; + } + if (compare_hex_encoded_buffer(token, strlen(token), + actual_master_key, + master_key_size)) { + printf("Bad value for master key: %s\n", token); + return -1; + } + + saw_client_random = 1; + } else { + printf("Unexpected token in buffer: %s\n", token); + return -1; + } + + token = strtok(NULL, " \n"); + } + + return saw_client_random; +} + +static int test_keylog(void) { + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *clientssl = NULL, *serverssl = NULL; + int testresult = 0; + + /* Clean up logging space */ + memset(client_log_buffer, 0, LOG_BUFFER_SIZE + 1); + memset(server_log_buffer, 0, LOG_BUFFER_SIZE + 1); + client_log_buffer_index = 0; + server_log_buffer_index = 0; + error_writing_log = 0; + + if (!create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(), &sctx, + &cctx, cert, privkey)) { + printf("Unable to create SSL_CTX pair\n"); + return 0; + } + + /* We cannot log the master secret for TLSv1.3, so we should forbid it. */ + SSL_CTX_set_options(cctx, SSL_OP_NO_TLSv1_3); + SSL_CTX_set_options(sctx, SSL_OP_NO_TLSv1_3); + + if (SSL_CTX_get_keylog_callback(cctx)) { + printf("Unexpected initial value for client " + "SSL_CTX_get_keylog_callback()\n"); + goto end; + } + if (SSL_CTX_get_keylog_callback(sctx)) { + printf("Unexpected initial value for server " + "SSL_CTX_get_keylog_callback()\n"); + goto end; + } + + SSL_CTX_set_keylog_callback(cctx, client_keylog_callback); + SSL_CTX_set_keylog_callback(sctx, server_keylog_callback); + + if (SSL_CTX_get_keylog_callback(cctx) != client_keylog_callback) { + printf("Unexpected set value for client " + "SSL_CTX_get_keylog_callback()\n"); + } + + if (SSL_CTX_get_keylog_callback(sctx) != server_keylog_callback) { + printf("Unexpected set value for server " + "SSL_CTX_get_keylog_callback()\n"); + } + + /* Now do a handshake and check that the logs have been written to. */ + if (!create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) { + printf("Unable to create SSL objects\n"); + goto end; + } + + if (!create_ssl_connection(serverssl, clientssl)) { + printf("Unable to create SSL connection\n"); + goto end; + } + + if (error_writing_log) { + printf("Error encountered while logging\n"); + goto end; + } + + if ((client_log_buffer_index == 0) || (server_log_buffer_index == 0)) { + printf("No logs written\n"); + goto end; + } + + /* Now we want to test that our output data was vaguely sensible. We + * do that by using strtok and confirming that we have more or less the + * data we expect. + */ + if (test_keylog_output(client_log_buffer, clientssl, + SSL_get_session(clientssl)) != 1) { + printf("Error encountered in client log buffer\n"); + goto end; + } + if (test_keylog_output(server_log_buffer, serverssl, + SSL_get_session(serverssl)) != 1) { + printf("Error encountered in server log buffer\n"); + goto end; + } + + testresult = 1; + +end: + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + + return testresult; +} + +#ifndef OPENSSL_NO_TLS1_3 +static int test_keylog_no_master_key(void) { + SSL_CTX *cctx = NULL, *sctx = NULL; + SSL *clientssl = NULL, *serverssl = NULL; + int testresult = 0; + + /* Clean up logging space */ + memset(client_log_buffer, 0, LOG_BUFFER_SIZE + 1); + memset(server_log_buffer, 0, LOG_BUFFER_SIZE + 1); + client_log_buffer_index = 0; + server_log_buffer_index = 0; + error_writing_log = 0; + + if (!create_ssl_ctx_pair(TLS_server_method(), TLS_client_method(), &sctx, + &cctx, cert, privkey)) { + printf("Unable to create SSL_CTX pair\n"); + return 0; + } + + if (SSL_CTX_get_keylog_callback(cctx)) { + printf("Unexpected initial value for client " + "SSL_CTX_get_keylog_callback()\n"); + goto end; + } + if (SSL_CTX_get_keylog_callback(sctx)) { + printf("Unexpected initial value for server " + "SSL_CTX_get_keylog_callback()\n"); + goto end; + } + + SSL_CTX_set_keylog_callback(cctx, client_keylog_callback); + SSL_CTX_set_keylog_callback(sctx, server_keylog_callback); + + if (SSL_CTX_get_keylog_callback(cctx) != client_keylog_callback) { + printf("Unexpected set value for client " + "SSL_CTX_get_keylog_callback()\n"); + } + + if (SSL_CTX_get_keylog_callback(sctx) != server_keylog_callback) { + printf("Unexpected set value for server " + "SSL_CTX_get_keylog_callback()\n"); + } + + /* Now do a handshake and check that the logs have been written to. */ + if (!create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) { + printf("Unable to create SSL objects\n"); + goto end; + } + + if (!create_ssl_connection(serverssl, clientssl)) { + printf("Unable to create SSL connection\n"); + goto end; + } + + if (error_writing_log) { + printf("Error encountered while logging\n"); + goto end; + } + + /* Now we want to test that our output data was vaguely sensible. For this + * test, we expect no CLIENT_RANDOM entry. + */ + if (test_keylog_output(client_log_buffer, clientssl, + SSL_get_session(clientssl)) != 0) { + printf("Error encountered in client log buffer\n"); + goto end; + } + if (test_keylog_output(server_log_buffer, serverssl, + SSL_get_session(serverssl)) != 0) { + printf("Error encountered in server log buffer\n"); + goto end; + } + + testresult = 1; + +end: + SSL_free(serverssl); + SSL_free(clientssl); + SSL_CTX_free(sctx); + SSL_CTX_free(cctx); + + return testresult; +} +#endif + static int execute_test_large_message(const SSL_METHOD *smeth, const SSL_METHOD *cmeth, int read_ahead) { @@ -1044,6 +1382,10 @@ int test_main(int argc, char *argv[]) ADD_TEST(test_ssl_bio_change_rbio); ADD_TEST(test_ssl_bio_change_wbio); ADD_ALL_TESTS(test_set_sigalgs, OSSL_NELEM(testsigalgs) * 2); + ADD_TEST(test_keylog); +#ifndef OPENSSL_NO_TLS1_3 + ADD_TEST(test_keylog_no_master_key); +#endif testresult = run_tests(argv[0]); -- 2.40.0