/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ #include #include #include "api/s2n.h" #include "crypto/s2n_fips.h" #include "error/s2n_errno.h" #include "stuffer/s2n_stuffer.h" #include "tls/s2n_alerts.h" #include "tls/s2n_async_pkey.h" #include "tls/s2n_cipher_suites.h" #include "tls/s2n_connection.h" #include "tls/s2n_kex.h" #include "tls/s2n_post_handshake.h" #include "tls/s2n_record.h" #include "tls/s2n_resume.h" #include "tls/s2n_tls.h" #include "tls/s2n_tls13.h" #include "tls/s2n_tls13_handshake.h" #include "tls/s2n_tls13_key_schedule.h" #include "utils/s2n_bitmap.h" #include "utils/s2n_random.h" #include "utils/s2n_safety.h" #include "utils/s2n_socket.h" /* clang-format off */ struct s2n_handshake_action { uint8_t record_type; uint8_t message_type; char writer; /* 'S' or 'C' for server or client, 'B' for both */ int (*handler[2]) (struct s2n_connection * conn); }; static int s2n_always_fail_send(struct s2n_connection *conn) { /* This state should never be sending a handshake message. */ POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); } static int s2n_always_fail_recv(struct s2n_connection *conn) { /* This state should never have an incoming handshake message. */ POSIX_BAIL(S2N_ERR_HANDSHAKE_UNREACHABLE); } /* Client and Server handlers for each message type we support. * See http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7 for the list of handshake message types */ static struct s2n_handshake_action state_machine[] = { /* message_type_t = {Record type Message type Writer S2N_SERVER S2N_CLIENT } */ [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, [SERVER_NEW_SESSION_TICKET] = {TLS_HANDSHAKE, TLS_SERVER_NEW_SESSION_TICKET,'S', {s2n_server_nst_send, s2n_server_nst_recv}}, [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, [SERVER_CERT_STATUS] = {TLS_HANDSHAKE, TLS_SERVER_CERT_STATUS, 'S', {s2n_server_status_send, s2n_server_status_recv}}, [SERVER_KEY] = {TLS_HANDSHAKE, TLS_SERVER_KEY, 'S', {s2n_server_key_send, s2n_server_key_recv}}, [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_cert_req_send, s2n_cert_req_recv}}, [SERVER_HELLO_DONE] = {TLS_HANDSHAKE, TLS_SERVER_HELLO_DONE, 'S', {s2n_server_done_send, s2n_server_done_recv}}, [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, [CLIENT_KEY] = {TLS_HANDSHAKE, TLS_CLIENT_KEY, 'C', {s2n_client_key_recv, s2n_client_key_send}}, [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_client_cert_verify_recv, s2n_client_cert_verify_send}}, [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_client_ccs_recv, s2n_ccs_send}}, [CLIENT_NPN] = {TLS_HANDSHAKE, TLS_NPN, 'C', {s2n_next_protocol_recv, s2n_next_protocol_send}}, [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_client_finished_recv, s2n_client_finished_send}}, [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_server_ccs_recv}}, [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_server_finished_send, s2n_server_finished_recv}}, [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, }; /* * Client and Server handlers for TLS1.3. */ static struct s2n_handshake_action tls13_state_machine[] = { /* message_type_t = {Record type, Message type, Writer, {Server handler, client handler} } */ [CLIENT_HELLO] = {TLS_HANDSHAKE, TLS_CLIENT_HELLO, 'C', {s2n_establish_session, s2n_client_hello_send}}, [SERVER_HELLO] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_send, s2n_server_hello_recv}}, [HELLO_RETRY_MSG] = {TLS_HANDSHAKE, TLS_SERVER_HELLO, 'S', {s2n_server_hello_retry_send, s2n_server_hello_retry_recv}}, [ENCRYPTED_EXTENSIONS] = {TLS_HANDSHAKE, TLS_ENCRYPTED_EXTENSIONS, 'S', {s2n_encrypted_extensions_send, s2n_encrypted_extensions_recv}}, [SERVER_CERT_REQ] = {TLS_HANDSHAKE, TLS_CERT_REQ, 'S', {s2n_tls13_cert_req_send, s2n_tls13_cert_req_recv}}, [SERVER_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'S', {s2n_server_cert_send, s2n_server_cert_recv}}, [SERVER_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'S', {s2n_tls13_cert_verify_send, s2n_tls13_cert_verify_recv}}, [SERVER_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'S', {s2n_tls13_server_finished_send, s2n_tls13_server_finished_recv}}, [CLIENT_CERT] = {TLS_HANDSHAKE, TLS_CERTIFICATE, 'C', {s2n_client_cert_recv, s2n_client_cert_send}}, [CLIENT_CERT_VERIFY] = {TLS_HANDSHAKE, TLS_CERT_VERIFY, 'C', {s2n_tls13_cert_verify_recv, s2n_tls13_cert_verify_send}}, [CLIENT_FINISHED] = {TLS_HANDSHAKE, TLS_FINISHED, 'C', {s2n_tls13_client_finished_recv, s2n_tls13_client_finished_send}}, [END_OF_EARLY_DATA] = {TLS_HANDSHAKE, TLS_END_OF_EARLY_DATA, 'C', {s2n_end_of_early_data_recv, s2n_end_of_early_data_send}}, /* Not used by TLS1.3, except to maintain middlebox compatibility */ [CLIENT_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'C', {s2n_basic_ccs_recv, s2n_ccs_send}}, [SERVER_CHANGE_CIPHER_SPEC] = {TLS_CHANGE_CIPHER_SPEC, 0, 'S', {s2n_ccs_send, s2n_basic_ccs_recv}}, [APPLICATION_DATA] = {TLS_APPLICATION_DATA, 0, 'B', {s2n_always_fail_send, s2n_always_fail_recv}}, }; #define MESSAGE_NAME_ENTRY(msg) [msg] = #msg static const char *message_names[] = { MESSAGE_NAME_ENTRY(CLIENT_HELLO), MESSAGE_NAME_ENTRY(SERVER_HELLO), MESSAGE_NAME_ENTRY(ENCRYPTED_EXTENSIONS), MESSAGE_NAME_ENTRY(SERVER_NEW_SESSION_TICKET), MESSAGE_NAME_ENTRY(SERVER_CERT), MESSAGE_NAME_ENTRY(SERVER_CERT_STATUS), MESSAGE_NAME_ENTRY(SERVER_CERT_VERIFY), MESSAGE_NAME_ENTRY(SERVER_KEY), MESSAGE_NAME_ENTRY(SERVER_CERT_REQ), MESSAGE_NAME_ENTRY(SERVER_HELLO_DONE), MESSAGE_NAME_ENTRY(CLIENT_CERT), MESSAGE_NAME_ENTRY(CLIENT_KEY), MESSAGE_NAME_ENTRY(CLIENT_CERT_VERIFY), MESSAGE_NAME_ENTRY(CLIENT_CHANGE_CIPHER_SPEC), MESSAGE_NAME_ENTRY(CLIENT_FINISHED), MESSAGE_NAME_ENTRY(SERVER_CHANGE_CIPHER_SPEC), MESSAGE_NAME_ENTRY(SERVER_FINISHED), MESSAGE_NAME_ENTRY(HELLO_RETRY_MSG), MESSAGE_NAME_ENTRY(END_OF_EARLY_DATA), MESSAGE_NAME_ENTRY(APPLICATION_DATA), MESSAGE_NAME_ENTRY(CLIENT_NPN), }; /* We support different ordering of TLS Handshake messages, depending on what is being negotiated. There's also a dummy "INITIAL" handshake * that everything starts out as until we know better. */ static message_type_t handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { [INITIAL] = { CLIENT_HELLO, SERVER_HELLO }, [NEGOTIATED] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, APPLICATION_DATA}, [NEGOTIATED | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, APPLICATION_DATA}, [NEGOTIATED | FULL_HANDSHAKE ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS ] ={ CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_NPN ] ={ CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET ] ={ CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN ] ={ CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_HELLO_DONE, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH| WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CERT_VERIFY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | NO_CLIENT_CERT | WITH_SESSION_TICKET | WITH_NPN ] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CERT, SERVER_CERT_STATUS, SERVER_KEY, SERVER_CERT_REQ, SERVER_HELLO_DONE, CLIENT_CERT, CLIENT_KEY, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_NPN, CLIENT_FINISHED, SERVER_NEW_SESSION_TICKET, SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, APPLICATION_DATA }, }; /* * This selection of handshakes resembles the standard set, but with changes made to support tls1.3. * * The CHANGE_CIPHER_SPEC messages are included only for middlebox compatibility. * See https://tools.ietf.org/html/rfc8446#appendix-D.4 */ static message_type_t tls13_handshakes[S2N_HANDSHAKES_COUNT][S2N_MAX_HANDSHAKE_LENGTH] = { [INITIAL] = { CLIENT_HELLO, SERVER_HELLO }, [INITIAL | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, SERVER_HELLO }, [INITIAL | HELLO_RETRY_REQUEST] = { CLIENT_HELLO, HELLO_RETRY_MSG }, [INITIAL | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, HELLO_RETRY_MSG }, [NEGOTIATED] = { CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | WITH_EARLY_DATA] = { CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, END_OF_EARLY_DATA, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | MIDDLEBOX_COMPAT | WITH_EARLY_DATA] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, END_OF_EARLY_DATA, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS | WITH_EARLY_DATA] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, END_OF_EARLY_DATA, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | HELLO_RETRY_REQUEST] = { CLIENT_HELLO, HELLO_RETRY_MSG, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE] = { CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST] = { CLIENT_HELLO, HELLO_RETRY_MSG, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH] = { CLIENT_HELLO, HELLO_RETRY_MSG, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT] = { CLIENT_HELLO, HELLO_RETRY_MSG, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, HELLO_RETRY_MSG, SERVER_CHANGE_CIPHER_SPEC, CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH] = { CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_CERT_VERIFY, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT] = { CLIENT_HELLO, SERVER_HELLO, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT] = { CLIENT_HELLO, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CHANGE_CIPHER_SPEC, CLIENT_CERT, CLIENT_FINISHED, APPLICATION_DATA }, [NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS] = { CLIENT_HELLO, CLIENT_CHANGE_CIPHER_SPEC, SERVER_HELLO, SERVER_CHANGE_CIPHER_SPEC, ENCRYPTED_EXTENSIONS, SERVER_CERT_REQ, SERVER_CERT, SERVER_CERT_VERIFY, SERVER_FINISHED, CLIENT_CERT, CLIENT_FINISHED, APPLICATION_DATA }, }; /* clang-format on */ #define MAX_HANDSHAKE_TYPE_LEN 142 static char handshake_type_str[S2N_HANDSHAKES_COUNT][MAX_HANDSHAKE_TYPE_LEN] = { 0 }; static const char *tls12_handshake_type_names[] = { "NEGOTIATED|", "FULL_HANDSHAKE|", "CLIENT_AUTH|", "NO_CLIENT_CERT|", "TLS12_PERFECT_FORWARD_SECRECY|", "OCSP_STATUS|", "WITH_SESSION_TICKET|", "WITH_NPN|", }; static const char *tls13_handshake_type_names[] = { "NEGOTIATED|", "FULL_HANDSHAKE|", "CLIENT_AUTH|", "NO_CLIENT_CERT|", "HELLO_RETRY_REQUEST|", "MIDDLEBOX_COMPAT|", "WITH_EARLY_DATA|", "EARLY_CLIENT_CCS|", }; #define IS_TLS13_HANDSHAKE(conn) ((conn)->handshake.state_machine == S2N_STATE_MACHINE_TLS13) #define ACTIVE_STATE_MACHINE(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_state_machine : state_machine) #define ACTIVE_HANDSHAKES(conn) (IS_TLS13_HANDSHAKE(conn) ? tls13_handshakes : handshakes) #define ACTIVE_MESSAGE(conn) ACTIVE_HANDSHAKES(conn)[(conn)->handshake.handshake_type][(conn)->handshake.message_number] #define ACTIVE_STATE(conn) ACTIVE_STATE_MACHINE(conn)[ACTIVE_MESSAGE((conn))] #define CCS_STATE(conn) (((conn)->mode == S2N_CLIENT) ? \ ACTIVE_STATE_MACHINE(conn)[SERVER_CHANGE_CIPHER_SPEC] : \ ACTIVE_STATE_MACHINE(conn)[CLIENT_CHANGE_CIPHER_SPEC]) #define EXPECTED_RECORD_TYPE(conn) ACTIVE_STATE(conn).record_type #define EXPECTED_MESSAGE_TYPE(conn) ACTIVE_STATE(conn).message_type #define CONNECTION_WRITER(conn) (conn->mode == S2N_CLIENT ? 'C' : 'S') #define CONNECTION_IS_WRITER(conn) (ACTIVE_STATE(conn).writer == CONNECTION_WRITER(conn)) /* Only used in our test cases. */ message_type_t s2n_conn_get_current_message_type(const struct s2n_connection *conn) { return ACTIVE_MESSAGE(conn); } static int s2n_advance_message(struct s2n_connection *conn) { /* Get the mode: 'C'lient or 'S'erver */ char previous_writer = ACTIVE_STATE(conn).writer; char this_mode = CONNECTION_WRITER(conn); /* Actually advance the message number */ conn->handshake.message_number++; /* When reading and using TLS1.3, skip optional change_cipher_spec states. */ if (ACTIVE_STATE(conn).writer != this_mode && EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && IS_TLS13_HANDSHAKE(conn)) { conn->handshake.message_number++; } /* Set TCP_QUICKACK to avoid artificial delay during the handshake */ POSIX_GUARD(s2n_socket_quickack(conn)); /* If optimized io hasn't been enabled or if the caller started out with a corked socket, * we don't mess with it */ if (!conn->corked_io || s2n_socket_was_corked(conn)) { return S2N_SUCCESS; } /* Are we changing I/O directions */ if (ACTIVE_STATE(conn).writer == previous_writer || ACTIVE_STATE(conn).writer == 'A') { return S2N_SUCCESS; } /* We're the new writer */ if (ACTIVE_STATE(conn).writer == this_mode) { if (s2n_connection_is_managed_corked(conn)) { /* Set TCP_CORK/NOPUSH */ POSIX_GUARD(s2n_socket_write_cork(conn)); } return S2N_SUCCESS; } /* We're the new reader, or we reached the "B" writer stage indicating that we're at the application data stage - uncork the data */ if (s2n_connection_is_managed_corked(conn)) { POSIX_GUARD(s2n_socket_write_uncork(conn)); } return S2N_SUCCESS; } int s2n_generate_new_client_session_id(struct s2n_connection *conn) { if (conn->mode == S2N_SERVER) { struct s2n_blob session_id = { 0 }; POSIX_GUARD(s2n_blob_init(&session_id, conn->session_id, S2N_TLS_SESSION_ID_MAX_LEN)); /* Generate a new session id */ POSIX_GUARD_RESULT(s2n_get_public_random_data(&session_id)); conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; } return S2N_SUCCESS; } /* Lets the server flag whether a HelloRetryRequest is needed while processing extensions */ int s2n_set_hello_retry_required(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); POSIX_ENSURE(conn->actual_protocol_version >= S2N_TLS13, S2N_ERR_INVALID_HELLO_RETRY); POSIX_GUARD_RESULT(s2n_handshake_type_set_tls13_flag(conn, HELLO_RETRY_REQUEST)); /* HelloRetryRequests also indicate rejection of early data. * *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 *# A server which receives an "early_data" extension MUST behave in one *# of three ways: * *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 *# - Request that the client send another ClientHello by responding *# with a HelloRetryRequest. **/ if (conn->early_data_state == S2N_EARLY_DATA_REQUESTED) { POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); } return S2N_SUCCESS; } bool s2n_is_hello_retry_message(struct s2n_connection *conn) { return (conn != NULL && s2n_result_is_ok(s2n_handshake_validate(&(conn->handshake))) && ACTIVE_MESSAGE(conn) == HELLO_RETRY_MSG); } bool s2n_is_hello_retry_handshake(struct s2n_connection *conn) { return IS_HELLO_RETRY_HANDSHAKE(conn); } static S2N_RESULT s2n_conn_set_tls13_handshake_type(struct s2n_connection *conn) { RESULT_ENSURE_REF(conn); /* Most handshake type flags should be reset before we calculate the handshake type, * in order to handle changes during retries. * However, flags that have already affected the message order must be kept to avoid * rewriting the past. */ conn->handshake.handshake_type &= (HELLO_RETRY_REQUEST | MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS); /* A handshake type has been negotiated */ RESULT_GUARD(s2n_handshake_type_set_flag(conn, NEGOTIATED)); if (conn->psk_params.chosen_psk == NULL) { RESULT_GUARD(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); } if (conn->early_data_state == S2N_EARLY_DATA_ACCEPTED) { conn->handshake.handshake_type |= WITH_EARLY_DATA; } s2n_cert_auth_type client_cert_auth_type; RESULT_GUARD_POSIX(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); if (conn->mode == S2N_CLIENT && client_cert_auth_type == S2N_CERT_AUTH_REQUIRED && IS_FULL_HANDSHAKE(conn)) { /* If we're a client, and Client Auth is REQUIRED, then the Client must expect the CLIENT_CERT_REQ Message */ RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); } else if (conn->mode == S2N_SERVER && client_cert_auth_type != S2N_CERT_AUTH_NONE && IS_FULL_HANDSHAKE(conn)) { /* If we're a server, and Client Auth is REQUIRED or OPTIONAL, then the server must send the CLIENT_CERT_REQ Message*/ RESULT_GUARD(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); } if (s2n_is_middlebox_compat_enabled(conn)) { RESULT_GUARD(s2n_handshake_type_set_tls13_flag(conn, MIDDLEBOX_COMPAT)); } return S2N_RESULT_OK; } static S2N_RESULT s2n_validate_ems_status(struct s2n_connection *conn) { RESULT_ENSURE_REF(conn); s2n_extension_type_id ems_ext_id = 0; RESULT_GUARD_POSIX(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); bool ems_extension_recv = S2N_CBIT_TEST(conn->extension_requests_received, ems_ext_id); /** *= https://www.rfc-editor.org/rfc/rfc7627#section-5.3 *# If the original session used the "extended_master_secret" *# extension but the new ClientHello does not contain it, the server *# MUST abort the abbreviated handshake. **/ if (conn->ems_negotiated) { RESULT_ENSURE(ems_extension_recv, S2N_ERR_MISSING_EXTENSION); } /* Since we're discarding the resumption ticket, ignore EMS value from the ticket */ conn->ems_negotiated = ems_extension_recv; return S2N_RESULT_OK; } int s2n_conn_set_handshake_type(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); POSIX_ENSURE_REF(conn->secure); POSIX_GUARD_RESULT(s2n_conn_choose_state_machine(conn, conn->actual_protocol_version)); if (IS_TLS13_HANDSHAKE(conn)) { POSIX_GUARD_RESULT(s2n_conn_set_tls13_handshake_type(conn)); return S2N_SUCCESS; } POSIX_GUARD_RESULT(s2n_handshake_type_reset(conn)); /* A handshake type has been negotiated */ POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NEGOTIATED)); s2n_cert_auth_type client_cert_auth_type; POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); 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 */ POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, 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*/ POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); } if (conn->npn_negotiated) { POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_NPN)); } if (conn->config->use_tickets) { if (conn->session_ticket_status == S2N_DECRYPT_TICKET) { /* We reuse the session if a valid TLS12 ticket is provided. * Otherwise, we will perform a full handshake and then generate * a new session ticket. */ if (s2n_result_is_ok(s2n_resume_decrypt_session_ticket(conn, &conn->client_ticket_to_decrypt))) { return S2N_SUCCESS; } POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); /* Set up the handshake to send a session ticket since a valid ticket was not provided */ if (s2n_result_is_ok(s2n_config_is_encrypt_key_available(conn->config))) { conn->session_ticket_status = S2N_NEW_TICKET; POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); } /* If a session ticket is presented by the client, then skip lookup in Session ID server cache */ goto skip_cache_lookup; } if (conn->session_ticket_status == S2N_NEW_TICKET) { POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, WITH_SESSION_TICKET)); } } /* If a TLS session is resumed, the Server should respond in its ServerHello with the same SessionId the * Client sent in the ClientHello. */ if (conn->actual_protocol_version <= S2N_TLS12 && conn->mode == S2N_SERVER && s2n_allowed_to_cache_connection(conn)) { int r = s2n_resume_from_cache(conn); if (r == S2N_SUCCESS || (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno))) { return r; } POSIX_GUARD_RESULT(s2n_validate_ems_status(conn)); } skip_cache_lookup: if (conn->mode == S2N_CLIENT && conn->client_session_resumed == 1) { return S2N_SUCCESS; } /* If we're doing full handshake, generate a new session id. */ POSIX_GUARD(s2n_generate_new_client_session_id(conn)); /* If we get this far, it's a full handshake */ POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); bool is_ephemeral = false; POSIX_GUARD_RESULT(s2n_kex_is_ephemeral(conn->secure->cipher_suite->key_exchange_alg, &is_ephemeral)); if (is_ephemeral) { POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, TLS12_PERFECT_FORWARD_SECRECY)); } if (s2n_server_can_send_ocsp(conn) || s2n_server_sent_ocsp(conn)) { POSIX_GUARD_RESULT(s2n_handshake_type_set_tls12_flag(conn, OCSP_STATUS)); } return S2N_SUCCESS; } int s2n_conn_set_handshake_no_client_cert(struct s2n_connection *conn) { s2n_cert_auth_type client_cert_auth_type; POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_MISSING_CLIENT_CERT); POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, NO_CLIENT_CERT)); return S2N_SUCCESS; } S2N_RESULT s2n_conn_choose_state_machine(struct s2n_connection *conn, uint8_t protocol_version) { RESULT_ENSURE_REF(conn); /* This should never be called before we know what version we're on */ RESULT_ENSURE_NE(protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); if (protocol_version == S2N_TLS13) { /* State machine should not change once set */ RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS12); conn->handshake.state_machine = S2N_STATE_MACHINE_TLS13; } else { /* State machine should not change once set */ RESULT_ENSURE_NE(conn->handshake.state_machine, S2N_STATE_MACHINE_TLS13); conn->handshake.state_machine = S2N_STATE_MACHINE_TLS12; } return S2N_RESULT_OK; } const char *s2n_connection_get_last_message_name(struct s2n_connection *conn) { PTR_ENSURE_REF(conn); PTR_GUARD_RESULT(s2n_handshake_validate(&(conn->handshake))); return message_names[ACTIVE_MESSAGE(conn)]; } const char *s2n_connection_get_handshake_type_name(struct s2n_connection *conn) { PTR_ENSURE_REF(conn); PTR_PRECONDITION(s2n_handshake_validate(&(conn->handshake))); uint32_t handshake_type = conn->handshake.handshake_type; if (handshake_type == INITIAL) { return "INITIAL"; } const char **handshake_type_names = tls13_handshake_type_names; size_t handshake_type_names_len = s2n_array_len(tls13_handshake_type_names); if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { handshake_type_names = tls12_handshake_type_names; handshake_type_names_len = s2n_array_len(tls12_handshake_type_names); } /* Not all handshake strings will be created already. If the handshake string * is not null, we can just return the handshake. Otherwise we have to compute * it down below. */ if (handshake_type_str[handshake_type][0] != '\0') { return handshake_type_str[handshake_type]; } /* Compute handshake_type_str[handshake_type] by concatenating * each applicable handshake_type. * * Unit tests enforce that the elements of handshake_type_str are always * long enough to contain the longest possible valid handshake_type, but * for safety we still handle the case where we need to truncate. */ char *p = handshake_type_str[handshake_type]; size_t remaining = sizeof(handshake_type_str[0]); for (size_t i = 0; i < handshake_type_names_len; i++) { if (handshake_type & (1 << i)) { size_t bytes_to_copy = MIN(remaining, strlen(handshake_type_names[i])); PTR_CHECKED_MEMCPY(p, handshake_type_names[i], bytes_to_copy); p[bytes_to_copy] = '\0'; p += bytes_to_copy; remaining -= bytes_to_copy; } } if (p != handshake_type_str[handshake_type] && '|' == *(p - 1)) { *(p - 1) = '\0'; } return handshake_type_str[handshake_type]; } S2N_RESULT s2n_handshake_message_send(struct s2n_connection *conn, uint8_t content_type, s2n_blocked_status *blocked) { RESULT_ENSURE_REF(conn); struct s2n_stuffer *in = &conn->handshake.io; uint32_t size = s2n_stuffer_data_available(in); if (size == 0) { return S2N_RESULT_OK; } if (s2n_connection_is_quic_enabled(conn)) { RESULT_GUARD(s2n_quic_write_handshake_message(conn)); RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); return S2N_RESULT_OK; } struct iovec iov = { 0 }; iov.iov_len = size; iov.iov_base = s2n_stuffer_raw_read(in, size); RESULT_ENSURE_REF(iov.iov_base); RESULT_GUARD_POSIX(s2n_stuffer_rewind_read(in, size)); uint32_t total_bytes_written = 0; while (total_bytes_written < size) { int bytes_written = s2n_record_writev(conn, content_type, &iov, 1, total_bytes_written, size - total_bytes_written); RESULT_GUARD_POSIX(bytes_written); total_bytes_written += bytes_written; RESULT_GUARD_POSIX(s2n_stuffer_skip_read(in, bytes_written)); RESULT_GUARD_POSIX(s2n_flush(conn, blocked)); } return S2N_RESULT_OK; } /* Writing is relatively straight forward, simply write each message out as a record, * we may fragment a message across multiple records, but we never coalesce multiple * messages into single records. * Precondition: secure outbound I/O has already been flushed */ static int s2n_handshake_write_io(struct s2n_connection *conn) { uint8_t record_type = EXPECTED_RECORD_TYPE(conn); s2n_blocked_status blocked = S2N_NOT_BLOCKED; /* Populate handshake.io with header/payload for the current state, once. * Check wiped instead of s2n_stuffer_data_available to differentiate between the initial call * to s2n_handshake_write_io and a repeated call after an EWOULDBLOCK. */ if (s2n_stuffer_is_wiped(&conn->handshake.io)) { if (record_type == TLS_HANDSHAKE) { POSIX_GUARD(s2n_handshake_write_header(&conn->handshake.io, ACTIVE_STATE(conn).message_type)); } POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn)); if (record_type == TLS_HANDSHAKE) { POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); } } POSIX_GUARD_RESULT(s2n_handshake_message_send(conn, record_type, &blocked)); if (record_type == TLS_HANDSHAKE) { POSIX_GUARD_RESULT(s2n_handshake_transcript_update(conn)); } /* We're done sending the last record, reset everything */ POSIX_GUARD(s2n_stuffer_wipe(&conn->out)); POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); /* Update the secrets, if necessary */ POSIX_GUARD_RESULT(s2n_tls13_secrets_update(conn)); POSIX_GUARD_RESULT(s2n_tls13_key_schedule_update(conn)); /* Advance the state machine */ POSIX_GUARD(s2n_advance_message(conn)); return S2N_SUCCESS; } /* * Returns: * 1 - more data is needed to complete the handshake message. * 0 - we read the whole handshake message. * -1 - error processing the handshake message. */ static int s2n_read_full_handshake_message(struct s2n_connection *conn, uint8_t *message_type) { uint32_t current_handshake_data = s2n_stuffer_data_available(&conn->handshake.io); if (current_handshake_data < TLS_HANDSHAKE_HEADER_LENGTH) { /* The message may be so badly fragmented that we don't even read the full header, take * what we can and then continue to the next record read iteration. */ if (s2n_stuffer_data_available(&conn->in) < (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data)) { POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); return 1; } /* Get the remainder of the header */ POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, (TLS_HANDSHAKE_HEADER_LENGTH - current_handshake_data))); } uint32_t handshake_message_length = 0; POSIX_GUARD_RESULT(s2n_handshake_parse_header(&conn->handshake.io, message_type, &handshake_message_length)); S2N_ERROR_IF(handshake_message_length > S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); uint32_t bytes_to_take = handshake_message_length - s2n_stuffer_data_available(&conn->handshake.io); bytes_to_take = MIN(bytes_to_take, s2n_stuffer_data_available(&conn->in)); /* If the record is handshake data, add it to the handshake buffer */ POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, bytes_to_take)); /* If we have the whole handshake message, then success */ if (s2n_stuffer_data_available(&conn->handshake.io) == handshake_message_length) { return 0; } /* We don't have the whole message, so we'll need to go again */ POSIX_GUARD(s2n_stuffer_reread(&conn->handshake.io)); return 1; } static int s2n_handshake_handle_sslv2(struct s2n_connection *conn) { S2N_ERROR_IF(ACTIVE_MESSAGE(conn) != CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); /* Add the message to our handshake hashes */ struct s2n_blob hashed = { 0 }; POSIX_GUARD(s2n_blob_init(&hashed, conn->header_in.blob.data + 2, 3)); POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); hashed.data = conn->in.blob.data; hashed.size = s2n_stuffer_data_available(&conn->in); POSIX_GUARD(s2n_conn_update_handshake_hashes(conn, &hashed)); /* Handle an SSLv2 client hello */ POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); /* Set the client hello version */ conn->client_hello_version = S2N_SSLv2; /* Execute the state machine handler */ int r = ACTIVE_STATE(conn).handler[conn->mode](conn); POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); /* We're done with the record, wipe it */ POSIX_GUARD_RESULT(s2n_record_wipe(conn)); WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); /* Advance the state machine */ POSIX_GUARD(s2n_advance_message(conn)); return S2N_SUCCESS; } static int s2n_try_delete_session_cache(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); if (s2n_allowed_to_cache_connection(conn) > 0) { conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len); } return S2N_SUCCESS; } static S2N_RESULT s2n_finish_read(struct s2n_connection *conn) { RESULT_ENSURE_REF(conn); RESULT_GUARD(s2n_handshake_transcript_update(conn)); RESULT_GUARD_POSIX(s2n_stuffer_wipe(&conn->handshake.io)); RESULT_GUARD(s2n_tls13_secrets_update(conn)); RESULT_GUARD(s2n_tls13_key_schedule_update(conn)); RESULT_GUARD_POSIX(s2n_advance_message(conn)); return S2N_RESULT_OK; } static S2N_RESULT s2n_handshake_app_data_recv(struct s2n_connection *conn) { if (conn->early_data_expected) { RESULT_GUARD(s2n_early_data_validate_recv(conn)); RESULT_BAIL(S2N_ERR_EARLY_DATA_BLOCKED); } if (conn->handshake.renegotiation) { RESULT_GUARD(s2n_renegotiate_validate(conn)); /* During renegotiation, Application Data may only be received until * the server acknowledges the new handshake with a ServerHello. */ RESULT_ENSURE(ACTIVE_MESSAGE(conn) == SERVER_HELLO, S2N_ERR_BAD_MESSAGE); RESULT_BAIL(S2N_ERR_APP_DATA_BLOCKED); } RESULT_BAIL(S2N_ERR_BAD_MESSAGE); } /* Reading is a little more complicated than writing as the TLS RFCs allow content * types to be interleaved at the record layer. We may get an alert message * during the handshake phase, or messages of types that we don't support (e.g. * HEARTBEAT messages), or during renegotiations we may even get application * data messages that need to be handled by the application. The latter is punted * for now (s2n does not support renegotiations). */ static int s2n_handshake_read_io(struct s2n_connection *conn) { uint8_t record_type = 0; uint8_t message_type = 0; int isSSLv2 = 0; /* Fill conn->in stuffer necessary for the handshake. * If using TCP, read a record. If using QUIC, read a message. */ if (s2n_connection_is_quic_enabled(conn)) { record_type = TLS_HANDSHAKE; POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); } else { int r = s2n_read_full_record(conn, &record_type, &isSSLv2); /** *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10 *# If the client attempts a 0-RTT handshake but the server *# rejects it, the server will generally not have the 0-RTT record *# protection keys and must instead use trial decryption (either with *# the 1-RTT handshake keys or by looking for a cleartext ClientHello in *# the case of a HelloRetryRequest) to find the first non-0-RTT message. *# *# If the server chooses to accept the "early_data" extension, then it *# MUST comply with the same error-handling requirements specified for *# all records when processing early data records. Specifically, if the *# server fails to decrypt a 0-RTT record following an accepted *# "early_data" extension, it MUST terminate the connection with a *# "bad_record_mac" alert as per Section 5.2. */ if ((r < S2N_SUCCESS) && (s2n_errno == S2N_ERR_EARLY_DATA_TRIAL_DECRYPT)) { POSIX_GUARD(s2n_stuffer_reread(&conn->in)); POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, s2n_stuffer_data_available(&conn->in))); POSIX_GUARD_RESULT(s2n_record_wipe(conn)); return S2N_SUCCESS; } POSIX_GUARD(r); } if (isSSLv2) { S2N_ERROR_IF(record_type != SSLv2_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE); POSIX_GUARD(s2n_handshake_handle_sslv2(conn)); } /* Now we have a record, but it could be a partial fragment of a message, or it might * contain several messages. */ if (record_type == TLS_APPLICATION_DATA) { POSIX_GUARD_RESULT(s2n_handshake_app_data_recv(conn)); } else if (record_type == TLS_CHANGE_CIPHER_SPEC) { /* TLS1.3 can receive unexpected CCS messages at any point in the handshake * due to a peer operating in middlebox compatibility mode. * However, when operating in QUIC mode, S2N should not accept ANY CCS messages, * including these unexpected ones.*/ if (!IS_TLS13_HANDSHAKE(conn) || s2n_connection_is_quic_enabled(conn)) { POSIX_ENSURE(EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC, S2N_ERR_BAD_MESSAGE); POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); } S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) != 1, S2N_ERR_BAD_MESSAGE); POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in))); POSIX_GUARD(CCS_STATE(conn).handler[conn->mode](conn)); POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); /* We're done with the record, wipe it */ POSIX_GUARD_RESULT(s2n_record_wipe(conn)); /* Advance the state machine if this was an expected message */ if (EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && !CONNECTION_IS_WRITER(conn)) { POSIX_GUARD(s2n_advance_message(conn)); } return S2N_SUCCESS; } else if (record_type != TLS_HANDSHAKE) { if (record_type == TLS_ALERT) { POSIX_GUARD(s2n_process_alert_fragment(conn)); } /* Ignore record types that we don't support */ /* We're done with the record, wipe it */ POSIX_GUARD_RESULT(s2n_record_wipe(conn)); return S2N_SUCCESS; } /* Record is a handshake message */ S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE); while (s2n_stuffer_data_available(&conn->in)) { /* We're done with negotiating but we have trailing data in this record. Bail on the handshake. */ S2N_ERROR_IF(EXPECTED_RECORD_TYPE(conn) == TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE); int r = 0; POSIX_GUARD((r = s2n_read_full_handshake_message(conn, &message_type))); /* Do we need more data? This happens for message fragmentation */ if (r == 1) { /* Break out of this inner loop, but since we're not changing the state, the * outer loop in s2n_handshake_io() will read another record. */ POSIX_GUARD_RESULT(s2n_record_wipe(conn)); return S2N_SUCCESS; } if (conn->mode == S2N_CLIENT) { s2n_cert_auth_type client_cert_auth_type = { 0 }; POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type)); /* If client auth is optional, we initially assume it will not be requested. * If we received a request, switch to a client auth handshake. */ if (client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) { POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST); POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE); POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH)); } /* According to rfc6066 section 8, the server may choose not to send a "CertificateStatus" * message even if it has sent a "status_request" extension in the ServerHello message. */ if (EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS && message_type != TLS_SERVER_CERT_STATUS) { POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS)); } } /* *= https://www.rfc-editor.org/rfc/rfc5246#section-7.4 *# The one message that is not bound by these ordering rules *# is the HelloRequest message, which can be sent at any time, but which *# SHOULD be ignored by the client if it arrives in the middle of a handshake. */ if (message_type == TLS_HELLO_REQUEST) { POSIX_GUARD_RESULT(s2n_client_hello_request_validate(conn)); POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); continue; } /* Check for missing Certificate Requests to surface a more specific error */ if (EXPECTED_MESSAGE_TYPE(conn) == TLS_CERT_REQ) { POSIX_ENSURE(message_type == TLS_CERT_REQ, S2N_ERR_MISSING_CERT_REQUEST); } POSIX_ENSURE(record_type == EXPECTED_RECORD_TYPE(conn), S2N_ERR_BAD_MESSAGE); POSIX_ENSURE(message_type == EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE); POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE); /* Call the relevant handler */ WITH_ERROR_BLINDING(conn, POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn))); /* Advance the state machine */ POSIX_GUARD_RESULT(s2n_finish_read(conn)); } /* We're done with the record, wipe it */ POSIX_GUARD_RESULT(s2n_record_wipe(conn)); return S2N_SUCCESS; } static int s2n_handle_retry_state(struct s2n_connection *conn) { /* If we were blocked reading or writing a record, then the handler is waiting on * external data. The handler will know how to continue, so we should call the * handler right away. We aren't going to read more handshake data yet or proceed * to the next handler because the current message has not finished processing. */ s2n_errno = S2N_ERR_OK; const int r = ACTIVE_STATE(conn).handler[conn->mode](conn); if (r < S2N_SUCCESS && S2N_ERROR_IS_BLOCKING(s2n_errno)) { /* If the handler is still waiting for data, return control to the caller. */ S2N_ERROR_PRESERVE_ERRNO(); } /* Resume the handshake */ conn->handshake.paused = false; if (!CONNECTION_IS_WRITER(conn)) { /* We're done parsing the record, reset everything */ POSIX_GUARD_RESULT(s2n_record_wipe(conn)); } if (CONNECTION_IS_WRITER(conn)) { POSIX_GUARD(r); /* If we're the writer and handler just finished, update the record header if * needed and let the s2n_handshake_write_io write the data to the socket */ if (EXPECTED_RECORD_TYPE(conn) == TLS_HANDSHAKE) { POSIX_GUARD(s2n_handshake_finish_header(&conn->handshake.io)); } } else { if (r < S2N_SUCCESS && conn->session_id_len) { s2n_try_delete_session_cache(conn); } WITH_ERROR_BLINDING(conn, POSIX_GUARD(r)); /* The read handler processed the record successfully, we are done with this * record. Advance the state machine. */ POSIX_GUARD_RESULT(s2n_finish_read(conn)); } return S2N_SUCCESS; } bool s2n_handshake_is_complete(struct s2n_connection *conn) { /* A deserialized connection implies that the handshake is complete because * connections cannot be serialized before completing the handshake. */ return conn && (ACTIVE_STATE(conn).writer == 'B' || conn->deserialized_conn); } int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked) { POSIX_ENSURE_REF(conn); POSIX_ENSURE_REF(blocked); while (!s2n_handshake_is_complete(conn) && ACTIVE_MESSAGE(conn) != conn->handshake.end_of_messages) { errno = 0; s2n_errno = S2N_ERR_OK; /* Flush any pending I/O or alert messages */ POSIX_GUARD(s2n_flush(conn, blocked)); POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX), S2N_ERR_CLOSED); /* If the handshake was paused, retry the current message */ if (conn->handshake.paused) { *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; POSIX_GUARD(s2n_handle_retry_state(conn)); } if (CONNECTION_IS_WRITER(conn)) { *blocked = S2N_BLOCKED_ON_WRITE; const int write_result = s2n_handshake_write_io(conn); if (write_result < S2N_SUCCESS) { if (!S2N_ERROR_IS_BLOCKING(s2n_errno)) { /* Non-retryable write error. The peer might have sent an alert. Try and read it. */ const int write_errno = errno; const int write_s2n_errno = s2n_errno; struct s2n_debug_info write_s2n_debug_info = _s2n_debug_info; if (s2n_handshake_read_io(conn) < 0 && s2n_errno == S2N_ERR_ALERT) { /* s2n_handshake_read_io has set s2n_errno */ S2N_ERROR_PRESERVE_ERRNO(); } else { /* Let the write error take precedence if we didn't read an alert. */ errno = write_errno; s2n_errno = write_s2n_errno; _s2n_debug_info = write_s2n_debug_info; S2N_ERROR_PRESERVE_ERRNO(); } } if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; conn->handshake.paused = true; } else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) { *blocked = S2N_BLOCKED_ON_EARLY_DATA; } S2N_ERROR_PRESERVE_ERRNO(); } } else { *blocked = S2N_BLOCKED_ON_READ; const int read_result = s2n_handshake_read_io(conn); if (read_result < S2N_SUCCESS) { /* One blocking condition is waiting on the session resumption cache. */ /* So we don't want to delete anything if we are blocked. */ if (!S2N_ERROR_IS_BLOCKING(s2n_errno) && conn->session_id_len) { s2n_try_delete_session_cache(conn); } if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { *blocked = S2N_BLOCKED_ON_APPLICATION_INPUT; conn->handshake.paused = true; } else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) { *blocked = S2N_BLOCKED_ON_EARLY_DATA; } S2N_ERROR_PRESERVE_ERRNO(); } } if (ACTIVE_STATE(conn).writer == 'B') { /* Clean up handshake secrets */ POSIX_GUARD_RESULT(s2n_tls13_secrets_clean(conn)); /* Send any pending post-handshake messages */ POSIX_GUARD(s2n_post_handshake_send(conn, blocked)); /* If the handshake has just ended, free up memory */ POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0)); } } *blocked = S2N_NOT_BLOCKED; return S2N_SUCCESS; } int s2n_negotiate(struct s2n_connection *conn, s2n_blocked_status *blocked) { POSIX_ENSURE_REF(conn); POSIX_ENSURE(!conn->negotiate_in_use, S2N_ERR_REENTRANCY); conn->negotiate_in_use = true; int result = s2n_negotiate_impl(conn, blocked); /* finish up sending and receiving */ POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn)); POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn)); conn->negotiate_in_use = false; return result; }