diff --git a/tests/saw/README.md b/tests/saw/README.md index c33d5b3e605..4bc5a174296 100644 --- a/tests/saw/README.md +++ b/tests/saw/README.md @@ -1,11 +1,11 @@ -#SAW tests for s2n +# SAW tests for s2n This repository contains specifications of the various parts of the HMAC algorithm used in TLS along with SAW scripts to prove the s2n implementation of this algorithm equivalent to the spec. -##The tests +## The tests Currently this directory houses a test that compares the s2n implementation of HMAC with a cryptol spec of the same. There are 3 @@ -37,7 +37,7 @@ instructions for how automated solvers can be used to prove this equivalence. When run with the command `saw HMAC.saw`, this file loads in the other two and proves the HMAC files equivalent. -##The build +## The build Running the saw tests will require a SAW executable, which must be able to find the Z3 prover on the path. Future examples might require diff --git a/tests/saw/s2n_handshake_io.saw b/tests/saw/s2n_handshake_io.saw index 08c5e36a341..780883a5fd6 100644 --- a/tests/saw/s2n_handshake_io.saw +++ b/tests/saw/s2n_handshake_io.saw @@ -111,7 +111,8 @@ let setup_connection = do { ocsp_flag <- crucible_fresh_var "ocsp_flag" (llvm_int 32); crucible_points_to (conn_status_type pconn) (crucible_term ocsp_flag); - + let client_cert_auth_type = {{ if cca_ov != 0 then cca else config_cca }}; + return (pconn, {{ {corked_io = corked_io ,mode = mode ,handshake = {message_number = message_number @@ -123,8 +124,9 @@ let setup_connection = do { ((ocsp_flag == 1) && (status_size > 0)) || ((mode == 1) && (ocsp_flag == 1)) ,resume_from_cache = False - ,client_auth_flag = (if cca_ov != 0 then cca != 0 else config_cca != 0) - } + ,client_auth_flag = if mode == S2N_CLIENT then client_cert_auth_type == 1 else + if mode == S2N_SERVER then client_cert_auth_type != 0 else False + } }}); }; diff --git a/tests/unit/s2n_optional_client_auth_test.c b/tests/unit/s2n_optional_client_auth_test.c index 30da50ab47d..445f8c26c5b 100644 --- a/tests/unit/s2n_optional_client_auth_test.c +++ b/tests/unit/s2n_optional_client_auth_test.c @@ -164,6 +164,77 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_config_free(client_config)); + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with S2N_CERT_AUTH_NONE for Server + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(client_config, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server does not request a Client Cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(server_config)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + int client_to_server[2]; + int server_to_client[2]; + + /* Craft a cipher preference with a cipher_idx cipher. */ + memcpy(&server_cipher_preferences, default_cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + struct s2n_cipher_suite *cur_cipher = default_cipher_preferences->suites[cipher_idx]; + + if (!cur_cipher->available) { + /* Skip ciphers that aren't supported with the linked libcrypto. */ + continue; + } + + server_cipher_preferences.suites = &cur_cipher; + client_config->cipher_preferences = &server_cipher_preferences; + server_config->cipher_preferences = &server_cipher_preferences; + + /* Create nonblocking pipes. */ + EXPECT_SUCCESS(pipe(client_to_server)); + EXPECT_SUCCESS(pipe(server_to_client)); + for (int i = 0; i < 2; i++) { + EXPECT_NOT_EQUAL(fcntl(client_to_server[i], F_SETFL, fcntl(client_to_server[i], F_GETFL) | O_NONBLOCK), -1); + EXPECT_NOT_EQUAL(fcntl(server_to_client[i], F_SETFL, fcntl(server_to_client[i], F_GETFL) | O_NONBLOCK), -1); + } + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_read_fd(client_conn, server_to_client[0])); + EXPECT_SUCCESS(s2n_connection_set_write_fd(client_conn, client_to_server[1])); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_read_fd(server_conn, client_to_server[0])); + EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, server_to_client[1])); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(try_handshake(server_conn, client_conn)); + + /* Verify that neither connections negotiated mutual auth. */ + EXPECT_FALSE(s2n_connection_client_cert_used(server_conn)); + EXPECT_FALSE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + for (int i = 0; i < 2; i++) { + EXPECT_SUCCESS(close(server_to_client[i])); + EXPECT_SUCCESS(close(client_to_server[i])); + } + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + /* * Test optional client auth using **s2n_config_set_client_auth_type** with no client cert provided. diff --git a/tls/s2n_handshake_io.c b/tls/s2n_handshake_io.c index 40a8fb069b1..fc3f499b424 100644 --- a/tls/s2n_handshake_io.c +++ b/tls/s2n_handshake_io.c @@ -242,6 +242,8 @@ static message_type_t handshakes[128][16] = { #define ACTIVE_STATE( conn ) state_machine[ ACTIVE_MESSAGE( (conn) ) ] #define PREVIOUS_STATE( conn ) state_machine[ PREVIOUS_MESSAGE( (conn) ) ] +#define EXPECTED_MESSAGE_TYPE( conn ) ACTIVE_STATE( conn ).message_type + /* Used in our test cases */ message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn) { @@ -323,7 +325,12 @@ int s2n_conn_set_handshake_type(struct s2n_connection *conn) s2n_cert_auth_type client_cert_auth_type; GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); - if(client_cert_auth_type != S2N_CERT_AUTH_NONE) { + + if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED) { + /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ + conn->handshake.handshake_type |= CLIENT_AUTH; + } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE) { + /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ conn->handshake.handshake_type |= CLIENT_AUTH; } @@ -592,8 +599,8 @@ static int handshake_read_io(struct s2n_connection *conn) /* Record is a handshake message */ while (s2n_stuffer_data_available(&conn->in)) { int r; - uint8_t handshake_message_type; - GUARD((r = read_full_handshake_message(conn, &handshake_message_type))); + uint8_t actual_handshake_message_type; + GUARD((r = read_full_handshake_message(conn, &actual_handshake_message_type))); /* Do we need more data? */ if (r == 1) { @@ -606,7 +613,19 @@ static int handshake_read_io(struct s2n_connection *conn) return 0; } - S2N_ERROR_IF(handshake_message_type != ACTIVE_STATE(conn).message_type, S2N_ERR_BAD_MESSAGE); + s2n_cert_auth_type client_cert_auth_type; + GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); + + /* If we're a Client, and received a ClientCertRequest message instead of a ServerHelloDone, and ClientAuth + * is set to optional, then switch the State Machine that we're using to expect the ClientCertRequest. */ + if(conn->mode == S2N_CLIENT + && client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL + && actual_handshake_message_type == TLS_CLIENT_CERT_REQ + && EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_HELLO_DONE) { + conn->handshake.handshake_type |= CLIENT_AUTH; + } + + S2N_ERROR_IF(actual_handshake_message_type != EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE); /* Call the relevant handler */ r = ACTIVE_STATE(conn).handler[conn->mode] (conn);